#sql #algorithm #sorting
#sql #алгоритм #сортировка
Вопрос:
С таким массивом, как этот:
[google, google, yahoo, yahoo, yahoo, msn, msn, msn, google]
каков наилучший способ упорядочить его с как можно меньшим количеством смежных значений?
Цель состоит в том, чтобы получить что-то вроде этого, например:
[google, msn, yahoo, google, yahoo, msn, yahoo, msn, google]
Еще лучше был бы алгоритм, который гарантирует, что любые похожие значения находятся как можно дальше друг от друга.
А еще лучше было бы сделать это непосредственно в SQL.
Я делаю это для скрипта, над которым я работаю, который отправляет информационные бюллетени, и я бы хотел избежать одновременной отправки слишком большого количества электронных писем в один и тот же домен.
[править] Я использую MySQL
Комментарии:
1. Это сортировка? Больше похоже на перетасовку с определенными ограничениями… в любом случае, что вы пробовали до сих пор?
2. @user996651 — какую базу данных вы используете?
3. @Felix Kling — пока я выбираю их случайным образом: ВЫБЕРИТЕ * ИЗ порядка таблицы С ПОМОЩЬЮ ‘RAND ()’
Ответ №1:
Используя SQL Server 2005
и выше, вы можете воспользоваться преимуществами ROW_NUMBER
функциональности
- Добавьте номер строки к каждому элементу, разделяя на sitename .
- выберите из этого подзапроса
- упорядочите результаты по номеру строки и имени.
Ваше требование о том, что они должны быть как можно дальше друг от друга, может быть не выполнено, но в целом этого будет достаточно.
Инструкция SQL
SELECT Site
FROM (
SELECT Site, o = ROW_NUMBER() OVER (PARTITION BY Site ORDER BY Site)
FROM q
) q
ORDER BY
o, Site
Результаты
google msn yahoo google msn yahoo google msn yahoo
Тестовый скрипт
;WITH q ([site])AS (
SELECT 'google'
UNION ALL SELECT 'google'
UNION ALL SELECT 'yahoo'
UNION ALL SELECT 'yahoo'
UNION ALL SELECT 'yahoo'
UNION ALL SELECT 'msn'
UNION ALL SELECT 'msn'
UNION ALL SELECT 'msn'
UNION ALL SELECT 'google'
)
SELECT Site
FROM (
SELECT Site, o = ROW_NUMBER() OVER (PARTITION BY Site ORDER BY Site)
FROM q
) q
ORDER BY
o, Site
Ответ №2:
Спасибо Ливен, я адаптировал ваш ответ к MySQL, и он отлично работает :
select *
from (
select
@i := if(@last_name != t2.name, 1, @i 1) as row_number,
@last_name := t2.name as `name`
from
site t2,
(select @i := 0) vt1,
(select @last_name := null) vt2
order by t2.name
) t1
order by t1.row_number, t1.name
;
дает :
------------ --------
| row_number | name |
------------ --------
| 1 | google |
| 1 | msn |
| 1 | yahoo |
| 2 | google |
| 2 | msn |
| 2 | yahoo |
| 3 | google |
| 3 | msn |
| 3 | yahoo |
------------ --------