To zostało zahaszowane i ponownie zahaszowane. Oprócz wskazówka, którą wskazałem w komentarzu oraz linki i wyjaśnienia @xQbert zamieszczone powyżej, na żądanie tutaj jest wyjaśnienie COALESCE vs. ISNULL przy użyciu podzapytania. Rozważmy te dwa zapytania, które pod względem wyników są identyczne:
SELECT COALESCE((SELECT TOP (1) name FROM sys.objects), N'foo');
SELECT ISNULL((SELECT TOP (1) name FROM sys.objects), N'foo');
(Komentarze na temat używania TOP bez ORDER BY do /dev/null/ dzięki.)
W przypadku COALESCE logika zostaje rozszerzona do czegoś takiego:
SELECT CASE WHEN (SELECT TOP (1) ...) IS NULL
THEN (SELECT TOP (1) ...)
ELSE N'foo'
END
Z ISNULL tak się nie dzieje. Istnieje wewnętrzna optymalizacja, która wydaje się zapewniać, że podzapytanie jest oceniane tylko raz. Nie wiem, czy ktoś spoza Microsoftu wie dokładnie, jak działa ta optymalizacja, ale możesz to zrobić, jeśli porównasz plany. Oto plan dla wersji COALESCE:
A oto plan dla wersji ISNULL - zauważ, o ile jest prostsza (i że skanowanie zdarza się tylko raz):
W przypadku COALESCE skanowanie odbywa się dwukrotnie. Oznacza to, że podzapytanie jest oceniane dwukrotnie, nawet jeśli nie daje żadnych wyników. Jeśli dodasz klauzulę WHERE w taki sposób, że podzapytanie da 0 wierszy, zobaczysz podobną rozbieżność — kształty planu mogą się zmienić, ale nadal będziesz widzieć podwójne wyszukiwanie+wyszukiwanie lub skanowanie w poszukiwaniu przypadku COALESCE. Oto nieco inny przykład:
SELECT COALESCE((SELECT TOP (1) name FROM sys.objects
WHERE name = N'no way this exists'), N'foo');
SELECT ISNULL((SELECT TOP (1) name FROM sys.objects
WHERE name = N'no way this exists'), N'foo');
Tym razem plan dla wersji COALESCE - ponownie widać całą gałąź reprezentującą podzapytanie powtórzone dosłownie:
I znowu dużo prostszy plan, wykonujący mniej więcej połowę pracy, używając ISNULL:
Możesz również zobaczyć to pytanie na dba.se, aby uzyskać więcej dyskusji:
Moja sugestia jest taka (a możesz zobaczyć moje powody w końcówce i powyższym pytaniu):zaufaj, ale weryfikuj. Zawsze używam COALESCE (ponieważ jest to standard ANSI, obsługuje więcej niż dwa argumenty i nie robi tak dziwnych rzeczy z pierwszeństwem typu danych) chyba, że Wiem, że używam podzapytania jako jednego z wyrażeń (którego nigdy nie pamiętam poza taką pracą teoretyczną) lub mam prawdziwy problem z wydajnością i po prostu chcę porównać, aby sprawdzić, czy COALESCE vs. ISNULL ma jakieś znaczna różnica w wydajności (której poza przypadkiem podzapytania jeszcze nie znalazłem). Ponieważ prawie zawsze używam COALESCE z argumentami o podobnych typach danych, rzadko muszę przeprowadzać jakiekolwiek testy poza patrzeniem wstecz na to, co powiedziałem o tym w przeszłości (byłem również autorem artykuł aspfaq, który wskazał xQbert , 7 lat temu).