如何使用 sql 查询对列表列表进行排序?
How to sort a list of lists using a sql query?
抽象题
我有一个 sql-table,其中包含以下形式的记录:
(list_id, value)
其中 list_id
是标识特定列表的整数,value
是具有顺序的东西。
我现在努力编写一个 sql 查询,该查询 returns table 的所有记录首先按列表与其他列表比较的排名排序,然后按value
。
抽象问题是,我想使用 sql.
对列表列表进行排序
比较两个列表的算法
比较两个列表的算法如下:
data CompareRes = FirstSmaller | FirstGreater | Equal deriving Show
compareLists :: Ord a => [a] -> [a] -> CompareRes
compareLists [] [] = Equal
-- Longer lists are considered to be smaller
compareLists _ [] = FirstSmaller
compareLists [] _ = FirstGreater
compareLists (x:xs) (y:ys)
| x < y = FirstSmaller
| x > y = FirstGreater
| otherwise = compareLists xs ys
详情
在我的特定情况下,值都是 Date
。
所以我的 table 看起来像这样:
CREATE TABLE `list_date` (
`list_id` INT NOT NULL,
`date` DATE NOT NULL,
PRIMARY KEY (`list_id`, `date`)
);
我正在使用 mysql:8.0 数据库,因此使用 WINDOW
函数的解决方案是 acceptable。
例子
数据
INSERT INTO `list_date` VALUES
(1, '2019-11-02'), (1, '2019-11-03'), (1, '2019-11-04'), (1, '2019-11-05'), (1, '2019-11-07'), (1, '2019-11-08'), (1, '2019-11-09'),
(2, '2019-11-01'), (2, '2019-11-03'), (2, '2019-11-04'),
(3, '2019-11-01'), (3, '2019-11-02'), (3, '2019-11-03'),
(4, '2019-11-02'), (4, '2019-11-04'), (4, '2019-11-13'), (4, '2019-11-14'),
(5, '2019-11-03'), (5, '2019-11-04'), (5, '2019-11-05'), (5, '2019-11-10'),
(6, '2019-11-01'), (6, '2019-11-02'), (6, '2019-11-03'), (6, '2019-11-05');
查询
我真正努力的地方是创建一个表达式来计算 list_rank
:
SELECT
`list_id`,
`date`,
<PLEASE HELP> as `list_rank`
FROM
`list_date`
ORDER BY
`list_rank`, `date`;
预期结果
| list_id | date | list_rank |
|---------|------------|-----------|
| 6 | 2019-11-01 | 1 |
| 6 | 2019-11-02 | 1 |
| 6 | 2019-11-03 | 1 |
| 6 | 2019-11-05 | 1 |
| 3 | 2019-11-01 | 2 |
| 3 | 2019-11-02 | 2 |
| 3 | 2019-11-03 | 2 |
| 2 | 2019-11-01 | 3 |
| 2 | 2019-11-03 | 3 |
| 2 | 2019-11-04 | 3 |
| 1 | 2019-11-02 | 4 |
| 1 | 2019-11-03 | 4 |
| 1 | 2019-11-04 | 4 |
| 1 | 2019-11-05 | 4 |
| 1 | 2019-11-07 | 4 |
| 1 | 2019-11-08 | 4 |
| 1 | 2019-11-09 | 4 |
| 4 | 2019-11-02 | 5 |
| 4 | 2019-11-04 | 5 |
| 4 | 2019-11-13 | 5 |
| 4 | 2019-11-14 | 5 |
| 5 | 2019-11-03 | 6 |
| 5 | 2019-11-04 | 6 |
| 5 | 2019-11-05 | 6 |
| 5 | 2019-11-10 | 6 |
或
该图像是我的应用程序生成的当前实时结果。目前排序是使用 Java.
实现的
编辑
在没有收到更好的答案后,我按照@gordon-linoff 的建议实施了一个解决方案:
SELECT
`list_id`,
`date`
FROM
`list_date`
INNER JOIN (
SELECT `sub`.`list_id`,
GROUP_CONCAT(`sub`.`date` ORDER BY `sub`.`date` SEPARATOR '') as `concat_dates`
FROM `list_date` as `sub`
GROUP BY `sub`.`list_id`
) `all_dates` ON (`all_dates`.`list_id` = `list_date`.`list_id`)
ORDER BY
`all_dates`.`concat_dates`, `date`;
我还创建了一个 SQL Fiddle - 所以你可以尝试你的解决方案。
但此解决方案并未按预期对列表进行排序,因为较长的列表被认为比较小的列表大。
所以我仍然希望收到一个解决我 100% 需求的解决方案:)
如果我没理解错的话,您可以按连接在一起的日期对列表进行排序:
select ld.*
from list_date ld join
(select list_id, group_concat(date) as dates
from ld
group by list_id
) ldc
on ld.list_id = ldc.list_id
order by ldc.dates, ld.date;
因为它用于 MySql 8,所以 window 函数可用于此(是的)。
这是一个首先计算一些指标的查询,用于计算排名:
SELECT
list_id,
`date`,
DENSE_RANK() OVER (ORDER BY ListMinDate ASC, ListCount DESC, ListMaxDate, list_id) AS list_rank
FROM
(
SELECT
list_id,
`date`,
COUNT(*) OVER (PARTITION BY list_id) AS ListCount,
MIN(`date`) OVER (PARTITION BY list_id) AS ListMinDate,
MAX(`date`) OVER (PARTITION BY list_id) AS ListMaxDate
FROM list_date
) q
ORDER BY list_rank, `date`
对 db<>fiddle here
的测试
抽象题
我有一个 sql-table,其中包含以下形式的记录:
(list_id, value)
其中 list_id
是标识特定列表的整数,value
是具有顺序的东西。
我现在努力编写一个 sql 查询,该查询 returns table 的所有记录首先按列表与其他列表比较的排名排序,然后按value
。
抽象问题是,我想使用 sql.
对列表列表进行排序比较两个列表的算法
比较两个列表的算法如下:
data CompareRes = FirstSmaller | FirstGreater | Equal deriving Show
compareLists :: Ord a => [a] -> [a] -> CompareRes
compareLists [] [] = Equal
-- Longer lists are considered to be smaller
compareLists _ [] = FirstSmaller
compareLists [] _ = FirstGreater
compareLists (x:xs) (y:ys)
| x < y = FirstSmaller
| x > y = FirstGreater
| otherwise = compareLists xs ys
详情
在我的特定情况下,值都是 Date
。
所以我的 table 看起来像这样:
CREATE TABLE `list_date` (
`list_id` INT NOT NULL,
`date` DATE NOT NULL,
PRIMARY KEY (`list_id`, `date`)
);
我正在使用 mysql:8.0 数据库,因此使用 WINDOW
函数的解决方案是 acceptable。
例子
数据
INSERT INTO `list_date` VALUES
(1, '2019-11-02'), (1, '2019-11-03'), (1, '2019-11-04'), (1, '2019-11-05'), (1, '2019-11-07'), (1, '2019-11-08'), (1, '2019-11-09'),
(2, '2019-11-01'), (2, '2019-11-03'), (2, '2019-11-04'),
(3, '2019-11-01'), (3, '2019-11-02'), (3, '2019-11-03'),
(4, '2019-11-02'), (4, '2019-11-04'), (4, '2019-11-13'), (4, '2019-11-14'),
(5, '2019-11-03'), (5, '2019-11-04'), (5, '2019-11-05'), (5, '2019-11-10'),
(6, '2019-11-01'), (6, '2019-11-02'), (6, '2019-11-03'), (6, '2019-11-05');
查询
我真正努力的地方是创建一个表达式来计算 list_rank
:
SELECT
`list_id`,
`date`,
<PLEASE HELP> as `list_rank`
FROM
`list_date`
ORDER BY
`list_rank`, `date`;
预期结果
| list_id | date | list_rank |
|---------|------------|-----------|
| 6 | 2019-11-01 | 1 |
| 6 | 2019-11-02 | 1 |
| 6 | 2019-11-03 | 1 |
| 6 | 2019-11-05 | 1 |
| 3 | 2019-11-01 | 2 |
| 3 | 2019-11-02 | 2 |
| 3 | 2019-11-03 | 2 |
| 2 | 2019-11-01 | 3 |
| 2 | 2019-11-03 | 3 |
| 2 | 2019-11-04 | 3 |
| 1 | 2019-11-02 | 4 |
| 1 | 2019-11-03 | 4 |
| 1 | 2019-11-04 | 4 |
| 1 | 2019-11-05 | 4 |
| 1 | 2019-11-07 | 4 |
| 1 | 2019-11-08 | 4 |
| 1 | 2019-11-09 | 4 |
| 4 | 2019-11-02 | 5 |
| 4 | 2019-11-04 | 5 |
| 4 | 2019-11-13 | 5 |
| 4 | 2019-11-14 | 5 |
| 5 | 2019-11-03 | 6 |
| 5 | 2019-11-04 | 6 |
| 5 | 2019-11-05 | 6 |
| 5 | 2019-11-10 | 6 |
或
该图像是我的应用程序生成的当前实时结果。目前排序是使用 Java.
实现的编辑
在没有收到更好的答案后,我按照@gordon-linoff 的建议实施了一个解决方案:
SELECT
`list_id`,
`date`
FROM
`list_date`
INNER JOIN (
SELECT `sub`.`list_id`,
GROUP_CONCAT(`sub`.`date` ORDER BY `sub`.`date` SEPARATOR '') as `concat_dates`
FROM `list_date` as `sub`
GROUP BY `sub`.`list_id`
) `all_dates` ON (`all_dates`.`list_id` = `list_date`.`list_id`)
ORDER BY
`all_dates`.`concat_dates`, `date`;
我还创建了一个 SQL Fiddle - 所以你可以尝试你的解决方案。
但此解决方案并未按预期对列表进行排序,因为较长的列表被认为比较小的列表大。
所以我仍然希望收到一个解决我 100% 需求的解决方案:)
如果我没理解错的话,您可以按连接在一起的日期对列表进行排序:
select ld.*
from list_date ld join
(select list_id, group_concat(date) as dates
from ld
group by list_id
) ldc
on ld.list_id = ldc.list_id
order by ldc.dates, ld.date;
因为它用于 MySql 8,所以 window 函数可用于此(是的)。
这是一个首先计算一些指标的查询,用于计算排名:
SELECT
list_id,
`date`,
DENSE_RANK() OVER (ORDER BY ListMinDate ASC, ListCount DESC, ListMaxDate, list_id) AS list_rank
FROM
(
SELECT
list_id,
`date`,
COUNT(*) OVER (PARTITION BY list_id) AS ListCount,
MIN(`date`) OVER (PARTITION BY list_id) AS ListMinDate,
MAX(`date`) OVER (PARTITION BY list_id) AS ListMaxDate
FROM list_date
) q
ORDER BY list_rank, `date`
对 db<>fiddle here
的测试