在本机编译存储过程中实现 OR 运算符
本机编译存储过程内的查询谓词中不支持 OR 运算符。 本机编译存储过程中的查询谓词也不支持 NOT 运算符,因而无法通过单独使用等同的逻辑运算符来模拟 OR 运算符的效果。 但是,借助内存优化表变量,也许能够模拟 OR 运算符的效果。
WHERE 子句中的 OR 运算符
如果 WHERE 子句中包含 OR 运算符,则可用以下方法模拟其行为:
创建具有相应架构的内存优化表变量。 这需要预定义的内存优化表类型。
从顶级 OR 运算符开始,根据 OR 运算符联接的谓词将 WHERE 子句分为两个部分。 如果 WHERE 子句中包含多个 OR 运算符,则可能需要多次执行此操作。 重复此步骤,直至表达式不含 OR 运算符为止。 例如,如果您有以下谓词:
pred1 OR (pred2 AND (pred3 OR pred4)) OR (pred5 AND pred6)
执行完此步骤,谓词应如下所示:
pred1 pred5 AND pred6 pred2 AND pred3 pred2 AND pred4
以在步骤 2 中找到的两个部分为谓词执行查询。 将每个查询的结果插入在步骤 1 中创建的内存优化表变量中。
如有必要,删除内存优化表变量中的重复项。
使用内存优化表变量的内容作为查询的结果。
以下示例使用 AdventureWorks2012 数据库中针对In-Memory OLTP 更新的表。 若要下载此示例的文件,请转到 AdventureWorks 数据库 - 2012、2008R2 和 2008。 若要将In-Memory OLTP 代码示例应用于 AdventureWorks2012,请转到 SQL Server 2014 In-Memory OLTP 示例。
将以下存储过程添加至数据库。 我们将把此存储过程转换为本机编译存储过程。
CREATE PROCEDURE Sales.usp_fuzzySearchSalesOrderDetail_ondisk
@SalesOrderId int = 0, @SalesOrderDetailId int = 0,
@CarrierTrackingNumber nvarchar(25) = N'', @ProductId int = 0,
@minUnitPrice money = 0, @maxUnitPrice money = 0
AS BEGIN
SELECT SalesOrderId, SalesOrderDetailId, ModifiedDate
FROM Sales.SalesOrderDetail_ondisk s
WHERE s.SalesOrderId = @SalesOrderId
OR s.SalesOrderDetailId = @SalesOrderDetailId
OR s.CarrierTrackingNumber = @CarrierTrackingNumber
OR s.ProductID = @ProductId
OR (s.UnitPrice > @minUnitPrice AND s.UnitPrice < @maxUnitPrice)
END
GO
转换后,表和存储过程架构如下所示:
CREATE TYPE Sales.fuzzySearchSalesOrderDetailType AS TABLE
(
SalesOrderId int not null,
SalesOrderDetailId int not null,
ModifiedDate datetime2(7) not null
INDEX ix_fuzzySearchSalesOrderDetailType NONCLUSTERED (SalesOrderId, SalesOrderDetailId)
) WITH (MEMORY_OPTIMIZED = ON)
GO
CREATE TYPE Sales.fuzzySearchSalesOrderDetailTempType AS TABLE
(
SalesOrderId int not null,
SalesOrderDetailId int not null,
recordcount int not null
INDEX ix_fuzzySearchSalesOrderDetailTempType NONCLUSTERED (SalesOrderId, SalesOrderDetailId)
) WITH (MEMORY_OPTIMIZED = ON)
GO
CREATE PROCEDURE Sales.usp_fuzzySearchSalesOrderDetail_inmem
@SalesOrderId int = 0, @SalesOrderDetailId int = 0,
@CarrierTrackingNumber nvarchar(25) = N'', @ProductId int = 0,
@minUnitPrice money = 0, @maxUnitPrice money = 0
WITH NATIVE_COMPILATION, SCHEMABINDING, EXECUTE AS OWNER
AS BEGIN ATOMIC WITH (TRANSACTION ISOLATION LEVEL = SNAPSHOT, LANGUAGE = N'ENGLISH')
DECLARE @retValue Sales.fuzzySearchSalesOrderDetailType
INSERT INTO @retValue (SalesOrderId, SalesOrderDetailId, ModifiedDate)
SELECT SalesOrderId, SalesOrderDetailId, ModifiedDate
FROM Sales.SalesOrderDetail_inmem s
WHERE s.SalesOrderId = @SalesOrderId
INSERT INTO @retValue (SalesOrderId, SalesOrderDetailId, ModifiedDate)
SELECT SalesOrderId, SalesOrderDetailId, ModifiedDate
FROM Sales.SalesOrderDetail_inmem s
WHERE s.SalesOrderDetailId = @SalesOrderDetailId
INSERT INTO @retValue (SalesOrderId, SalesOrderDetailId, ModifiedDate)
SELECT SalesOrderId, SalesOrderDetailId, ModifiedDate
FROM Sales.SalesOrderDetail_inmem s
WHERE s.CarrierTrackingNumber COLLATE Latin1_General_BIN2 = @CarrierTrackingNumber COLLATE Latin1_General_BIN2
INSERT INTO @retValue (SalesOrderId, SalesOrderDetailId, ModifiedDate)
SELECT SalesOrderId, SalesOrderDetailId, ModifiedDate
FROM Sales.SalesOrderDetail_inmem s
WHERE s.ProductID = @ProductId
INSERT INTO @retValue (SalesOrderId, SalesOrderDetailId, ModifiedDate)
SELECT SalesOrderId, SalesOrderDetailId, ModifiedDate
FROM Sales.SalesOrderDetail_inmem s
WHERE (s.UnitPrice > @minUnitPrice AND s.UnitPrice < @maxUnitPrice)
-- After the above statements, there will be duplicates inside @retValue
-- Delete the duplicates from @retValue
DECLARE @duplicates Sales.fuzzySearchSalesOrderDetailTempType
INSERT INTO @duplicates (SalesOrderId, SalesOrderDetailId, recordcount)
SELECT SalesOrderId, SalesOrderDetailId, COUNT(*) AS recordCount
FROM @retValue
GROUP BY SalesOrderId, SalesOrderDetailId
-- Now we have one row per pair
-- clear and rebuild the result set
DELETE FROM @retValue
INSERT INTO @retValue
SELECT s.SalesOrderId, s.SalesOrderDetailId, s.ModifiedDate
FROM Sales.SalesOrderDetail_inmem s
JOIN @duplicates d ON s.SalesOrderId = d.SalesOrderId AND s.SalesOrderDetailId = d.SalesOrderDetailId
-- After this every pair of (SalesOrderId, SalesOrderDetailId) in @retValue should be unique.
SELECT SalesorderId, SalesOrderDetailId, ModifiedDate FROM @retValue
END
GO
JOIN 条件中的 OR 运算符
如果 SELECT 语句的 JOIN 条件中包含 OR 运算符,则可用以下方法模拟其行为。 如果 JOIN 条件中包含多个 OR 运算符,或有多个带 OR 运算符的 JOIN 条件,则可能需要多次执行此操作。
如果存在 OUTER JOIN 条件,则可将本解决方法与 OUTER JOIN 条件的解决方法结合使用。
创建具有相应架构的内存优化表变量。 这需要预定义的内存优化表类型。
根据 OR 运算符联接的谓词将 JOIN 条件中的谓词分为两个部分。 如果存在多个 JOIN 条件,则可能需要针对每个 JOIN 条件执行此操作,然后创建一组结果片段组合。 例如,存在三个 JOIN 条件,每个 JOIN 条件包含一个 OR 运算符,则共有 2x2x2=8 个谓词。
针对步骤 2 生成的每个谓词,创建将其结果插入到内存优化表变量(在步骤 1 中创建)中的查询。
如有必要,删除内存优化表变量中的重复项。
使用内存优化表变量的内容作为查询的结果。
以下示例使用 AdventureWorks2012 数据库中针对In-Memory OLTP 更新的表。 若要下载此示例的文件,请转到 AdventureWorks 数据库 - 2012、2008R2 和 2008。 若要将In-Memory OLTP 代码示例应用于 AdventureWorks2012,请转到 SQL Server 2014 In-Memory OLTP 示例。
将以下存储过程添加至数据库。 我们将把此存储过程转换为本机编译存储过程。 此示例使用 INNER JOIN 条件。
CREATE PROCEDURE Sales.usp_fuzzySearchSalesSpecialOffers_ondisk
@SpecialOfferId int
AS BEGIN
SELECT s.SalesOrderId, s.SalesOrderDetailId, s.SpecialOfferID, s.ModifiedDate
FROM Sales.SalesOrderDetail_ondisk s
JOIN Sales.SpecialOffer_onDisk offer
ON s.SpecialOfferID = offer.SpecialOfferID
OR s.ProductID IN (SELECT ProductId FROM Sales.SpecialOfferProduct sop WHERE sop.SpecialOfferID = @SpecialOfferId)
END
转换后,表和存储过程架构如下所示:
CREATE TYPE Sales.fuzzySearchSalesSpecialOffers_Type AS TABLE
(
SalesOrderId int not null,
SalesOrderDetailId int not null,
SpecialOfferId int not null,
ModifiedDate datetime2(7) not null
INDEX ix_fuzzySearchSalesSpecialOffers_Type NONCLUSTERED (SalesOrderId, SalesOrderDetailId)
) WITH (MEMORY_OPTIMIZED = ON)
GO
CREATE TYPE Sales.fuzzySearchSalesSpecialOffers_TempType AS TABLE
(
SalesOrderId int not null,
SalesOrderDetailId int not null,
SpecialOfferId int not null,
recordcount int null
INDEX ix_fuzzySearchSalesSpecialOffers_TempType NONCLUSTERED (SalesOrderId, SalesOrderDetailId)
) WITH (MEMORY_OPTIMIZED = ON)
GO
CREATE PROCEDURE Sales.usp_fuzzySearchSalesSpecialOffers_inmem
@SpecialOfferId int
WITH NATIVE_COMPILATION, SCHEMABINDING, EXECUTE AS OWNER
AS BEGIN ATOMIC WITH (TRANSACTION ISOLATION LEVEL = SNAPSHOT, LANGUAGE = N'ENGLISH')
DECLARE @retValue Sales.FuzzySearchSalesSpecialOffers_Type
-- Find all special offers matching the conditions
INSERT INTO @retValue (SalesOrderId, SalesOrderDetailId, SpecialOfferid, ModifiedDate)
SELECT s.SalesOrderId, s.SalesOrderDetailId, s.SpecialOfferID, s.ModifiedDate
FROM Sales.SalesOrderDetail_inmem s
JOIN Sales.SpecialOffer_inmem offer
ON s.SpecialOfferID = offer.SpecialOfferID
INSERT INTO @retValue (SalesOrderId, SalesOrderDetailId, SpecialOfferid, ModifiedDate)
SELECT s.SalesOrderId, s.SalesOrderDetailId, s.SpecialOfferID, s.ModifiedDate
FROM Sales.SalesOrderDetail_inmem s
JOIN Sales.SpecialOfferProduct_inmem sop
ON sop.SpecialOfferId = @SpecialOfferId AND s.ProductID = sop.ProductId
-- Now we need to remove the duplicates from @matchingSpecialOffers
DECLARE @duplicates Sales.fuzzySearchSalesSpecialOffers_TempType
INSERT INTO @duplicates (SalesOrderId, SalesOrderDetailId, SpecialOfferid, recordcount)
SELECT SalesOrderId, SalesOrderDetailId, SpecialOfferId, COUNT(*)
FROM @retValue
GROUP BY SalesOrderId, SalesOrderDetailId, SpecialOfferId
-- now there should be no duplicates within @duplicate
-- use @duplicate for join.
SELECT s.SalesOrderId, s.SalesOrderDetailId, s.SpecialOfferID, s.ModifiedDate
FROM Sales.SalesOrderDetail_inmem s
JOIN @duplicates offer
ON s.SalesOrderId = offer.SalesOrderId
AND s.SalesOrderDetailId = offer.SalesOrderDetailID
AND s.SpecialOfferId = offer.SpecialOfferId
END
GO
副作用
如果 WHERE 子句或 JOIN 条件中包含多个 OR 运算符,则模拟此行为所需执行的查询数将呈指数增加。 这可能会降低查询性能,并增加内存使用量(因为需要用到内存优化表变量)。