выбрать все строки с минимальным значением


9

В Sqlite 3 я пытаюсь выяснить, как выбирать строки на основе минимального значения. Я думаю, что я ограничен тем, что не знаю достаточно терминологии для эффективного поиска в Google.

Стол выглядит так:

num         text        num2      
----------  ----------  ----------
0           a           1         
0           a           2         
1           a           3         
1           b           4         

Я хочу получить строки, где num2есть 1, 2, и 4. Я хочу сделать выбор на основе минимального значения num для каждого уникального значения текстового столбца.

Итак, для text = 'a', минимальное значение numравно 0, поэтому я хочу строки 1 и 2. Для text = 'b', минимальное значение numравно 1, поэтому я хочу строки 4.

Используя различные комбинации группы по, я могу получить или строки 1и 2или строку 1и 4. Я чувствую, что мне не хватает компонента SQL, который будет делать то, что я хочу, но я не смог понять, что это может быть.

Как правильно сделать этот тип запроса?

Возможное решение

Я нашел в способ сделать это. Я не достаточно авторитетный, чтобы ответить на свой вопрос, поэтому я делаю обновление здесь. Я не уверен, всегда ли это правильно или какова эффективность. Любые комментарии приветствуются.

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

sqlite> select num, text from t group by text having num = min( num );
num         text      
----------  ----------
0           a         
1           b         

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

sqlite> with u as
      ( select num, text from t group by text having num = min( num ) )
        select t.* from t join u on t.num = u.num and t.text = u.text;
num         text        num2      
----------  ----------  ----------
0           a           1         
0           a           2         
1           b           4         

Ответы:


10

Как вы видели, простой GROUP BY не будет работать, потому что он будет возвращать только одну запись на группу.

Ваше объединение работает отлично. Для большой таблицы это будет эффективно только при наличии индекса в столбцах соединения ( numи text).

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

SELECT *
FROM t
WHERE num = (SELECT MIN(num)
             FROM t AS t2
             WHERE t2.text = t.text);

SQLFiddle

При выполнении этого запроса не требуется временная таблица (ваш запрос делает для результата u), но он будет выполнять подзапрос для каждой записи в t, поэтому textдолжен быть проиндексирован. (Или используйте индекс на обоих textи numполучите индекс покрытия .)


у него нет никакой временной таблицы в его запросе, только CTE, который является совершенно другим.
ypercubeᵀᴹ

При выполнении результат uзапроса сохраняется во временной таблице независимо от того, записан ли он как CTE, представление или встроенный как подзапрос.
кл.

Спасибо, эту версию гораздо проще написать, чем ту, на которую я наткнулся. Знание правильной терминологии также помогает мне разобраться в этом подробнее.
user35292

@CL Так SQLite выполняет запросы с CTE? У вас есть ссылка на это? Потому что другие СУБД не обязательно используют временные таблицы для ctes.
ypercubeᵀᴹ

@ypercube , CTE, представления и подзапросы сплющенные или реализованы как сопрограммы, если это возможно. Но GROUP BY в неиндексированном столбце должен иметь возможность собирать данные для всех групп параллельно, поэтому требуется некоторая форма временной таблицы (во всех базах данных).
кл.

1

Я склонен делать такие вещи с внешним самовключением:

SELECT
    M1.Num,
    M1.Text,
    M1.Num2
FROM
    MyDb M1
LEFT OUTER JOIN
    MyDB M2
ON
    M1.text = M2.text
AND
    M1.num > m2.num
WHERE
    M2.num is null

Это в основном говорит; дай мне все записи, которые не имеют более высокого значения, то есть ноль.


1

Так как же найти ответ на свой вопрос в следующий раз? На мой взгляд, это путем разложения и следования логике. И вы поняли это правильно:

Я хочу сделать выбор на основе минимального значения num для каждого уникального значения текстового столбца

Это переводится как:

select text, min(num) from t group by text;

(Это должно быть эквивалентно вашему havingзапросу. Может быть интересно взглянуть на строки, гдеnum равные NULL. Точнее: посмотрите, что имеют строки с нулевыми эффектами, которые вы могли бы сначала отфильтровать с помощью a where num is not null)

Отсюда вы можете достичь желаемого результата:

select * from t where (num, text) in ( *insert query above* )

Или используя соединение:

select t1.* from t t1,
    (select text, min(num) as n from t group by text) t2
where t1.num = t2.n and t1.text = t2.text.

А когда производительности недостаточно для ваших таблиц, начните смотреть на более сложные операторы.


-2

Не должен ли этот запрос быть именно тем, что вам нужно?

select min(num), text, num2 group by text, num2

Это вернет все четыре записи, потому что num2значения уникальны.
кл.
Используя наш сайт, вы подтверждаете, что прочитали и поняли нашу Политику в отношении файлов cookie и Политику конфиденциальности.
Licensed under cc by-sa 3.0 with attribution required.