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

Zewnętrzne Zastosuj Powracające kolumny nieoczekiwanie NIE NULL, gdy nie ma dopasowania

Jest to z pewnością błąd w produkcie.

Podobny błąd został już zgłoszony i zamknięty jako „Nie naprawi” .

W tym to pytanie, połączony element połączenia i inny dwa pytania w tej witrynie Widziałem cztery przypadki tego typu zachowań z wbudowanymi funkcjami TVF i OUTER APPLY - Wszystkie miały format

OUTER APPLY dbo.SomeFunction(...) F

I zwrócił poprawne wyniki, gdy został zapisany jako

OUTER APPLY (SELECT * FROM dbo.SomeFunction(...)) F

To wygląda na możliwe obejście.

Dla zapytania

WITH Test AS
(
       SELECT 12 AS PropertyID,
              $350000 AS Ap1,
              350000 AS Ap2
)
SELECT LP.*
FROM Test T
OUTER APPLY dbo.TVFTest
(
       T.PropertyID,
       T.Ap1,
       T.Ap2
) LP;

Plan wykonania wygląda tak

A lista kolumn wyjściowych w końcowej projekcji jest. Wyr1000, Wyr1001, Wyr1003, Wyr1004.

Jednak tylko dwie z tych kolumn są zdefiniowane w tabeli stałych w prawym dolnym rogu.

Dosłowne $350000 jest zdefiniowana w tabeli stałych w prawym górnym rogu (Expr1001). To następnie zostaje połączone na zewnątrz do tabeli stałych w prawym dolnym rogu. Ponieważ żaden wiersz nie pasuje do warunku złączenia, dwie zdefiniowane tam kolumny (Wyr1003, Wyr1004) są poprawnie oceniane jako NULL. następnie w końcu skalar obliczeniowy dodaje literał 12 do przepływu danych jako nowa kolumna (Expr1000) niezależnie od wyniku połączenia zewnętrznego.

To wcale nie jest poprawna semantyka. Porównaj z (prawidłowym) planem, gdy wbudowany TVF jest wstawiany ręcznie.

WITH Test
     AS (SELECT 12      AS PropertyID,
                $350000 AS Ap1,
                350000  AS Ap2)
SELECT LP.*
FROM   Test T
       OUTER APPLY (SELECT KeyID,
                           MatchValue1,
                           MatchValue2,
                           CASE
                             WHEN MatchValue1 <> MatchValue2
                               THEN 'Not equal'
                             ELSE 'Something else'
                           END AS MatchTest
                    FROM   (SELECT T.PropertyID AS KeyID,
                                   T.Ap1        AS MatchValue1,
                                   T.Ap2        AS MatchValue2) TestRow
                    WHERE  MatchValue1 <> MatchValue2) LP 

Tutaj kolumny użyte w końcowej projekcji to Expr1003, Expr1004, Expr1005, Expr1006 . Wszystkie te elementy są zdefiniowane w prawym dolnym rogu stałego skanowania.

W przypadku TVF wszystko wydaje się bardzo wcześnie pójść źle.

Dodanie OPTION (RECOMPILE, QUERYTRACEON 3604, QUERYTRACEON 8606); pokazuje, że drzewo wejściowe do procesu jest już nieprawidłowe. Wyrażone w SQL jest to coś w rodzaju.

SELECT Expr1000,
       Expr1001,
       Expr1003,
       Expr1004
FROM   (VALUES (12,
               $350000,
               350000)) V1(Expr1000, Expr1001, Expr1002)
       OUTER APPLY (SELECT Expr1003,
                           IIF(Expr1001 <> Expr1003, 
                               'Not equal', 
                               'Something else') AS Expr1004
                    FROM   (SELECT CAST(Expr1002 AS MONEY) AS Expr1003) D
                    WHERE  Expr1001 <> Expr1003) OA 

Pełne dane wyjściowe tej flagi śledzenia są następujące (8605 pokazuje w zasadzie to samo drzewo).

*** Input Tree: ***
        LogOp_Project COL: Expr1000  COL: Expr1001  COL: Expr1003  COL: Expr1004 

            LogOp_Apply (x_jtLeftOuter)

                LogOp_Project

                    LogOp_ConstTableGet (1) [empty]

                    AncOp_PrjList 

                        AncOp_PrjEl COL: Expr1000 

                            ScaOp_Const TI(int,ML=4) XVAR(int,Not Owned,Value=12)

                        AncOp_PrjEl COL: Expr1001 

                            ScaOp_Const TI(money,ML=8) XVAR(money,Not Owned,Value=(10000units)=(-794967296))

                        AncOp_PrjEl COL: Expr1002 

                            ScaOp_Const TI(int,ML=4) XVAR(int,Not Owned,Value=350000)

                LogOp_Project

                    LogOp_Select

                        LogOp_Project

                            LogOp_ConstTableGet (1) [empty]

                            AncOp_PrjList 

                                AncOp_PrjEl COL: Expr1003 

                                    ScaOp_Convert money,Null,ML=8

                                        ScaOp_Identifier COL: Expr1002 

                        ScaOp_Comp x_cmpNe

                            ScaOp_Identifier COL: Expr1001 

                            ScaOp_Identifier COL: Expr1003 

                    AncOp_PrjList 

                        AncOp_PrjEl COL: Expr1004 

                            ScaOp_IIF varchar collate 53256,Var,Trim,ML=14

                                ScaOp_Comp x_cmpNe

                                    ScaOp_Identifier COL: Expr1001 

                                    ScaOp_Identifier COL: Expr1003 

                                ScaOp_Const TI(varchar collate 53256,Var,Trim,ML=9) XVAR(varchar,Owned,Value=Len,Data = (9,Not equal))

                                ScaOp_Const TI(varchar collate 53256,Var,Trim,ML=14) XVAR(varchar,Owned,Value=Len,Data = (14,Something else))

            AncOp_PrjList 

*******************


  1. Database
  2.   
  3. Mysql
  4.   
  5. Oracle
  6.   
  7. Sqlserver
  8.   
  9. PostgreSQL
  10.   
  11. Access
  12.   
  13. SQLite
  14.   
  15. MariaDB
  1. Optymalizacja TempDB:unikanie wąskich gardeł i problemów z wydajnością

  2. Pobierasz aktualną datę w SQL Server?

  3. Ile rozmiaru ma wartość Null w SQL Server

  4. Zaimplementuj funkcję stronicowania (pomiń / weź) za pomocą tego zapytania

  5. Jak wykonać LEFT ANTI SEMI JOIN w SQL Server