MySql: упорядочить списки по количеству элементов, а затем по содержанию - PullRequest
0 голосов
/ 04 января 2019

Я использую MySql 5.7 и мне нужно отсортировать списки, которые хранятся так:

| list_id | item   | item_index |
| ------- | ------ | ---------- |
| 0       | apple  | 0          |
| 0       | bread  | 1          |
| 1       | apple  | 0          |
| 1       | banana | 1          |
| 2       | orange | 0          |

Но эти элементы не обязательно являются строками , они могут быть целыми или логическими, это просто упрощение.

Списки должны быть упорядочены по:

  1. количество элементов в списке
  2. если два списка имеют одинаковое количество элементов, то их элементы должны сравниваться в порядке item_index

Таким образом, результат для этого примера должен быть:

  1. 2 - оранжевый
  2. 1 - яблоко, банан
  3. 0 - яблоко, хлеб

Я использую group by и count(*) для сортировки по длине списка, но проблема в том, как сортировать по содержимому списка, если максимальное количество элементов в списке неизвестно?

Единственное решение, которое я придумал, - это сделать N левые соединения на одной и той же таблице, где N - это неизвестная максимальная длина списка, каждое соединение для каждого возможного элемента списка. См. Таблицу и мои попытки отсортировать ее на DB Fiddle .

Есть ли способы сортировки таким образом, не зная максимального количества элементов в списке?

Ответы [ 3 ]

0 голосов
/ 04 января 2019

Вы можете заказать по count(*), а затем по group_concat(item), чтобы отсортировать по длине списка, а затем сравнив элементы:

select list_id, group_concat(item order by item_index asc) as items, count(*) as list_length
from yourtable
group by list_id
order by list_length asc, items asc

Обновление:

Если вы хотите упорядочить числа, то group_concat () по-прежнему работает, потому что mysql неявно преобразует числа в строки. Просто оставьте слева цифры 0, чтобы обеспечить правильную сортировку, потому что при сравнении строк 19 меньше 2. Итак, измените group_concat в следующем порядке (целое число не может содержать более 10 цифр):

group_concat(LPAD(item, 10, '0') order by item_index asc)
0 голосов
/ 04 января 2019

Это то, что вы хотите? Я не уверен насчет заказа

SELECT list_id,GROUP_CONCAT(item SEPARATOR ' ') aggregate_items ,SUM(item_index) aggregate_item_index 
FROM grocery_list
GROUP BY list_id
ORDER BY list_id DESC,COUNT(item) ASC,SUM(item_index) ASC
0 голосов
/ 04 января 2019

Вот так, если вы хотите 5 рядов из вашей скрипки:

select * from 

  --the main data
  grocery_lists gl

  --joined with
  inner join

  --the count of items in each list
  (
    select list_id, group_concat(item order by item_index asc) as grouped_items, count(*) as total_count 
    from grocery_lists gl
    group by list_id
  ) ct
  on gl.list_id = ct.list_id

--ordered by the count of items, then the index 
order by ct.total_count, ct.grouped_items, gl.item_index

Таким образом, вы получаете строки вроде:

   2, orange, 0  --sorts first because count - 1
   1, apple, 0   --sorts ahead of list 0 because "apple, banana" < "apple, bread"
   1, banana, 1
   0, apple, 0
   0, bread, 1

Если элементы списка являются целыми (и вам нужно 5 строк)

Я думаю, вам нужно сделать это:

select * from 

  --the main data
  grocery_lists gl

  --joined with
  inner join

  --the count of items in each list
  (
    select list_id, group_concat(LPAD(item, 10, '0') order by item_index asc) as grouped_items, count(*) as total_count 
    from grocery_lists gl
    group by list_id
  ) ct
  on gl.list_id = ct.list_id

--ordered by the count of items, then by padded aggregate ints, then index 
order by ct.total_count, ct.grouped_items, gl.item_index

Если ваши элементы целые, например, добавление их к ширине, например, 10, с 0 приводит к сортировке, потому что "0000000123, 00000000124" <"0000000123, 0000000125" </p>

Я выбрал 10 в ширину, потому что int max составляет 4,5 миллиарда; 10 цифр. если ваши интты будут меньше, вы можете добавить меньше

Если вы сравниваете логические значения, похожую стратегию, возможно, конвертируйте их в INT (true = 0, false = 1?), Чтобы они сортировались корректно, даже если они объединены в строку.

Если список T, T, F сортируется впереди T, F, F, тогда сделать T = 0 и F = 1 .. например

Если вы хотите 3 ряда из вашей скрипки ..

Заимствовано из тени и скорректировано на item, являющееся целым:

select list_id, group_concat(item order by item_index asc) as items, count(*) as list_length
from yourtable
group by list_id
order by list_length asc, group_concat(LPAD(item, 8, '0') order by item_index asc) asc
...