【问题标题】:SQL GroupBy for Stored Procedure caused Error存储过程的 SQL Group By 导致错误
【发布时间】:2020-07-20 21:29:20
【问题描述】:

我有一个小问题,为什么我的 SQL 存储过程不能正常工作。有人可以解释我的存储过程查询有什么问题吗? 错误:“每个 GROUP BY 表达式必须至少包含一列不是外部引用。”

        SELECT 
          @TabGroupBy + @TabGroupByName + ',' 
          ,SUM(Value) AS Sum
          ,[Unit]
          ,[Child_Name]

      FROM (      
           SELECT [model_id]
             ,[Child_ID]
          ,[Property_ID]
          ,[DDate]
          ,[Hour]
          ,[Value]
        FROM [RP_IRP].[M_PLEXOS].[dat_Generators]
        where parent_ID=1 and  child_ID in(9, 357,358) and Property_ID in (4,31)
           ) a
      inner join [RP_IRP].[M_PLEXOS].[Child_Object] b on a.child_id=b.child_id
      inner join [M_PLEXOS].[Property] d on d.[Property_ID] = a.[Property_ID]
      inner join [M_PLEXOS].[Units] e on d.[Unit_ID]=e.[Unit_ID]
      inner join [M_PLEXOS].[Model_Config] f on a.[Model_id]=f.[Model_id] 
      WHERE Child_Name =  @SelectedChildValue AND Property =  @SelectedPropertyValue
      AND Unit =  @SelectedUnitValue
      GROUP BY  Child_Name ,  @TabGroupBy ,  Unit HAVING SUM(Value) > @MinValue 

【问题讨论】:

  • @niksrinivas 被称为别名;这是必要的
  • @CaiusJard 谢谢,不知道!

标签: c# sql stored-procedures


【解决方案1】:

它永远无法按照您尝试的方式工作,因为根据定义SELECT-part 中的所有列名,以及 all GROUP BY-clause 中的列名 不能来自@-variables。它们必须写成纯文本,因为它们是标识符。

FROMJOIN 子句中的表名 + 列名也是如此。

这样做的原因是查询编译器的构建是为了检查所有指定的列(和表、模式等)与数据库中存在的对象,这需要成功之前 一行编译代码运行。您应该始终牢记在编译时,@-变量还不存在并且不能有值(因为它们还不存在)。

解决方案是使用动态 SQL。您可以通过在NVARCHAR(max) 类型的@SQL 变量中构建您想要执行的实际SQL 字符串来实现您想要的,然后执行该变量的内容。 EXEC 将根据其参数的内容调用 SQL 查询编译器。

示例代码可能不是 100% 完美,因为由于您的数据库不可用,我无法运行它,但这应该可以帮助您:

DECLARE @SQL NVARCHAR(max) =
    'SELECT ' +
      QUOTENAME(@TabGroupBy) + ' AS ' + QUOTENAME(@TabGroupByName) + ', ' +
      'SUM(Value) AS Sum, ' + 
      '[Unit], ' +
      '[Child_Name] ' +
    'FROM ( ' +
      'SELECT [model_id],[Child_ID],[Property_ID],[DDate],[Hour],[Value] ' +
      'FROM [RP_IRP].[M_PLEXOS].[dat_Generators] ' +
      'where parent_ID = 1 ' +
      '  and child_ID in (9, 357, 358) ' +
      '  and Property_ID in (4, 31) ' +
      ') a ' +
    'inner join [RP_IRP].[M_PLEXOS].[Child_Object] b on a.child_id=b.child_id ' +
    'inner join [M_PLEXOS].[Property] d on d.[Property_ID] = a.[Property_ID] ' +
    'inner join [M_PLEXOS].[Units] e on d.[Unit_ID]=e.[Unit_ID] ' +
    'inner join [M_PLEXOS].[Model_Config] f on a.[Model_id]=f.[Model_id] ' +
    'WHERE Child_Name =  ''' + REPLACE(@SelectedChildValue   , '''', '''''') + ''' ' +
    '  AND Property   =  ''' + REPLACE(@SelectedPropertyValue, '''', '''''') + ''' ' +
    '  AND Unit       =  ''' + REPLACE(@SelectedUnitValue    , '''', '''''') + ''' ' +
    'GROUP BY Child_Name , ' + QUOTENAME(@TabGroupBy) + ', Unit ' +
    'HAVING SUM(Value) > ' + CAST(@MinValue AS VARCHAR(20)) -- assuming @MinValue is INT or FLOAT

EXEC (@SQL)

此代码假定@-变量内可能有引号。如果 @-variables 表示字符串值,则始终使用 REPLACE 来加双引号,甚至更好:使用动态 SQL 和 @-parameters,请参阅 this Q & A 了解如何做到这一点。

对于 @-variables 表示数据库标识符(列名等)的情况,您需要在示例代码中使用 QUOTENAME(...) 以确保不会发生滥用。

【讨论】:

  • 如果最终用户可以传递参数值,您是否发现此代码有任何问题?
  • 感谢您让我对细节保持敏锐,我现在已经转义了引号 + 我在需要的地方使用了 QUOTENAME()。还提到了使用参数运行动态 SQL 的替代方案
【解决方案2】:

您不能参数化 GROUP BY 列。从 GROUP BY 中删除参数 @TabGroupBy

【讨论】:

  • 但是 @TabGroupBy 是 Group By 的一部分,所以如果我删除它,查询就不再正确了。你有什么其他的建议?谢谢。
  • @TabGroupBy 不是 group by 的一部分;该错误消息告诉您它不能成为 group by 的一部分。您必须删除它。在任何情况下,该变量都是一个固定的单个值,例如 'Hello World' 的字符串 - 按常量分组是非操作。将列名放在@tabGroupBy 中不会导致查询按该列分组,就像说DECLARE @tableName VARCHAR = 'tblPerson'; SELECT * FROM @tableName 会从 tblPerson 中提取所有行一样——有些地方你不能使用变量。这就是其中之一。如果要运行此查询,则必须将其删除。
  • 如果您尝试在其中放置一个列名并希望查询将按它分组,那么您可以将所有可能的列预先编程为以下情况:GROUP BY x, CASE WHEN @y = 'ChildID' THEN ChildId WHEN @y = 'ParentID' THEN ParentID ...或者您可以使用动态 sql,但您不能将标识符加载到任何语言的字符串变量中,然后将变量视为标识符。除了 JavaScript ;)
猜你喜欢
  • 1970-01-01
  • 2013-04-25
  • 2019-01-06
  • 2016-10-12
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多