Избегайте повторения условий WHERE в подзапросах

#sql #sqlite #join #subquery #inner-join

#sql #sqlite #Присоединиться #подзапрос #внутреннее соединение

Вопрос:

У меня есть SQL-запрос в соответствии с:

 SELECT * FROM a 
INNER JOIN
    (
        SELECT date, MAX(z) FROM a
        WHERE a.x = "foo" AND a.y IN (1, 2, 3)
        GROUP BY z
    ) b
    ON a.date = b.date
WHERE a.x = "foo" AND a.y IN (1, 2, 3)
  

Мой вопрос в том, пахнет ли кодом это повторяющееся условие WHERE? Возможно, ответ зависит от конкретного контекста, но я надеюсь на ответ «в целом» или некоторые указания на то, как я мог бы улучшить его.

Комментарии:

1. Это даже не компилируется: вы не можете группировать по выражению, которое вы агрегируете. Я не знаю, что вы пытаетесь сделать, по group by date крайней мере, скомпилировать. Пожалуйста, исправьте свой запрос, чтобы он был синтаксически корректным

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

3. @Bohemian, ты можешь объяснить? Он компилируется здесь: db-fiddle.com/f/xevfg5qvjeuVDy57uMV69E/1 . Я скопировал / вставил непосредственно в свой вопрос.

4. @TheImpaler, это не для конкретного примера. Я спрашиваю о том, повторяются ли условия WHERE «НОРМАЛЬНО» или нет, или всегда есть лучший способ. Если вы считаете, что это зависит от конкретного примера, пожалуйста, так и скажите.

5. @jarthur повторил условия WHERE, если они решают проблему, и лучшей альтернативы нет. В вашем случае неясно, что вы хотите сделать. Если вы уточните свое требование, возможно, мы сможем предложить лучшее решение.

Ответ №1:

Этот подзапрос:

 SELECT date, MAX(z) FROM a
WHERE a.x = "foo" AND a.y IN (1, 2, 3)
GROUP BY z
  

разрешено в SQLite, но это было бы недопустимо в большинстве других баз данных, потому GROUP BY z что, хотя вы также выполняете агрегирование z и выбираете неагрегированный столбец date .

В любом случае, что делает этот запрос, это фильтрует таблицу в соответствии с условиями WHERE предложения, а затем возвращает 1 строку для каждого отдельного z (поскольку MAX(z) фактически равно z в его собственной группе) с произвольным значением for date .

Затем вы соединяете таблицу с помощью подзапроса this в произвольные даты, которые возвращает подзапрос. Все это очень неясно в отношении того, что вы имеете в виду в качестве ожидаемого результата.

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

В вашем случае есть решение a CTE , потому что в вашем основном запросе и вашем подзапросе вы выполняете одну и ту же фильтрацию, поэтому вас интересуют только строки, для которых выполняются эти условия:

 a.x = "foo" AND a.y IN (1, 2, 3)
  

Таким образом, вы можете получить такие же результаты, как это:

 WITH cte AS (
  SELECT * FROM a 
  WHERE a.x = "foo" AND a.y IN (1, 2, 3)
)
SELECT * 
FROM cte c
INNER JOIN (
  SELECT date, MAX(z) FROM cte
  GROUP BY z
) b ON b.date = c.date 
  

Смотрите демонстрацию.

Комментарии:

1. «Но, если вас беспокоят повторяющиеся условия, тогда я должен сказать, что всегда лучше, если вы можете их избежать». Это было действительно то, что я искал. Я должен был больше подчеркнуть, что пример запроса в OP на самом деле не имеет отношения к делу (в моем фактическом запросе нет GROUP BY проблемы с агрегацией — это был недосмотр с моей стороны). Также благодарим вас за публикацию альтернативного решения, это очень полезно.

Ответ №2:

Используйте оконные функции. Если вам нужна максимальная дата на z:

 SELECT a.*,
       MAX(z) OVER (PARTITION BY date)
FROM a 
WHERE a.x = "foo" AND a.y IN (1, 2, 3);
  

Ответ №3:

Предполагая, что вам нужно максимальное z значение для the date , тогда вам нужны оба where предложения, потому что без его where предложения подзапрос вернул бы max z для любой строки для the date , а не max z для строк, которые вы запрашиваете.

В вашем запросе есть небольшая ошибка: подзапрос должен использовать GROUP BY date , а не GROUP BY z :

 SELECT * FROM a 
INNER JOIN
(
    SELECT date, MAX(z) FROM a
    WHERE a.x = "foo" AND a.y IN (1, 2, 3)
    GROUP BY date
) b
ON a.date = b.date
WHERE a.x = "foo" AND a.y IN (1, 2, 3)