Warum verwendet SQL Optimizer meine Einschränkung nicht?

7

Ich möchte eine Tabelle mit einer NOT NULLBool-Spalte erstellen .
Ich benutze TINYINTmit CHECKZwang BETWEEN 0 and 1. Die Einschränkung ist neu und daher vertrauenswürdig

Jetzt würde ich erwarten, dass SQL Optimizer jetzt weiß, dass diese Spalte nur 0 und 1 sein kann. Wenn ich also eine Abfrage schreibe, col >= 2wird Constant Scan im tatsächlichen Ausführungsplan angezeigt (wie wenn ich nach NULLoder suchen würdeSELECT TOP (0)

Dies ist jedoch nicht der Fall, sondern entscheidet sich für die Tabelle Scan. Muss ich auch einen Index für diese Spalte haben?

In meinem Test unten verwende ich TINYINTmit CHECKEinschränkung. Benutzerdefinierter Typ basierend auf TINYINTgebundenem RULEund gutem Alter BIT.

GO
CREATE TYPE dbo.myBool 
FROM [INT] NOT NULL
GO

CREATE RULE dbo.R_Bool AS @value BETWEEN 0 AND 1
go

EXEC sys.sp_bindrule @rulename = N'R_Bool'
                   , @objname = N'myBool'
GO 


DROP TABLE IF EXISTS dbo.RuleTest
CREATE TABLE dbo.RuleTest
(
    Id INT NOT NULL IDENTITY(1,1) PRIMARY KEY CLUSTERED
    , oldSchoolBool TINYINT NOT NULL CHECK (oldSchoolBool BETWEEN 0 AND 1)
    , customBool dbo.myBool NOT NULL
    , myBit BIT NOT NULL
)

;WITH tally (n)
AS
(
    SELECT 
        ROW_NUMBER() OVER (ORDER BY (SELECT NULL))
    FROM        (VALUES (0), (0), (0), (0), (0), (0), (0), (0), (0), (0))   AS a(n)
    CROSS JOIN  (VALUES (0), (0), (0), (0), (0), (0), (0), (0), (0), (0))   AS b(n)
    CROSS JOIN  (VALUES (0), (0), (0), (0), (0), (0), (0), (0), (0), (0))   AS c(n)

)
INSERT INTO dbo.RuleTest 
(oldSchoolBool, customBool, myBit)
SELECT
    ABS(CHECKSUM(NewId())) % 2 
    ,ABS(CHECKSUM(NewId())) % 2 
    ,ABS(CHECKSUM(NewId())) % 2 
FROM tally t

SET STATISTICS IO ON;

SELECT * FROM dbo.RuleTest rt
WHERE rt.oldSchoolBool IS NULL


SELECT * FROM dbo.RuleTest rt
WHERE rt.oldSchoolBool >=2 

go
SELECT * FROM dbo.RuleTest rt
WHERE rt.customBool >=2 
go
SELECT * FROM dbo.RuleTest rt
WHERE rt.myBit >= 2

SET STATISTICS IO OFF;

Ich sehe einen konstanten Scan für die NULL-Prüfung und 3 Tabellenscans für den Rest.

Zikato
quelle
Wenn Sie als boolescher Typ verwenden möchten, warum tinyint in SQL Server, können Sie den BIT-Datentyp verwenden.
Learning_DBAdmin
Dies ist theoretisch, es kann auch Gründe dafür geben, kein Bit zu verwenden, wie Berechnungen oder Aggregationen.
Zikato
1
Ich frage mich, warum Sie WHERE myBit >= 2Bedingungen haben , wenn Sie wissen, dass die Werte niemals größer als 1 sein können.
ypercubeᵀᴹ

Antworten:

11

Abfrage 2

Das Problem ist die automatische Parametrisierung .

In Ihrem Fall wird die Konstante 2durch einen tinyint-Parameter @1anstelle des Literal ersetzt, 2da dieser Parameter den Wert haben könnte 0oder 1es für den Abfrageoptimierer nicht gültig wäre, anzunehmen, dass die Prüfbedingung dem widerspricht.

Mit der folgenden Abfrage können Sie einen Plan abrufen, der die Widersprüchlichkeitserkennung verwendet (dies 1=1verhindert die automatische Parametrisierung). Die Widersprüchlichkeitserkennung erfolgt dann im Rahmen der Vereinfachung (siehe das Diagramm der Optimierungspipeline hier).

SELECT * 
FROM dbo.RuleTest rt
WHERE rt.oldSchoolBool >=2 and 1=1

Der resultierende Plan wird zu einem konstanten Scan vereinfacht

Geben Sie hier die Bildbeschreibung ein

Abfrage 3

Regeln werden seit ungefähr 20 Jahren entmutigt / veraltet. 2000 BOL beschreibt sie als

eine Abwärtskompatibilitätsfunktion ... CHECK-Einschränkungen sind die bevorzugte Standardmethode

Das CREATE RULEThema besagt

Regeln gelten nicht für Daten, die zum Zeitpunkt der Erstellung der Regeln bereits in der Datenbank vorhanden waren

Daher stelle ich mir vor, dass diese vom Abfrageoptimierer niemals als vertrauenswürdig eingestuft werden, da Folgendes möglich ist und Daten vorliegen, die nicht der Regel entsprechen

CREATE TYPE dbo.myBool FROM [INT] NOT NULL

GO

CREATE TABLE dbo.RuleTest
(
  customBool dbo.myBool NOT NULL
)

INSERT INTO  dbo.RuleTest VALUES (10)

go

CREATE RULE dbo.R_Bool AS @value BETWEEN 0 AND 1
GO

EXEC sys.sp_bindrule @rulename = N'R_Bool'
                   , @objname = N'myBool'

Obwohl es technisch möglich sein könnte, ein vertrauenswürdiges Regelkonzept analog zu vertrauenswürdigen Einschränkungen beizubehalten, glaube ich nicht, dass dies existiert.

Abfrage 4

Sie müssen eine redundante Prüfeinschränkung CHECK (myBit BETWEEN 0 AND 1)oder eine gleichwertige hinzufügen, wenn Sie möchten, dass diese Widersprüche erkannt wird. Obwohl ein nicht nullbares Bit nur diese beiden Werte enthalten kann, wird dieser Widerspruch ohne diesen nicht erkannt

Martin Smith
quelle
Ich habe es getestet und es gibt einen konstanten Scan für den oldSchoolBool zurück, aber nicht für die anderen beiden (benutzerdefinierter Typ und Bit). Gibt es einen Grund dafür? Es gilt sowohl für Ihren Zustand) 1 = 1 als auch für Brents (1 = (SELECT 1))
Zikato
@ MarinSmith-Regeln sind das Zeug der Mythen, aber auf einigen Produktionsservern sehr real. Und das bisschen?
Zikato