getting the count of distinct duplicate ids in mysql - mysql

this is the query
select count(*),
ss.pname,
ttu.user_id,
ttl.location_name ,
group_concat(em.customer_id),
count(em.customer_id)
from seseal as ss,
track_and_trace_user as ttu,
track_and_trace_location as ttl,
eseal_mapping as em
where ss.real_id=em.e_id
and em.user_id=ttu.user_id
and ttu.location_id=ttl.location_id
group by ss.pname, ttu.user_id, ttl.location_name
having count(em.customer_id)>1 ;
and following is the results:
+----------+----------------+---------+---------------+------------------------------+-----------------------+
| count(*) | pname | user_id | location_name | group_concat(em.customer_id) | count(em.customer_id) |
+----------+----------------+---------+---------------+------------------------------+-----------------------+
| 6 | Nokia N91 | 1 | Malad | 60,51,60,51,58,58 | 6 |
| 2 | SUPERIA 1000gm | 4 | Raichur | 51,46 | 2 |
| 5 | SUPERIA 1000gm | 5 | west bengal | 51,46,51,51,46 | 5 |
| 2 | SUPERIA 500gm | 4 | Raichur | 59,59 | 2 |
| 3 | SUPERIA 500gm | 5 | west bengal | 59,46,59 | 3 |
+----------+----------------+---------+---------------+------------------------------+-----------------------+
Now the problem is, as you can see in result set, the second last column in some rows the customer_ids are duplicate and in some rows are unique. And the last column is giving the count of it.
Now what i want is to pick the 3rd row, there are two customer ids namely 51 and 46 and these are duplicate in that row, so my last column for this row should contain 2.
Similarly for last row my last column should contain 1 as there is only one customer id which is duplicated i.e. 59.
So if you understand the exact problem then the 2nd row should not be part of this result set as it doesn't contain any customer ids that are duplicate.

How about:
group_concat(distinct em.customer_id)
and
count(distinct em.customer_id)

Related

How can I merge two strings of comma-separated numbers in MySQL?

For example, there are three rooms.
1|gold_room|1,2,3
2|silver_room|1,2,3
3|brown_room|2,4,6
4|brown_room|3
5|gold_room|4,5,6
Then, I'd like to get
gold_room|1,2,3,4,5,6
brown_room|2,3,4,6
silver_room|1,2,3
How can I achieve this?
I've tried: select * from room group by name; And it only prints the first row. And I know CONCAT() can combine two string values.
Please use below query,
select col2, GROUP_CONCAT(col3) from data group by col2;
Below is the Test case,
https://dbfiddle.uk/?rdbms=mysql_8.0&fiddle=ab35e8d66ffe3ac6436c17faf97ee9af
I'm not making an assumption that the lists don't have elements in common on separate rows.
First create a table of integers.
mysql> create table n (n int primary key);
mysql> insert into n values (1),(2),(3),(4),(5),(6);
You can join this to your rooms table using the FIND_IN_SET() function. Note that this cannot be optimized. It will execute N full table scans. But it does create an interim set of rows.
mysql> select * from n inner join rooms on find_in_set(n.n, rooms.csv) order by rooms.room, n.n;
+---+----+-------------+-------+
| n | id | room | csv |
+---+----+-------------+-------+
| 2 | 3 | brown_room | 2,4,6 |
| 3 | 4 | brown_room | 3 |
| 4 | 3 | brown_room | 2,4,6 |
| 6 | 3 | brown_room | 2,4,6 |
| 1 | 1 | gold_room | 1,2,3 |
| 2 | 1 | gold_room | 1,2,3 |
| 3 | 1 | gold_room | 1,2,3 |
| 4 | 5 | gold_room | 4,5,6 |
| 5 | 5 | gold_room | 4,5,6 |
| 6 | 5 | gold_room | 4,5,6 |
| 1 | 2 | silver_room | 1,2,3 |
| 2 | 2 | silver_room | 1,2,3 |
| 3 | 2 | silver_room | 1,2,3 |
+---+----+-------------+-------+
Use GROUP BY to reduce these rows to one row per room. Use GROUP_CONCAT() to put the integers together into a comma-separated list.
mysql> select room, group_concat(distinct n.n order by n.n) as csv
from n inner join rooms on find_in_set(n.n, rooms.csv) group by rooms.room
+-------------+-------------+
| room | csv |
+-------------+-------------+
| brown_room | 2,3,4,6 |
| gold_room | 1,2,3,4,5,6 |
| silver_room | 1,2,3 |
+-------------+-------------+
I think this is a lot of work, and impossible to optimize. I don't recommend it.
The problem is that you are storing comma-separated lists of numbers, and then you want to query it as if the elements in the list are discrete values. This is a problem for SQL.
It would be much better if you did not store your numbers in a comma-separated list. Store multiple rows per room, with one number per row. You can run a wider variety of queries if you do this, and it will be more flexible.
For example, the query you asked about, to produce a result with numbers in a comma-separated list is more simple, and you don't need the extra n table:
select room, group_concat(n order by n) as csv from rooms group by room
See also my answer to Is storing a delimited list in a database column really that bad?

How do I select records in MySQL with multiple columns matching map of values?

I have the following 3-column table:
+----+---------+------------+
| ID | First | Last |
+----+---------+------------+
| 1 | Maurice | Richard |
| 2 | Yvan | Cournoyer |
| 3 | Carey | Price |
| 4 | Guy | Lafleur |
| 5 | Steve | Shutt |
+----+---------+------------+
If I want to look for everyone in (Maurice,Guy) I can do select * from table where first in (Maurice,Guy).
If I want to find just Maurice Richard, I can do select * from table where first = "Maurice" and last = "Richard".
How do I do a map, an array of multiples?
[
[Maurice, Richard]
[Guy,Lafleur]
[Yvan,Cournoyer]
]
If I have an arbitrary number of entries, I cannot construct a long complex where (first = "Maurice" and last = "Richard") or (first = "Guy" and last = "Lafleur") or .....
How do I do the moral equivalent of where (first, last) in ((Guy,Lafleur),(Maurice,Richard)) ?
You can do it just like you describe it:
SELECT *
FROM mytable
WHERE (first, last) IN (('Guy','Lafleur'),('Maurice','Richard'))
Demo here

SQL select default value when there is no such value

I have following tables in DB.
ACCOUNT TABLE
User_id| first_name | last_name | age |
_______|_____________|____________|_________|
1 | LeBron | James | 28 |
2 | Kobe | Bryent | 29 |
3 | Kevin | Durant | 30 |
4 | Jim | Jones | 31 |
5 | Paul | Pierce | 32 |
6 | Jeremy | Lin | 33 |
USER_BOOKMARK TABLE
User_id| Bookmarked_user_id
_______|____________________
1 | 2
1 | 3
1 | 4
2 | 1
2 | 4
3 | 1
5 | 6
I want to select user's information from ACCOUNT table and also whether that person is in my Bookmark list
ex) Lebron James wants to know Jeremy Lin's information and whether Jeremy is in he's bookmark lists.
Desired results =>
User_id| first_name | last_name | age | isBookmarked |
_______|_____________|____________|_________|______________|
6 | Jeremy | Lin | 33 | 0 | =>0 means no.
*It must return only one row.
*If user is on my bookmark list, value of isBookmarked is my user_id.
What I tried =>
SELECT ACCOUNT.user_id, ACCOUNT.firstname, ACCOUNT.lastname, coalesce(User_Bookmark.user_id, 0) as isBookmarked
FROM Account LEFT OUTER JOIN User_Bookmark ON Account.user_id = User_Bookmark.Bookmarked_user_id
WHERE Account.user_id=6 AND User_Bookmark.user_id=1
But this query returns zero rows... since I'm not an expert on sql, I assume that I'm missing something. Can anyone help me?
The User_Bookmark.user_id = 1 test is filtering out the non-matching rows, because that column will be NULL when there's no match. When doing a LEFT JOIN, you have to put conditions on the second table into the ON clause rather than WHEN.
SELECT ACCOUNT.user_id, ACCOUNT.firstname, ACCOUNT.lastname, coalesce(User_Bookmark.user_id, 0) as isBookmarked
FROM Account
LEFT OUTER JOIN User_Bookmark
ON Account.user_id = User_Bookmark.Bookmarked_user_id AND User_Bookmark.user_id=1
WHERE Account.user_id=6

Multiple Columns with Duplicate Values

Currently, I have a MySQL table that has a few columns in it. The following is a sample of the table with data:
+----------+---------------------+
| hospt_id | file_id | clinic_id |
+----------+---------------------+
| 212837 | 9 | NULL |
| 123837 | 14 | 2134319 |
| 345567 | 9 | NULL |
| 123456 | 14 | 2134320 |
| 123456 | 14 | 2134320 |
+----------+---------------------+`
What I am trying to do is to write a query that will return all records where the three columns are repeate.
For example, the last two rows are repeated. So I would want to get those returned. I know how to do duplicate searches for a single column, but not sure how to do for multiple columns.
You just need to group by all three records to get a count of how many rows are in each group. You can then filter it down to those that have more than one matching row in the having clause.
select hospt_id, file_id, clinic_id, count(*)
from <table>
group by hospt_id, file_id, clinic_id
having count(*) > 1;
Here's a demo: http://sqlfiddle.com/#!9/91bf9/2

SQL 'COUNT' not returning what I expect, and somehow limiting results to one row

Some background: an 'image' is part of one 'photoshoot', and may be a part of zero or many 'galleries'. My tables:
'shoots' table:
+----+--------------+
| id | name |
+----+--------------+
| 1 | Test shoot |
| 2 | Another test |
| 3 | Final test |
+----+--------------+
'images' table:
+----+-------------------+------------------+
| id | original_filename | storage_location |
+----+-------------------+------------------+
| 1 | test.jpg | store/test.jpg |
| 2 | test.jpg | store/test.jpg |
| 3 | test.jpg | store/test.jpg |
+----+-------------------+------------------+
'shoot_images' table:
+----------+----------+
| shoot_id | image_id |
+----------+----------+
| 1 | 1 |
| 1 | 2 |
| 3 | 3 |
+----------+----------+
'gallery_images' table:
+------------+----------+
| gallery_id | image_id |
+------------+----------+
| 1 | 1 |
| 1 | 2 |
| 2 | 3 |
| 3 | 1 |
| 4 | 1 |
+------------+----------+
What I'd like to get back, so I can say 'For this photoshoot, there are X images in total, and these images are featured in Y galleries:
+----+--------------+-------------+---------------+
| id | name | image_count | gallery_count |
+----+--------------+-------------+---------------+
| 3 | Final test | 1 | 1 |
| 2 | Another test | 0 | 0 |
| 1 | Test shoot | 2 | 4 |
+----+--------------+-------------+---------------+
I'm currently trying the SQL below, which appears to work correctly but only ever returns one row. I can't work out why this is happening. Curiously, the below also returns a row even when 'shoots' is empty.
SELECT shoots.id,
shoots.name,
COUNT(DISTINCT shoot_images.image_id) AS image_count,
COUNT(DISTINCT gallery_images.gallery_id) AS gallery_count
FROM shoots
LEFT JOIN shoot_images ON shoots.id=shoot_images.shoot_id
LEFT JOIN gallery_images ON shoot_images.image_id=gallery_images.image_id
ORDER BY shoots.id DESC
Thanks for taking the time to look at this :)
You are missing the GROUP BY clause:
SELECT
shoots.id,
shoots.name,
COUNT(DISTINCT shoot_images.image_id) AS image_count,
COUNT(DISTINCT gallery_images.gallery_id) AS gallery_count
FROM shoots
LEFT JOIN shoot_images ON shoots.id=shoot_images.shoot_id
LEFT JOIN gallery_images ON shoot_images.image_id=gallery_images.image_id
GROUP BY 1, 2 -- Added this line
ORDER BY shoots.id DESC
Note: The SQL standard allows GROUP BY to be given either column names or column numbers, so GROUP BY 1, 2 is equivalent to GROUP BY shoots.id, shoots.name in this case. There are many who consider this "bad coding practice" and advocate always using the column names, but I find it makes the code a lot more readable and maintainable and I've been writing SQL since before many users on this site were born, and it's never cause me a problem using this syntax.
FYI, the reason you were getting one row before, and not getting and error, is that in mysql, unlike any other database I know, you are allowed to omit the group by clause when using aggregating functions. In such cases, instead of throwing a syntax exception, mysql returns the first row for each unique combination of non-aggregate columns.
Although at first this may seem abhorrent to SQL purists, it can be incredibly handy!
You should look into the MySQL function group by.