SQL-запрос - как применить лимит внутри группы по - PullRequest
16 голосов
/ 15 апреля 2009

У меня есть таблица с именем t1 со следующими полями: ROWID, CID, PID, Score, SortKey

имеет следующие данные:

1, C1, P1, 10, 1
2, C1, P2, 20, 2
3, C1, P3, 30, 3

4, C2, P4, 20, 3
5, C2, P5, 30, 2

6, C3, P6, 10, 1
7, C3, P7, 20, 2

какой запрос я пишу, чтобы он применял group by к CID, но вместо того, чтобы возвращать мне 1 единственный результат на группу, он возвращает мне максимум 2 результата на группу. также, где условие - оценка> = 20, и я хочу, чтобы результаты упорядочивались по CID и SortKey.

Если бы мне пришлось выполнить свой запрос на вышеуказанных данных, я бы ожидал следующий результат:

РЕЗУЛЬТАТЫ ДЛЯ С1 - примечание: РОУИД 1 не считается его оценкой <20 </strong>

C1, P2, 20, 2
C1, P3, 30, 3

РЕЗУЛЬТАТЫ ДЛЯ C2 - примечание: ROWID 5 появляется перед ROWID 4, так как ROWID 5 имеет меньшее значение SortKey

C2, P5, 30, 2
C2, P4, 20, 3

РЕЗУЛЬТАТЫ ДЛЯ C3 - примечание: ROWID 6 не отображается, так как его оценка меньше 20, поэтому здесь возвращается только 1 запись

C3, P7, 20, 2

КОРОТКО, Я ХОЧУ ОГРАНИЧИТЬСЯ В ГРУППЕ BY. Я хочу самое простое решение и хочу избежать временных таблиц. подзапросы в порядке. Также обратите внимание, что я использую SQLite для этого.

Ответы [ 5 ]

13 голосов
/ 16 ноября 2012

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

SELECT *
FROM table1 a 
WHERE a."ROWID" IN (
    SELECT b."ROWID" 
    FROM table1 b 
    WHERE b."Score" >= 20 
      AND b."ROWID" IS NOT NULL 
      AND a."CID" = b."CID" 
    ORDER BY b."CID", b."SortKey" 
    LIMIT 2
)
ORDER BY a."CID", a."SortKey";

В запросе используется коррелированный подзапрос с сортировкой и ограничением для создания списка ROWID с, который должен появиться в конечном результате. Поскольку коррелированный подзапрос выполняется для каждой строки, независимо от того, включен ли он в результат, он может быть не таким эффективным, как версия оконной функции, приведенная ниже, - но в отличие от этой версии он будет работать на SQLite3, который не поддерживает окно функции.

Этот запрос требует, чтобы ROWID был уникальным (может использоваться как первичный ключ).

Я тестировал вышеупомянутое в PostgreSQL 9.2 и в SQLite3 3.7.11; это отлично работает в обоих. Это не будет работать на MySQL 5.5 или последней версии 5.6, потому что MySQL не поддерживает LIMIT в подзапросе, используемом с IN.

Демонстрации SQLFiddle:

  • PostgreSQL (работает нормально): http://sqlfiddle.com/#!12/22829/3

  • SQLite3 (работает нормально, тот же текст запроса, но необходимы вставки с одним значением из-за явного ограничения драйвера JDBC): http://sqlfiddle.com/#!7/9ecd8/1

  • MySQL 5.5 (не работает двумя способами; MySQL не нравится a."ROWID" цитирование даже в режиме ANSI, поэтому мне пришлось снять кавычки; затем произойдет сбой с This version of MySQL doesn't yet support 'LIMIT & IN/ALL/ANY/SOME subquery): http://sqlfiddle.com/#!2/e1f31/2

Демонстрация SQLite, показывающая, что она прекрасно работает в командной строке SQLite3: http://pastebin.com/26n4NiUC

Вывод (PostgreSQL):

 ROWID | CID | PID | Score | SortKey 
-------+-----+-----+-------+---------
     2 | C1  | P2  |    20 |       2
     3 | C1  | P3  |    30 |       3
     5 | C2  | P5  |    30 |       2
     4 | C2  | P4  |    20 |       3
     7 | C3  | P7  |    20 |       2
(5 rows)

Если вы хотите выполнить фильтрацию для определенного CID, просто добавьте AND "CID" = 'C1' или что-то еще к external WHERE предложению.

Вот тесно связанный ответ с более подробными примерами: https://stackoverflow.com/a/13411138/398670


Поскольку это было изначально помечено просто SQL (без SQLite) ... просто для полноты, в PostgreSQL или других БД с поддержкой оконной функции стандарта SQL, я бы, вероятно, сделал это:

SELECT "ROWID", "CID", "PID", "Score", "SortKey"
FROM (
  SELECT *, row_number() OVER (PARTITION BY "CID" ORDER BY "SortKey") AS n
  FROM table1
  WHERE "Score" >= 20
) x
WHERE n < 3
ORDER BY "CID", "SortKey";

, который дает тот же результат. SQLFiddle, включая дополнительную строку C1, чтобы продемонстрировать, что ограничивающий фильтр действительно работает: http://sqlfiddle.com/#!12/22829/1

Если вы хотите выполнить фильтрацию для определенного CID, просто добавьте AND "CID" = 'C1' или что-то еще к внутреннему WHERE предложению.


Кстати, ваших тестовых данных недостаточно, так как они не могут иметь более двух строк для любого CID со счетом> 20.

4 голосов
/ 22 октября 2011

Это на самом деле не проблема GROUP BY (вы не агрегируете значения). Это самая большая проблема для каждой группы (я думаю, что на самом деле в Stackoverflow есть тег greatest-n-per-group).

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

SELECT * FROM table T1 WHERE Score > 20 AND
  (SELECT COUNT(*) FROM table T2 
      WHERE T2.CID = T1.CID AND T2.SortKey <= T1.SortKey AND T2.RowID <> T1.RowID
        AND T1.Score > 20) < 2;
  ORDER BY CID, SortKey;

Для этого нужно рассмотреть только те строки с оценками выше 20. Затем для каждой строки-кандидата подсчитывается количество других строк в той же таблице, которые имеют оценки> 20, но сортировки по ключам меньше или равны на сортировку этого ряда. Если это число равно 0 или 1 строке, то эта строка соответствует критериям включения в результаты.

Наконец, ЗАКАЗАТЬ выполняет ваш род.

0 голосов
/ 15 апреля 2009

Разве это не должно быть так просто, как **

SELECT CID, PID, Score, SortKey
FROM T1
WHERE score >= 20
ORDER BY CID, Sortkey

**

или я что-то упустил?

0 голосов
/ 02 июня 2011

Мы можем использовать предложение LIMIT в SQLite. Например:

select * from tableName where field1='something' limit 10;

это дает 10 первых строк.

0 голосов
/ 15 апреля 2009

В MySQL:

SELECT  l.*
FROM    (
        SELECT  cid,
                COALESCE(
                (
                SELECT  id
                FROM    mytable li
                WHERE   li.cid = dlo.cid
                        AND li.score >= 20
                ORDER BY
                        li.cid, li.id
                LIMIT 1, 1
                ), CAST(0xFFFFFFFF AS DECIMAL)) AS mid
        FROM    (
                SELECT  DISTINCT cid
                FROM    mytable dl
                ) dlo
        ) lo, mytable l
WHERE   l.cid >= lo.cid
        AND l.cid <= lo.cid
        AND l.id <= lo.mid
        AND l.score >= 20
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...