我有一个存储过程,它根据2个输入参数返回一组数据。其中一个参数是可选的,所以我正在使用
WHERE
(tbl_Process.ProjectID = @ProjectID)
AND
(tbl_AnalysisLookup.AnalysisCodeID = 7)
AND
(tbl_ProcessSubStep.ProcessID = ISNULL(@ProcessID,tbl_ProcessSubStep.ProcessID))
@ProcessID是可选参数,因此用户可能/可能不提供它。 现在我需要更改我的存储过程以适应多个ProcessId,即用户现在可以选择多个ProcessId,Single ProcessID或No ProcessID的列表,并且存储过程应该处理所有这些场景。除非绝对必要,否则不使用动态查询即可实现此目的的最佳方法是什么。 简而言之,我希望我的存储过程处理具有多个值的可选参数(WHERE IN子句)。下面提供了我从中获得的网页的解决方案和相关链接。这是一篇非常好的文章,可以帮助您根据自己的要求选择合适的解决方案。
答案 0 :(得分:1)
我终于想出了如何实现这一目标。有几种方法可以做到这一点,我现在使用的是一个基于分隔符拆分ProcessID的字符串然后将它们插入表中的函数。然后在我的存储过程中使用该表。这是代码和网页链接。
<强> http://www.codeproject.com/Articles/58780/Techniques-for-In-Clause-and-SQL-Server 强>
CREATE FUNCTION [dbo].[ufnDelimitedBigIntToTable]
(
@List varchar(max), @Delimiter varchar(10)
)
RETURNS @Ids TABLE
(Id bigint) AS
BEGIN
DECLARE @list1 VARCHAR(MAX), @Pos INT, @rList VARCHAR(MAX)
SET @List = LTRIM(RTRIM(@List)) + @Delimiter
SET @pos = CHARINDEX(@Delimiter, @List, 1)
WHILE @pos > 0
BEGIN
SET @list1 = LTRIM(RTRIM(LEFT(@List, @pos - 1)))
IF @list1 <> ''
INSERT INTO @Ids(Id) VALUES (CAST(@list1 AS bigint))
SET @List = SUBSTRING(@List, @pos+1, LEN(@List))
SET @pos = CHARINDEX(@Delimiter, @list, 1)
END
RETURN
END
一旦完成,表函数可用于查询:
Collapse | Copy Code
CREATE PROCEDURE [dbo].[GetUsingDelimitedFunctionTable]
@Ids varchar(max)
AS
BEGIN
SET NOCOUNT ON
SELECT s.Id,s.SomeString
FROM SomeString s (NOLOCK)
WHERE EXISTS ( SELECT *
FROM ufnDelimitedBigIntToTable(@Ids,',') Ids
WHERE s.Id = Ids.id )
END
Link还提供了更多实现此目的的方法。
答案 1 :(得分:0)
不是最好的,但一种方法是将双方转换为"varchar"
并使用"Like"
运算符进行比较。它不需要任何大的修改,只需将参数的数据类型更改为"varchar"
即可。以下代码喜欢:
'%[,]' + Convert(varchar(10), tbl_ProcessSubStep.ProcessID) + '[,]%' Like @ProcessIDs
希望它有所帮助。
答案 2 :(得分:0)
您没有在问题中指定数据库产品,但我将从您正在使用SQL Server的 @Pararemter 命名样式中猜测。
除了将空输入解释为“全部”的异常要求之外,这是对Arrays in SQL问题的重述,由Erland Sommarskog彻底探讨。阅读他关于这个主题的所有文章,以便对你可以使用的所有技术进行分析。
在这里,我将解释如何使用表值参数来解决您的问题。
一起执行以下脚本,以幂等方式设置测试环境。
首先创建一个新的空测试数据库StackOverFlow13556628
:
USE master;
GO
IF DB_ID('StackOverFlow13556628') IS NOT NULL
BEGIN
ALTER DATABASE StackOverFlow13556628 SET SINGLE_USER WITH ROLLBACK IMMEDIATE;
DROP DATABASE StackOverFlow13556628;
END;
GO
CREATE DATABASE StackOverFlow13556628;
GO
USE StackOverFlow13556628;
GO
接下来,创建一个用户定义的表格类型PrinciapalList
,其中包含一列principal_id
。此类型包含用于查询系统表sys.database_principals
的输入值。
CREATE TYPE PrincipalList AS TABLE (
principal_id INT NOT NULL PRIMARY KEY
);
GO
之后,创建存储过程GetPrincipals
,它将PrincipalList
表值参数作为输入,并从sys.database_principals
返回结果集。
CREATE PROCEDURE GetPrincipals (
@principal_ids PrincipalList READONLY
)
AS
BEGIN
IF EXISTS(SELECT * FROM @principal_ids)
BEGIN
SELECT *
FROM sys.database_principals
WHERE principal_id IN (
SELECT principal_id
FROM @principal_ids
);
END
ELSE
BEGIN
SELECT *
FROM sys.database_principals;
END;
END;
GO
如果表值参数包含行,则该过程返回sys.database_principals
中具有匹配principal_id
值的所有行。如果表值参数为空,则返回所有行。
您可以像这样查询多个主体:
DECLARE @principals PrincipalList;
INSERT INTO @principals (principal_id) VALUES (1);
INSERT INTO @principals (principal_id) VALUES (2);
INSERT INTO @principals (principal_id) VALUES (3);
EXECUTE GetPrincipals
@principal_ids = @principals;
GO
结果:
principal_id name
1 dbo
2 guest
3 INFORMATION_SCHEMA
您可以像这样查询单个主体:
DECLARE @principals PrincipalList;
INSERT INTO @principals (principal_id) VALUES (1);
EXECUTE GetPrincipals
@principal_ids = @principals;
GO
结果:
principal_id name
1 dbo
您可以查询所有这样的主体:
EXECUTE GetPrincipals;
结果:
principal_id name
0 public
1 dbo
2 guest
3 INFORMATION_SCHEMA
4 sys
16384 db_owner
16385 db_accessadmin
16386 db_securityadmin
16387 db_ddladmin
16389 db_backupoperator
16390 db_datareader
16391 db_datawriter
16392 db_denydatareader
16393 db_denydatawriter
此解决方案效率低下,因为您始终必须从表值参数中读取两次。实际上,除非你的表值参数有数百万行,否则它可能不是主要的瓶颈。
以这种方式使用空表值参数会感觉不直观。更明显的设计可能只是拥有两个存储过程 - 一个返回所有行,另一个只返回匹配id的行。调用应用程序可以选择调用哪一个。