sp_executesql分组“必须包含至少一个不是外部引用的列”

时间:2015-07-28 10:33:29

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

在简化版中我试图做类似的事情:

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

5 个答案:

答案 0 :(得分:3)

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

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())

答案 1 :(得分:2)

其他人没有意识到的是你提出了一个问题并自己解决了。 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是字符串值。它实际上是试图:

SELECT'String'FROM'Table'GROUP BY'Btring'

它实际上不需要表格来回答这个问题。 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 

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

答案 2 :(得分:0)

我认为简单的选择应该做这个工作。 查询是

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

对于动态查询:

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

答案 3 :(得分:0)

如果您在运行时使用变量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);

答案 4 :(得分:0)

@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.