добавить столбец для выбора оператора при наличии определенного условия - PullRequest
0 голосов
/ 24 апреля 2019

У меня есть оператор SQL для данных из order_details, таблица, которая имеет много столбцов, включая название продукта, код и т. Д. Как я могу добавить столбец к утверждению select, если у заказа есть определенный продукт (код продукта, который мне нуженназывается «Пап») он пишет флаг «Пап», так что я могу визуально узнать, какие заказы имеют этот продукт?

Я попробовал код ниже:

select distinct order_id, customer_id,
  (select distinct order_id from order_details 
   group by 1 having sum (case when product_code='pap' 
   then 1 else 0 end)=1
  ) as pap from orders 
left join order_details 
  on order_details.order_id=orders.order_id
group by 1,2,3

Код яПопытка дает мне ошибку "[Firebird] несколько строк в синглтоне выбора; HY000".

Ответы [ 4 ]

1 голос
/ 24 апреля 2019

В предположении, вы хотите показать 'pap' для заказов, которые имеют один или несколько order_details с product_code 'pap', в этом случае вы можете использовать:

select order_id, customer_id,
  (select max(order_details.product_code)
      from order_details 
      where order_details.order_id = orders.order_id 
      and order_details.product_code = 'pap') as pap
from orders 

или более общее решение (это не зависит от кода продукта для отображения значения):

select order_id, customer_id,
  case 
    when exists(
      select 1 
      from order_details 
      where order_details.order_id = orders.order_id 
      and order_details.product_code = 'pap') 
      then 'pap' 
  end as pap
from orders 
1 голос
/ 25 апреля 2019

Давайте попробуем построить ваш запрос шаг за шагом. От простого к более сложному в устаревшей моде снизу вверх: -)

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

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 */) Вы также можете начать видеть изменение времени.

0 голосов
/ 25 апреля 2019

выберите order_id, customer_id (выберите max (order_details.product_code) из order_details, где order_details.order_id = orders.order_id и order_details.product_code = 'pap') в качестве pap из заказов

0 голосов
/ 24 апреля 2019
SELECT
    ...
    <foreign_table>.<your_desired_extra_column>
FROM
    <current_table>
LEFT JOIN
    <foreign_table> ON <foreign_table>.id = <current_table>.id
    AND 
    <current_table>.<condition_field> = <condition_value>

Дополнительный столбец будет НЕДЕЙСТВИТЕЛЕН, если условие не выполнено.

...