【问题标题】:Is it possible to have an SQL query that uses AGG functions in this way?是否有可能以这种方式使用 AGG 函数的 SQL 查询?
【发布时间】:2011-10-23 23:51:05
【问题描述】:

假设我有以下聚合函数:

  • AGG1
  • AGG2
  • AGG3
  • AGG4

是否可以像这样编写有效的 SQL(以与 db 无关的方式):

SELECT [COL1, COL2 ....], AGG1(param1), AGG2(param2) FROM [SOME TABLES]
WHERE [SOME CRITERIA]
HAVING AGG3(param2) >-1 and AGG4(param4) < 123
GROUP BY COL1, COL2, ... COLN
ORDER BY COL1, COLN ASC
LIMIT 10

其中 COL1 ... COLN 是正在查询的表中的列,param1 ... paramX 是传递给 AGG 函数的参数。

注意:AGG1 和 AGG2 在结果中作为列返回(但不会出现在 HAVING CLAUSE 中,AGG3 和 AGG4 出现在 HAVING CLAUSE 中但不会在结果集中返回。

理想情况下,我想要一个与数据库无关的解决方案答案,但如果我必须绑定到一个数据库,我使用的是 PostgreSQL (v9.x)。

编辑

澄清一下:我不反对在查询中使用 GROUP BY。我的 SQL 不是很好,所以上面的示例 SQL 可能有点误导。我已经编辑了上面的伪 sql 语句,希望能让我的意图更清楚。

我想知道的主要是使用 AGG 函数的选择查询是否可以:

  • 在返回的列中包含 agg 函数值,而不在 HAVING 子句中指定它们。
  • 在 HAVING 子句中指定了 agg 函数,但未在结果集中返回。

从我目前收到的答案来看,这两个问题的答案似乎都是肯定的。为了更正我的 SQL,我唯一需要做的就是添加一个 GROUP BY 子句以确保返回的行是唯一的。

【问题讨论】:

    标签: sql postgresql aggregate-functions ansi-sql


    【解决方案1】:

    PostgreSQL 主要版本包括点后的第一个数字,因此“PostgreSQL (v9.x)”不够具体。正如@kekekela 所说,没有(便宜的)完全与数据库无关的方式。即使在 PostgreSQL 9.0 和 9.1 之间也存在重要的语法差异。

    如果您只有分组值AGG1(param1), AGG2(param2),您将无需提供明确的GROUP BY 子句就可以逃脱。由于您混合了分组列和非分组列,您必须提供一个GROUP BY 子句,其中包含出现在SELECT 中的所有非分组列。对于任何版本的 PostgreSQL 都是如此。阅读GROUP BY and HAVING it in the manual

    不过,从 9.1 版本开始,一旦您在 GROUP BY 中列出了主键,您就可以跳过此表的其他列,并且仍然在 SELECT 列表中使用它们。 release notes for version 9.1告诉我们:

    当primary时允许查询目标列表中的非GROUP BY列 键在 GROUP BY 子句中指定 (Peter Eisentraut)

    关于参数

    您是否打算向聚合函数提供一个常量值?重点是什么? docs tell us

    聚合函数从多个输入行计算单个结果。

    或者您希望这些参数是列名?只要语句在提交到数据库之前生成,这种动态 SQL 就可以工作。不适用于准备好的语句或简单的 sqlplpgsql 函数。为此,您必须在 plpgsql 函数中使用 EXECUTE

    作为防范 SQLi 的安全措施,对 使用 USING $1, $2 语法,对列或表 名称 使用 quote_ident()

    【讨论】:

    • 我的查询将使用相同的表,但可能涉及在结果中返回的不同列 - 这是否意味着查询将使用 EXECUTE 运行,如您上面指出的那样?如果可以的话,我宁愿不使用 EXECUTE,因为缺少缓存。
    • @HomunculusReticulli:对不起,你必须这样做。否则,您不能将列名作为参数传递。但别担心,EXECUTE 很快。有时甚至可以更快,因为查询计划可以适应每次调用的实际值。在我的回答中查看EXECUTE 的附加详细信息。
    • Brandstetter:感谢您对性能问题的及时响应、进一步信息和保证 :)。我怀疑无论如何我都必须使用 EXECUTE 。不过要清楚一点,参数不是列名,它们只是(AGG)函数参数 - 即通常是简单的数据类型 - 例如整数、浮点数、布尔值(可能还有字符串)。
    • @HomunculusReticulli: EXECUTE 仅在您使用动态标识符操作时才需要。您可以传递给任何函数的值。但是,如果这些 aggregate 函数不对表列进行操作,那为什么还要它们呢?也许您可以发布一两个函数示例以避免误解..
    【解决方案2】:

    不使用 GROUP BY 对列进行聚合的唯一方法是使用窗口函数。您遗漏了问题的详细信息,但以下内容可能是您要查找的内容:

    SELECT *
    FROM (
        SELECT [COL1, COL2 ....], 
               AGG1(param1) over (partition by some_grouping_column) as agg1, 
               AGG2(param2) over (partition by some_grouping_column) as agg2,
               row_number() over () as rn
        FROM [SOME TABLES]
        WHERE [SOME CRITERIA]
        ORDER BY COL1
    )  t
    WHERE AGG3 >-1 
      AND AGG4 < 123
      AND rn <= 10
    ORDER BY col1
    

    这是标准的 ANSI SQL,适用于大多数数据库,包括 PostgreSQL(自 8.4 起)。

    请注意,您不需要为 partition by 子句中的两个聚合使用相同的分组列。

    如果你想坚持使用 ANSI SQL,那么你应该使用row_number() 函数来限制结果。如果您仅在 PostgreSQL(或以某种方式支持 LIMIT 的其他 DBMS)上运行此操作,则将 LIMIT 原因移动到派生表(内部查询)中

    【讨论】:

      【解决方案3】:

      这应该从高层次的角度工作,除非您在 GROUP BY 语句中需要 COL1、COL2 等,否则它们在 SELECT 列表中将无效。在 SELECT 列表中而不是在 HAVING 中包含 AGG1 等不是问题。

      就 db 不可知论而言,无论你做什么,你都必须调整语法(例如,我不知道在 PostgreSQL、SQL SERVER 和 Oracle 中的 LIMIT 会有所不同) ,但是如果您的高级表示是可靠的,您可以构建逻辑来正确地为每个语句构造语句。

      【讨论】:

        猜你喜欢
        • 2022-06-23
        • 2016-06-20
        • 1970-01-01
        • 1970-01-01
        • 2013-02-25
        • 1970-01-01
        • 2022-06-23
        • 2015-12-07
        • 1970-01-01
        相关资源
        最近更新 更多