2017-02-23 21 views
6

Jeśli mam kwerendy tak:Czy postgres COALESCE jest leniwy?

SELECT COALESCE(
    (SELECT value FROM precomputed WHERE ...), 
    alwaysComputeValue(...) 
); 

Will drugie wyrażenie jest oceniane? Czy to może zależeć od planowania realizacji lub jest niezależna?

+4

Dobre pytanie. Powiedziałbym: sprawdź plan. – wildplasser

Odpowiedz

4

Koncepcyjnie jest leniwy:

Podobnie jak wyrażenie CASE, zlewają ocenia jedynie argumenty, które są potrzebne do ustalenia wyniku; to znaczy, argumenty z prawej strony pierwszego nie-zerowego argumentu nie są oceniane.

https://www.postgresql.org/docs/9.6/static/functions-conditional.html

Jednakże, jeśli wyrażenie w prawo nie jest lotny to powinno robić żadnej różnicy, czy to leniwy lub nie, więc w takim przypadku byłoby to dopuszczalne dla projektanta zapytań niecierpliwością ocenić prawy argument, jeśli był stabilny lub niezmienny, jeśli wydawało się to sensowną optymalizacją.

Oczywistym jest, że sprawa z SELECT COALESCE(a, b) FROM table będzie prawdopodobnie odzyskać a i b pól wszystkich wierszy zamiast pobierania a a następnie pobierania b w razie potrzeby.

Jedynym sposobem uzyskania widocznego efektu jest napisanie funkcji niestabilnej i celowo błędnie oznaczono ją jako stable lub immutable. Byłoby wówczas możliwe oszacowanie, czy po prawej stronie coalesce, gdzie lewa ręka nie była pusta. (Oczywiście byłaby możliwa funkcja, która naprawdę byłaby stabilna, ale gdyby była stabilna, nie miałaby skutków ubocznych, a gdyby nie miała efektów ubocznych, to czy byłaby, czy nie, nie byłaby obserwowalna).

Dane:

CREATE OR REPLACE FUNCTION immutable_func(arg integer) 
RETURNS integer 
AS $BODY$ 
BEGIN 
    RAISE NOTICE 'Immutable function called with %', arg; 
    RETURN arg; 
END; 
$BODY$ LANGUAGE plpgsql IMMUTABLE; 

WITH data AS 
(
    SELECT 10 AS num 
    UNION ALL SELECT 5 
    UNION ALL SELECT 20 
) 
select coalesce(num, immutable_func(2)) 
from data 

Planista wie, że będzie miał taki sam wynik dla immutable_func(2) dla każdego wiersza i wzywa go jeden raz dla całego zapytania, dając nam wiadomość Immutable function called with 2. Tak więc został on faktycznie oceniony, mimo że nie mieści się w zasadzie "argumenty z prawej strony pierwszego nie-zerowego argumentu nie są oceniane". Opłatą jest to, że w (rozsądnym do spodziewanego) przypadku wielokrotnego null num nadal będzie działać tylko jeden raz.

Jest to sprzeczne z literą udokumentowanego zachowania, ponieważ powiedzieliśmy, że taka optymalizacja jest ważna. Jeśli spowodowałoby to problem, błąd oznaczałby funkcję oznaczoną jako IMMUTABLE, a nie w gorącej ocenie.

Może być również w części. Z wartością SELECT COALESCE(a, Some_Func(b)) FROM table nie będzie on chętnie oceniać wartości Some_Func(b), ale będzie mógł pobrać b, aby móc to zrobić.

Za każdym razem, gdy ma to wpływ na zaobserwowane zachowanie (nieuczestniczące), przestrzegana jest zasada.

+0

Wow, tak długa odpowiedź, ale .. Wiem, że można ją ocenić * w teorii * jeśli zaznaczę funkcję niezmienną lub stabilną i ustawię ją jako niską dla planisty, * ale * czy to * naprawdę * może się wydarzyć? :-) – langpavel

+0

Tak, może. Zaktualizuje odpowiedź. –

4

Od the documentation:

Podobnie jak wyrażenie CASE, COALESCE ocenia jedynie argumenty, które są potrzebne do ustalenia wyniku; to znaczy, argumenty z prawej strony pierwszego nie-zerowego argumentu nie są oceniane.