Sort GROUP BY agregats - mysql

Imagine this table t1,
+----------+-------+--------+
| group_id | name | age |
+----------+-------+--------+
| 1 | A1 | 1 |
| 1 | A2 | 2 |
| 1 | A3 | 3 |
| 2 | B1 | 4 |
+----------+-------+--------+
Using the following query in MySQL,
SELECT group_id, name, COUNT(*) FROM t1 GROUP BY group_id
we get,
+----------+-------+--------+----------+
| group_id | name | age | COUNT(*) |
+----------+-------+--------+----------+
| 1 | A1 | 2 | 3 |
| 2 | B1 | 4 | 1 |
+----------+-------+--------+----------+
As you can see here, it's possible that values name=A1 and age=2 are not from the same record.
My question is, how can I control which single results form the name and age columns are shown, so the content is from one record? Is there a way to sort them in some way? Fro example sorting by age in reverse order would give
+----------+-------+--------+----------+
| group_id | name | age | COUNT(*) |
+----------+-------+--------+----------+
| 1 | A3 | 3 | 3 |
| 2 | B1 | 4 | 1 |
+----------+-------+--------+----------+
Thanks.

I don't know why do you say that your query works. You should also group by name...
SELECT group_id, name, COUNT(*) FROM t1 GROUP BY group_id, name
If you want to get only one of them, try:
SELECT group_id, MIN(name), COUNT(*) FROM t1 GROUP BY group_id

I don't know about full control, but you can do like this
SELECT student_name, MIN(test_score), MAX(test_score)
FROM student
GROUP BY student_name;

SELECT group_id, name, COUNT(*)
FROM t1
WHERE name IN ( 'xxx', 'yyy', ..., 'zzz' )
GROUP BY group_id
SORT BY COUNT(*)

Related

Comparing values from rows with the same group id?

I'm trying to write a SQL query to do the following:
Given the following table:
+----+----------+-----------+
| id | group_id | value |
+----+----------+-----------+
| 1 | 1 | 0 |
+----+----------+-----------+
| 2 | 1 | 0 |
+----+----------+-----------+
| 3 | 2 | null |
+----+----------+-----------+
| 4 | 3 | -1 |
+----+----------+-----------+
| 5 | 3 | 1 |
+----+----------+-----------+
| 6 | 4 | something |
+----+----------+-----------+
| 7 | 5 | something |
+----+----------+-----------+
select *
where values do not equal each other
group by group_id
For this example, output should be:
+----+----------+-----------+
| id | group_id | value |
+----+----------+-----------+
| 4 | 1 | -1 |
+----+----------+-----------+
| 5 | 1 | 1 |
+----+----------+-----------+
Does anyone know if this is possible?
If you only want to find the group_id values which have different value, you can use this query:
SELECT group_id
FROM data
GROUP BY group_id
HAVING MIN(value) != MAX(value)
Output for your sample data:
group_id
3
If you want to get the rows that are associated with that group_id, use the above query as a subquery for an IN expression:
SELECT *
FROM data
WHERE group_id IN (
SELECT group_id
FROM data
GROUP BY group_id
HAVING MIN(value) != MAX(value)
)
Output
id group_id value
4 3 -1
5 3 1
Demo on dbfiddle
You can do with exists to get group_id which does not have same value. here is the demo.
select
distinct group_id
from data d1
where exists
(
select
group_id
from data d2
where d1.group_id = d2.group_id
and d1.value <> d2.value
)
Output
*--------*
|group_id|
*--------*
| 3 |
*--------*
If you want group_id and value both then try the follwoing
select
group_id,
value
from data d1
where exists
(
select
group_id
from data d2
where d1.group_id = d2.group_id
and d1.value <> d2.value
)
Output:
*------------------*
|group_id | value |
*------------------*
| 3 | -1 |
| 3 | 1 |
*------------------*
This query:
select group_id
from tablename
group by group_id
having count(distinct value) > 1 and count(distinct value) = count(value)
returns the group_ids that you want, so use it with the operator IN:
select * from tablename
where group_id in (
select group_id
from tablename
group by group_id
having count(distinct value) > 1 and count(distinct value) = count(value)
)
See the demo.
Results:
| id | group_id | value |
| --- | -------- | ----- |
| 4 | 3 | -1 |
| 5 | 3 | 1 |

Query data by year from joined table

I have one table named colors and I need to create a query that returns how many unique colors are used each year based on the date in my other table, programs.
It looks like this:
colors
+----+----------+
| id | name |
+----+----------+
| 1 | blue |
| 2 | yellow |
+----+----------+
programs
+----+------------+
| id | date |
+----+------------+
| 1 | 2016-01-08 |
| 2 | 2016-02-08 |
| 3 | 2017-02-08 |
+----+------------+
programs_colors
+------------+----------+
| program_id | color_id |
+------------+----------+
| 1 | 1 |
| 1 | 1 |
| 2 | 2 |
| 2 | 1 |
| 3 | 1 |
| 3 | 1 |
+------------+----------+
I have tried with this:
SELECT min(date), count(*) FROM (
SELECT min(date) AS date FROM programs_colors INNER JOIN programs ON programs.id = program_id GROUP BY color_id
) AS a GROUP BY year(date)
min(date): count(*):
2016-01-08 2
2017-01-08 0
But the above query groups my colors as a whole, but I need them grouped by each year
Expected result:
min(date): count(*):
2016-01-08 2
2017-01-08 1
I hope my question makes sense
SELECT min(date), count(distinct color_id)
FROM programs_colors
INNER JOIN programs
ON programs.id = program_id
GROUP BY year(date);
If I understand right, you might need to do something like this
SELECT min(date), sum(count) FROM (
SELECT min(date) AS date, count(*) as count FROM programs_colors INNER JOIN programs ON programs.id = program_id GROUP BY color_id
) AS a GROUP BY year(date)

Compare two columns from two tables and return SUM of matches

I am working on a project where I have to compare two tables together and sum the number of occurrences. For this example I will use name as the key that I would like to compare. I have used Union all and Count(*) but it wouldn't give me the desired output.
Table apple
+----+-------+---------+
| Id | Name | Surname |
+----+-------+---------+
| 1 | Adam | Jaxon |
| 2 | Adam | Brixton |
| 3 | Brian | Simpson |
| 4 | Adam | Steper |
| 5 | Brian | Bastion |
+----+-------+---------+
Table orange
+----+-------+---------+
| id | name | surname |
+----+-------+---------+
| 1 | Adam | Thompson|
| 2 | Brian | Coach |
| 3 | Jhon | Sinded |
+----+-------+---------+
There is one name match for Adam and one match for Brian so the desired output I would like to receive is
+-------+
| Total |
+-------+
| 3 |
| 1 |
+--------+
The query that I am using similar to the person who had answered the question however there are few changes. Unfortunately this only returns number of matches for each name
SELECT COUNT(*)
FROM
(
SELECT Name
FROM
apple
UNION ALL
SELECT
NAME
FROM
orange
) as named
GROUP BY name
+----------+
| Count(*) |
+----------+
| 2
| 3
+----------+
Try it, grouping and count name:
SELECT
id,
name,
surname
count(1) as total
FROM
(
SELECT
id,
name,
surname
FROM apple
UNION ALL
SELECT
id,
name,
surname
FROM orange
)
GROUP BY name
UPDATE
To SUM all results before your query:
SELECT sum(total)
FROM (
SELECT
id,
name,
surname
count(1) as total
FROM
(
SELECT
id,
name,
surname
FROM apple
UNION ALL
SELECT
id,
name,
surname
FROM orange
)
GROUP BY name
)
No DB server available, so haven't tested it - should do the trick, however:
SELECT COUNT(*) FROM
(
SELECT DISTINCT apple.name FROM apple
JOIN orange ON apple.name = orange.name
);

Find the maximum value(s) from a column and selecting their rows

After looking at other examples I still have not been able to find a solution, that is why I am asking for some help.
My table structure:
V_id | name | group_id | other columns
----------------------
1 | | 1
2 | | 1
3 | | 2
4 | | 3
5 | | 3
I have been struggling to build a query, to select all the rows which have the maximum value from the group_id column.
therefore output should be like this:
V_id | name | group_id | other columns
----------------------
4 | | 3
5 | | 3
which I believe can be solved by selecting all records where group_id is the highest.
and also need a query to get all the other remaining rows.
which in this case, should be like this:
V_id | name | group_id | other columns
----------------------
1 | | 1
2 | | 1
3 | | 2
which I believe can be done by selecting all records where group_id < Max(group_id)
for the first part of the problem,
SELECT *
FROM tableName
WHERE group_id = (SELECT MAX(group_ID) FROM TableName)
and for the second part,
SELECT *
FROM tableName
WHERE group_id < (SELECT MAX(group_ID) FROM TableName)
You can use JOIN for that:
SELECT a.*
FROM Table1 a
JOIN (SELECT MAX(Group_ID) AS MAXID
FROM Table1) B
ON a.Group_id = B.MaxID;
Result:
| V_ID | NAME | GROUP_ID |
----------------------------
| 4 | (null) | 3 |
| 5 | (null) | 3 |
For the remaining rows use LEFT JOIN with a condition like this:
SELECT a.*
FROM Table1 a
LEFT JOIN (SELECT MAX(Group_ID) AS MAXID
FROM Table1) B
ON a.Group_id = B.MaxID
WHERE B.MaxID IS NULL;
Result:
| V_ID | NAME | GROUP_ID |
----------------------------
| 1 | (null) | 1 |
| 2 | (null) | 1 |
| 3 | (null) | 2 |
See this SQLFiddle

MySQL GROUP BY "and filter"

Let's say i have query like this:
SELECT name, GROUP_CONCAT(number)
FROM objects
GROUP BY name
And it outputs:
+----------+----------------------+
| NAME | GROUP_CONCAT(NUMBER) |
+----------+----------------------+
| false_1 | 2,1 |
| false_2 | 3,4 |
| true_1 | 4,3,2,1 |
| true_2 | 2,3 |
+----------+----------------------+
Now how can i return rows having 2 AND 3 as number?
Note: This query is grouped - table has 10 rows, like so:
+---------+--------+
| NAME | NUMBER |
+---------+--------+
| true_1 | 1 |
| true_1 | 2 |
| true_1 | 3 |
| ... | ... |
+---------+--------+
[Link to SQLFiddle]
SELECT name, GROUP_CONCAT(number)
FROM objects
WHERE number IN (2,3)
GROUP BY name
HAVING COUNT(*) = 2
SEE SQLFiddle Demo
or if you want to retain all value on which the name has,
SELECT a.name, GROUP_CONCAT(A.number)
FROM objects a
INNER JOIN
(
SELECT name
FROM objects
WHERE number IN (2,3)
GROUP BY name
HAVING COUNT(*) = 2
) b ON a.Name = b.Name
GROUP BY a.name
SEE SQLFiddle Demo