Являются ли встроенные запросы плохой идеей? - PullRequest
3 голосов
/ 21 ноября 2008

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

У меня есть запрос, который сделает это, но мне интересно, если это лучший вариант. Я не могу не думать, что использование WHERE x IN (SELECT ...) лениво и не лучший способ сформулировать запрос - любой запрос.

Таблица выглядит следующим образом:

CREATE TABLE generator_logs (
    id integer NOT NULL,
    site_id character varying(4) NOT NULL,
    start timestamp without time zone NOT NULL,
    "end" timestamp without time zone NOT NULL,
    duration integer NOT NULL
);

И запрос:

SELECT id, site_id, start, "end", duration 
FROM generator_logs
WHERE start IN (SELECT MAX(start) AS start 
                FROM generator_logs 
                GROUP BY site_id) 
ORDER BY start DESC

Объем данных невелик, поэтому я не беспокоюсь об оптимизации запроса. Однако мне приходится делать аналогичные вещи с таблицами с десятками миллионов строк (насколько мне известно, с большими таблицами!), И там оптимизация важнее.

Так есть ли лучший запрос для этого и являются ли встроенные запросы вообще плохой идеей?

Ответы [ 5 ]

4 голосов
/ 21 ноября 2008

Если ваш запрос не коррелируется? i.e.:

SELECT id, site_id, start, "end", duration 
FROM generator_logs g1
WHERE start = (SELECT MAX(g2.start) AS start 
               FROM generator_logs  g2
               WHERE g2.site_id = g1.site_id) 
ORDER BY start DESC

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

Или альтернативно:

SELECT id, site_id, start, "end", duration 
FROM generator_logs g1
WHERE (site_id, start) IN (SELECT site_id, MAX(g2.start) AS start 
                           FROM generator_logs  g2
                           GROUP BY site_id)
ORDER BY start DESC
1 голос
/ 21 ноября 2008

Я бы использовал соединения, так как они работают намного лучше, чем предложение "IN":

select gl.id, gl.site_id, gl.start, gl."end", gl.duration 
from 
    generator_logs gl
    inner join (
        select max(start) as start, site_id
        from generator_logs 
        group by site_id
    ) gl2
        on gl.site_id = gl2.site_id
        and gl.start = gl2.start

Также как Тони указал , что вы пропустили корреляцию в исходном запросе

0 голосов
/ 14 июня 2014

Способ найти записи, имеющие значение MAX на группу , - выбрать те записи, для которых нет записи в той же группе , имеющей более высокое значение:

SELECT id, site_id, "start", "end", duration 
FROM generator_logs g1
WHERE NOT EXISTS (
    SELECT 1
    FROM generator_logs g2
    WHERE g2.site_id = g1.site_id
    AND g2."start" > g1."start"
    );
0 голосов
/ 14 июня 2014

Эта проблема - поиск не только MAX, но и остальной части соответствующей строки - является распространенной. К счастью, Postgres предоставляет хороший способ сделать это одним запросом, используя DISTINCT ON:

SELECT DISTINCT ON (site_id)
  id, site_id, start, "end", duration
FROM generator_logs
ORDER BY site_id, start DESC;

DISTINCT ON (site_id) означает «вернуть одну запись за site_id». Условие order by определяет, какая это запись. Однако обратите внимание, что это немного отличается от вашего исходного запроса - если у вас есть две записи для одного и того же сайта с одинаковым start, ваш запрос вернет две записи, в то время как будет возвращена только одна.

0 голосов
/ 21 ноября 2008

В MYSQL это может быть проблематично, потому что Last i Checked не смог эффективно оптимизировать подзапросы (т. Е. Путем переписывания запросов)

Многие СУБД имеют планировщики Генетических запросов, которые будут выполнять одно и то же независимо от структуры ваших входных запросов.

В некоторых случаях MYSQL создает временную таблицу, в других случаях - нет, и в зависимости от обстоятельств индексация, условия, подзапросы все еще могут выполняться довольно быстро.

Некоторые жалуются, что подзапросы трудны для чтения, но они прекрасно работают, если разбить их на локальные переменные.

$maxids = 'SELECT MAX(start) AS start FROM generator_logs GROUP BY site_id';
$q ="     
    SELECT id, site_id, start, \"end\", duration 
       FROM generator_logs
       WHERE start IN ($maxids) 
       ORDER BY start DESC
";
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...