pgSQL FULL OUTER JOIN 'WHERE' Условие - PullRequest
0 голосов
/ 16 марта 2019

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

Перед добавлением 2-го условия 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 условие как "все или ничего". Если продажи не ведутся, результатов нет.

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

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

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

Ответы [ 2 ]

1 голос
/ 16 марта 2019

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

В вашем случае предложение 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() имеет компонент времени. Наименование столбцов сравнения предполагает, что это просто даты без времени.
1 голос
/ 16 марта 2019

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

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

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

Идентичноto:

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

Чтобы решить эту проблему, переместите предикат из позиции и в 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 естественно содержит несколько нулей;он не может различить значения NULL, которые изначально присутствуют в b.col, и значения NULL, которые вводятся в результате сбоя в соединении, чтобы сопоставить строку из b со строкой из a (если только мы не посмотрим на нулевое значение столбца объединенного идентификатора)

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 равен нулю или нет, хотя количество строк одинаково.Если бы мы считали 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 не поддерживается ни одним из предикатов

...