Is it possible to group rows twice in MySQL? - mysql

I have a table like this:
someid somestring
1 Hello
1 World
1 Blah
2 World
2 TestA
2 TestB
...
Currently I'm grouping by the id and concatenating the strings, so I end up with this:
1 Hello,World,Blah
2 World,TestA,TestB
...
Is it possible to do a second grouping so that if there are multiple entries that end up with the same string, I can group those too?

Yes, just put your current query in an inner select and apply a new GROUP BY to the outer select. Note that you will probably want to use ORDER BY of GROUP_CONCAT to ensure that the strings are always concatenated in the same order.
SELECT somelist, COUNT(*) FROM
(
SELECT
someid,
GROUP_CONCAT(somestring ORDER BY somestring) AS somelist
FROM table1
GROUP BY someid
) AS T1
GROUP BY somelist
Result:
'Blah,Hello,World', 1
'TestA,TestB,World', 2
Here's the test data I used:
CREATE TABLE table1 (someid INT NOT NULL, somestring NVARCHAR(100) NOT NULL);
INSERT INTO table1 (someid, somestring) VALUES
(1, 'Hello'),
(1, 'World'),
(1, 'Blah'),
(2, 'World'),
(2, 'TestA'),
(2, 'TestB'),
(3, 'World'),
(3, 'TestB'),
(3, 'TestA');

Related

Extract only rows with highest values

I am relatively new to SQL and I am trying to extract rows where they have the highest values.
For example, the table look like this:
user_id fruits
1 apple
1 orange
2 apple
1 pear
I would like to extract the data such that it would look like this:
user_id fruits
1 3
If user_id 2 has 3 fruits, it should display:
user_id fruits
1 3
2 3
I can only manage to get the if I use LIMIT = 1 by DESC order, but that is not the right way to do it. Otherwise I am getting only:
user_id fruits
1 3
2 1
Not sure where to store the max value to put in the where clause. Appreciate any help, thank you
Use RANK():
WITH cte AS (
SELECT user_id, COUNT(*) AS cnt, RANK() OVER (ORDER BY COUNT(*) DESC) rnk
FROM yourTable
GROUP BY user_id
)
SELECT user_id, cnt AS fruits
FROM cte
WHERE rnk = 1;
Here's one answer (with sample data):
CREATE TABLE something (user_id INT NOT NULL, fruits VARCHAR(10) NOT NULL, PRIMARY KEY (user_id, fruits));
INSERT INTO something VALUES (1, 'apple');
INSERT INTO something VALUES (1, 'orange');
INSERT INTO something VALUES (2, 'apple');
INSERT INTO something VALUES (1, 'pear');
INSERT INTO something VALUES (2, 'orange');
INSERT INTO something VALUES (2, 'pear');
SELECT user_id, COUNT(*) AS cnt
FROM something
GROUP BY user_id
HAVING COUNT(*) >= ALL (SELECT COUNT(*) FROM something GROUP BY user_id);

MySQL Joining multiple tables each with multiple rows

I have tables (bar, baz) which have 1 or more rows relating to another table (foo). When I join both bar & baz to foo I get results for each row of each table.
http://sqlfiddle.com/#!9/1c13f2/1/0
CREATE TABLE foo (`id` int, `value` varchar(5));
INSERT INTO foo (`id`, `value`) VALUES
(1, 'two'),
(2, 'two'),
(3, 'one');
CREATE TABLE bar (`id` int, `foo_id` int, `value` int);
INSERT INTO bar (`id`, `foo_id`, `value`) VALUES
(1, 1, 1),
(2, 1, 1),
(3, 2, 1),
(4, 2, 1),
(5, 3, 1);
CREATE TABLE baz (`id` int, `foo_id` int, `value` int);
INSERT INTO baz (`id`, `foo_id`, `value`) VALUES
(1, 1, 1),
(2, 1, 1),
(3, 2, 1),
(4, 2, 1),
(5, 3, 1);
The query:
SELECT foo.value, SUM(bar.value), SUM(baz.value)
FROM foo
JOIN bar ON bar.foo_id = foo.id
JOIN baz ON baz.foo_id = foo.id
GROUP BY foo.id
Result:
value SUM(bar.value) SUM(baz.value)
two 4 4
two 4 4
one 1 1
Expected result:
value SUM(bar.value) SUM(baz.value)
two 2 2
two 2 2
one 1 1
The result is the expected behavior, from a cross (semi-Cartesian) product, multiple rows from bar matched to multiple rows from baz.
To avoid this, we can pre-aggregate counts from bar and baz, and then do the join.
Also consider, what result is expected when there are matching rows in bar but no matching rows in baz. Do we want to return the total from bar? With the current query, we wouldn't get total from bar. (In the example data, consider what the query will return after row id=5 is deleted from baz.)
I'd write the query like this:
SELECT foo.value
, IFNULL( r.tot_bar_value ,0) AS tot_bar_value
, IFNULL( z.tot_baz_value ,0) AS tot_baz_value
FROM foo
LEFT
JOIN ( -- aggregate total from bar
SELECT bar.foo_id
, SUM(bar.value) AS tot_bar_value
FROM bar
GROUP BY bar.foo_id
) r
ON r.foo_id = foo.id
LEFT
JOIN ( -- aggregate total from bar
SELECT baz.foo_id
, SUM(baz.value) AS tot_baz_value
FROM baz
GROUP BY baz.foo_id
) z
ON z.foo_id = foo.id
Note that we are using outer joins, to handle the case when there not matching rows in either bar or baz.
For testing, we can run separately just the SELECT query inside the parens, to see what is returned.

Query to select a object with two exact records

I have following table in MySQL:
some_table
user_id | obj_id
-----------------
5 | 1
6 | 1
7 | 2
8 | 2
Now I need a Query, which will get me the obj_id, if this obj_id has both user_id = 5 and user_id = 6 (in the example above it is obj_id=1).
Is something like this possible with MySQL?
One method uses group by and having:
select obj_id
from some_table t
where user_id in (5, 6)
group by obj_id
having count(distinct user_id) = 2;
As #GordonLinoff already provided a better solution.
But this is one of the possible way to get your expected result using INTERSECT:
SELECT DISTINCT obj_id FROM #TEST WHERE user_id = 5
INTERSECT
SELECT DISTINCT obj_id FROM #TEST WHERE user_id = 6
Sample execution:
CREATE TABLE some_table (`user_id` INT, obj_id INT);
INSERT INTO some_table (`user_id`, obj_id) VALUES
(5, 1),
(6, 1),
(7, 2),
(8, 2),
(5, 3),
(6, 4);
SELECT DISTINCT obj_id FROM some_table WHERE `user_id` = 5
INTERSECT
SELECT DISTINCT obj_id FROM some_table WHERE `user_id` = 6
Result will be 1.

How to find the column having duplicate value in SQL Server

I have a table:
create table #t
(
ID int,
value nvarchar(5)
)
insert #t
values (1,'A'), (2, 'B'), (3, 'A'), (3, 'B')
Sample data:
ID value
------------
1 A
2 B
3 A
3 B
For my project I need the ID which has having both the values
Result :
ID
3
Kindly help me out.
To get IDs having 2 values
select id
from #t
group by id
having count(distinct value) >= 2
or to get all IDs having A and B
select id
from #t
where value in ('A','B')
group by id
having count(distinct value) = 2
or to make it more generic to get IDs having all values
select id
from #t
group by id
having count(distinct value) = (select count(distinct value) from #t)

MySQL query: Single Table multiple comparisions

I have the following mysql table:
id | member |
1 | abc
1 | pqr
2 | xyz
3 | pqr
3 | abc
I have been trying to write a query which would return the id which has exact same members as a given id. For example, if given id is 1 then the query should return 3 because both id 1 and id 3 have exact same members viz. {abc, pqr}. Any pointers? Appreciate it.
EDIT: The table may have duplicates, e.g. id 3 may have members {abc, abc} instead of {pqr, abc}, in which case the query should not return id 3.
Here's a solution that finds matching pairs for the entire table - you can add a where clause to filter as needed. Basically it does a self-join based on equal "member" and unequal "id". It then compares the resulting count grouped by the 2 ids and compares them to the total count of those ids from the original table. If they both match, it means they have the same exact members.
select
t1.id, t2.id
from
table t1
inner join table t2
on t1.member = t2.member
and t1.id < t2.id
inner join (select id, count(1) as cnt from table group by id) c1
on t1.id = c1.id
inner join (select id, count(1) as cnt from table group by id) c2
on t2.id = c2.id
group by
t1.id, t2.id, c1.cnt, c2.cnt
having
count(1) = c1.cnt
and count(1) = c2.cnt
order by
t1.id, t2.id
This is some sample data I used which returned matches of (1,3) and (6,7)
insert into table
values
(1, 'abc'), (1, 'pqr'), (2, 'xyz'), (3, 'pqr'), (3, 'abc'), (4, 'abc'), (5, 'pqr'),
(6, 'abc'), (6, 'def'), (6, 'ghi'), (7, 'abc'), (7, 'def'), (7, 'ghi')
similar (to Derek Kromm's) approach using sub-queries:
SELECT id
FROM mc a
WHERE
id != 1 AND
member IN (
SELECT member FROM mc WHERE id=1)
GROUP BY id
HAVING
COUNT(*) IN (
SELECT COUNT(*) FROM mc WHERE id=1) AND
COUNT(*) IN (
SELECT COUNT(*) FROM mc where id=a.id);
a logic here is we need all ids that match following 2 conditions:
member is among those that belong to id 1
total number of members is same as number of those that belong to id 1
total number of selected members equal to total number of members for current id
try this:
declare #id int
set #id=1
select a.id from
(select id,COUNT(*) cnt from sample_table
where member in (select member from sample_table where id=#id)
and id <>#id
group by id)a
join
(select count(distinct member) cnt from sample_table where id=#id)b
on a.cnt=b.cnt