So, let say I have this data
id | value | group
1 | 100 | A
2 | 120 | A
3 | 150 | B
4 | 170 | B
I want to sort it so it become like this
id | value | group
1 | 100 | A
3 | 150 | B
2 | 120 | A
4 | 170 | B
there will be more group than that, so if I the data ordered the group like (A,C,B,D,B,C,A), it will become (A,B,C,D,A,B,C)
You can add a counter column to the table, which will be used to sort the table:
select t.id, t.value, t.`group`
from (
select t.id, t.value, t.`group`,
(select count(*) from tablename
where `group` = t.`group` and id < t.id) counter
from tablename t
) t
order by t.counter, t.`group`
See the demo.
Results:
| id | value | group |
| --- | ----- | ----- |
| 1 | 100 | A |
| 3 | 150 | B |
| 2 | 120 | A |
| 4 | 170 | B |
You can approach this as
SELECT *
FROM `tablename`
ORDER BY
row_number() OVER (PARTITION BY `group` ORDER BY `group`), `group`
Related
I have a problem with selecting specific amount of data. The problem is that one of the keys have the same repeated value.
--------------------
| id | name | key |
--------------------
| 1 | alfa | a |
| 2 | alfa | b |
| 3 | alfa | c |
| 4 | beal | a |
| 5 | beal | b |
| 6 | gala | c |
| 7 | gala | d |
| 8 | delt | a |
| 9 | ceta | a |
--------------------
In this situation I want to select three individual names. For example I want to limit distinct name to 3 positions to get this result:
SAMPLE DUMP CODE:
SELECT * in Table
WHERE `name` LIKE '%al%'
LIMIT BY DISTINCT
`name`, 3
------ RESULT ------
| 1 | alfa | a |
| 2 | alfa | b |
| 3 | alfa | c |
| 4 | beal | a |
| 5 | beal | b |
| 6 | gala | c |
| 7 | gala | d |
--------------------
I will be glad for help.
Without window functions:
select *
from (
select distinct name
from mytable
where `name` like '%al%'
order by name
limit 3
) n
natural join mytable
db-fiddle
If you don't like NATURAL JOINs you can also use
select t.*
from (
select distinct name
from mytable
where `name` like '%al%'
order by name
limit 3
) n
join mytable t on t.name = n.name
If window functions are supported, you can use DENSE_RANK():
with cte as (
select *,
dense_rank() over (order by name) as dr
from mytable
where `name` like '%al%'
)
select id, name, `key`
from cte
where dr <= 3
db-fiddle
I prefer the LIMIT 3 subquery, since it can stop the index scan (depending on optimizer) after three distinct names are found.
MySQL 8.0 solution utilizing Window functions is as follows:
SELECT
dt.id, dt.name, dt.`key`
FROM
(
SELECT
ROW_NUMBER() OVER (PARTITION BY name ORDER BY id) AS rn,
id,
name,
`key`
FROM your_table_name
WHERE name LIKE '%al%'
) AS dt
WHERE dt.rn <= 3
ORDER BY dt.id
Explanation:
In a Derived table (subquery), determine Row_Number() within a partition (group) of specific name, ordered by id in ascending order. We will consider only names matching %al% condition.
Now, use the subquery result to SELECT only the rows having row number upto 3 (basically limiting to 3 rows per name).
By the way, key is a Reserved Keyword in MySQL. You should consider renaming column to something else; otherwise you will need to use backticks around it.
Result
| id | name | key |
| --- | ---- | --- |
| 1 | alfa | a |
| 2 | alfa | b |
| 3 | alfa | c |
| 4 | beal | a |
| 5 | beal | b |
| 6 | gala | c |
| 7 | gala | d |
View on DB Fiddle
my table has duplicate row values in specific columns. i would like to remove those rows and keep the row with the latest id.
the columns i want to check and compare are:
sub_id, spec_id, ex_time
so, for this table
+----+--------+---------+---------+-------+
| id | sub_id | spec_id | ex_time | count |
+----+--------+---------+---------+-------+
| 1 | 100 | 444 | 09:29 | 2 |
| 2 | 101 | 555 | 10:01 | 10 |
| 3 | 100 | 444 | 09:29 | 23 |
| 4 | 200 | 321 | 05:15 | 5 |
| 5 | 100 | 444 | 09:29 | 8 |
| 6 | 101 | 555 | 10:01 | 1 |
+----+--------+---------+---------+-------+
i would like to get this result
+----+--------+---------+---------+-------+
| id | sub_id | spec_id | ex_time | count |
+----+--------+---------+---------+-------+
| 5 | 100 | 444 | 09:29 | 8 |
| 6 | 101 | 555 | 10:01 | 1 |
+----+--------+---------+---------+-------+
i was able to build this query to select all duplicate rows from multiple columns, according to this question
select t.*
from mytable t join
(select id, sub_id, spec_id, ex_time, count(*) as NumDuplicates
from mytable
group by sub_id, spec_id, ex_time
having NumDuplicates > 1
) tsum
on t.sub_id = tsum.sub_id and t.spec_id = tsum.spec_id and t.ex_time = tsum.ex_time
but now im not sure how to wrap this select with a delete query to delete the rows except for the ones with highest id.
as shown here
You can modify your sub-select query, to get maximum value of id for each duplication combination.
Now, while joining to the main table, simply put a condition that id value will not be equal to the maximum id value.
You can now Delete from this result-set.
Try the following:
DELETE t
FROM mytable AS t
JOIN
(SELECT MAX(id) as max_id,
sub_id,
spec_id,
ex_time,
COUNT(*) as NumDuplicates
FROM mytable
GROUP BY sub_id, spec_id, ex_time
HAVING NumDuplicates > 1
) AS tsum
ON t.sub_id = tsum.sub_id AND
t.spec_id = tsum.spec_id AND
t.ex_time = tsum.ex_time AND
t.id <> tsum.max_id
I was having problems in creating counting rows by grouping based on a given field value.
For example: I have a Table A structure like this:
+------+------------+
| id | Person |
+------+------------+
| 1 | "Sandy" |
| 2 | "Piper" |
| 3 | "Candy" |
| 4 | "Pendy" |
+------------+------+
Also I have a Table B structure like this:
+------+------------+---------+
| id | Person | Point |
+------+------------+---------+
| 1 | "Sandy" | 10 |
| 2 | "Piper" | 20 |
| 3 | "Candy" | 30 |
| 4 | "Sandy" | 10 |
| 5 | "Piper" | 20 |
| 6 | "Zafar" | 30 |
+------------+------+---------+
And needed a result like:
+------+------------+---------+
| id | Person | Point |
+------+------------+---------+
| 1 | "Piper" | 40 |
| 2 | "Candy" | 30 |
| 3 | "Zafar" | 30 |
| 4 | "Sandy" | 20 |
| 5 | "Pendy" | 0 |
+------------+------+---------+
I hope the table examples are itself self-explanatory.
SELECT person
, SUM(point) total
FROM
( SELECT person,point FROM table_b
UNION
ALL
SELECT person,0 FROM table_a
) x
GROUP
BY person
ORDER
BY total DESC;
It is a simple left join with a group by
select tableA.person, sum(tableB.points) from tableA left join tableB on tableA.person = tableB.person group by tableA.person
union
select tableB.person, sum(tableB.points) from tableB left join tableA on tableA.person = tableB.person where tableA.id is null group by tableA.person
I think below sql useful to you.
select a.id, a.Person,b.total_point from (
select id, Person from tablea) as a join
(select Person, sum(Point) as total_point from tableb group by person) as b on a.person =b.person
Thank you
If I make a selection from my MySQL table like:
SELECT * FROM mytable ORDER BY country_id, category
I get the following table:
+------------+----------+-------+
| country_id | category | order |
+------------+----------+-------+
| 1 | A | 0 |
| 1 | B | 0 |
| 1 | F | 0 |
| 3 | A | 0 |
| 3 | C | 0 |
| 5 | B | 0 |
| 5 | L | 0 |
| 5 | P | 0 |
+------------+----------+-------+
What I would like to do is update the order column so that the value of that column is the order of that row for it's country_id. In other words the final table should look like this:
+------------+----------+-------+
| country_id | category | order |
+------------+----------+-------+
| 1 | A | 1 |
| 1 | B | 2 |
| 1 | F | 3 |
| 3 | A | 1 |
| 3 | C | 2 |
| 5 | B | 1 |
| 5 | L | 2 |
| 5 | P | 3 |
+------------+----------+-------+
If I use the original query given at the top in a subquery, that would give me the correct order but I can't figure out how to iterate through the table and suspect I thinking about it wrongly.
How can I get this result?
Many thanks
Try to use this query -
UPDATE mytable t1
JOIN (
SELECT t1.country_id, t1.category, COUNT(*) `order`
FROM mytable t1
LEFT JOIN mytable t2
ON t2.country_id = t1.country_id AND t2.category <= t1.category
GROUP BY
t1.country_id, t1.category
) t2
ON t1.country_id = t2.country_id AND t1.category = t2.category
SET t1.`order` = t2.`order`
As an aside note, you can remove order field from the table because this can calculated this on the fly.
How to select 1st, 2nd or 3rd value before MAX ?
usually we do it with order by and limit
SELECT * FROM table1
ORDER BY field1 DESC
LIMIT 2,1
but with my current query I don't know how to make it...
Sample table
+----+------+------+-------+
| id | name | type | count |
+----+------+------+-------+
| 1 | a | 1 | 2 |
| 2 | ab | 1 | 3 |
| 3 | abc | 1 | 1 |
| 4 | b | 2 | 7 |
| 5 | ba | 2 | 1 |
| 6 | cab | 3 | 9 |
+----+------+------+-------+
I'm taking name for each type with max count with this query
SELECT
`table1b`.`name`
FROM
(SELECT
`table1a`.`type`, MAX(`table1a`.`count`) AS `Count`
FROM
`table1` AS `table1a`
GROUP BY `table1a`.`type`) AS `table1a`
INNER JOIN
`table1` AS `table1b` ON (`table1b`.`type` = `table1a`.`type` AND `table1b`.`count` = `table1a`.`Count`)
and I want one more column additional to name with value before max(count)
so result should be
+------+------------+
| name | before_max |
+------+------------+
| ab | 2 |
| b | 1 |
| cab | NULL |
+------+------------+
Please ask if something isn't clear ;)
AS per your given table(test) structure, the query has to be as follows :
select max_name.name,before_max.count
from
(SELECT type,max(count) as max
FROM `test`
group by type) as type_max
join
(select type,name,count
from test
) as max_name on (type_max.type = max_name.type and count = type_max.max )
left join
(select type,count
from test as t1
where count != (select max(count) from test as t2 where t1.type = t2.type)
group by type
order by count desc) as before_max on(type_max.type = before_max .type)