MySQL: ORDER BY + GROUP BY с индивидуальным заказом - PullRequest
2 голосов
/ 23 февраля 2012

У меня есть несколько сложный вопрос, связанный с MySQL. Это таблица, которую я имею:

keyword   args     title    namespace_id      ratio
en        1        A        23                0.5
en        1        B        89                0.6
en        0        C        89                0.4
foo       1        Foo      23                0.7
bar       1        Bar      89                0.3

Я хочу получить список всех строк без дубликатов (ключевые слова, аргументы). Если есть дубликаты, выбранная строка должна быть выбрана в порядке, указанном мною namespace_ids. Оставшиеся строки должны быть упорядочены по соотношению.

Пример результата с namespace_id порядка 23,89, x, y:

keyword   args     title    namespace_id      ratio
foo       1        Foo      23                0.7
en        1        A        23                0.5
en        0        C        89                0.4
bar       1        Bar      89                0.3

Пример результата с порядком пространства имен 89,23, x, y:

keyword   args     title    namespace_id      ratio
foo       1        Foo      23                0.7
en        1        B        89                0.6
en        0        C        89                0.4
bar       1        Bar      89                0.3

Есть ли какой-нибудь способ, которым я могу это сделать прямо в MySQL? Я взглянул на GROUP BY, ORDER BY и заметил даже функцию GROUP_CONCAT (), но мне не удалось собрать все вместе должным образом. Утверждение, которое так или иначе мне нужно:

  SELECT keyword, args, title, namespace_id, ratio 
    FROM tbl 
GROUP BY keyword, args 
ORDER BY ratio DESC;

Но теперь я не знаю, как внести порядок namespace_id.

Я нашел похожие вопросы, подобные этим: MySQL: "упорядочить" внутри "группы по" Ответ там близок к тому, что я хочу, однако мой порядок namespace_id варьируется и не может быть рассчитан функцией MAX ().

РЕДАКТИРОВАТЬ: Задача здесь состоит в том, чтобы сказать GROUP BY, какую строку выбрать. Обычный ORDER BY, по-видимому, этого не делает, он использует только вывод GROUP BY.

Ответы [ 5 ]

1 голос
/ 26 февраля 2012

Попробуйте использовать FIELD () и подзапрос:

SELECT t1.keyword, t1.args, t1.title, t1.namespace_id, t1.ratio
FROM tbl t1, (SELECT keyword, args, MIN(FIELD(namespace_id, 23, 89))
minfield FROM tbl GROUP BY keyword, args) t2
WHERE t1.keyword = t2.keyword AND t1.args = t2.args AND
FIELD(t1.namespace_id, 23, 89) = t2.minfield
ORDER BY ratio DESC;

или self-external-join:

SELECT t1.keyword, t1.args, t1.title, t1.namespace_id, t1.ratio
FROM tbl t1
LEFT OUTER JOIN tbl t2 ON
    t1.keyword = t2.keyword AND
    t1.args = t2.args AND
    FIELD(t1.namespace_id, 23, 89) < FIELD(t2.namespace_id, 23, 89)
WHERE t2.keyword IS NULL AND t2.args IS NULL
ORDER BY t1.ratio DESC;

Редактировать: Возможно, стоит взглянуть на некоторые(коммерческая) СУБД, поддерживающая операции OLAP (если вы можете выбирать и обрабатываете ли вы большие наборы данных).В случае с Георгом я думаю, что ключевые слова OVER и PARTITION BY помогли бы: http://publib.boulder.ibm.com/infocenter/rbhelp/v6r3/topic/com.ibm.redbrick.doc6.3/sqlrg/sqlrg36.htm#sii06377181

0 голосов
/ 25 февраля 2012

После пересмотра вашего вопроса и ваших комментариев к моему предыдущему ответу, я думаю, вы просто не можете сделать это . И вот почему:

Поскольку вы хотите фильтровать строки после упорядочения их, единственным вариантом будет предложение HAVING, которое, насколько мне известно, единственное, что обрабатывается после предложения ORDER BY.

Поскольку предложение HAVING просматривает только каждую строку по отдельности, но вы хотите фильтровать по его относительной позиции в наборе (т.е. вам нужна только первая строка каждого подмножества с одинаковым ключевым словом / аргументом), вам придется «переправить» это значение в каждую строку. Я кратко подумал об этом варианте, но мог придумать только глупости.

Пропустите GROUP ing, получите право ORDER (связав сортируемое значение priority с каждым namespace_id), а затем в своем коде используйте структуру данных, которая имеет ключ к keyword и arg и игнорировать дубликаты при заполнении набора результатов.

Если вам по какой-то непонятной причине абсолютно необходимо все делать в SQL, вы можете смоделировать то, что я описал выше, создав временную таблицу (в памяти), которая имеет ограничение UNIQUE на два столбца и выполните INSERT IGNORE INTO temp_table SELECT ...


Примечание: каждый раз, когда вы замечаете, что SQL хочет что-то сделать со строкой, которая зависит от других строк в наборе результатов, вам, вероятно, не повезло. Я неоднократно сталкивался со случаями, которые, по моему мнению, были бы легкими, пока не заметил, что я приблизился к наборам результатов, как если бы они были циклами . Мой любимый анекдот - это время, когда я отчаянно пытался вычесть значение в одном столбце из его аналога в предыдущей строке. Иногда вы можете попытаться взломать свой выход с помощью странных JOIN с (объединяя связанные строки в одну), но даже если это работает, это, вероятно, дорого.

0 голосов
/ 23 февраля 2012

Вы можете использовать FIELD () для генерации пользовательской последовательности сортировки следующим образом:

SELECT keyword, args, title, namespace_id, FIELD(namespace_id, 32, 89) sorting, ratio 
FROM tbl 
GROUP BY keyword, args
ORDER BY sorting DESC, ratio DESC;

Обратите внимание, что любой namespace_id, не указанный в функции FIELD(), получит значение 0 sorting, поэтому, чтобы эти элементы отображались first в результатах, необходимо указать их в обратном порядке и используйте DESC для порядка сортировки.

Таким образом, FIELD(namespace_id, 32, 89) с ORDER BY sorting DESC приведет к:

x x 89 x
x x 32 x
...

Принимая во внимание, что FIELD(namespace_id, 89, 32) с ORDER BY sorting DESC приведет к:

x x 32 x
x x 89 x
...
0 голосов
/ 24 февраля 2012

Один слегка хакерский способ сделать это - упорядочить по нескольким логическим выражениям, например так:

SELECT keyword, args, title, namespace_id, ratio 
FROM tbl 
GROUP BY keyword, args 
ORDER BY namespace_id != 89,
         namespace_id != 23,
         namespace_id != x,
         namespace_id != y,
         ratio DESC;

Очевидно, это очень непрактично.

Если вы действительнонеобходимо сделать это на стороне SQL, я предлагаю вам создать еще одну таблицу, содержащую столбцы namespace_id (которые должны иметь ограничение UNIQUE) и priority (или аналогичные).Тогда вы JOIN в этой таблице и ORDER BY priority.

Скорее всего, у вас уже есть таблица для пространств имен, упомянутых namespace_id.В этом случае просто добавьте столбец priority в эту таблицу.

0 голосов
/ 23 февраля 2012

Я думаю, это то, что вы хотите -

SELECT t1.* FROM tbl t1
  JOIN(SELECT keyword, args, MIN(ratio) ratio FROM tbl GROUP BY keyword, args) t2
    ON t1.keyword = t2.keyword AND t1.args = t2.args AND t1.ratio = t2.ratio
  ORDER BY ratio DESC;

и второй -

SELECT t1.* FROM tbl t1
  JOIN(SELECT keyword, args, MAX(ratio) ratio FROM tbl GROUP BY keyword, args) t2
    ON t1.keyword = t2.keyword AND t1.args = t2.args AND t1.ratio = t2.ratio
  ORDER BY ratio DESC;
...