MySQL не поддерживает предложение limit внутри подвыбора, как я могу это сделать? - PullRequest
0 голосов
/ 07 февраля 2009

У меня есть следующая таблица на MySQL 5.1.30:

CREATE TABLE  article (
  article_id int(10) unsigned NOT NULL AUTO_INCREMENT,
  category_id int(10) unsigned NOT NULL,
  title varchar(100) NOT NULL,
  PRIMARY KEY (article_id)
);

С этой информацией:

1, 1, 'foo'
2, 1, 'bar'
3, 1, 'baz'
4, 1, 'quox'
5, 2, 'quonom'
6, 2, 'qox'

Мне нужно получить первые три статьи в каждой категории для всех категорий, в которых есть статьи. Примерно так:

1, 1, 'foo'
2, 1, 'bar'
3, 1, 'baz'
5, 2, 'quonom'
6, 2, 'qox'

Конечно, союз будет работать:

select * from articles where category_id = 1 limit 3
union
select * from articles where category_id = 2 limit 3

Но в базе данных есть неизвестное количество категорий. Кроме того, порядок должен быть указан столбцами is_sticky и publ_date, которые я упустил из примеров для упрощения.

Можно ли создать запрос, который извлекает эту информацию?

ОБНОВЛЕНИЕ: Я попробовал следующее, что, казалось бы, сработало за исключением , что MySQL не поддерживает предложение limit внутри подвыбора. Вы знаете способ симуляции лимита там?

select *
  from articles a
  where a.article_id in (select f.article_id
                      from articles f
                      where f.category_id = a.category_id
                      order by f.is_sticky, f.published_at
                      limit 3)

Спасибо

Ответы [ 3 ]

1 голос
/ 07 февраля 2009

SELECT ... LIMIT не поддерживается в подзапросах, я боюсь, поэтому пришло время раскрыть магию самосоединения:

SELECT article.*
FROM article
JOIN (
    SELECT a0.category_id AS id, MIN(a2.article_id) AS lim
    FROM article AS a0
    LEFT JOIN article AS a1 ON a1.category_id=a0.category_id AND a1.article_id>a0.article_id
    LEFT JOIN article AS a2 ON a2.category_id=a1.category_id AND a2.article_id>a1.article_id
    GROUP BY id
) AS cat ON cat.id=article.category_id
WHERE article.article_id<=cat.lim OR cat.lim IS NULL
ORDER BY article_id;

Бит посередине определяет идентификатор статьи с третьим наименьшим идентификатором для каждой категории, пытаясь объединить три копии одной и той же таблицы в порядке возрастания идентификаторов. Если для категории меньше трех статей, левые объединения гарантируют, что предел равен NULL, поэтому внешний WHERE также должен подобрать этот случай.

Если ваше требование «top 3» может измениться на «top n» в какой-то момент, это станет громоздким. В этом случае вы можете пересмотреть идею запроса списка отдельных категорий, а затем объединить запросы для каждой категории.

ETA: заказ на две колонки: eek, новые требования! : -)

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

Мы используем самосоединение с символом «<» для воспроизведения эффекта «ORDER BY article_id». К сожалению, хотя вы можете сделать «ЗАКАЗАТЬ А, В», вы <em>не можете сделать '(а, b) <(с, d)' ... и вы не можете сделать 'МИН (а, б)». Кроме того, вы бы на самом деле упорядочивали по трем столбцам: issticky, опубликованные <em>и article_id, потому что вам нужно убедиться, что каждое значение упорядочения уникально, чтобы избежать возврата четырех или более строк.

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

LEFT JOIN article AS a1
ON a1.category_id=a0.category_id
AND HEX(a1.issticky)+HEX(a1.published_at)+HEX(a1.article_id)>HEX(a0.issticky)+HEX(a0.published_at)+HEX(a0.article_id)

это становится невероятно уродливым, и расчеты лишат вас шансов на использование индексов для повышения эффективности запроса. В этот момент вам лучше просто выполнять отдельные LIMITed-запросы для каждой категории.

0 голосов
/ 07 февраля 2009

Вот что-то, чем я не горжусь (в MS SQL - не уверен, будет ли это работать в MySQL)

select a2.article_id, a2.category_id, a2.title
from 
    (select distinct category_id
    from article) as a1
    inner join article a2 on a2.category_id = a1.category_id

where a2.article_id <= (
    select top 1 a4.article_id 
    from (
         select top 3 a3.article_id 
         from article a3 
         where a3.category_id = a1.category_id 
         order by a3.article_id asc
    ) a4 
    order by a4.article_id desc)

Это будет зависеть от поддержки MySQL подзапросов таким образом. По сути, он вырабатывает третий по величине article_id для каждой категории и объединяет все статьи, которые меньше или равны этим для каждой категории.

SELECT TOP n * должен работать так же, как SELECT * LIMIT n, я надеюсь ...

0 голосов
/ 07 февраля 2009

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

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...