Как отсортировать список списков с помощью SQL-запроса? - PullRequest
0 голосов
/ 31 октября 2019

Абстрактный вопрос

У меня есть sql-таблица, которая содержит записи в следующей форме:
(list_id, value), где list_id - это целое число, идентифицирующее конкретный список, а value - это нечтоэто имеет порядок.

Теперь я пытаюсь написать sql-запрос, который возвращает все записи этой таблицы, сначала упорядоченные по рангу, который список сравнил с другими списками, а затем упорядоченный по 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 с. Моя таблица выглядит следующим образом:

CREATE TABLE `list_date` (
  `list_id` INT  NOT NULL,
  `date`    DATE NOT NULL,
  PRIMARY KEY (`list_id`, `date`)
);

Я использую базу данных mysql: 8.0, поэтому решение с использованием WINDOW -функций является приемлемым.

Пример

Данные

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         |

или

visual representation of the expected result

Это изображение - текущий живой результат, который выдает мое приложение. В настоящее время сортировка реализована с использованием 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`;

IМы также создали SQL Fiddle - так что вы можете поиграть со своим решением.

Но это решение не сортирует списки должным образом, поскольку более длинные списки считаются большими, чем меньшие списки.

Так что я все еще надеюсь получить решение, которое решит 100% моих требований:)

Ответы [ 2 ]

0 голосов
/ 11 ноября 2019

Так как это для MySql 8, для этого могут использоваться оконные функции (yay).

Вот запрос, который сначала вычисляет некоторые метрики для использования при расчете рейтинга:

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 здесь

0 голосов
/ 31 октября 2019

Если я правильно понимаю, вы можете отсортировать списки по объединенным датам:

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;
...