【问题标题】:Dynamic SQL Comma-Delimited Value Query动态 SQL 逗号分隔值查询
【发布时间】:2010-10-05 17:54:11
【问题描述】:

[更新:使用 SQL Server 2005]

您好,我想做的是使用逗号分隔的值 (id) 列表查询我的存储过程,以检索数据行。

我收到的问题是转换错误:

Conversion failed when converting the varchar value ' +
@PassedInIDs + ' to data type int.

我的 where 子句和错误中的语句是:

...
AND (database.ID IN (' + @PassedInIDs + '))

注意:database.ID 是 int 类型。

我一直在关注这篇文章:

http://www.sql-server-helper.com/functions/comma-delimited-to-table.aspx

但由于错误未完成。

在我的执行脚本中,我有:

...
@PassedInIDs= '1,5'

我在这里做错了吗? 感谢您的帮助。

【问题讨论】:

  • 您使用的是什么 SQL?它不应该试图将该值转换为 int。你能展示更多的代码吗?
  • SQL Server 2005?副手,这应该工作。该错误看起来像是将 ' + @PassedInIDs + ' 视为字符串...您是否缺少引号?
  • 缺少整个查询,我不能告诉你。当您使用动态 SQL 时,所有内容都会被引用,直到您对引用的字符串运行 exec。它在任何时候都不应该将 ' + @PassedInIDs + ' 视为文字字符串。在我看来,这就像错误出现在错误代码中的情况。
  • 请显示整个查询 - 看起来您没有正确构建动态 sql 字符串...
  • casper...您所做的编辑可能会更改一段文字粘贴的代码。

标签: sql dynamic-sql delimiter delimited


【解决方案1】:
Try this:

    DECLARE @Ids varchar(50);
    SET @Ids = '1,2,3,5,4,6,7,98,234';
    
    SELECT * 
    FROM sometable 
    WHERE ','+@Ids+',' LIKE '%,'+CONVERT(VARCHAR(50),tableid)+',%';

【讨论】:

    【解决方案2】:

    这是我发现并测试过的:

    SET QUOTED_IDENTIFIER ON
    SET ANSI_NULLS ON
    GO
    CREATE  FUNCTION [dbo].[SplitStrings] ( @IDsList VARCHAR(MAX) )
    RETURNS @IDsTable TABLE ( [ID] VARCHAR(MAX) )
    AS 
    BEGIN
        DECLARE @ID VARCHAR(MAX)
        DECLARE @Pos VARCHAR(MAX)
    
        SET @IDsList = LTRIM(RTRIM(@IDsList)) + ','
        SET @Pos = CHARINDEX(',', @IDsList, 1)
    
        IF REPLACE(@IDsList, ',', '') <> '' 
            BEGIN
                WHILE @Pos > 0 
                    BEGIN
                        SET @ID = LTRIM(RTRIM(LEFT(@IDsList, @Pos - 1)))
                        IF @ID <> '' 
                            BEGIN
                                INSERT  INTO @IDsTable
                                        ( [ID] )
                                VALUES  ( CAST(@ID AS VARCHAR) )
                            END
                        SET @IDsList = RIGHT(@IDsList, LEN(@IDsList) - @Pos)
                        SET @Pos = CHARINDEX(',', @IDsList, 1)
                    END
            END 
        RETURN
    END
    
    GO
    

    函数调用如下:

    SELECT * FROM dbo.SplitStrings('123,548,198,547,965')
    

    【讨论】:

      【解决方案3】:

      这可以通过Narayana的文章Passing a list/array to an SQL Server stored procedure中提到的6种方法来解决

      我最严格的实现是

      declare @statement nvarchar(256)
      set @statement = 'select * from Persons where Persons.id in (' + @PassedInIDs +')'
      exec sp_executesql @statement

        -

      【讨论】:

        【解决方案4】:

        我会创建一个 CLR 表值函数:

        http://msdn.microsoft.com/en-us/library/ms131103.aspx

        在其中,您将解析字符串并执行转换为一组行。然后,您可以加入该表的结果,或使用 IN 查看列表中是否有 id。

        【讨论】:

        • 或者像他想要的那样使用动态 SQL?
        • @cmartin:但他不是,因为这是在一个存储过程中,他想将逗号分隔的值列表放入 SP。可以在 SP 中使用动态 SQL,但这只是一种浪费。
        • 如果您查看他使用的链接,他肯定在尝试使用动态 SQL。我也不觉得这样做是浪费。我们正在查看可能已完成 99% 的查询,而不是重新开始并以“更好”的方式进行。
        【解决方案5】:

        SQL 中没有字符串求值。这个:

        database.ID IN (' + @PassedInIDs + ')
        

        不会转向:

        database.ID IN (1,2,3)
        

        只是因为@PassedInIDs 参数恰好包含'1,2,3'。甚至没有查看该参数,因为您所拥有的只是一个包含" + @PassedInIDs + "字符串。在语法上,这相当于:

        database.ID IN ('Bob')
        

        简而言之,您无法在 SQL 中执行您在此处尝试的操作。但还有其他四种可能性:

        1. 你用调用语言构造 SQL 字符串,完全放弃存储过程
        2. 在 IN 子句中使用动态预准备语句,其参数数量与您平移使用的一样多
        3. 您使用带有 10 个参数的固定预准备语句:IN (?,?,?,?,?,?,?,?,?,?),只填充您需要的数量,将其他设置为 NULL
        4. 您创建一个存储过程,例如,有 10 个参数并传入任意数量的参数,将其他参数设置为 NULL:IN (@p1, @p2, ..., @p10)

        【讨论】:

        • 他正在尝试使用动态 SQL。您当然可以将“1,2,3”作为字符串传递,将其连接成动态 SQL,然后就可以完成了。我一直都这样做。你错了,在这里。
        • 那你还没有看清楚我的回答。他显然认为他可以存储过程中执行动态SQL。你不能。至少不是这样。
        • 你当然可以。您将要使用的子查询保存为字符串,然后执行。我个人不喜欢注射潜力,但他并没有要求绝对安全的东西。
        • 这就是我所说的:“至少不是这样。”。我知道您可以使用 EXECUTE 在服务器上执行动态 SQL。但这不是问题所在,而且我绝对不建议在存储过程中使用“从野外”传入的参数。
        【解决方案6】:

        首先,您将一个字符串传递给 SQL 中的 IN 函数。如果您回头看原始文章,您会发现它没有发出直接的 SQL 语句,而是构建了一个 string,这是要执行的 SQL 语句。

        【讨论】:

        • 他给出的链接是动态 SQL。他只是在语法的某个地方有一个错字。我们需要完整的代码来看看发生了什么。
        • 他发布的错误是如果你不使用动态 SQL 会发生什么。由于我们没有完整的代码,所有的赌注都没有了。
        【解决方案7】:

        我强烈建议您使用该链接中的第二种方法。创建一个用户定义的函数,将逗号分隔的字符串转换为表格,然后您可以轻松地从中进行选择。

        如果您在 Erland 和“Dynamic SQL”上使用 Google,他会很好地记录其中的陷阱。

        【讨论】:

          【解决方案8】:

          我建议在 SQL 2005 中为此使用 XML。有点笨重,但更容易。它允许您将 XML 选择到一个表中,然后可以加入或插入等。

          如果您还没有,请查看 Sql Server 的 OPENXML()。

          例如,您可以传入如下内容: '12...'

          然后使用:

          exec sp_xml_preparedocument @doc OUTPUT, @xmlParam
          
          SELECT element 
          FROM OPENXML (@doc, 'Array/Value', 2) WITH (element varchar(max) 'text()')
          

          这应该是一个开始

          【讨论】:

          • @freelookenstein:不幸的是,在解析和为列表生成 XML 时,使用较大的值列表会产生巨大的开销。
          • 为什么不直接使用动态 SQL,就像他正在尝试的那样?
          • casperOne,您指的是哪种重大开销?如果您可以将我链接到一些文档,我将不胜感激。
          • XML 解析是“巨大的开销”。至少,您从解析一个字符串到一种语言(SQL)到两种(sql 和 XML),不用担心生成 XML 本身的任何开销。
          【解决方案9】:

          您需要将 ufn_CSVToTable 视为一张表格。所以你可以加入这个功能:

          JOIN ufn_CSVToTable(@PassedInIDs) uf ON database.ID = uf.[String]
          

          【讨论】:

          • 上述动态SQL查询完全没有必要。 evals 的最终字符串将只是“IN (1,5)”,这是有效的 SQL。
          猜你喜欢
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 2015-11-29
          • 2011-12-27
          • 1970-01-01
          • 1970-01-01
          相关资源
          最近更新 更多