--------------------------------------
-- **** www.paulholmes.net ***
-- Step by step guide to logical trees
--------------------------------------

-- The following are repros demonstrating the observations made in
-- Logical Trees Part 4
-- http://www.paulholmes.net/2020/08/logical-trees-part-4-filtering-with.html

-- Examples should work in all version of AdventureWorks from 2008R2 onwards.
USE AdventureWorks2008R2;





-- Simple equality comparison, comparing a column with a constant
SELECT p.Name
FROM Production.Product AS p
WHERE p.ProductID = 500
OPTION (RECOMPILE, QUERYTRACEON 8605, QUERYTRACEON 8606, QUERYTRACEON 8607, QUERYTRACEON 3604);
/* *** Converted Tree: ***
LogOp_Project QCOL: [p].Name
    LogOp_Select
        LogOp_Get TBL: Production.Product(alias TBL: p) Production.Product TableID=482100758 TableReferenceID=0 IsRow: COL: IsBaseRow1000 
        ScaOp_Comp x_cmpEq
            ScaOp_Identifier QCOL: [p].ProductID
            ScaOp_Const TI(int,ML=4) XVAR(int,Not Owned,Value=500)
    AncOp_PrjList

LogOp_Select has two childen:
A logical operator describing the input data
A scalar comparison operator (ScaOp_Comp), specifying an equality comparison (x_cmpEq)

ScaOp_Comp has two children, describing the two values to be compared
ScaOp_Identifier specifies a column, using the alias declared in the LogOp_Get
ScaOp_Const specifies a constant, with a data type and value.
*/




-- Simple comparison with boolean expression
SELECT p.Name
FROM Production.Product AS p
WHERE p.Color = 'Silver'
      AND p.ProductLine = 'M'
OPTION (RECOMPILE, QUERYTRACEON 8605, QUERYTRACEON 8606, QUERYTRACEON 8607, QUERYTRACEON 3604);
/* *** Converted Tree: ***
LogOp_Project QCOL: [p].Name
	LogOp_Select
        LogOp_Get TBL: Production.Product(alias TBL: p) Production.Product TableID=482100758 TableReferenceID=0 IsRow: COL: IsBaseRow1000 
        ScaOp_Logical x_lopAnd
            ScaOp_Comp x_cmpEq
                ScaOp_Identifier QCOL: [p].Color
                ScaOp_Const TI(nvarchar collate 872468488,Var,Trim,ML=12) XVAR(nvarchar,Owned,Value=Len,Data = (12,83105108118101114))
            ScaOp_Comp x_cmpEq
                ScaOp_Identifier QCOL: [p].ProductLine
                ScaOp_Const TI(nvarchar collate 872468488,Var,Trim,ML=2) XVAR(nvarchar,Owned,Value=Len,Data = (2,77))

    AncOp_PrjList 
LogOp_Select has two childen:
A logical operator describing the input data
A logical comparison operator (ScaOp_Logical), specifying an AND comparison (x_lopAnd)

ScaOp_Logical has two children, specifying the two comparisons eacg returning a boolean result, as input to the AND expression.
*/




-- Simple comparison with 3 part boolean expression
SELECT p.Name
FROM Production.Product AS p
WHERE p.Color = 'Silver'
      AND p.ProductLine = 'M'
      AND p.FinishedGoodsFlag = 1
OPTION (RECOMPILE, QUERYTRACEON 8605, QUERYTRACEON 8606, QUERYTRACEON 8607, QUERYTRACEON 3604);
/* *** Converted Tree: *** (partial. operators omitted for clarity, indicated by :)
		:
		ScaOp_Logical x_lopAnd
			ScaOp_Comp x_cmpEq
			:
			ScaOp_Comp x_cmpEq
			:
			ScaOp_Comp x_cmpEq
		:
Note that unlike SQL syntax, the logical operator can take more than 2 inputs. 
*/




-- NOT expression rewritten using De Morgan's laws.
SELECT p.ProductID
FROM Production.Product AS p
WHERE NOT (
              p.Color = 'Silver'
              AND p.ProductLine = 'M'
          )
OPTION (RECOMPILE, QUERYTRACEON 8605, QUERYTRACEON 8606, QUERYTRACEON 8607, QUERYTRACEON 3604);
/* *** Converted Tree: *** (partial. operators omitted for clarity, indicated by :)
	:
	ScaOp_Logical x_lopOr
	    ScaOp_Comp x_cmpNe
	        ScaOp_Identifier QCOL: [p].Color
	        ScaOp_Const TI(nvarchar collate 872468488,Var,Trim,ML=12) XVAR(nvarchar,Owned,Value=Len,Data = (12,83105108118101114))
	    ScaOp_Comp x_cmpNe
	        ScaOp_Identifier QCOL: [p].ProductLine
	        ScaOp_Const TI(nvarchar collate 872468488,Var,Trim,ML=2) XVAR(nvarchar,Owned,Value=Len,Data = (2,77))
	:

Tree shows the expression with NOT eliminated, and the AND converted to OR
*/



-- *** IN Test against constants
SELECT p.Name
FROM Production.Product AS p
WHERE p.ProductID IN ( 500, 501 )
OPTION (RECOMPILE, QUERYTRACEON 8605, QUERYTRACEON 8606, QUERYTRACEON 8607, QUERYTRACEON 3604);

/* *** Converted Tree: ***
	:
	ScaOp_Logical x_lopOr
	    ScaOp_Comp x_cmpEq
	        ScaOp_Identifier QCOL: [p].ProductID
	        ScaOp_Const TI(int,ML=4) XVAR(int,Not Owned,Value=501)
	    ScaOp_Comp x_cmpEq
	        ScaOp_Identifier QCOL: [p].ProductID
	        ScaOp_Const TI(int,ML=4) XVAR(int,Not Owned,Value=500)w
	:

Described using OR and multiple equality comparisons
*/





-- Optimization: Contradiction (Explicit)
SELECT p.Name
FROM Production.Product AS p
WHERE 1 = 0
OPTION (RECOMPILE, QUERYTRACEON 8605, QUERYTRACEON 8606, QUERYTRACEON 8607, QUERYTRACEON 3604);

/* *** Converted Tree: ***
LogOp_Project QCOL: [p].Name
    LogOp_Select
        LogOp_Get TBL: Production.Product(alias TBL: p) Production.Product TableID=482100758 TableReferenceID=0 IsRow: COL: IsBaseRow1000 
        ScaOp_Const TI(bit,ML=1) XVAR(bit,Not Owned,Value=0)
    AncOp_PrjList 

Initially, a select operator has a filter defined with a zero constant, i.e. always false


*** Simplified Tree: ***

LogOp_ConstTableGet (0) COL: IsBaseRow1000  QCOL: [p].ProductID QCOL: [p].Name COL: ProductNumber  COL: rowguid 

In this tree, the 'always false' select has been replaced by LogOp_ConstTableGet.
This includes a rowcount (0), indicating no rows will be returned.
Instead it gives an indication that some table metadata will be needed  (e.g. column names, data types).
*/



-- Optimization: Contradiction (Check Constraint)
SELECT p.Name
FROM Production.Product AS p
WHERE p.ListPrice < 0
OPTION (RECOMPILE, QUERYTRACEON 8605, QUERYTRACEON 8606, QUERYTRACEON 8607, QUERYTRACEON 3604);

/* *** Converted Tree: ***
	:
	LogOp_Select
	    LogOp_Get TBL: Production.Product(alias TBL: p) Production.Product TableID=482100758 TableReferenceID=0 IsRow: COL: IsBaseRow1000 
	    ScaOp_Comp x_cmpLt
	        ScaOp_Identifier QCOL: [p].ListPrice
	        ScaOp_Const TI(money,ML=8) XVAR(money,Not Owned,Value=(10000units)=(0))
	:

The specified comparison is in the converted tree.


*** Simplified Tree: ***

LogOp_ConstTableGet (0) COL: IsBaseRow1000  QCOL: [p].ProductID QCOL: [p].Name COL: ProductNumber  COL: rowguid 

In this tree, the comparison that can never return true due to the check constraint, is replaced with LogOp_ConstTableGet (0)
*/



-- Optimization: Domain Simplification - Apparent Rule: SelPredNorm
SELECT p.ProductID
FROM Production.Product AS p
WHERE p.ProductID
      BETWEEN 1 AND 5
      AND p.ProductID
      BETWEEN 5 AND 10
OPTION (RECOMPILE, QUERYTRACEON 8605, QUERYTRACEON 8606, QUERYTRACEON 8607, QUERYTRACEON 3604);

/* *** Converted Tree: ***
	:
	ScaOp_Logical x_lopAnd
	    ScaOp_Comp x_cmpGe
	        ScaOp_Identifier QCOL: [p].ProductID
	        ScaOp_Const TI(int,ML=4) XVAR(int,Not Owned,Value=1)
	    ScaOp_Comp x_cmpLe
	        ScaOp_Identifier QCOL: [p].ProductID
	        ScaOp_Const TI(int,ML=4) XVAR(int,Not Owned,Value=5)
	    ScaOp_Comp x_cmpGe
	        ScaOp_Identifier QCOL: [p].ProductID
	        ScaOp_Const TI(int,ML=4) XVAR(int,Not Owned,Value=5)
	    ScaOp_Comp x_cmpLe
	        ScaOp_Identifier QCOL: [p].ProductID
	        ScaOp_Const TI(int,ML=4) XVAR(int,Not Owned,Value=10)
	:

Initialy the comparison is expressed in full.


*** Simplified Tree: ***
	:
	ScaOp_Comp x_cmpEq
	    ScaOp_Identifier QCOL: [p].ProductID
	    ScaOp_Const TI(int,ML=4) XVAR(int,Not Owned,Value=5)
	:

In the simplified tree, it is rationalised to ProductId = 5
*/


