Sqlserver
 sql >> база данни >  >> RDS >> Sqlserver

Външно прилагане Връща неочаквано колони NOT NULL, когато няма съвпадение

Това със сигурност е грешка в продукта.

Подобна грешка вече беше докладвана и затворена като „Няма да се коригира“ .

Включително този въпрос, свързаният елемент за свързване и друго две въпроси на този сайт Виждал съм четири случая на този тип поведение с вградени TVFs и OUTER APPLY - Всички бяха във формат

OUTER APPLY dbo.SomeFunction(...) F

И върна правилни резултати, когато е написано като

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

Така че това изглежда като възможно заобиколно решение.

За заявката

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;

Планът за изпълнение изглежда така

И списъкът с изходни колони в крайната проекция е. Expr1000, Expr1001, Expr1003, Expr1004.

Само две от тези колони обаче са дефинирани в таблицата с константи в долния десен ъгъл.

Литералът $350000 се дефинира в таблицата с константи в горния десен ъгъл (Expr1001). След това това се свързва външно към таблицата с константи в долния десен ъгъл. Тъй като нито един ред не отговаря на условието за свързване, двете колони, дефинирани там (Expr1003, Expr1004), се оценяват правилно като NULL. след това накрая изчислителният скалар добавя литерала 12 в потока от данни като нова колона (Expr1000) независимо от резултата от външното свързване.

Това изобщо не е правилната семантика. Сравнете с (правилния) план, когато вграденият TVF е ръчно вграден.

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 

Тук колоните, използвани в крайната проекция, са Expr1003, Expr1004, Expr1005, Expr1006 . Всички те са дефинирани в долното дясно постоянно сканиране.

В случая с TVF изглежда всичко се обърква много рано.

Добавяне на OPTION (RECOMPILE, QUERYTRACEON 3604, QUERYTRACEON 8606); показва, че входното дърво на процеса вече е неправилно. Изразено в SQL, това е нещо подобно.

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 

Пълният резултат от този флаг за проследяване е както следва (И 8605 показва основно същото дърво.)

*** 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. Try_Convert за SQL Server 2008 R2

  2. Вътрешни елементи на седемте сорта SQL сървъри – част 2

  3. Представяне на DateTime в милисекунди?

  4. Изходният параметър на съхранена процедура връща @Value

  5. Как SCHEMA_ID() работи в SQL Server