How to ORDER BY in each GROUP BY - mysql

i have data like this:
Name
Class
Pos
Nam
A
5
Hung
B
1
Tran
A
6
Hoang
A
1
Bao
B
4
so now i want to make query on this table to group all students by class, in each class, studen will be sorted descending by index.
My expected table be like:
Name
Class
Pos
Tran
A
6
Nam
A
5
Hoang
A
1
Bao
B
4
Hung
B
1
Here is my query
SELECT * FROM
(
SELECT *
from
(
SELECT 'Nam' as name, "A" as class, 5 as pos
UNION
SELECT 'Hung' as name, "B" as class, 1 as pos
UNION
SELECT 'Tran' as name, "A" as class, 6 as pos
UNION
SELECT 'Hoang' as name, "A" as class, 1 as pos
UNION
SELECT 'Bao' as name, "B" as class, 4 as pos
)t0
ORDER BY pos DESC
)t1
GROUP BY name, class
But the output look so different:
Name
Class
Pos
Nam
A
5
Hung
B
1
Tran
A
6
Hoang
A
1
Bao
B
4
if i filter only class A, it look like
Name
Class
Pos
Hoang
A
1
Tran
A
6
Nam
A
5
only correct for class B:
Name
Class
Pos
Bao
B
4
Hung
B
1
Look like the order in sub-query can not be kept after group by.
My finally query will look like:
SELECT class as Class, COLLECT_LIST(name) as Name FROM
(
SELECT *
from
(
SELECT 'Nam' as name, "A" as class, 5 as pos
UNION
SELECT 'Hung' as name, "B" as class, 1 as pos
UNION
SELECT 'Tran' as name, "A" as class, 6 as pos
UNION
SELECT 'Hoang' as name, "A" as class, 1 as pos
UNION
SELECT 'Bao' as name, "B" as class, 4 as pos
)t0
ORDER BY pos DESC
)t1
GROUP BY class
If GROUP BY can keep order base on pos in sub-query then COLLECT_LIST will produce expected result like this:
Class
Name
A
[Tran, Nam,Hoang]
B
[Bao,Hung]
How to group and order internal in each group?
Thanks

The ORDER BY, in the middle of your query is useless. It should be put after the GROUP BY.
SELECT * FROM
(
SELECT *
from
(
SELECT 'Nam' as name, 'A' as class, 5 as pos
UNION
SELECT 'Hung' as name, 'B' as class, 1 as pos
UNION
SELECT 'Tran' as name, 'A' as class, 6 as pos
UNION
SELECT 'Hoang' as name, 'A' as class, 1 as pos
UNION
SELECT 'Bao' as name, 'B' as class, 4 as pos
)t0
)t1
WHERE class='A'
GROUP BY name, class
ORDER BY pos DESC;
output:
+-------+-------+-----+
| name | class | pos |
+-------+-------+-----+
| Tran | A | 6 |
| Nam | A | 5 |
| Hoang | A | 1 |
+-------+-------+-----+
An explanation about ORDER BY can be found here: https://mariadb.com/kb/en/why-is-order-by-in-a-from-subquery-ignored/
A "table" (and subquery in the FROM clause too) is - according to the
SQL standard - an unordered set of rows. Rows in a table (or in a
subquery in the FROM clause) do not come in any specific order.
EDIT: If you have ONLY_FULL_GROUP_BY enabled (which is the default in MySQL8.0+), you will get an error like this:
ERROR 1055 (42000): Expression #3 of SELECT list is not in GROUP BY
clause and contains nonaggregated column 't1.pos' which is not
functionally dependent on columns in GROUP BY clause; this is
incompatible with sql_mode=only_full_group_by
This is because grouping is done on name and class so SQL does not know which value to use for pos in that group. Should it that the highest value? of the lowest value? (or some other value...).
I did not notice (not enough coffee...). A re-write with this problems solved looks like this:
SELECT
name,
class,
min(pos) as pos
FROM
(
SELECT 'Nam' as name, "A" as class, 5 as pos
UNION
SELECT 'Hung' as name, "B" as class, 1 as pos
UNION
SELECT 'Tran' as name, "A" as class, 6 as pos
UNION
SELECT 'Hoang' as name, "A" as class, 1 as pos
UNION
SELECT 'Bao' as name, "B" as class, 4 as pos
)t1
WHERE class="A"
GROUP BY name, class
ORDER BY pos DESC;

If I understand your requirement correctly, you just need a two level sort first by class, then by index:
SELECT Class, GROUP_CONCAT(Name ORDER BY Pos DESC) AS Name
FROM yourTable
GROUP BY Class
ORDER BY Class;

Related

Efficiently match highest category

I have a table with 6 digit numbers that can range from 0-9 and I would match that against a number in 6 categories
first number match
first two number match
first three number match
first four number match
first five number match
all numbers match
But only the highest category per matching number should be selected. An example
Number: 123456
If one has the number [123]756 then this would fall into category first three number match
On number 023456 then this would be no match
I created a fiddle for it https://www.db-fiddle.com/f/TZCrFPnJpkw4fyxA5Q6mR/1
Here an example:
Numbers:
number
123456
123000
023456
123477
133456
Number to match against: 123456 should return
common_digits
number
6
123456
3
123000
0
023456
4
123477
1
133456
What would be an efficient method? The brute force solution would be a double loop I suppose starting with 6 matches, 5 matches, ...
SELECT #number tested_number, 7 - LENGTH(nums.num) common_digits, bids.*
FROM bids
JOIN (SELECT 1 num UNION
SELECT 10 UNION
SELECT 100 UNION
SELECT 1000 UNION
SELECT 10000 UNION
SELECT 100000) nums
WHERE #number DIV nums.num = bids.ticketNumber DIV nums.num
ORDER BY nums.num LIMIT 1;
https://www.db-fiddle.com/f/TZCrFPnJpkw4fyxA5Q6mR/4
You can do:
select *
from (
select 6 as score, b.* from bids b where ticketNumber like '123456%'
union all select 5, b.* from bids b where ticketNumber like '12345%'
union all select 4, b.* from bids b where ticketNumber like '1234%'
union all select 3, b.* from bids b where ticketNumber like '123%'
union all select 2, b.* from bids b where ticketNumber like '12%'
union all select 1, b.* from bids b where ticketNumber like '1%'
) x
order by score desc
limit 1
Result:
score id roundId address ticketNumber
------ --- -------- -------- ------------
6 1 1 12345 123456
See example at DB Fiddle.
Alternatively you can use a recursive CTE, but that's not available in MySQL 5.7 (as your fiddle implies).

MySQL Select parents and childs in proper order with single query

I have a MySQL table with following data:
ID Name ParentID
1 Foo null
2 Bar null
3 Foo SubA 1
4 Bar SubA 2
5 Foo SubC 1
6 Foo SubB 1
I would like to retreive all data with following order:
1 Foo null
3 Foo SubA 1
6 Foo SubB 1
5 Foo SubC 1
2 Bar null
4 Bar SubA 2
Is it possible with MySQL and single query?
If this is a two-level hierarchie, i.e. no grandparents and grandchildren, it's a mere ORDER BY clause:
select id, name, parentid
from mytable
order by coalesce(parentid, id), parentid is not null, name;
This makes use of MySQL's true = 1, false = 0. parentid is not null is 0 for the parent and 1 for the children.
You could use recursive CTE (MySQL 8.0+):
-- 2 level hierarchy (parent-child)
WITH RECURSIVE cte AS
(
SELECT tx.*, 1 AS lvl, ID AS grp FROM tx WHERE ParentID IS NULL
UNION ALL
SELECT tx.*, lvl+1, cte.ID FROM tx JOIN cte WHERE tx.ParentId = cte.Id
)
SELECT ID, Name, ParentId
FROM cte
ORDER BY grp, lvl, Name;
DBFiddle Demo

Modify the values obtained through group_concat() in mysql

I have the following [table a]
id result
1 a
1 b
1 b
1 c
2 e
2 e
2 e
2 f
I'm getting the following after doing a group_concat
select id , Group_Concat(result) from [table a]
group by id
id result
1 a,b,b,c
2 e,e,e,f
BUT i want to display the no of times a value occurs before the value in the result set to avoid redundancy like the following
id result
1 a,2 b,c
2 3 e,f
How can I achieve it ?
Group by ID and result first to get the count. Then group by ID to build your strings.
select
id,
group_concat(case when cnt = 1 then result else concat(cnt, ' ', result) end) as results
from
(
select id, result, count(*)
from mytable
group by id, result
) t
group by id;

Count Duplicates with same id passing in one coulmn

Hi there m trying to calculate the row count for same value,
id,value
1 | a
2 | b
3 | c
4 | d
5 | e
and my query is
select value, count(*) as Count from mytable where id in('4','2','4','1','4') group by value having count(*) > 1
for which my expected output will be,
value,Count
d | 3
b | 1
a | 1
Thanks, any help will be appreciated
Try that:
SELECT value, count(value) AS Count
FROM mytable m
WHERE value = m.value
GROUP BY value
SELECT t.id, t.value, COUNT(t.id)
FROM
test t
JOIN
( SELECT 1 AS id
UNION ALL SELECT 3
UNION ALL SELECT 3
UNION ALL SELECT 4
UNION ALL SELECT 1
UNION ALL SELECT 1 ) AS tmp
ON t.id = tmp.id
GROUP BY t.id
Sample on sqlfiddle.com
See also: Force MySQL to return duplicates from WHERE IN clause without using JOIN/UNION?
Of course, your IN parameter will be dynamic, and thus you will have to generate the corresponding SQL statement for the tmp table.
That's the SQL-only way to do it. Another possibility is to have the query like you have it in your question and afterwards programmatically associate the rows to the count passed to the IN parameter.

Counting unique numbers in a column MySQL

I have a query that returns data in the following format:
id | name | number
1 John 12545
1 John 50496
2 Mary 23443
3 Mark 54
3 Mark 5600
3 Mark 50206
I would like to find out the number of distinct ids that appear in the result set. For example, for the result above. I would like to obtain the value 3.
Is there any way to add a column so the result looks like this instead?
count | id | name | number
3 1 John 12545
3 1 John 50496
3 2 Mary 23443
3 3 Mark 54
3 3 Mark 5600
3 3 Mark 50206
My query is:
SELECT * FROM (
SELECT id FROM tableA
WHERE xyz
) as t1
JOIN tableB using (id)
SELECT (SELECT COUNT(DISTINCT id) FROM tableName) totalCount,
id,name,number
FROM tableName
or by using CROSS JOIN
SELECT x.totalCount,
a.id, a.name, a.number
FROM tableName a, (SELECT COUNT(DISTINCT id) totalCount
FROM tableName) x
You should try :
SELECT id,name,number, (SELECT COUNT(DISTINCT name) FROM YourTableName) FROM YourTableName
Good luck
SELECT COUNT(DISTINCT id) would be faster than using column name.
SELECT (SELECT COUNT(DISTINCT id) FROM tableName) as 'count',
id,name,number
FROM tableName
SELECT COUNT(id) AS count , id, name, number
FROM
(
SELECT id
FROM tableA
WHERE xyz
) as t1
JOIN tableB using (id)
GROUP BY id, name, number