需要SQL查询帮助,根据单个列匹配存储过程列表参数

需要SQL查询帮助,根据单个列匹配存储过程列表参数,sql,tsql,stored-procedures,Sql,Tsql,Stored Procedures,我有一个存储过程 Proc: spMyStoredProcedure 获取一个参数: @List varchar(255) @List将是一个逗号分隔的值列表,即,@List='1,2,3' (清晰度..单个值1表示col1=true的所有记录) 我有一个包含以下列的表:IDint、col1bit、col2bit、col3bit、col4bit ID | col1 | col2 | col3 | col4 ------------------------------ 12 | 0 |

我有一个存储过程

Proc:
spMyStoredProcedure
获取一个参数:

@List varchar(255)
@List将是一个逗号分隔的值列表,即,@List='1,2,3'
(清晰度..单个值1表示col1=true的所有记录)

我有一个包含以下列的表:IDint、col1bit、col2bit、col3bit、col4bit

ID | col1 | col2 | col3 | col4
------------------------------
12 |   0  |   1  |   0  |  0
13 |   1  |   0  |   0  |  0
14 |   0  |   0  |   1  |  0
15 |   0  |   0  |   0  |  1
我希望我的结果只包括列表中那些行的ID。 i、 e.12、13、14


我的第一个想法是循环浏览列表并进行选择。即,对于第一个值为1,我获取col1中的所有记录(结果是记录12)。然后移到第二个值2,并获取col2中1(true)的所有记录(结果是记录14),依此类推。。我想知道是否有更有效/更好/更干净的方法来实现这一点?

我认为这解决了问题:

declare @sql as nvarchar(max)

set @sql = 'select * from table where col' + 
           replace(@list, ',', '=1 or col') + '=1'
sp_executesql @sql
这是假设列表不是由用户生成的,它是由代码生成的,所以我不会防范SQL注入攻击。像这样的列表通常不是用户生成的,所以这就是为什么我假设它是用户生成的


这就是你要找的吗?

**回答错误,请检查Eric的答案,以获得更好的回答

幸运的是,您的列表似乎符合以下格式:

 select * from yourtable where id in (1,2,3,4)
因此,您可以轻松构建动态查询:

declare @query varchar(4000)
set @query = 'select * from yourtable where id in (' +
    @list + ')'
 exec sp_executesql @query

请记住,您的客户机必须确保@list参数是干净的。您可以在存储过程中验证它是否不包含任何单引号。

如果您使用的是Sql Server 2005,您应该能够使用PIVOT函数来转换数据。

使用动态Sql考虑以下几点。这可能不是最好的方法,但这是我能想到的唯一一种不针对表发出多个select语句的方法。这假设您已经清理了值列表,并且首先进行了错误检查

CREATE TABLE #tmp
(
    ID INT,
    col1 BIT,
    col2 BIT,
    col3 BIT,
    col4 BIT
)
INSERT INTO #tmp (ID, col1, col2, col3, col4) VALUES (12,0,1,0,0)
INSERT INTO #tmp (ID, col1, col2, col3, col4) VALUES (13,1,0,0,0)
INSERT INTO #tmp (ID, col1, col2, col3, col4) VALUES (14,0,0,1,0)
INSERT INTO #tmp (ID, col1, col2, col3, col4) VALUES (15,0,0,0,1)

DECLARE @List VARCHAR(255)
SELECT @List = '1,2,3'

-- create dynamic sql statement
DECLARE @sql VARCHAR(MAX)
SET @sql = 'SELECT ID FROM #tmp WHERE '

-- append where statement
IF CHARINDEX('1',@List) > 0 BEGIN
    SET @sql = @sql + 'col1 = 1 OR '
END
IF CHARINDEX('2',@List) > 0 BEGIN
    SET @sql = @sql + 'col2 = 1 OR '
END
IF CHARINDEX('3',@List) > 0 BEGIN
    SET @sql = @sql + 'col3 = 1 OR '
END
IF CHARINDEX('4',@List) > 0 BEGIN
    SET @sql = @sql + 'col4 = 1 OR '
END

-- remove the trailing 'OR' and execute
SET @sql = SUBSTRING(@sql,0,LEN(@sql)-2)
EXEC (@sql)

通常我使用一个计算列来存储总共的位列。假设您不能定义计算列,我们可以使用类似的方法

SELECT ID FROM SampleTable WHERE (col1*1 + col2*2 + col3*4 + col4*8) IN (1, 2, 3)
由于1、2、3都连接在一个变量中,因此我通常使用在中定义的用户定义函数

所以,它会是这样的

SELECT ID FROM SampleTable WHERE (col1*1 + col2*2 + col3*4 + col4*8) IN dbo.CSVToInt(@List)

我们使用UDF解析逗号列表并返回表值:

CREATE FUNCTION [dbo].[ListToTable] (
  @list VARCHAR(MAX),
  @separator VARCHAR(MAX) = ','
)
RETURNS @table TABLE (Value VARCHAR(MAX))
AS BEGIN
   DECLARE @position INT, @previous INT
   SET @list = @list + @separator
   SET @previous = 1
   SET @position = CHARINDEX(@separator, @list)
   WHILE @position > 0 BEGIN
      IF @position - @previous > 0
         INSERT INTO @table VALUES (LTRIM(RTRIM(SUBSTRING(@list, @previous, @position - @previous))))
      IF @position >= LEN(@list) BREAK
      SET @previous = @position + 1
      SET @position = CHARINDEX(@separator, @list, @previous)
   END
   RETURN
END
然后,您可以执行以下操作:

SELECT *
FROM MyTable m
JOIN dbo.ListToTable(@List) l
  ON m.ID = l.Value
上面的函数还采用了一个可选的分隔符,如下所示:

SELECT *
FROM MyTable m
JOIN dbo.ListToTable('1:2:3', ':') l
  ON m.ID = l.Value

我认为你的问题还不够清楚-1(很抱歉,其他人发布了一个问题并将其删除)…这将为sql注入打开大门..我不同意Myagi。如果您没有在数据进入存储过程之前清理数据,则说明您做得不对。不应使用T-SQL来阻止注入。许多应用程序使用动态SQL而不易受到注入攻击(我确信stackoverflow就是其中之一。)@Andomar-我不同意您使用动态SQL…但这种特殊情况为SQL注入打开了大门…不,在动态SQL的情况下,它们不会。我建议你仔细阅读一下。即使参数化,使用参数直接输入动态sql也可能导致执行任意代码。无论如何,在这种情况下,我认为修改sql注入是愚蠢的,只要您相信列表的来源,就不会发生这种情况。+1巧妙地使用replace()。这比上面我自己的解决方案要好得多。