【问题标题】:sp_executesql Group by "must contain at least one column that is not an outer reference"sp_executesql 分组方式“必须至少包含一列不是外部引用”
【发布时间】:2015-10-18 20:40:59
【问题描述】:

在简化版本中,我正在尝试做类似的事情:

Set @datepart = 'DATEPART(year, myDate)' 
Set @SQLQuery = 'SELECT @datepart AS TIME 
                 FROM someTable 
                 GROUP BY @datepart'
Execute sp_executesql @SQLQuery, N'@datepart nvarchar(1000)', @datepart

但我明白了:

每个 GROUP BY 表达式必须至少包含一列 外部参考。

如果我不对其进行参数化,它就可以工作,意味着

Set @SQLQuery = 'SELECT ' + @datepart + ' AS TIME 
                 FROM someTable 
                 GROUP BY ' + @datepart
Execute sp_executesql @SQLQuery

【问题讨论】:

    标签: sql-server tsql dynamic-sql sp-executesql


    【解决方案1】:

    我认为简单的选择应该可以完成这项工作。 查询是

    SELECT DISTINCT DATEPART(year,mydate) AS TIME 
    FROM someTable 
    

    对于动态查询:

    Set @SQLQuery = 'SELECT DISTINCT' + @datepart + ' AS TIME 
                     FROM someTable'
    Execute sp_executesql @SQLQuery
    

    【讨论】:

    • 不能用参数?我不能在我的用例中使用简单的查询,第二个解决方案是——正如我刚刚学到的——很容易通过 SQLinjection 和低性能来利用。 stackoverflow.com/questions/31670688/…
    【解决方案2】:

    通过使用参数化查询,您无法实现您想要做的唯一方法是简单的动态查询

    Set @SQLQuery = 'SELECT ' + @datepart + ' AS TIME 
                     FROM someTable 
                     GROUP BY ' + @datepart
    Execute sp_executesql @SQLQuery
    

    因为变量只是作为动态查询中的值。请考虑以下示例。

    DECLARE @datepart NVARCHAR(1000),
            @SQLQuery NVARCHAR(max)
    
    SET @datepart = 'DATEPART(year, getdate())'
    SET @SQLQuery = 'SELECT @datepart AS TIME'
    
    EXECUTE Sp_executesql
      @SQLQuery,
      N'@datepart nvarchar(1000)',
      @datepart 
    

    根据你的说法,答案应该是2015,但结果是

    DATEPART(year, getdate())
    

    【讨论】:

      【解决方案3】:

      其他人完全没有意识到的是,您提出了一个问题并自己解决了它。 SQL Server 非常正确地指出,您正试图 某物进行分组,但没有报告任何输出。

      我接受了您的查询并制作了一个几乎我们所有人都应该能够在 SQL 2005+ 上使用的版本:

      DECLARE  @datepart NVARCHAR(MAX), @SQLQUERY NVARCHAR(MAX)
      SEt @datepart  = 'DATEPART(year, myDate)' 
      Set @SQLQuery = 'SELECT @datepart AS TIME 
                       FROM sys.objects 
                       GROUP BY @datepart'
      Execute sp_executesql @SQLQuery, N'@datepart nvarchar(1000)', @datepart
      

      现在,这将像预期的那样爆炸。原因是 @datepart 是一个 string 值。它实际上是在尝试:

      按“字符串”从“表”组中选择“字符串”

      在任何时候它实际上都不需要表格来回答这个问题。 SQL 在混乱中举起双手。你需要做的是实现你似乎想要做的只是将字符串连接在一起,但是这可能是不可取的:

      DECLARE  @datepart NVARCHAR(MAX), @SQLQUERY NVARCHAR(MAX)
      SEt @datepart  = 'DATEPART(year, create_date)' 
      Set @SQLQuery = 'SELECT ' + @datepart +' AS TIME 
                       FROM sys.objects 
                       GROUP BY '+ @datepart
      Execute sp_executesql @SQLQuery 
      

      或者在您的具体情况下:

      DECLARE  @datepart NVARCHAR(MAX), @SQLQUERY NVARCHAR(MAX)
      SEt @datepart  = 'DATEPART(year, myDate)' 
      Set @SQLQuery = 'SELECT ' + @datepart +' AS TIME 
                       FROM someTable 
                       GROUP BY '+ @datepart
      Execute sp_executesql @SQLQuery 
      

      如果您提交这样的代码,我建议您输入一份简历。这是不好的做法。

      【讨论】:

        【解决方案4】:

        如果你REPLACE在运行时使用变量查询,那么你可以实现2015 动态

        DECLARE @datepart NVARCHAR(100),
                @SQLQuery NVARCHAR(max)
        
        SET @datepart = 'DATEPART(year, getdate())'
        SET @SQLQuery = 'SELECT @datepart AS TIME'
        
        SET @SQLQuery    =   REPLACE(@SQLQuery, '@datepart', @datepart)
        exec (@SQLQuery);
        

        【讨论】:

        • 请检查我对你的回放的回答,目前是 1 cmets 以上
        【解决方案5】:

        @tinka,这很好用。但我只是用另一个例子进行了测试,它似乎对 SQLinjection 没有帮助。也许您的解决方案与使用普通的串联查询相同?

        DECLARE @SQLQueryInnen nvarchar(max)
        DECLARE @ParameterDefinition nvarchar(max)
        DECLARE @aufnr nvarchar(200)
        SET @aufnr = '1234; SELECT * FROM sensitiveTable'-- here the attacker inserted an SQL Statement in aufnr
        
        
        Set @SQLQueryInnen = 'SELECT * FROM myTable WHERE aufnr = @aufnr'
        Set @SQLQueryInnen = REPLACE(@SQLQueryInnen, '@aufnr', @aufnr)
        Set @ParameterDefinition =  '@aufnr nvarchar(200)'
        Execute sp_Executesql @SQLQueryInnen, @ParameterDefinition, @aufnr
        -- Result: The illegal SELECT statement gets executed!!!
        

        如果我在没有 REPLACE 的情况下执行此示例,则攻击将不起作用,但您会得到:Conversion failed when converting the nvarchar value '1234; SELECT * FROM qdb_tab_imp_me_q_fehler' to data type int.

        【讨论】:

          猜你喜欢
          • 2012-05-31
          • 1970-01-01
          • 2017-09-16
          • 2020-05-10
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          相关资源
          最近更新 更多