Count distinct values on 2 colums when using GROUP BY - mysql

Considering the following query:
SELECT t.recording_id, m.release_id
FROM track t
JOIN medium m ON t.medium_id = m.medium_id
i get a result set similar to this one
recording id release id
----------------------------------
1 25
1 25
1 37
1 76
1 300
1 336
2 37
... ...
i need to output the following
recording id count
---------------------------------------------------
1 5
2 1
In other words, i need to group by the recording_id but not count the release_id duplicates for that recording_id
After researching this board i've tried the following, with no success :
SELECT t.recording_id, count(t.recording_id)
FROM track t
JOIN medium m ON t.medium_id = m.medium_id
group by t.recording_id, m.release_id
but, im getting
recording id release id
--------------------------
1 2
1 1
1 1
1 1
1 1
2 1
What's wrong?

Try this, you can use distinct in your count function to return distinct release ids for a recording_id
SELECT t.recording_id, count(distinct m.release_id) cnt
FROM track t
JOIN medium m ON t.medium_id = m.medium_id
group by t.recording_id

Related

Trying to get latest status for related shipment but the results I receive are incorrect

I am currently working on a project while trying to learn MySQL and I would like to join three tables and get the latest status for each related shipment. Here are the tables I'm working with (with example data):
shipments
id
consignee
tracking_number
shipper
weight
import_no
1
JOHN BROWN
TBA99900000121
AMAZON
1
101
2
HELEN SMITH
TBA99900000190
AMAZON
1
102
3
JACK BLACK
TBA99900000123
AMAZON
1
103
4
JOE BROWM
TBA99900000812
AMAZON
1
104
5
JULIA KERR
TBA99900000904
AMAZON
1
105
statuses
id
name
slug
1
At Warehouse
at_warehouse
2
Ready For Pickup
ready_for_pickup
3
Delivered
delivered
shipment_status (pivot table)
id
shipment_id
status_id
1
1
1
2
2
1
3
3
1
4
4
1
5
5
1
6
1
2
7
2
2
8
3
2
9
4
2
10
5
2
all tables do have created_at and updated_at timestamp columns
Example of the results I'm trying to achieve
slug
shipment_id
status_id
ready_for_pickup
1
2
ready_for_pickup
2
2
ready_for_pickup
3
2
ready_for_pickup
4
2
ready_for_pickup
5
2
Here's the query I wrote to try to achieve what I'm looking for based on examples and research I did during the past couple of days. I find that sometimes there is sometimes a mismatch with the latest status that relates to the shipment
SELECT
statuses.slug AS slug,
MAX(shipments.id) AS shipment_id,
statuses.id AS status_id,
FROM
`shipments`
INNER JOIN `shipment_status` ON `shipment_status`.`shipment_id` = `shipments`.`id`
INNER JOIN `statuses` ON `shipment_status`.`status_id` = `statuses`.`id`
GROUP BY
`shipment_id`
Because we need to reference other fields from the same record that evaluates from the MAX aggregation, you need to do it in two steps, there are other ways, but I find this syntax simpler:
SELECT
shipments.id AS id,
statuses.slug AS slug,
statuses.id AS status_id,
shipment_status.shipment_id as shipment_id
FROM
`shipments`
INNER JOIN `shipment_status` ON `shipment_status`.`shipment_id` = `shipments`.`id`
INNER JOIN `statuses` ON `shipment_status`.`status_id` = `statuses`.`id`
WHERE
shipment_status.id = (
SELECT MAX(shipment_status.id)
FROM `shipment_status`
WHERE shipment_status.shipment_id = shipments.id
)
try it out!
This query makes the assumption that the id field is an identity column, so the MAX(shipment_status.id) represents only the most recent status for the given shipment_id
You can use window functions:
SELECT s.id, st.slug, st.id
FROM shipments s JOIN
(SELECT ss.*,
ROW_NUMBER() OVER (PARTITION BY shipment_id ORDER BY ss.id DESC) as seqnum
FROM shipment_status ss
) ss
ON ss.shipment_id = s.id JOIN
statuses st
ON ss.status_id` = st.id
WHERE ss.seqnum = 1;
Also note the use of table aliases so the query is easier to write and to read.

Mysql select statement contains where clause so unsuitable for insert into

I'm very inexperienced. I've prepared a select statement which gives the information I need to populate a matches table. However it is not suitable because it contains a where clause. Is there a different way to use it, or how can I change it so that it is suitable for INSERT INTO.
The tables are as follows:-
match_order
match_order_id||match_descrip||first_player||second_player
1 1v2 1 2
2 1v3 1 3
3 2v3 2 3
4 1v4 1 4
5 2v4 2 4
6 3v4 3 4
entries
entry_id||round_id||league_id||box_id||box_position
1 1 1 1 1
2 1 1 1 2
3 1 1 1 3
4 1 2 1 4
5 1 2 1 2
6 1 2 1 1
7 1 2 1 1
matches
match_id||round_id||league_id||box_id||match_order_id||player1||player2
I need to insert new rows every month for a new round of matches. League size, box size & positions change each month.
This is the statement which gives the correct rows.
SELECT e.round_id, e.league_id, e.box_id, mo.match_order_id, e.entry_id as player1, e1.entry_id as player2
FROM match_order mo
LEFT JOIN entries e ON mo.first_player = e.box_position
LEFT JOIN entries e1 ON mo.second_player = e1.box_position
WHERE e.round_id = e1.round_id AND e.league_id = e1.league_id AND e.box_id = e1.box_id
ORDER BY round_id, league_id, box_id, match_order_id
Any help & advise would be greatly appreciated.
Thank you
Assuming match_id is an auto-increment column, you have the data for the other columns. You can just add the INSERT statement before your SELECT.
INSERT INTO matches(round_id, leage_id, box_id, match_order_id, player1, player2)
SELECT e.round_id, e.league_id, e.box_id, mo.match_order_id, e.entry_id as player1, e1.entry_id as player2
FROM match_order mo
LEFT JOIN entries e ON mo.first_player = e.box_position
LEFT JOIN entries e1 ON mo.second_player = e1.box_position
WHERE e.round_id = e1.round_id AND e.league_id = e1.league_id AND e.box_id = e1.box_id

MySQL multiple count based on two column with multiple GROUP BY in single table

I have a query like below, it is working fine but not optimized, since it takes 1.5 sec to run. How to make this to an optimized result?
select h.keyword_id,
( select count(DISTINCT(user_id)) from history where category_id = 6
and h.keyword_id=keyword_id group by keyword_id ) as cat_6,
( select count(DISTINCT(user_id)) from history where category_id = 7
and h.keyword_id = keyword_id group by keyword_id ) as cat_7
from
history h group by h.keyword_id
History table
his_id keyword_id category_id user_id
1 1 6 12
2 1 6 12
3 1 7 12
4 1 7 12
5 2 6 13
6 2 6 13
7 2 7 13
8 3 6 13
Result:
keyword_id cat_6 cat_7
1 2 2 (unique users)
2 2 1
3 1 0
You can rewrite your query like this:
select h.keyword_id,
count(distinct if(category_id = 6, user_id, null)) as cat_6,
count(distinct if(category_id = 7, user_id, null)) as cat_7
from
history h
group by h.keyword_id
Your desired result based on the sample data is by the way false. In each keyword_id there's always just one distinct user_id.
you can see the query in action in an sqlfiddle here
For more optimization, you'd have to post the result of show create table history and the output of explain <your_query>;

mysql extra count field for each row

I have a query here, anyone can help me to count the total duplicated fields?
SELECT *
FROM item
INNER JOIN itemgroup on item.itemgroupid = itemgroup.itemgroupid
INNER JOIN status on status.statusid = item.status
INNER JOIN owner on owner.ownerid = item.owner
INNER JOIN
(
SELECT code //, (SELECT count(*) FROM item WHERE ....) as 'total_duplicateds'
FROM item
GROUP BY code
HAVING count(code) > 1
) dup ON item.code = dup.code
Total items: 500
Total items with duplicated codes: 149
Now I get a total of 149 fields returned, how can I add this as a new field to each row?
After the slash is how I learnt to do it but this is a little higher level for me..
Can someone help me out?
To be even more specific
What I'd like to get returned is like:
itemid | code| itemname | itemgroup | owner | total_duplicateds
1 1000 X 1 1 3
2 1000 X 2 2 3
3 1001 A 1 1 3
4 1000 B 3 1 3
5 1002 U 2 1 3
Add COUNT aggregation and GROUP BY all columns that are interesting you.

MYSQL select query using count (*)

i have a problem concerning a select query in MYSQL
i have two different tables and i want to obtain a certain result
i used COUNT method which gave me only the results (>=1)
But in reality , i want to use all counts with zero included how to do it?
My query is:
SELECT
first.subscriber_id,
second.tag_id,
COUNT(*)
FROM
content_hits first
JOIN content_tag second ON first.content_id=second.content_id
GROUP BY
second.Tag_id,first.Subscriber_id<br>
First table:Content_hits
CONTENT_ID SUBSCRIBER_ID
30 1
10 10
34 4
32 2
40 3
28 3
30 6
31 8
12 3
Second table:Content_tag
CONTENT_ID TAG_ID
1 1
2 1
3 1
4 1
5 1
6 1
7 1
8 1
9 1
10 1
11 2
12 2
13 2
14 2
Result but incomplete For example:Subsrciber6 for tag_id=1 should have a count(*)=0
subscriber_id tag_id COUNT(*)
1 1 4
2 1 7
3 1 2
4 1 1
5 1 3
7 1 2
8 1 1
9 1 1
10 1 3
1 2 2
2 2 3
3 2 2
Now that you have further elaborated on what you actually want to achieve, it can be seen that the problem is much more complex. You actually want all combinations of subscriber_id and tag_id, and then count the number of actual entries in the joined table product. whew. So here goes the SQL:
SELECT combinations.tag_id,
combinations.subscriber_id,
-- correlated subquery to count the actual hits by tag/subscriber when joining
-- the two tables using content_id
(SELECT count(*)
FROM content_hits AS h
JOIN content_tag AS t ON h.content_id = t.content_id
WHERE h.subscriber_id = combinations.subscriber_id
AND t.tag_id = combinations.tag_id) as cnt
-- Create all combinations of tag/subscribers first, before counting anything
-- This will be necessary to have "zero-counts" for any combination of
-- tag/subscriber
FROM (
SELECT DISTINCT tag_id, subscriber_id
FROM content_tag
CROSS JOIN content_hits
) AS combinations
Not sure, but is this what you want?
SELECT first.subscriber_id, second.tag_id, COUNT(*) AS c
FROM content_hits first JOIN content_tag second ON first.content_id=second.content_id
GROUP BY second.Tag_id,first.Subscriber_id HAVING c = 0