SQL 存储过程的 GroupBy 导致错误

SQL GroupBy for Stored Procedure caused Error

我有一个简短的问题,为什么我的 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 

您不能参数化 GROUP BY 列。从 GROUP BY

中删除参数 @TabGroupBy

它永远不会像您尝试的那样工作,因为根据定义SELECT部分[=40]中的所有列名=],以及GROUP BY-子句中的所有列名不能来自@-变量。它们必须写成纯文本,因为它们是标识符。

这同样适用于 table 名称 + FROMJOIN 子句中的列名称。

这样做的原因是查询编译器被构建为根据数据库中存在的对象检查所有指定的列(和 tables、模式等),这需要在 一行编译代码 运行 之前取得成功。你应该永远记住在编译时,@变量还不存在并且不能有值(因为它们还不存在)。

解决方案是使用动态 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 一起使用,请参阅 了解如何完成。

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