Freigeben über


Implementieren des OR-Operators in nativ kompilierten gespeicherten Prozeduren

OR-Operatoren werden in Abfrage-Prädikaten in nativ kompilierten gespeicherten Prozeduren nicht unterstützt. Da NOT-Operatoren auch in Abfrage-Prädikaten in nativ kompilierten gespeicherten Prozeduren nicht unterstützt werden, können die Auswirkungen von OR-Operatoren nicht allein mithilfe entsprechender logischer Operatoren simuliert werden. Die Auswirkungen eines OR-Operators können jedoch mit speicheroptimierten Tabellenvariablen simuliert werden.

Der OR-Operator in der WHERE-Klausel

Wenn Sie über einen OR-Operator in einer WHERE-Klausel verfügen, können Sie den folgenden Ansatz verwenden, um sein Verhalten zu simulieren:

  1. Erstellen Sie eine speicheroptimierte Tabellenvariable mit dem entsprechenden Schema. Dies erfordert einen vordefinierten speicheroptimierten Tabellentyp.

  2. Beginnen Sie mit dem obersten OR-Operator und trennen Sie die WHERE-Klausel in zwei Teile gemäß den Prädikaten, die durch den OR-Operator verbunden sind. Wenn Sie mehr als einen OR-Operator in einer WHERE-Klausel haben, müssen Sie dies möglicherweise mehrmals tun. Wiederholen Sie diesen Schritt, bis keine OR-Operatoren verbleiben. Wenn Sie beispielsweise das folgende Prädikat haben:

    pred1 OR (pred2 AND (pred3 OR pred4)) OR (pred5 AND pred6)  
    

    Nach diesem Schritt sollten Sie über die folgenden Prädikate verfügen:

    pred1  
    pred5 AND pred6  
    pred2 AND pred3  
    pred2 AND pred4  
    
  3. Führen Sie eine Abfrage mit jedem der beiden Teile aus, die in Schritt 2 als Prädikat gefunden wurden. Fügen Sie das Ergebnis für jede Abfrage in die speicheroptimierte Tabellenvariable ein, die in Schritt 1 erstellt wurde.

  4. Entfernen Sie bei Bedarf Duplikate aus der speicheroptimierten Tabellenvariable.

  5. Verwenden Sie den Inhalt der speicheroptimierten Tabellenvariable als Ergebnis der Abfrage.

Im folgenden Beispiel werden Tabellen aus der AdventureWorks2012-Datenbank verwendet, die für In-Memory OLTP aktualisiert wurden. Um die Dateien für dieses Beispiel herunterzuladen, wechseln Sie zu AdventureWorks Databases - 2012, 2008R2 und 2008. Um In-Memory OLTP-Codebeispiel auf AdventureWorks2012 anzuwenden, wechseln Sie zu SQL Server 2014 In-Memory OLTP-Beispiel.

Fügen Sie der Datenbank die folgende gespeicherte Prozedur hinzu. Wir wandeln diese gespeicherte Prozedur zur Verwendung der nativen Kompilierung um.

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  

Nach der Konvertierung lautet das Tabellen- und gespeicherte Prozedurschema wie folgt:

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  

OR-Operator in JOIN-Bedingung

Wenn Sie über einen OR-Operator in einer JOIN-Bedingung einer SELECT-Anweisung verfügen, können Sie den folgenden Ansatz verwenden, um sein Verhalten zu simulieren. Wenn Sie mehrere OR-Operatoren in einer JOIN-Bedingung haben oder mehrere JOIN-Bedingung mit OR-Operatoren haben, müssen Sie dies möglicherweise mehrmals tun.

Wenn Sie ÜBER OUTER JOIN-Bedingungen verfügen, können Sie diese Problemumgehung mit der Problemumgehung für OUTER JOIN-Bedingungen kombinieren.

  1. Erstellen Sie eine speicheroptimierte Tabellenvariable mit dem entsprechenden Schema. Dies erfordert einen vordefinierten speicheroptimierten Tabellentyp.

  2. Trennen Sie das Prädikat in der JOIN-Bedingung in zwei Teile entsprechend den durch den ODER-Operator verbundenen Prädikaten. Wenn Sie über mehrere JOIN-Bedingungen verfügen, müssen Sie dies möglicherweise für jede JOIN-Bedingung tun und dann eine Reihe von Kombinationen der resultierenden Fragmente erstellen. Wenn Sie beispielsweise drei JOIN-Bedingungen mit einem ODER-Operator in jeder JOIN-Bedingung haben, verfügen Sie möglicherweise über 2x2x2=8 Prädikate.

  3. Erstellen Sie für jedes von Schritt 2 erstellte Prädikat eine Abfrage, die das Ergebnis in die speicheroptimierte Tabellenvariable einfügt, die in Schritt 1 erstellt wurde.

  4. Entfernen Sie bei Bedarf Duplikate aus der speicheroptimierten Tabellenvariable.

  5. Verwenden Sie den Inhalt der speicheroptimierten Tabellenvariable als Ergebnis der Abfrage.

Im folgenden Beispiel werden Tabellen aus der AdventureWorks2012-Datenbank verwendet, die für In-Memory OLTP aktualisiert wurden. Um die Dateien für dieses Beispiel herunterzuladen, wechseln Sie zu AdventureWorks Databases - 2012, 2008R2 und 2008. Um In-Memory OLTP-Codebeispiel auf AdventureWorks2012 anzuwenden, wechseln Sie zu SQL Server 2014 In-Memory OLTP-Beispiel.

Fügen Sie der Datenbank die folgende gespeicherte Prozedur hinzu. Wir werden diese gespeicherte Prozedur umwandeln, um native Kompilierung zu verwenden. In diesem Beispiel werden INNER JOIN-Bedingungen verwendet.

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  

Nach der Konvertierung lautet das Tabellen- und gespeicherte Prozedurschema wie folgt:

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  

Nebeneffekte

Wenn Sie mehrere OR-Operatoren in der WHERE-Klausel oder JOIN-Bedingung haben, kann die Anzahl der Abfragen, die Sie ausführen müssen, um das Verhalten zu simulieren, exponentiell steigen. Dies kann die Abfrageleistung verlangsamen und die Speicherauslastung erhöhen, da speicheroptimierte Tabellenvariablen verwendet werden müssen.

Siehe auch

Migrationsprobleme für nativ kompilierte gespeicherte Prozeduren