pgSQL ПОЛНОЕ ВНЕШНЕЕ СОЕДИНЕНИЕ 'ГДЕ' Условие

Я пытаюсь создать один запрос для получения текущей цены и специальной цены распродажи, если распродажа проводится; Когда распродажи нет, я хочу, чтобы store_picture_monthly_price_special.price AS special_price возвращал значение null.

Перед добавлением второго условия WHERE запрос выполняется, как я и ожидал: store_picture_monthly_price_special.price возвращает null, поскольку в настоящее время распродаж нет.

 store_picture_monthly_reference | tenure |   name    | regular_price | special_price 
                               3 |     12 | 12 Months |        299.99 | {Null}             
                               2 |      3 | 3 Months  |         79.99 | {Null}            
                               1 |      1 | 1 Month   |         29.99 | {Null}            

pgSQL обрабатывает второе условие WHERE как "все или ничего". Если нет продаж, нет и результатов.

Можно ли настроить этот запрос, чтобы я каждый раз получал регулярные цены и специальную цену продажи либо в виде долларового значения, когда выполняется специальное предложение, либо возвращает null. Требуется ли выполнить то, что я пытаюсь сделать? подзапрос?

Это запрос, как он у меня сейчас есть:

SELECT store_picture_monthly.reference AS store_picture_monthly_reference , store_picture_monthly.tenure , store_picture_monthly.name , store_picture_monthly_price_regular.price AS regular_price , store_picture_monthly_price_special.price AS special_price

FROM ( store_picture_monthly INNER JOIN store_picture_monthly_price_regular ON store_picture_monthly_price_regular.store_picture_monthly_reference = store_picture_monthly.reference )

FULL OUTER JOIN store_picture_monthly_price_special ON store_picture_monthly.reference = store_picture_monthly_price_special.store_picture_monthly_reference

WHERE 
    ( store_picture_monthly_price_regular.effective_date < NOW() ) 
    AND
    ( NOW() BETWEEN store_picture_monthly_price_special.begin_date AND store_picture_monthly_price_special.end_date )

GROUP BY store_picture_monthly.reference , store_picture_monthly_price_regular.price , store_picture_monthly_price_regular.effective_date , store_picture_monthly_price_special.price

ORDER BY store_picture_monthly_price_regular.effective_date DESC

Таблица "store_picture_monthly"

reference bigint,
name text,
description text,
tenure bigint,
available_date timestamp with time zone,
available_membership_reference bigint

Таблица store_picture_monthly_price_regular

reference bigint ,
store_picture_monthly_reference bigint,
effective_date timestamp with time zone,
price numeric(10,2),
membership_reference bigint

Таблица store_picture_monthly_price_special

reference bigint,
store_picture_monthly_reference bigint,
begin_date timestamp with time zone,
end_date timestamp with time zone,
price numeric(10,2),
created_date timestamp with time zone DEFAULT now(),
membership_reference bigint

person Ron Piggott    schedule 16.03.2019    source источник
comment
Найдите и используйте псевдонимы таблиц. Узнайте, что такое левое соединение при возврате: внутреннее соединение строк плюс несопоставленные левые строки таблицы, расширенные нулями. Всегда знайте, какое внутреннее соединение вы хотите использовать как часть левого соединения. Внутреннее соединение в или там, где это требует, чтобы правый столбец таблицы не был нулевым после левого соединения, удаляет все строки, расширенные нулями, т. е. оставляет только внутреннее соединение в строках, т. е. превращает внешнее соединение во внутреннее соединение. У вас есть это.   -  person philipxy    schedule 16.03.2019
comment
Это часто задаваемые вопросы. Прежде чем рассматривать публикацию, пожалуйста, всегда гуглите много четких, кратких и точных формулировок вашего вопроса/проблемы/цели и/или сообщения об ошибке, с вашими конкретными строками/именами и без них, и читайте много ответов. Если вы публикуете вопрос, используйте одну фразу в качестве заголовка. Пожалуйста, в вопросах кода дайте минимальный воспроизводимый пример - вырезайте, вставляйте и выполняйте код, а также желаемый результат, четкую спецификацию и объяснение. Минимальный означает добавление минимального проблемного кода к минимальному рабочему коду. Поэтому дайте минимальный код, который вы показываете, делает то, что вы ожидаете, и минимальный код с первым местом, где вы ошибетесь.   -  person philipxy    schedule 16.03.2019
comment
Возможный дубликат Left Outer Join не возвращается все строки из моей левой таблицы?   -  person philipxy    schedule 16.03.2019
comment
Я поддерживаю комментарий PhilipXY об Элисон, ваши таблицы ... ваши имена таблиц слишком длинные, и запись их 100 раз в каждом sql означает, что большая часть этой стены моноширинного пространства - это просто имена таблиц, которые очень трудно читать   -  person Caius Jard    schedule 16.03.2019
comment
Я действительно не смотрел вглубь, но зачем вам ПОЛНЫЙ внешний вид? Если бы у меня было две таблицы с обычными и специальными ценами, мой запрос в основном выглядел бы так: SELECT COALESCE(sp.price, no.price) FROM normalprices no LEFT JOIN specialprices sp ON no.prodid=sp.prodid AND NOW() BETWEEN sp.promostart AND sp.promoend - это объединяет две таблицы по идентификатору продукта и выводит специальные цены только в том случае, если период акции действует, затем объединение дает получить специальную цену или если ноль получить нормальную цену   -  person Caius Jard    schedule 16.03.2019


Ответы (2)


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

В вашем случае предложение WHERE в любом случае превращает ваш FULL JOIN в LEFT JOIN, потому что предложение WHERE требует допустимых значений из первой таблицы.

SELECT spm.reference AS store_picture_monthly_reference, 
       spm.tenure, spm.name, 
       spmpr.price AS regular_price,
       spmps.price AS special_price
FROM store_picture_monthly spm INNER JOIN
     store_picture_monthly_price_regularspmpr
     ON spmpr.store_picture_monthly_reference = spm.reference LEFT JOIN
     store_picture_monthly_price_special spmps 
     ON spm.reference = spmps.store_picture_monthly_reference AND
        NOW() BETWEEN spmps.begin_date AND spmps.end_date
WHERE spmpr.effective_date < NOW();

Примечания:

  • Я ввел псевдонимы таблиц, чтобы запрос было проще писать и читать.
  • Условие о датах продажи теперь находится в пункте ON.
  • Я удалил GROUP BY. Это не кажется необходимым. Если это так, вы можете использовать вместо него SELECT DISTINCT. И я бы исследовал проблемы с данными, если это необходимо.
  • Я с подозрением отношусь к сравнению дат. NOW() имеет компонент времени. Название столбцов сравнения предполагает, что это просто даты без времени.
person Gordon Linoff    schedule 16.03.2019

Каждый раз, когда вы помещаете предикат where в таблицу, которая является внешним соединением, оно преобразует внешнее соединение во внутреннее соединение, потому что нули, введенные внешним соединением, никогда нельзя сравнивать ни с чем, чтобы получить истину (поэтому внешнее соединение помещает нагрузку строк с нулями, где строки не совпадают, а затем WHERE снова выводит всю строку)

Рассмотрим этот более простой пример:

SELECT * FROM 
a LEFT JOIN b ON a.id = b.id
WHERE b.col = 'value'

Идентичен:

SELECT * FROM 
a INNER JOIN b ON a.id = b.id
WHERE b.col = 'value'

Чтобы решить эту проблему, переместите предикат из where в ON

SELECT * FROM 
a LEFT JOIN b ON a.id = b.id AND b.col = 'value'

Вы также можете рассмотреть:

SELECT * FROM 
a LEFT JOIN b ON a.id = b.id
WHERE b.col = 'value' OR b.col IS NULL

но это может собрать данные, которые вам не нужны, если b.col естественно содержит некоторые нули; он не может различать нули, которые изначально присутствуют в b.col, и нули, которые появляются из-за сбоя в соединении, чтобы сопоставить строку из b со строкой из a (если мы также не посмотрим на нулевое значение объединенного столбца id)

A
id
1
2
3

B
id, col
1, value
3, null

--wrong, behaves like inner
A left join B ON a.id=b.id WHERE b.col = 'value'
1, 1, value

--maybe wrong, b.id 3 might be unwanted
A left join B ON a.id=b.id WHERE b.col = 'value' or b.col is null
1, 1, value
2, null, null
3, 3, null

--maybe right, simpler to maintain than the above
A left join B ON a.id=b.id AND b.col = 'value' 
1, 1, value
2, null, null
3, null, null

В этих двух последних разница заключается в том, что b.id имеет значение null или нет, хотя количество строк одинаково. Если бы мы считали b.id, наш подсчет мог бы закончиться неправильно. Важно понимать этот нюанс поведения соединения. Возможно, вам даже понадобится это, если вы хотите исключить строку 3, но включить строку 2, создав предложение where из a LEFT JOIN b ON a.id=b.id WHERE b.col = 'value' OR b.id IS NULL — это сохранит строку 2, но исключит строку 3, потому что даже если соединение успешно находит b.id 3 он не сохраняется ни одним из предикатов

person Caius Jard    schedule 16.03.2019