#sql #sql-server-2014
#sql #sql-server-2014
Вопрос:
Мне нужно написать запрос для заказов на поставку и получить данные заголовка и строки. Это отношение «один ко многим», где существует одна строка заголовка PO_ID
( PS_PO_HDR
) к одной или нескольким связанным строкам ( PS_PO_LINE
) через PO_ID
идентификатор. Мне нужно, чтобы запрос исключал строки, где значение CANCEL_STATUS
равно «X», ЕСЛИ это не одно и то же PO_ID
, и BUSINESS_UNIT
имеет другие строки со значениями, CANCEL_STATUS
отличными от X'
.
По сути, мне нужно иметь возможность запрашивать с использованием других критериев в моем запросе ниже (кроме конкретного PO_ID
, который соответствует этому сценарию), который будет учитывать вышеуказанные критерии. Если PO_ID
/ BUSINESS_UNIT
имеет CANCEL_STATUS
значение ‘X’ во всех его строках, я не хочу, чтобы это PO_ID
было включено в результаты.
Я бы хотел, чтобы приведенное ниже PO_ID
было включено, потому что, хотя у него есть строка с CANCEL_STATUS = 'X'
, у него также есть другие строки CANCEL_STATUS
с другими значениями (кроме ‘X’)
Как я могу определить PO_ID
, где это происходит?
SELECT LINE.BUSINESS_UNIT, LINE.PO_ID, LINE.LINE_NBR, LINE.CANCEL_STATUS, LINE.CHANGE_STATUS
FROM PS_PO_LINE LINE
INNER JOIN PS_PO_HDR HEADER ON HEADER.BUSINESS_UNIT = LINE.BUSINESS_UNIT
AND HEADER.PO_ID = LINE.PO_ID
--ONLY USING THE FOLLOWING PO_ID FILTER TO ILLUSTRATE THIS EXAMPLE!!
WHERE LINE.PO_ID = 'A010027905'
AND HEADER.PO_STATUS NOT IN ('C','X','PX')
Комментарии:
1. Я предполагаю, что цвета на вашем изображении присутствуют не случайно и что-то значат. Было бы полезно поделиться этим значением.
2. @GordonLinoff, я думаю, он просто выделил значения «X» против значений «C» и «A», чтобы подчеркнуть свою точку зрения. Определенный PO может содержать несколько строк, и каждая из этих строк может иметь разное значение CANCEL_STATUS. В случае с его примером, даже если одна строка имеет значение CANCEL_STATUS «X», поскольку другие строки не являются «X», тогда это значение должно появиться в результатах запроса. И наоборот, если это было так, что каждая строка для этого PO имела значение CANCEL_STATUS «X», то это PO не должно отображаться в результатах запроса
3. @Craig да, это именно то, чего я пытаюсь достичь.
Ответ №1:
Вы должны получить результаты, которые вам нужны, с помощью чего-то вроде этого
SELECT LINE.BUSINESS_UNIT, LINE.PO_ID, LINE.LINE_NBR, LINE.CANCEL_STATUS, LINE.CHANGE_STATUS
FROM PS_PO_LINE LINE
INNER JOIN PS_PO_HDR HEADER ON HEADER.BUSINESS_UNIT = LINE.BUSINESS_UNIT
AND HEADER.PO_ID = LINE.PO_ID
WHERE EXISTS
(SELECT 1 FROM PS_PO_LINE AS [LineCheck] WHERE [LineCheck].PO_ID = HEADER.PO_ID AND [LineCheck].BUSINESS_UNIT = HEADER.BUSINESS_UNIT AND [LineCheck].CANCEL_STATUS != 'X');
Не уверен, насколько велики ваши таблицы данных на самом деле и какой тип индексации применяется. Если это очень большие таблицы, которые не индексируются в некоторых из этих столбцов запроса, вам, возможно, придется посмотреть на это, чтобы обеспечить приемлемую производительность
Комментарии:
1. Кроме того, я не мог точно определить из вашего вопроса, хотите ли вы по-прежнему включать отмененную строку в фактические выходные результаты — в случае, когда есть хотя бы одна строка PO, которая не отменена. Мое решение, приведенное выше, дает вашему запросу логику, позволяющую решить, должен ли он выводить определенный результат или нет. Я уверен, что затем вы сможете добавить другие критерии по мере необходимости для достижения конечного результата
2. в этом случае я бы не хотел включать отмененную строку в выходные данные.
3. Понял. Итак, вы просто добавляете СТРОКУ И. CANCEL_STATUS != ‘X’ в конец запроса, чтобы эти строки фактически не были включены в выходные результаты
Ответ №2:
Вы должны быть в состоянии сделать что-то вроде этого. Использование exists
для фильтрации строк — сравните количество строк для po_id с количеством строк, имеющих статус ‘X’, если значения отличаются, пропустите его.
select l.BUSINESS_UNIT, l.PO_ID, l.LINE_NBR, l.CANCEL_STATUS, l.CHANGE_STATUS
from PS_PO_LINE l
inner join PS_PO_HDR h on h.BUSINESS_UNIT = l.BUSINESS_UNIT
and h.PO_ID = l.PO_ID
where exists (
select * from PS_PO_LINE x
where x.po_id=l.po_id
having Count(*) != Sum(case when cancel_status='X' then 1 else 0 end)
)
Комментарии:
1. Не уверен, что это сработает, как следует из вашего ответа. Вы хотели сделать COUNT(*) != SUM(…. , или иначе это должно быть ТАМ, ГДЕ НЕ СУЩЕСТВУЕТ?
2. @craig да, вы правы, у меня было две версии, я тестировал одну с exists и not exists, я пропустил
!=
ошибку копирования и вставки. Внесены изменения!