Давайте попробуем построить ваш запрос шаг за шагом. От простого к более сложному в устаревшей моде снизу вверх: -)
Я предлагаю вам выполнить каждый запрос, чтобы увидеть результаты и посмотреть, как данные постепенно улучшаются, и проверить на ранней стадии, верно ли ваше предположение.
1-й неизвестен order_details
- может ли один заказ иметь несколько строк с одним и тем же продуктом? Можно ли заказать с 2 + 3 Папами или только с одним 5 Папами? (order_id,product_code
) unique constraint
или primary key
над этой таблицей или нет?
Select Count(1), order_id, product_code
From order_details
Group by 2,3
Order by 1 DESC
Это может показать, существует ли такое повторение, но даже если нет - вы должны проверить метаданные (схему), чтобы увидеть, разрешено ли это таблицей constraints
или indices
.
Дело в том, что когда вы JOIN
таблицы - их совпадающие строки умножаются (в терминах теории множеств). Так что, если у вас может есть несколько строк о Папах в одном порядке - тогда мы должны принять специальные меры по этому поводу. Что добавит дополнительную нагрузку на сервер, если мы не найдем способ сделать это бесплатно.
Мы можем легко проверить наличие одного конкретного заказа на этот продукт.
select 'pap' from order_details where order_id = :parameter_id and product_code='pap'
Затем мы можем подавлять повторы - если они не были запрещены ограничениями - стандартным способом (но требующим дополнительной сортировки) или специфичным для Firebird (но бесплатным) способом.
select DISTINCT 'pap' from order_details where order_id = :parameter_id and product_code='pap'
или
select FIRST(1) 'pap' from order_details where order_id = :parameter_id and product_code='pap'
Однако это может удовлетворить ответ Марка с коррелированным подзапросом:
select o.order_id, o.customer_id,
coalesce(
( select first(1) 'pap' /* the flag */ from order_details d
where o.order_id = d.order_id and d.product_code = 'pap' )
, '' /* just avoiding NULL */
) as pap
from orders o
Lifehack: обратите внимание, как использование coalesce
и first(1)
здесь заменяет использование case
и exists
в исходном ответе Марка. Этот прием можно использовать в Firebird везде, где вы используете единственный (и потенциально пустой) запрос с 1 столбцом в качестве выражения.
Чтобы избежать нескольких подзапросов и перейти к внешнему объединению, нам нужно сделать один запрос, чтобы иметь ВСЕ идентификаторы заказа с папками, но только один раз.
select distinct order_id from order_details where product_code='pap'
Должен сделать свое дело. Но, вероятно, ценой дополнительной сортировки для подавления возможного дублирования (опять же, возможно ли это?)
select order_id, count(order_id)
from order_details
where product_code='pap'
group by 1 order by 2 desc
Показывалось бы как повторы, если они уже есть. Просто чтобы объяснить, что я имею в виду. И посмотреть, сможете ли вы применить SQL constraints
к уже существующим данным, если у вас их нет и вы захотите укрепить структуру вашей базы данных.
Таким образом, нам просто нужно выполнить внешнее соединение с ним и использовать CASE
(или некоторую его сокращенную форму), чтобы выполнить типичную хитрость фильтрации строк NULL внешнего соединения.
select o.order_id, o.customer_id,
iif( d.order_id is null, '', 'pap') as pap
from orders o
left join (
select distinct order_id
from order_details
where product_code = 'pap'
and product_quantity > 0 ) d
on o.order_id=d.order_id
Как кто-то сказал, это выглядит некрасиво, есть еще один «современный» способ написать именно этот запрос, возможно, он бы выглядел лучше: -D
with d as (
select distinct order_id
from order_details
where product_code = 'pap'
and product_quantity > 0 )
select o.order_id, o.customer_id,
iif( d.order_id is null, '', 'pap') as pap
from orders o left join d on o.order_id=d.order_id
Если повторения 'pap' не могут (заметьте, НЕ ДЕЛАЮТ, но НЕ МОГУТ) произойти в течение одного order_id
, тогда запрос станет еще проще и быстрее:
select o.order_id, o.customer_id,
iif( d.order+id is null, '', 'pap') as pap
from orders o
left join order_details d
on o.order_id=d.order_id
and d.product_code='pap'
and d.product_quantity>0
Обратите внимание на важную деталь: d.product_code='pap'
устанавливается как внутреннее условие (до) объединения. Вы бы поместили его во внешнее предложение WHERE
после объединения - это не сработало бы!
Теперь, чтобы сравнить эти два подхода, JOIN против коррелированных подзапросов, вы должны увидеть статистику запросов, сколько выборок и кэшированных выборок сгенерировано. Скорее всего - на средних таблицах и с разогретыми кешами ОС и Firebird вы не увидите разницу во времени. Но вы бы хотя бы выключили и перезапустили службу Firebird, а лучше всего весь компьютер - чтобы очистить упомянутые кэши - и затем доставили бы эти запросы в последние строки (выполнив «fetch all» или «scroll to the last row» в вашей IDE базы данных или, включив запросы моего и Марка в
select count(1) from ( /* measured query here */)
Вы также можете начать видеть изменение времени.