【问题标题】:SQL syntax issue using "IN" keyword with dynamic data使用带有动态数据的“IN”关键字的 SQL 语法问题
【发布时间】:2010-12-15 21:21:20
【问题描述】:

我使用的是 SQL Server 2000,但遇到一个奇怪的 SQL 代码块问题(如下):
用户可以输入“GM”作为可能的参数或“F”。如果用户在存储的 proc 查询字符串中输入“GM”作为参数,我需要 AutoDivision 包括 GMC、CAD、CHE、SAT

declare @AutoDivision as varchar(15) 
set @AutoDivision = 'GM' 

if @AutoDivision = 'GM' 
            Begin 
                Select @AutoDivision = '''Cad'', ''GMC'', ''Sat'', ''Che'''
            End 

            SELECT 
                 oh.OrderNumber, lg.[lgh_number]
            FROM 
                [dbo].[OrderHeader] oh (NOLOCK)
            INNER JOIN 
                [dbo].[DistrctHeader] lg (NOLOCK) ON oh.[inv_number] = lg.[inv_number]
            INNER JOIN
                [dbo].[DealerCompany] c (NOLOCK) ON c.cmp_id = LEFT(oh.[ordernumber],3)
            INNER JOIN
                [dbo].[divisionXREF] x (NOLOCK) ON x.Division = c.comp_revtype
            WHERE
                oh.ord_number = '113-889257'
                AND x.AutoDivision IN (@AutoDivision)
                --AND x.AutoDivision IN ('Cad', 'Sat', 'GMC', 'Che')
                AND lg.[lgh_outstatus] IN ('AVAIL', 'PLAN', 'DISP', 'STRTD', 'PEND','COMP')  

但是,当我运行下面的代码时,我没有取回任何记录。 当我取消注释代码行时
--AND x.AutoDivision IN ('Cad', 'Sat', 'GMC', 'Che')

它有效(我收到一条返回记录)。

当我做print 'AND x.AutoDivision IN (' + cast(@AutoDivision as varchar) + ')'
我回AND x.AutoDivision IN ('Cad', 'GMC', 'Sat', 'Che')

我在这里错过了什么?

【问题讨论】:

    标签: sql sql-server tsql sql-server-2000 subquery


    【解决方案1】:

    您不能使用单个变量来表示 SQL 中的 IN 参数的逗号分隔列表 - Oracle、MySQL、SQL Server... 没关系。

    要使这个变量方法起作用,您需要使用动态 SQL,以便您能够首先将查询创建为字符串(使用连接从变量中获取 IN 参数),然后执行查询语句:

    DECLARE @cmd VARCHAR(1000)
    
    SET @cmd = 'SELECT oh.OrderNumber, 
                       lg.[lgh_number]
                  FROM [dbo].[OrderHeader] oh (NOLOCK)
                  JOIN [dbo].[DistrctHeader] lg (NOLOCK) ON oh.[inv_number] = lg.[inv_number]
                  JOIN [dbo].[DealerCompany] c (NOLOCK) ON c.cmp_id = LEFT(oh.[ordernumber],3)
                  JOIN [dbo].[divisionXREF] x (NOLOCK) ON x.Division = c.comp_revtype
                 WHERE oh.ord_number = '113-889257'
                   AND x.AutoDivision IN ('+ @AutoDivision +')
                   AND lg.[lgh_outstatus] IN (''AVL'', ''PLN'', ''DSP'', ''STD'', ''PND'',''CMP'')  '
    
    EXEC(@cmd)
    

    我建议在实施动态 SQL 解决方案之前阅读The Curse and Blessings of Dynamic SQL

    表值函数

    table valued function would allow you do what you want without using dynamic SQL -- there's more info in this article

    【讨论】:

      【解决方案2】:

      为避免这种情况,您可以创建一个临时表,填写它,然后使用

      IN (SELECT myField from #myTable)
      

      【讨论】:

      • 您的解决方案简单且易于维护。谢谢!!
      • 不客气,这就是我的目标。 :0) 顺便说一句,我有一个方便的过程,可以自动将一串这样的选择转换为表格。如果你发现自己经常这样做,你可以查一下。
      【解决方案3】:

      即使您连接看起来像几个不同的参数,您的 IN 子句实际上是在将其作为单个字符串进行测试(您确实将其声明为 varchar),并且没有记录与该谓词匹配也就不足为奇了。

      看起来您正在尝试混合使用动态 sql 和标准查询。那是行不通的。您的查询要么必须全部动态创建,然后专门执行,要么您的 IN 子句必须输入单个参数,这些参数可以由 subqery 完成。

      你可以这样做:

      ... AND x.AutoDivision IN 
      (SELECT Division WHERE Corp = 'GM') ...
      

      ... AND x.AutoDivision IN 
      (SELECT 'Cad' UNION SELECT 'GMC' UNION SELECT 'Sat' UNION SELECT 'Che') ...
      

      试试这个:

              SELECT 
                   oh.OrderNumber, lg.[lgh_number]
              FROM 
                  [dbo].[OrderHeader] oh (NOLOCK)
              INNER JOIN 
                  [dbo].[DistrctHeader] lg (NOLOCK) ON oh.[inv_number] = lg.[inv_number]
              INNER JOIN
                  [dbo].[DealerCompany] c (NOLOCK) ON c.cmp_id = LEFT(oh.[ordernumber],3)
              INNER JOIN
                  [dbo].[divisionXREF] x (NOLOCK) ON x.Division = c.comp_revtype
              WHERE
                  oh.ord_number = '113-889257'
                  AND x.AutoDivision IN 
                 (SELECT 'Cad' UNION SELECT 'GMC' UNION SELECT 'Sat' UNION SELECT 'Che')
                  AND lg.[lgh_outstatus] IN ('AVL', 'PLN', 'DSP', 'STD', 'PND','CMP') 
      

      这是一个内联子查询,它将任意字符串联合到一个结果集中。 (持保留态度。我离 Sql Server 接口还有很长的路要走。)

      【讨论】:

      • Paul,您知道,用户可以输入“GM”或“F”(更新了我的原始帖子)。如果他们输入“F”,一切都很好。如果他们输入“GM”,那就是复杂性的开始。
      • 因为通用汽车有部门而福特没有?好的。试试看。 (将字符串替换为 UNION 子查询集)
      • 嗯,从技术上讲,因为 GM 需要展示一份详细的报告……但这就是业务逻辑
      【解决方案4】:
         AND x.AutoDivision IN (@AutoDivision)
              --AND x.AutoDivision IN ('Cad', 'Sat', 'GMC', 'Che')
      

      你能澄清一下这两行代码,它们不是在做同样的事情吗?

      【讨论】:

      • SQL不支持前者;后者(已注释掉)支持。
      • 应该是一样的,但是最上面一行不返回记录,下面一行返回记录。
      【解决方案5】:
      CREATE FUNCTION [dbo].[Split]
      (
          @String NVARCHAR(4000),
          @Delimiter NCHAR(1)
      )
      RETURNS TABLE 
      AS
      RETURN 
      (
          WITH Split(stpos,endpos) 
          AS(
              SELECT 0 AS stpos, CHARINDEX(@Delimiter,@String) AS endpos
              UNION ALL
              SELECT endpos+1, CHARINDEX(@Delimiter,@String,endpos+1)
                  FROM Split
                  WHERE endpos > 0
          )
          SELECT 'Id' = ROW_NUMBER() OVER (ORDER BY (SELECT 1)),
              'Data' = SUBSTRING(@String,stpos,COALESCE(NULLIF(endpos,0),LEN(@String)+1)-stpos)
          FROM Split
      )
      GO
      
      DECLARE @AutoDivision varchar(50) = NULL
      SET @AutoDivision ='Cad, Sat, GMC, Che'
      
      SELECT * FROM divisionXREF P INNER JOIN dbo.SPLIT(ISNULL(@AutoDivision,''),',') as T ON P.lgh_outstatus = CASE T.strval WHEN '' THEN P.PG_CODE ELSE T.strval END
      

      【讨论】:

      • 您需要对其进行格式化并解释您正在做什么以提供一个好的答案。
      猜你喜欢
      • 2013-04-28
      • 1970-01-01
      • 2021-08-10
      • 2011-06-01
      • 2020-02-24
      • 1970-01-01
      • 1970-01-01
      • 2011-01-19
      • 1970-01-01
      相关资源
      最近更新 更多