我使用“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 */