Добавить столбец для выбора оператора при наличии определенного условия

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

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

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».

Если бы вы правильно отформатировали это, я бы опубликовал свою душу с вашим кодом: D

SirPilan 24.04.2019 19:21

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

Mark Rotteveel 24.04.2019 19:42
order_details - может ли в одном заказе быть несколько строк с одним и тем же товаром? Возможен ли заказ с 2+3 капсулами или только с одним итоговым 5 капсулами? Является ли (order_id,product_code) unique constraint или primary key над этой таблицей или нет?
Arioch 'The 25.04.2019 10:39
ReactJs | Supabase | Добавление данных в базу данных
ReactJs | Supabase | Добавление данных в базу данных
Это и есть ваш редактор таблиц в supabase.👇
Понимание Python и переход к SQL
Понимание Python и переход к SQL
Перед нами лабораторная работа по BloodOath:
0
3
74
4
Перейти к ответу Данный вопрос помечен как решенный

Ответы 4

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>

Дополнительный столбец будет NULL, если условие не выполнено.

Ответ принят как подходящий

Полагаю, вы хотите показать «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 

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

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 

Отличный Марк! Общее решение работало отлично. Спасибо

Jonathan Livingston Seagull 24.04.2019 20:19

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

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

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

Лайфхак: обратите внимание, как использование coalesce и first(1) здесь заменяет использование case и exists в оригинальном ответе Марка. Этот трюк можно использовать в Firebird везде, где вы используете единичный (и потенциально пустой) запрос с 1 столбцом в качестве выражения.


Чтобы избежать нескольких подзапросов и переключиться на внешнее соединение, нам нужно сделать один запрос, чтобы получить ВСЕ идентификаторы заказов с Paps, но только один раз.

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 и улучшить весь компьютер - очистить указанные кеши - а затем получить эти запросы к последним строкам (выполнив «выбрать все» или «прокрутить до последней строки» в вашей базе данных IDE , или завернув мои запросы и запросы Марка в select count(1) from ( /* measured query here */) вы также можете заметить изменение времени.

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

постарайтесь выделить ключевые слова и четко указать формат, это поможет донести ваш ответ до других

Agilanbu 25.04.2019 11:48
MAX(null) = null - этот запрос, вероятно, потребует еще одного шага для замены NULL пустой строкой. Мы не знаем, как клиентское приложение отреагирует на строки NULL, поэтому более безопасным подходом было бы экранировать от него NULL.
Arioch 'The 25.04.2019 11:57

Другие вопросы по теме