带有可选参数SQL的IN子句

带有可选参数SQL的IN子句,sql,stored-procedures,Sql,Stored Procedures,我有一个存储过程,它根据两个输入参数返回一组数据。其中一个参数是可选的,因此我使用 WHERE (tbl_Process.ProjectID = @ProjectID) AND (tbl_AnalysisLookup.AnalysisCodeID = 7) AND (tbl_ProcessSubStep.ProcessID = ISNULL(@ProcessID,tbl_ProcessSubStep.ProcessID)) @ProcessID是可选参数,因此用户可以/不可以提供它。 现在

我有一个存储过程,它根据两个输入参数返回一组数据。其中一个参数是可选的,因此我使用

WHERE 
(tbl_Process.ProjectID = @ProjectID)
AND 
(tbl_AnalysisLookup.AnalysisCodeID = 7)
AND
(tbl_ProcessSubStep.ProcessID = ISNULL(@ProcessID,tbl_ProcessSubStep.ProcessID))
@ProcessID是可选参数,因此用户可以/不可以提供它。 现在我需要更改存储过程以适应多个ProcessId,即用户现在可以选择多个ProcessId、单个ProcessId或无ProcessId的列表,并且存储过程应该处理所有这些场景。除非绝对需要,否则在不使用动态查询的情况下实现这一点的最佳方法是什么。
简而言之,我希望我的存储过程能够处理具有多个值的可选参数(WHERE In子句)。下面提供了解决方案和我从中获得的网页的相关链接。这是一篇非常好的文章,它将帮助您根据您的需求选择正确的解决方案。

不是最好的,但一种方法是将双方都转换为
“varchar”
,并使用
“Like”
操作符来比较它们。它不需要任何巨大的修改,只需将参数的数据类型更改为
“varchar”
。类似以下代码的东西:

'%[,]' + Convert(varchar(10), tbl_ProcessSubStep.ProcessID) + '[,]%' Like @ProcessIDs

希望能有所帮助。

我终于找到了实现这一目标的方法。有两种方法可以做到这一点,我现在使用的是一个函数,根据分隔符分割一个ProcessID字符串,然后将它们插入到表中。然后在我的存储过程中使用该表。这是代码和网页的链接

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

该链接还提供了更多实现此目的的方法。

您在问题中没有指定数据库产品,但我将从@Pararemter命名样式猜测您正在使用SQL Server

除了将空输入解释为“全部”这一不寻常的要求外,这是对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
接下来,创建一个用户定义的表类型
princiaalplist
,其中有一列
principalpid
。此类型包含用于查询系统表的输入值
sys.database\u principals

CREATE TYPE PrincipalList AS TABLE (
  principal_id INT NOT NULL PRIMARY KEY
);
GO
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
然后,创建存储过程
GetPrincipals
,该过程将
PrincipalList
表值参数作为输入,并从
sys.database\u princials
返回结果集

CREATE TYPE PrincipalList AS TABLE (
  principal_id INT NOT NULL PRIMARY KEY
);
GO
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\u principals
中具有匹配
principal\u 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
DECLARE @principals PrincipalList;

INSERT INTO @principals (principal_id) VALUES (1);

EXECUTE GetPrincipals
  @principal_ids = @principals;
GO
EXECUTE GetPrincipals;
结果:

principal_id    name
1   dbo
2   guest
3   INFORMATION_SCHEMA
principal_id    name
1   dbo
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
您可以像这样查询单个主体:

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
DECLARE @principals PrincipalList;

INSERT INTO @principals (principal_id) VALUES (1);

EXECUTE GetPrincipals
  @principal_ids = @principals;
GO
EXECUTE GetPrincipals;
结果:

principal_id    name
1   dbo
2   guest
3   INFORMATION_SCHEMA
principal_id    name
1   dbo
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
您可以像这样查询所有主体:

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
DECLARE @principals PrincipalList;

INSERT INTO @principals (principal_id) VALUES (1);

EXECUTE GetPrincipals
  @principal_ids = @principals;
GO
EXECUTE GetPrincipals;
结果:

principal_id    name
1   dbo
2   guest
3   INFORMATION_SCHEMA
principal_id    name
1   dbo
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的行。调用应用程序可以选择调用哪一个。

将字符串视为整数可能是错误的做法。但如果你受到诱惑,请看乔尔·斯波尔斯基的,以及所有的注意事项。@isme谢谢你提到这一点。正如我所说的,这不是最好的方法,但修改量最小。欢呼当输入有(a)一个ProjectID(b)多个ProjectID(c)没有ProjectID时,存储过程应该返回什么?我想您需要再次阅读这个问题,有两个参数,ProjectID和ProcessID。ProjectID不是可选的,因为ProcessID是。此外,用户可以从列表中选择所有ProcessId,一个ProcessId或无。在你否决投票之前先阅读并理解问题>对不起,我弄糊涂了。当输入有(a)一个ProcessID(b)多个ProcessID(c)没有ProcessID时,仍然不清楚查询应该做什么。查询应该做的是,如果只选择了一个ProcessID,则返回一个ProcessID的数据;如果选择了multiuple,则返回多个ProcessID的数据;如果未选择任何ProcessID,则返回完整的数据集。现在在我的问题中,我故意错过了查询的其余部分,因为我只想集中讨论带有可选参数的Where子句。如果你能删除你的否决票,让更多的人看到这个问题并给出他们的意见,我也会非常感激。使用函数的好处是,你将来可能不得不做一些类似的事情,拥有一个函数会比编写冗余代码容易得多。我正在使用一种非常类似的方法但是当我将
NULL
'
作为ID传递时,我不会得到任何结果,我希望得到所有结果。我缺少什么?我实际上有两个存储过程。我的项目相当复杂,因此我无法解释用户拥有的所有选项(多个相互关联的下拉列表),但如果只有一个ProcessID,我将使用我的原始存储过程。如果用户选择多个ProcessID,然后在其他下拉列表中选择某些选项,我将使用此存储过程和函数。但就像我说的,这取决于你的要求。该链接中还有其他选项。此外,我还没有详细阅读您的解决方案和评论,一旦阅读,我将发布我的评论。顺便说一句,谢谢您的输入。