Sqlserver
 sql >> Baza danych >  >> RDS >> Sqlserver

Najlepsze odpowiedzi na 5 palących pytań dotyczących funkcji COALESCE w SQL Server

Jak fajna jest funkcja COALESCE w SQL?

To na tyle fajne, że jest dla mnie tak ważne. I z przyjemnością zatrudnię nowego faceta, który nie ma złego nawyku ignorowania celu COALESCE. Obejmuje to inne wyrażenia i funkcje do obsługi podobnych sytuacji.

Dzisiaj znajdziesz odpowiedzi na pięć najczęściej zadawanych pytań dotyczących wyrażenia SQL COALESCE. Jedna z nich jest nieustannie dyskutowana.

Zaczniemy?

Do czego służy funkcja COALESCE w SQL?

Można na nie odpowiedzieć w 2 słowach:obsługa wartości null .

Null to pustka wartości. Innymi słowy, nieznany. Różni się od pustego ciągu lub liczby zerowej. Obsługa wartości null wymaga użycia wyrażeń i funkcji. Jednym z nich jest COALESCE.

Aby zrozumieć, co mam na myśli, zobacz poniższe stwierdzenia:

DECLARE @number INT

SELECT @number + 33587

Czy będzie działać dobrze? Będzie.

Jest jakiś problem? Obecnie brak.

Ale instrukcje dadzą NULL, ponieważ dodanie null do liczby równa się NULL.

Jeśli wszystkie Twoje zapytania spadną tylko do tego poziomu, możesz przestać czytać. Ale oczywiście nie są. Zarabiamy na więcej niż tylko za wyprodukowanie tego rodzaju kodu.

Teraz dodajmy trochę „katastrofy”:

DECLARE @number INT

SET @number = @number + 33587

UPDATE myTable
set col1 = @number   -- Surprise! col1 is NOT NULLABLE
where ID = 1

PRINT 'Success!'

Wykonanie powyższego kodu nastąpi w ślepym zaułku, gdy dotrze do instrukcji UPDATE. Nie wypisze „Sukces!”, ponieważ nie można umieścić wartości null w kolumnie, która nie dopuszcza wartości null. Czy to stwierdzenie mówi jasno, dlaczego musimy radzić sobie z wartościami null?

Zmieńmy kod, aby dodać siatkę bezpieczeństwa:

DECLARE @number INT

SET @number = COALESCE(@number,0) + 33587     -- our safety net. Thanks to COALESCE.

UPDATE myTable
set col1 = @number               -- Disaster averted!
where ID = 1

PRINT 'Success!'

COALESCE zmieni wartość pustą na zero, a suma nie będzie pusta.

Lekcja jest taka, że ​​COALESCE jest jedną z siatek zabezpieczających przed wartościami zerowymi. Co więcej, prawidłowa obsługa wartości null w kodzie SQL zmniejsza bóle głowy i pozwala szybciej wrócić do domu. To na pewno.

Teraz rozumiesz, dlaczego chcę w moim zespole kogoś, kto pilnie radzi sobie z wartościami zerowymi.

Więcej praktycznych przykładów użycia SQL COALESCE

Odwołajmy się do bardziej praktycznych przykładów.

Załóżmy, że mieszkasz w regionie, w którym niektórzy ludzie mają drugie imiona, a inni nie. Jak utworzysz pełne imię z imienia, drugiego imienia i nazwiska bez wpadania w pułapkę zerową?

Oto jedno z możliwych rozwiązań przy użyciu COALESCE:

USE AdventureWorks
GO

SELECT
p.LastName + ', ' + p.FirstName + ' ' + COALESCE(p.MiddleName + ' ','') AS FullName
FROM Person.Person p

Inny przykład:załóżmy, że jesteś pracownikiem firmy, w której wynagrodzenie brutto jest obliczane inaczej dla każdego pracownika. Dla niektórych z nich obowiązują stawki godzinowe. Inni otrzymują wynagrodzenie według stawek tygodniowych lub miesięcznych.

Oto próbka tabeli wraz z rozwiązaniem zapytania przy użyciu COALESCE:

-- STEP 1: Create the table CREATE TABLE EmployeeWages ( employee_id INT PRIMARY KEY, hourly_rate SMALLMONEY, weekly_rate SMALLMONEY, monthly_rate MONEY, CHECK( hourly_rate IS NOT NULL OR weekly_rate IS NOT NULL OR monthly_rate IS NOT NULL) ); -- STEP 2: Insert data INSERT INTO EmployeeWages( employee_id, hourly_rate, weekly_rate, monthly_rate ) VALUES (1,60, NULL,NULL), (2,40, NULL,NULL), (3,NULL, 1000,NULL), (4,NULL, NULL,7000), (5,NULL, NULL,5000); -- STEP 3: Query the monthly salary. SELECT employee_id, COALESCE( hourly_rate*22.00*8.00, weekly_rate*4.00, monthly_rate ) AS monthly_salary FROM EmployeeWages;

Tabela zawiera różne tryby płatności na identyfikator pracownika. Zapytanie wymaga podania miesięcznego wynagrodzenia dla wszystkich.

W tym miejscu zabłyśnie COALESCE:przyjmuje listę wartości i może być dowolna liczba pozycji. COALESCE wybierze pierwszy, który nie jest pusty:

Fajnie, prawda?

Jak działa COALESCE w SQL?

Definicja COALESCE to wyrażenie, które zwraca pierwszą niepustą wartość z listy wartości. Składnia COALESCE to:

POŁĄCZENIE ( wyrażenie [ ,…n ] )

Ilustruje to nasz poprzedni przykład z różnymi trybami wypłaty wynagrodzeń.

Co kryje się pod maską

Pod maską funkcja COALESCE w SQL to wyrażenie pokryte cukrem dla znacznie dłuższego wyrażenia CASE. Eliminuje to konieczność wpisywania odpowiednika CASE, który jest dłuższy (i męczący dla leniwych maszynistek takich jak ja). Wynik będzie taki sam.

Czy możemy to udowodnić? Tak! Po pierwsze, Microsoft to przyznaje.

Ale dobrze dla nas, Microsoft uwzględnił to w planie wykonania. Dzięki temu wiemy, co się dzieje.

Wypróbujmy to na poprzednim przykładzie z płacami. Ale zanim ponownie uruchomisz poniższe zapytanie, włącz Uwzględnij rzeczywisty plan wykonania lub po prostu naciśnij CTRL-M .

SELECT
    employee_id,
    COALESCE(
        hourly_rate * 22.00 * 8.00,
        weekly_rate * 4.00,
        monthly_rate
    ) AS monthly_salary
FROM
    EmployeeWages;

Kliknij Plan wykonania w wynikach. Wygląda na proste, ale nasz ukryty klejnot tkwi w Skalarze obliczeniowym węzeł. Po najechaniu na niego myszą zobaczysz wyrażenie o nazwie Expr1002 (Rysunek 2). Co to może być?

Poszukajmy głębiej. Kliknij go prawym przyciskiem myszy i wybierz Pokaż kod XML planu wykonania . Pojawi się nowe okno. Spójrz na rysunek 3 poniżej:

Oto twoje oświadczenie CASE. Poniżej znajduje się całość sformatowana i wcięta dla czytelności:

CASE WHEN CONVERT_IMPLICIT(numeric(10,4),[TestDatabase].[dbo].[EmployeeWages].[hourly_rate],0)
                                            *(22.00)*(8.00) IS NOT NULL
     THEN CONVERT_IMPLICIT(numeric(23,8),CONVERT_IMPLICIT(numeric(10,4),
                       [TestDatabase].[dbo].[EmployeeWages].[hourly_rate],0)*(22.00)*(8.00),0)
     ELSE CASE WHEN    
          CONVERT_IMPLICIT(numeric(10,4),[TestDatabase].[dbo].[EmployeeWages].[weekly_rate],0)
                                            *(4.00) IS NOT NULL
          THEN CONVERT_IMPLICIT(numeric(23,8),CONVERT_IMPLICIT(numeric(10,4),                                                         
                       [TestDatabase].[dbo].[EmployeeWages].[weekly_rate],0)*(4.00),0)
          ELSE CONVERT_IMPLICIT(numeric(23,8),[TestDatabase].[dbo].[EmployeeWages].[monthly_rate],0)
          END
END

To dość długo w porównaniu do

COALESCE(
        hourly_rate * 22.00 * 8.00,
        weekly_rate * 4.00,
        monthly_rate
        )

To właśnie zrobił SQL Server z naszym zapytaniem z COALESCE. Wszystko służy do pobrania pierwszej wartości, która nie jest null na liście wartości.

Czy może być krócej?

Wiem, o czym myślisz. Jeśli SQL Server robi to podczas przetwarzania zapytań, COALESCE musi działać wolno. Nie wspominając o wielu wystąpieniach CONVERT_IMPLICIT. Wolisz używać alternatyw?

Po pierwsze, możesz samodzielnie wpisać krótszą instrukcję CASE. Możesz też użyć ISNULL. Więcej o tym później. Mówiąc o byciu powolnym, miałem to omówione przed końcem tego postu.

Jakie są różnice między COALESCE i ISNULL w SQL?

Jedną z alternatyw dla COALESCE jest ISNULL. Używanie COALESCE z 2 wartościami czyni go podobnym do ISNULL. Przynajmniej wyniki wyglądają podobnie. Mimo to istnieją znaczące różnice. Możesz go użyć jako przewodnika przy podejmowaniu decyzji, czy użyjesz COALESCE czy ISNULL.

(1) ISNULL akceptuje 2 argumenty. COALESCE akceptuje listę argumentów

To najbardziej oczywista różnica. Składnia na pewno jest inna.

ISNULL (wyrażenie_czeku , wartość_zamienna )
COALESCE ( wyrażenie [ ,…n ] )

Gdy używasz obu z 2 argumentami, wyniki są takie same. Poniższe 2 stwierdzenia dadzą wynik 1:

SELECT ISNULL(NULL, 1)
SELECT COALESCE(NULL, 1)

Chociaż wyniki są takie same, mają inne znaczenie:

  • ISNULL(NULL, 1) zwrócił 1, ponieważ pierwszy argument to NULL.
  • COALESCE(NULL, 1) zwrócił 1, ponieważ 1 jest pierwszą niepustą wartością na liście .

(2) COALESCE to standard SQL-92

Zgadza się. Możesz to sprawdzić. Na przykład, jeśli chcesz przenieść swój kod SQL do MySQL z SQL Server, COALESCE będzie działać tak samo. Zobacz rysunek 4 i porównaj wynik z rysunku 1:

Jednak użycie ISNULL w MySQL spowoduje błąd, jeśli użyjesz składni SQL Server.

np. Uruchom następujące polecenie w SQL Server Management Studio i MySQL Workbench:

SELECT ISNULL(null,1)

Co się stało? W SQL Server wynik to 1. Ale w MySQL wynik to błąd:

06:36:52 SELECT ISNULL(null,1) Kod błędu:1582. Nieprawidłowa liczba parametrów w wywołaniu natywnej funkcji „ISNULL”

Chodzi o to, że ISNULL w MySQL przyjmuje 1 argument i zwraca 1, jeśli argument jest pusty. W przeciwnym razie zwraca 0.

(3) Przejście 2 wartości NULL w trybie COALESCE powoduje błąd. W porządku z ISNULL

To wywoła błąd:

SELECT COALESCE(NULL, NULL)

Błąd brzmi:„Przynajmniej jeden z argumentów funkcji COALESCE musi być wyrażeniem, które nie jest stałą NULL”.

To będzie w porządku:

SELECT ISNULL(NULL, NULL)

Zmiana kodu COALESCE na podobny poniżej nie wywoła błędu:

DECLARE @value INT = NULL
SELECT COALESCE(@value,null)

Stało się tak, ponieważ @value nie jest stałą zerową.

(4) SQL COALESCE jest konwertowany na CASE. ISNULL pozostaje ISNULL

Widzieliśmy to w „Jak działa COALESCE w SQL?” Sekcja. Tutaj przeanalizujmy inny przykład:

SELECT
P.LastName + ', ' + P.FirstName + ' ' + COALESCE(P.MiddleName + ' ','') AS FullName
FROM Person.Person p

Inspekcja Execution Plan XML dla operatora skalarnego ujawnia konwersję na CASE:

 [AdventureWorks].[Person].[Person].[LastName] as [p].[LastName]+N', '
+[AdventureWorks].[Person].[Person].[FirstName] as [p].[FirstName]+N' '
+CASE WHEN ([AdventureWorks].[Person].[Person].[MiddleName] as [p].[MiddleName]+N' ') IS NOT NULL
      THEN [AdventureWorks].[Person].[Person].[MiddleName] as [p].[MiddleName]+N' '
      ELSE N''
 END

Teraz uruchom równoważne zapytanie używając ISNULL:

SELECT
P.LastName + ', ' + P.FirstName + ' ' + ISNULL(P.MiddleName + ' ','') AS FullName
FROM Person.Person p

Następnie sprawdź Execution Plan XML dla operatora skalarnego:

 [AdventureWorks].[Person].[Person].[LastName] as [p].[LastName]+N', '
+[AdventureWorks].[Person].[Person].[FirstName] as [p].[FirstName]+N' '
+isnull([AdventureWorks].[Person].[Person].[MiddleName] as [p].[MiddleName]+N' ',N'')

ISNULL to nadal ISNULL.

(5) Typ danych wynikowego wyrażenia jest inny

Określanie typu danych wynikowego wyrażenia jest również inne w przypadku COALESCE i ISNULL:

  • ISNULL używa typu danych pierwszego parametru.
  • COALESCE zwraca typ danych wartości o najwyższym priorytecie.

Listę pierwszeństwa typów danych znajdziesz pod tym linkiem.

Spójrzmy na przykład:

SELECT
 employee_id
,COALESCE(CAST(weekly_rate * 4 AS MONEY),0.0000) AS monthly_rate
FROM EmployeeWages

Następnie sprawdź konwersję na CASE w Execution Plan XML :

CASE WHEN CONVERT(money,[TestDatabase].[dbo].[EmployeeWages].[weekly_rate]
                                           *CONVERT_IMPLICIT(smallmoney,[@1],0),0) IS NOT NULL
     THEN CONVERT_IMPLICIT(numeric(19,4), CONVERT(money,[TestDatabase].[dbo].[EmployeeWages].[weekly_rate]
                                           *CONVERT_IMPLICIT(smallmoney,[@1],0),0),0)
     ELSE (0.0000)
END

W powyższym wyrażeniu CASE typem danych wyniku będzie numeric(19,4).

Czemu? Ma wyższy priorytet niż pieniądze i małe pieniądze nawet jeśli prześlesz go na pieniądze . Dlaczego liczbowe a nie pieniądze ? Ze względu na stałą 0.0000.

Jeśli zastanawiasz się, czym jest @1, Execution Plan XML ma odpowiedź. Jest to stała liczba 4.

<ParameterList>
 <ColumnReference Column="@1" ParameterDataType="int" ParameterCompiledValue="(4)"  
       ParameterRuntimeValue="(4)" />
</ParameterList>

Spróbujmy z ISNULL:

SELECT
 employee_id
,ISNULL(CAST(weekly_rate * 4 AS MONEY),0.0000) AS monthly_rate
FROM EmployeeWages

Ponownie poszukaj operatora skalarnego ScalarString :

ISNULL(CONVERT(MONEY,[TestDatabase].[dbo].[EmployeeWages].[weekly_rate]*($4.0000),0),($0.0000))

Na koniec typem danych wynikowego wyrażenia będzie pieniądze . Jest to typ danych pierwszego argumentu.

Jak przechytrzyć pierwszeństwo danych

Możesz „przechytrzyć” pierwszeństwo danych, dodając kilka zmian w kodzie. Wcześniej wynik miał liczbę typ danych. Jeśli chcesz, aby wynikiem były pieniądze typ danych i pozbądź się CONVERT_IMPLICIT, wykonaj następujące czynności:

SELECT
 employee_id
,COALESCE(CAST(weekly_rate AS MONEY) * ($4.0000),($0.0000)) AS monthly_rate
FROM EmployeeWages

Czy zauważyłeś stałe (4,000 $) i (0,0000 $)? To są pieniądze stałe. Co dzieje się dalej, pojawia się w Execution Plan XML ScalarString :

CASE WHEN CONVERT(money,[TestDatabase].[dbo].[EmployeeWages].[weekly_rate],0)*($4.0000) IS NOT NULL 
     THEN CONVERT(money,[TestDatabase].[dbo].[EmployeeWages].[weekly_rate],0)*($4.0000) 
     ELSE ($0.0000) 
END

Tak jest dużo lepiej. Jest krótszy, a CONVERT_IMPLICIT zniknął. A wynikowy typ danych to pieniądze .

(6) Zerowość wynikowego wyrażenia jest inna

ISNULL(NULL, 1) i COALESCE(NULL, 1) mają podobne wyniki, ale ich wartości dotyczące wartości null są różne. COALESCE nie dopuszcza wartości null. ISNULL nie jest. Możesz to zobaczyć, używając go w kolumnach obliczeniowych.

Odwołajmy się do przykładu. Poniższa instrukcja wywoła błąd, ponieważ KLUCZ PODSTAWOWY nie może akceptować wartości NULL. Jednocześnie dopuszczalność wartości null wyrażenia COALESCE dla kolumny2 ocenia na NULL.

CREATE TABLE NullabilityDemo  
(  
  column1 INTEGER NULL,  
  column2 AS COALESCE(column1, 0) PRIMARY KEY,  
  column3 AS ISNULL(column1, 0)  
);

Ta instrukcja powiedzie się, ponieważ dopuszczalność wartości null funkcji ISNULL ocenia jako NOT NULL.

CREATE TABLE NullabilityDemo  
(  
  column1 INTEGER NULL,  
  column2 AS COALESCE(column1, 0),  
  column3 AS ISNULL(column1, 0) PRIMARY KEY  
);

(7) Lewy argument ISNULL jest oceniany raz. To przeciwieństwo z COALESCE

Rozważ ponownie poprzedni przykład:

SELECT
    employee_id,
    COALESCE(
        hourly_rate*22.00*8.00,
        weekly_rate*4.00,
        monthly_rate
    ) AS monthly_salary
FROM
    EmployeeWages;

Następnie sprawdź ScalarString w tym celu:

CASE WHEN CONVERT_IMPLICIT(numeric(10,4),[TestDatabase].[dbo].[EmployeeWages].[hourly_rate],0)
                                              *(22.00)*(8.00) IS NOT NULL 
     THEN CONVERT_IMPLICIT(numeric(23,8),CONVERT_IMPLICIT(numeric(10,4),
                                              [TestDatabase].[dbo].[EmployeeWages].[hourly_rate],0)
                                              *(22.00)*(8.00),0) 
     ELSE CASE WHEN CONVERT_IMPLICIT(numeric(10,4),[TestDatabase].[dbo].[EmployeeWages].[weekly_rate],0)
                                              *(4.00) IS NOT NULL 
               THEN CONVERT_IMPLICIT(numeric(23,8),CONVERT_IMPLICIT(numeric(10,4),
                                        [TestDatabase].[dbo].[EmployeeWages].[weekly_rate],0)*(4.00),0) 
               ELSE CONVERT_IMPLICIT(numeric(23,8),[TestDatabase].[dbo].[EmployeeWages].[monthly_rate],0) 
          END 
END

Gdy funkcja COALESCE w SQL jest konwertowana na CASE, każde wyrażenie jest oceniane dwukrotnie (z wyjątkiem ostatniego). Jak widać powyżej, hourly_rate*22.00*8.00 pojawił się dwukrotnie. To samo z weekly_rate*4.00 . Ostatnie wyrażenie, monthly_rate , pojawił się raz.

Ponieważ funkcja COALESCE oceni wyrażenia dwukrotnie, może wystąpić obniżka wydajności. Więcej na ten temat później.

Sprawdź jednak odpowiednik ISNULL:

SELECT
 employee_id,
 ISNULL(hourly_rate * 22.00 * 8.00,ISNULL(weekly_rate * 4.00,monthly_rate)) AS  
                                                       monthly_salary
FROM EmployeeWages

Następnie sprawdzamy ScalarString w Execution Plan XML :

isnull(CONVERT_IMPLICIT(numeric(10,4),[TestDatabase].[dbo].[EmployeeWages].[hourly_rate],0)*(22.00)*(8.00),
       CONVERT_IMPLICIT(numeric(19,8),
isnull(CONVERT_IMPLICIT(numeric(10,4),[TestDatabase].[dbo].[EmployeeWages].[weekly_rate],0)*(4.00),
CONVERT_IMPLICIT(numeric(14,6),[TestDatabase].[dbo].[EmployeeWages].[monthly_rate],0)),0))

Jak widać powyżej, hourly_rate , stawka_tygodniowa i comiesięczna_stawka pojawił się tylko raz. Więc nie musisz się martwić, że wyrażenia zostaną ocenione dwukrotnie za pomocą ISNULL.

Czy możemy użyć funkcji COALESCE w klauzuli WHERE?

Jasne. Nie ma lepszego sposobu niż pokazanie przykładu, aby to udowodnić.

-- Query all the names in Person table with no middle name

USE AdventureWorks
GO

SELECT
 p.LastName
,p.FirstName
FROM person.Person p
WHERE COALESCE(p.MiddleName,'') = ''

COALESCE zwróci pusty ciąg, jeśli MiddleName ma wartość NULL. Oczywiście istnieje krótsza droga do uzyskania wyniku. Ale to pokazuje, że COALESCE działa w klauzuli WHERE.

Co jest szybsze:COALESCE czy ISNULL?

Wreszcie doszliśmy do gorącego tematu:Wydajność!

Dostaniesz wiele stron z testami i porównaniami, ale w komentarzach będzie bitwa zwolenników COALESCE i ISNULL. Usuniemy kurz i dymy tych wojen.

Co jest szybsze:COALESCE czy ISNULL? Zacznę od stwierdzenia, że ​​odpowiedź brzmi:

(bębny toczące się)

TO ZALEŻY!

(szczęka opadła)

Zawiedziony? Za chwilę to wyjaśnię.

Po pierwsze, zgadzam się, że obie wydają się mieć różnice w wydajności, jeśli użyjesz czasu, który upłynął jako miernika. Niektórzy poparli ten fakt, gdy SQL Server tłumaczy instrukcje COALESCE na CASE. Tymczasem ISNULL pozostaje bez zmian.

Inni mogą rozumować inaczej ze względu na różne wyniki. Również dla nich konwersja COALESCE na CASE jest szybsza niż mrugamy oczami. Tak jak wskazano w tym wątku, różnica w wydajności „jest malutka”. Zgadzam się. Oto kolejny artykuł, w którym stwierdzono, że różnica „jest niewielka”.

Jednak jest to wielka sprawa:czy możemy ufać minimalnemu czasowi, który upłynął, jako metryce? To, co naprawdę ma znaczenie, to ile logicznych odczytów ma zapytanie. Właśnie to pokażemy w naszych następnych przykładach.

(Sprawdź mój inny artykuł o odczytach logicznych i dlaczego ten czynnik opóźnia Twoje zapytania.)

Przykład 1

Przeanalizujmy ten sam przykład i porównajmy ich odczyty logiczne i plany wykonania. Przed uruchomieniem upewnij się, że STATISTICS IO jest WŁĄCZONE i Uwzględnij rzeczywisty plan wykonania jest włączony.

SET STATISTICS IO ON

SELECT
P.LastName + ', ' + P.FirstName + ' ' + COALESCE(P.MiddleName + ' ','') AS FullName
FROM Person.Person p

SELECT
P.LastName + ', ' + P.FirstName + ' ' + ISNULL(P.MiddleName + ' ','') AS FullName
FROM Person.Person p

Oto fakty:

Odczyty logiczne dla obu zapytań są takie same. Oba mają 107 * 8 KB stron. Jeśli mamy milion rekordów, oczywiście wzrośnie liczba odczytów logicznych. Ale odczyty logiczne dla obu zapytań będą równe. Dzieje się tak, nawet jeśli COALESCE zostanie przekonwertowane na CASE:

Sprawdźmy plan wykonania. Oto jak to zrobimy:

  1. Wykonaj pierwszą instrukcję SELECT z wyrażeniem COALESCE.
  2. Kliknij Plan wykonania zakładka w wynikach. Kliknij go prawym przyciskiem myszy i wybierz Zapisz plan wykonania jako . I nazwij plik plan1.sqlplan .
  3. Wykonaj drugą instrukcję SELECT z funkcją ISNULL.
  4. Kliknij Plan wykonania zakładka w wynikach.
  5. Kliknij prawym przyciskiem myszy i wybierz Porównaj plan pokazowy .
  6. Wybierz plik plan1.sqlplan . Pojawi się nowe okno.

W ten sposób zamierzamy sprawdzić plan wykonania pod kątem wszystkich przykładów.

Wracając do naszego pierwszego przykładu, zobacz Rysunek 6, aby zobaczyć porównanie planu wykonania:

Czy zauważyłeś te ważne punkty na rysunku 6?

  • Zacieniowana część 2 planów (Skanowania indeksu) oznacza, że ​​SQL Server użył tych samych operacji dla 2 zapytań.
  • QueryPlanHash dla 2 planów to 0x27CEB4CCE12DA5E7, co oznacza, że ​​plan jest taki sam dla obu.
  • Kilka milisekund różnic w upływającym czasie jest nieistotne.

Na wynos

To jak porównywanie jabłek do jabłek, ale różnych rodzajów. Jedno to jabłko Fuji z Japonii, drugie to czerwone jabłko z Nowego Jorku. Mimo to oba są jabłkami.

Podobnie SQL Server wymaga tych samych zasobów i wybranego planu wykonania dla obu zapytań. Jedyną różnicą jest użycie COALESCE lub ISNULL. Niewielkie różnice, ponieważ wynik końcowy jest taki sam.

Przykład 2

Duża różnica pojawia się, gdy używasz podzapytania jako argumentu zarówno dla COALESCE, jak i ISNULL:

USE AdventureWorks
GO

SELECT COALESCE(
       (SELECT
        SUM(th.ActualCost)
        FROM Production.TransactionHistory th
        WHERE th.ProductID = 967)
       ,0) 

SELECT ISNULL(
       (SELECT
        SUM(th.ActualCost)
        FROM Production.TransactionHistory th
        WHERE th.ProductID = 967)
       ,0)

Powyższy kod będzie miał ten sam wynik, ale wnętrze jest zupełnie inne.

Zacznijmy od odczytów logicznych:

Instrukcja SELECT z wyrażeniem COALESCE ma dwa razy więcej odczytów logicznych niż ta, która użyła ISNULL.

Ale po co podwajać logiczne odczyty? Porównanie planów wykonania ujawni jeszcze więcej:

Rysunek 8 wyjaśnia, dlaczego odczyty logiczne są podwójne przy użyciu COALESCE. Zobacz 2 węzły Stream Aggregate na dolnym planie:są to duplikaty. Następne pytanie brzmi:dlaczego został zduplikowany?

Przypomnijmy, że punkt dotyczy sytuacji, w której COALESCE jest konwertowane na CASE. Ile razy wyrażenia są oceniane w argumentach? DWA RAZY!

Tak więc podzapytanie jest oceniane dwukrotnie. Pokazuje się w planie wykonania ze zduplikowanymi węzłami.

Wyjaśnia to również podwójne odczyty logiczne przy użyciu COALESCE w porównaniu z ISNULL. Jeśli planujesz spojrzeć na XML planu wykonania zapytania za pomocą COALESCE, jest on dość długi. Ale pokazuje, że podzapytanie zostanie wykonane dwukrotnie.

Co teraz? Czy możemy to przechytrzyć? Oczywiście! Jeśli kiedykolwiek spotkałeś się z czymś takim, co, jak sądzę, jest rzadkie, możliwym rozwiązaniem jest dzielenie i podbijanie. Lub użyj ISNULL, jeśli jest to tylko jedno podzapytanie.

Jak uniknąć dwukrotnej oceny wyrażenia podzapytania

Oto jak uniknąć dwukrotnej oceny podzapytania:

  • Zadeklaruj zmienną i przypisz do niej wynik podzapytania.
  • Następnie przekaż zmienną jako argument do COALESCE.
  • Powtórz te same kroki w zależności od liczby podzapytań.

Jak powiedziałem, powinno to być rzadkie, ale jeśli tak się stanie, wiesz, co teraz zrobić.

Kilka słów o poziomie izolacji

Dwukrotna ocena podzapytania może spowodować inny problem. W zależności od poziomu izolacji zapytania, wynik pierwszej oceny może różnić się od drugiej w środowisku wielu użytkowników. To szaleństwo.

Aby zapewnić powracanie stabilnych wyników, możesz spróbować użyć IZOLACJI ZDJĘĆ. Możesz także użyć ISNULL. Lub może to być podejście typu „dziel i zwyciężaj”, jak wskazano powyżej.

Na wynos

Więc jaka jest istota?

  • Zawsze sprawdzaj odczyty logiczne. Liczy się bardziej niż czas, który upłynął. Wykorzystaj upływający czas i zabierz zdrowie psychiczne. Twoja maszyna i serwer produkcyjny zawsze będą miały różne wyniki. Zrób sobie przerwę.
  • Zawsze sprawdzaj plan wykonania, aby wiedzieć, co dzieje się pod maską.
  • Pamiętaj, że kiedy funkcja COALESCE jest tłumaczona na CASE, wyrażenia są oceniane dwukrotnie. Wówczas problemem może być podzapytanie lub coś podobnego. Rozwiązaniem może być przypisanie wyniku podzapytania do zmiennej przed użyciem go w funkcji COALESCE.
  • To, co jest szybsze, zależy od wyników odczytów logicznych i planów wykonania. Nie ma ogólnej zasady określającej, czy połączenie COALESCE czy ISNULL jest szybsze. W przeciwnym razie Microsoft może o tym poinformować, tak jak w przypadku HierarchyID i SQL Graph.

Ostatecznie to naprawdę zależy.

Wniosek

Mam nadzieję, że warto przeczytać ten artykuł. Oto punkty, które omówiliśmy:

  • COALESCE to jeden ze sposobów radzenia sobie z wartościami null. To siatka bezpieczeństwa, która pozwala uniknąć błędów w kodzie.
  • Akceptuje listę argumentów i zwraca pierwszą niepustą wartość.
  • Zostaje przekonwertowany na wyrażenie CASE podczas przetwarzania zapytania, ale nie spowalnia zapytania.
  • Chociaż istnieje kilka podobieństw do ISNULL, istnieje 7 znaczących różnic.
  • Może być używany z klauzulą ​​WHERE.
  • Wreszcie, nie jest szybszy ani wolniejszy niż ISNULL.

Jeśli podoba Ci się ten post, przyciski mediów społecznościowych czekają na Twoje kliknięcie. Dzielenie się to troska.

Dziękuję.

Przeczytaj również

Skuteczna obsługa wartości NULL za pomocą funkcji SQL COALESCE dla początkujących

Praktyczne zastosowanie funkcji SQL COALESCE


  1. Database
  2.   
  3. Mysql
  4.   
  5. Oracle
  6.   
  7. Sqlserver
  8.   
  9. PostgreSQL
  10.   
  11. Access
  12.   
  13. SQLite
  14.   
  15. MariaDB
  1. Konfiguracje w zakresie bazy danych SQL Server i automatyczna korekta planu

  2. Znajdź znaki spoza zestawu ASCII w kolumnach varchar za pomocą SQL Server

  3. Jak uzyskać wynik zmiennoprzecinkowy, dzieląc dwie wartości całkowite za pomocą T-SQL?

  4. T-SQL:Wybieranie kolumny na podstawie MAX (inna kolumna)

  5. CHARINDEX() vs PATINDEX() w SQL Server — jaka jest różnica?