【问题标题】:More efficient strategy for Optional Parameter tables更有效的可选参数表策略
【发布时间】:2013-11-11 20:51:25
【问题描述】:

我将列表传递到我的存储过程中,以便用户可以选择他们的产品线、制造商和类别来进行搜索。他们不必提供此条件,如果不提供,则搜索会在所有这些产品线、制造商和/或类别中进行。我有两种策略,我正在尝试决定哪种策略更有效。

策略 #1 是动态 SQL,我尽量避免使用它,但在阅读了我正在考虑使用的 Sommarskog 文章后。例子...

IF @productLines IS NOT NULL 

BEGIN
     CREATE TABLE #TempProductLines (lineID uniqueidentifier)

     INSERT INTO @TempProductLines
     SELECT gID FROM dbo.f_ConvertGuidList_To_Table(@productLines) --varchar list of Guid values
END   

SET @sql = "SELECT * FROM tblRL_PRoducts p "
IF @proudctLInes IS NOT NULL
SET @sql = @sql + " JOIN tblRL_PRoductLines pl ON p.prodID=pl.prodID "

SET @sql = @sql + " WHERE....."   --- where clause

EXEC sp_executesql @sql, @paramlist,... params

策略 #2,避免使用 LEFT JOIN 的动态 SQL

CREATE TABLE #TempProductLines (lineID uniqueidentifier)

INSERT INTO @TempProductLines
SELECT gID FROM dbo.f_ConvertGuidList_To_Table(@productLines) --varchar list of Guid values

SELECT * 
FROM tblRL_PRoducts p
 LEFT JOIN #TempProductLines pl ON p.prodID=pl.prodID

WHERE 
   (
     (
       @productLInes IS NOT NULL
       AND
       pl.lineID IS NOT NULL
     )
     OR
     @productLines IS NULL
   )
   AND
   (
     .... rest of WHERE clause
   )

实际上我最终会在上面有 7 个块用于搜索中使用的不同潜在列表。

【问题讨论】:

    标签: sql-server tsql sql-server-2005


    【解决方案1】:

    我使用“MYCOUNT = 0 或存在”技巧。

    你可以在这里完整地看到它:

    http://www.sqlservercentral.com/articles/Stored+Procedures/thezerotonparameterproblem/2283/

    哇。我需要为非 OPENXML 更新它并使用存在而不是“IN”子句。

    以下是该代码的更现代版本。只需针对现有的 Northwind 数据库运行它即可。

    /*  START TSQL CODE */
    
    /*  Stored Procedure Definition */
    
    Use Northwind
    GO
    
    
    IF EXISTS 
        (
        SELECT * FROM INFORMATION_SCHEMA.ROUTINES
        WHERE ROUTINE_TYPE = N'PROCEDURE' and ROUTINE_SCHEMA = N'dbo' and ROUTINE_NAME = N'uspOrderDetailsGetByXmlParams'  
        )
    BEGIN
        DROP PROCEDURE [dbo].[uspOrderDetailsGetByXmlParams]
    END
    
    
    GO
    
    CREATE Procedure dbo.uspOrderDetailsGetByXmlParams(
    @parametersXML XML
    )
    AS
    
    BEGIN
    
        SET NOCOUNT ON
    
    
        /* build a table (variable) to store the xml-based result set (for specific orderid's) */
        DECLARE @orderCount int
        DECLARE @orders TABLE ( /* used to track which specific OrderID's you want */
        OrderID int
        )
    
        DECLARE @customerCount int DECLARE @customers TABLE ( /* used to track which specific customers you want  */
        CustomerID varchar(5) 
        )
    
        declare @dateOrderDateAfter datetime /* used to track with orders with OrderDate after you want */
        declare @dateOrderDateBefore datetime /* used to track with orders with OrderDate before you want */
    
        /* build a table (table-variable) to store the xml-based result set (for specific Countries) */
        DECLARE @customerCountryCount int DECLARE @customerCountry TABLE ( /* used to track which specific Countries you want */
        CountryName varchar(15) )
    
    
        /* Start XML usage */  
        /* Only incur the penalty of XML parsing, if XML was specified */
        if (@parametersXML IS NOT NULL) AND (Datalength(@parametersXML) > 10 ) 
            /* Only process the xml If the xml exists, and it has at least 10 chars. 10 is just a somewhat */ 
            /* arbritrary number, saying, that an xml doc with <10 chars doesn't have a whole lot going for it */ /* || DataLength is used for Text datatype  */
    
        BEGIN
    
    
            /*  (Do not forget that XML (and xpaths below) are CASE SENSITIVE, no matter what your database collation happens to be.) */
            INSERT INTO @orders ( OrderID )
            SELECT T.parameter.value('(OrderID)[1]', 'INT') AS OrderID
            FROM @parametersXML.nodes('ParametersDS/Order') AS T(parameter);
    
            INSERT INTO @customers (CustomerID)
            SELECT T.parameter.value('(CustomerID)[1]', 'varchar(5)') AS CustomerID
            FROM @parametersXML.nodes('ParametersDS/Customer') AS T(parameter);
    
            /* Note, a single scalar value below */
            SELECT @dateOrderDateBefore = (
            SELECT T.parameter.value('(OrderDateBefore)[1]', 'datetime') AS OrderDateBefore
            FROM @parametersXML.nodes('ParametersDS/SingleValueParam') AS T(parameter) );
    
            /* Note, a single scalar value below */
            SELECT @dateOrderDateAfter =  (
            SELECT T.parameter.value('(OrderDateAfter)[1]', 'datetime') AS OrderDateAfter
            FROM @parametersXML.nodes('ParametersDS/SingleValueParam') AS T(parameter) );
    
            INSERT INTO @customerCountry (CountryName)
            SELECT T.parameter.value('(CountryName)[1]', 'varchar(15)') AS CountryName
            FROM @parametersXML.nodes('ParametersDS/CustomerCountry') AS T(parameter);
    
        END 
        /* End XML usage */
    
    
        /* These count variables help distinquish between when a parameter is and isn't specified */
        select @orderCount = count(*) from @orders 
        select @customerCount = count(*) from @customers 
        select @customerCountryCount = count(*) from @customerCountry 
        /* Note, if the xml doesn't supply any dates, @dateOrderDateBefore and @dateOrderDateAfter will remain null  */
    
        /* */
        /* Debugging queries */
        /*
        select * from @orders 
        select * from @customers 
        print @dateOrderDateBefore 
        print @dateOrderDateAfter 
        select * from @customerCountry 
        */
    
        /* Above are the variables and variable-tables for parameters */
        /**/
    
        DECLARE @ordersWhichMetCriteriaTable TABLE ( /* used to track the orderid's we're interested in */
        OrderID int ) 
    
        /* A new variable table holds (just) the OrderID's which meet the input parmeters. 
         You'll see the use of the @ordersWhichMetCriteriaTable later. 
         */
    
        Insert into @ordersWhichMetCriteriaTable 
        SELECT 
            OrderID 
        FROM 
            Orders o 
        /* Note, this below join to the Customers table is only necessary because of the Country */
        /* if you didn't want to retrieve the Customer.Country column, you could leave this join out */
        INNER JOIN Customers c 
            ON o.CustomerID = c.CustomerID 
        WHERE 
                    /* the parentheses play an important role, so be careful altering them */
            /* */
            (
                (@orderCount = 0) 
                OR
                ( exists (select null from @orders innerVariableTable where innerVariableTable.OrderID = o.OrderID ))
            ) 
    
            /* */
            AND 
            (
                (@customerCount = 0) 
                OR
                ( exists (select null from @customers innerVariableTable where innerVariableTable.CustomerID = o.CustomerID ))
            ) 
    
            /* */
            AND (( @dateOrderDateBefore IS NULL ) OR (o.OrderDate <= @dateOrderDateBefore )) 
    
            /* */
            AND (( @dateOrderDateAfter IS NULL ) OR (o.OrderDate >= @dateOrderDateAfter )) 
    
            /* */
            AND 
                /*CountryName is a string, so watch the case sensitivity, get around this by using UPPER() function */
                (
                    (@customerCountryCount = 0) 
                    OR
                    ( exists (select null from @customerCountry innerVariableTable where UPPER(innerVariableTable.CountryName) = UPPER(c.Country)  ))
                ) 
        /*  ORDER BY is unnecessary here */
    
    
    
        /* Below are 3 queries/result sets we're interested in. Notice the piggyback off the @ordersWhichMetCriteriaTable every time. */ 
        /* */
        /* */
    
        /* ResultSet #1 */
        /* All Customer Information (for the specific orders in the @ordersWhichMetCriteriaTable table) */
        SELECT 
            c.CustomerID, c.CompanyName,c.ContactName,c.ContactTitle,c.[Address],c.City,c.Region,c.PostalCode,c.Country ,c.Phone,c.Fax 
        FROM 
            Customers c 
            JOIN Orders o ON c.CustomerID = o.CustomerID 
        WHERE 
            exists (select null from @ordersWhichMetCriteriaTable innerHolder where innerHolder.OrderID = o.OrderID )
        ORDER BY 
            c.CustomerID 
    
        /* */
        /* ResultSet #2 */ 
        /*All Order Information (for the specific orders in the @ordersWhichMetCriteriaTable table) */
        SELECT o.OrderID,o.CustomerID,o.EmployeeID,o.OrderDate,o.RequiredDate,o.ShippedDate,o.ShipVia ,o.Freight,o.ShipName,o.ShipAddress,o.OrderID,o.CustomerID,o.EmployeeID,o.OrderDate 
        FROM 
            Orders o 
         WHERE 
            exists (select null from @ordersWhichMetCriteriaTable innerHolder where innerHolder.OrderID = o.OrderID )
        ORDER BY 
            o.CustomerID , o.OrderID 
    
        /* */
        /* ResultSet #3 */
        /* All Order Detail Information (for the specific orders in the @ordersWhichMetCriteriaTable table) */
         SELECT od.OrderID,od.ProductID,od.UnitPrice,od.Quantity,od.Discount 
         FROM 
            [Order Details] od 
         WHERE 
            exists (select null from @ordersWhichMetCriteriaTable innerHolder where innerHolder.OrderID = od.OrderID )
         ORDER BY 
            od.OrderID 
    
    
    END
    
    GO 
    
    
    /* */
    /*  The user stored procedure definition is above, the use of the user stored procedure is below.  */
    /*  (Put the below code in a new Query Analyser window) */
    /*  "How to Use" the procedure above. (Put this code in a new Query Analyser window.) */
    
    /* */
    
    Use Northwind
    GO
    
    /* no parameters */
    print 'No Filters, Just Give me back all the Data'
    EXEC uspOrderDetailsGetByXmlParams '
    <ParametersDS>
    </ParametersDS>
    '
    
    GO
    
    
    /* just CustomerID */
    print 'Filter on specific Customers'
    EXEC uspOrderDetailsGetByXmlParams '
    <ParametersDS>
    
     <Customer>
      <CustomerID>CENTC</CustomerID>
     </Customer>
     <Customer>
      <CustomerID>GROSR</CustomerID>
     </Customer>
    
    
    </ParametersDS>
    '
    
    
    GO
    
    
    
    /* Order Dates (Before) */
    print 'Filter on the OrderDates being Before'
    EXEC uspOrderDetailsGetByXmlParams '
    <ParametersDS>
     <SingleValueParam>
      <OrderDateBefore>7/7/1996</OrderDateBefore>
     </SingleValueParam>
    </ParametersDS>
    '
    
    GO
    
    /* Order Dates (After) */
    print 'Filter on the OrderDates being After'
    EXEC uspOrderDetailsGetByXmlParams '
    <ParametersDS>
     <SingleValueParam>
      <OrderDateAfter>5/5/1998</OrderDateAfter>
     </SingleValueParam>
    </ParametersDS>
    '
    
    GO
    
    
    /* Order Dates (both) */
    print 'Filter on the OrderDates being (before and after) the input dates'
    EXEC uspOrderDetailsGetByXmlParams '
    <ParametersDS>
     <SingleValueParam>
      <OrderDateBefore>12/31/1997</OrderDateBefore>
      <OrderDateAfter>1/1/1997</OrderDateAfter>
     </SingleValueParam>
    </ParametersDS>
    '
    
    GO
    
    
    
    
    
    print 'Filter on specific OrderIDs'
    EXEC uspOrderDetailsGetByXmlParams '
    <ParametersDS>
    
     <Order>
      <OrderID>10265</OrderID>
     </Order>
     <Order>
      <OrderID>10267</OrderID>
     </Order>
     <Order>
      <OrderID>10269</OrderID>
     </Order>
    
    </ParametersDS>
    '
    
    
    GO
    
    /* Specific Countries */
    print 'Filter on specific Countries'
    EXEC uspOrderDetailsGetByXmlParams '
    <ParametersDS>
    
     <CustomerCountry>
      <CountryName>Austria</CountryName>
     </CustomerCountry>
    
     <CustomerCountry>
      <CountryName>Belgium</CountryName>
     </CustomerCountry>
    
    
    </ParametersDS>
    '
    GO
    
    
    
    /* Specific Countries */
    print 'Filter on specific Countries and OrderDate'
    EXEC uspOrderDetailsGetByXmlParams '
    <ParametersDS>
    
     <CustomerCountry>
      <CountryName>AustriA</CountryName>
     </CustomerCountry>
    
     <CustomerCountry>
      <CountryName>BelgiuM</CountryName>
     </CustomerCountry>
    
     <SingleValueParam>
      <OrderDateBefore>2/28/1997</OrderDateBefore>
      <OrderDateAfter>1/1/1997</OrderDateAfter>
     </SingleValueParam>
    
    </ParametersDS>
    '
    
    
    
    
    
    
    
    print 'Order ID that does not exist'
    EXEC uspOrderDetailsGetByXmlParams '
    <ParametersDS>
    
     <Order>
      <OrderID>-9999</OrderID>
     </Order>
    
    
    </ParametersDS>
    '
    
    
    GO
    
    /*  END TSQL CODE  */
    

    【讨论】:

      猜你喜欢
      • 2013-04-19
      • 2019-09-30
      • 2014-02-17
      • 1970-01-01
      • 2018-10-18
      • 1970-01-01
      • 1970-01-01
      • 2014-01-24
      • 2017-11-23
      相关资源
      最近更新 更多