【问题标题】:Ordering the input to an aggregate function将输入排序到聚合函数
【发布时间】:2014-08-16 11:24:12
【问题描述】:

在我们公司的 Netezza 机器上使用一些 SQL 查询,我试图将多个值连接到一个字符串中。问题是我需要对这些值进行排序,但 Netezza 不允许我按未分组的术语进行排序,因为它会在进行分组后应用排序。

我正在使用一个名为 group_concat 的 UDA,它连接字符串并在它们之间添加分隔符。我很确定 UDA 运行正常(在对其进行调整使其内部不进行任何排序之后)。

这是我的测试数据:

CREATE TABLE TEST (GRP INTEGER, ID INTEGER, DATA VARCHAR(10));
INSERT INTO TEST VALUES (1,3,"Three");
INSERT INTO TEST VALUES (1,1,"One");
INSERT INTO TEST VALUES (1,2,"Two");
INSERT INTO TEST VALUES (2,3,"Three");
INSERT INTO TEST VALUES (2,2,"Two");
INSERT INTO TEST VALUES (2,1,"One");

我想要以下输出:

  • GRP:1,ConcatData:“一、二、三”
  • GRP:2,ConcatData:“一、二、三”

这是我想做的事情:

SELECT GRP, GROUP_CONCAT(DATA)
FROM TEST
ORDER BY ID
GROUP BY GRP;

但这是不可能的:语法错误,因为 group by 必须在 order by 之前,并且在执行该 order by 之后只能应用于结果集中出现的术语。

其他人建议使用子选择来解决这个问题:子查询中的顺序和外部查询中的分组,如下所示:

SELECT GRP, GROUP_CONCAT(DATA,',') AS CONCATDATA
FROM
(
    SELECT *
    FROM TEST
    ORDER BY GRP, ID
) AS X
GROUP BY GRP;

这似乎在 PostgreSQL 9.3 中有效,但在 Netezza 中无效。每次运行查询时,结果的顺序都会发生变化。

最后一个查询的问题与 group by 无关。外部选择忽略了内部选择的顺序,如下面的 sn-p 所示:

SELECT *
FROM
(
    SELECT *
    FROM TEST
    ORDER BY GRP, ID
) AS X;

内部选择按预期对结果进行排序,但外部选择会任意重新排序(据我所知)。

所以我的问题是:

  • 为什么 Netezza 会忽略我的结果排序?
  • 如何构建一串分组但有序的数据?

PS:我应该如何在我的问题中包含和格式化结果集?我看不到如何制作表格。

编辑:在@Alex 的评论之后,我已经明确表示我想聚合一列(数据)中的值,但按另一列(id)排序。

编辑:我意识到 Netezza 可能无法以与其他一些数据库引擎相同的方式订购事物,因为数据是并行分布和处理的。 Netezza UDF 开发人员指南解释说,在 UDA 中,每个 SPU 首先聚合其拥有的数据,然后集中合并来自每个 SPU 的数据。在一个简单的 UDA 中,比如我看过的那些,合并函数不知道数据应该是什么顺序,即使数据是在每个 SPU 上排序的,也不能保证最终聚合的数据是有序的。也许有一种方法可以编写一个接受 ORDER BY 子句的 UDA……或者,我可以编写一个接受两个参数的 UDA,第一个是要聚合的字符串,第二个是顺序,但是,我不知道可以轻松地使用 UDA 中的关联数组。

编辑:Niederee's solution 有效,所以我接受了它,但我最终在 PostgreSQL 中创建了字符串,因为在加载到 Netezza 之前我们已经有一个 PostgreSQL 预处理阶段。仅供参考,这是将顶点坐标列表转换为可在 Netezza Spatial Toolkit(类似于 PostGIS)中使用的WKT string

【问题讨论】:

    标签: sql netezza


    【解决方案1】:

    编辑:更好的解决方案。

    SELECT 
        GRP, 
        CONCAT_DATA
    FROM (
        SELECT 
            GRP,
            GROUP_CONCAT(data) OVER (PARTITION BY grp ORDER BY id ASC) concat_data,
            row_number() OVER (PARTITION BY grp ORDER BY id DESC) rn
        FROM 
            test
    ) x
    WHERE rn = 1;
    

    请注意,此解决方案依赖于使用经过略微修改的 group_concat UDX,其中删除了 sort 行。

    较早的解决方案留给后代:

    刚刚找到了一个相当紧凑的解决方案,但我不确定它对 Netezza 未来变化的稳健程度。通过使用有序窗口函数来强制对子查询进行排序,我似乎得到了始终按正确顺序排列的结果。请注意,结果没有明确的顺序,并且行号不用于任何事情,但是如果您注释掉 MAX(rn),那么结果将不再排序,可能是因为对 row_number() 的调用被优化掉了。

    SELECT
        MAX(rn) as dummy, -- this prevents the row_number() from being optimised away and forces the output to be ordered
        GRP, 
        GROUP_CONCAT(DATA,',') AS CONCATDATA
    FROM
    (
        SELECT GRP, ID, DATA, ROW_NUMBER() OVER (PARTITION BY GRP ORDER BY ID) rn
        FROM TEST
    ) AS X
    GROUP BY GRP;
    

    【讨论】:

      【解决方案2】:

      如果您安装了 SQL Functions Toolkit,那么执行此操作的方法并不简单,那就是使用Arrays。我认为更好的方法是添加 IBM 的 group_concat UDF。下面的数组示例:

      CREATE temp TABLE TEST (GRP INTEGER, ID INTEGER, DATA VARCHAR(10));
      INSERT INTO TEST VALUES (1,3,'Three');
      INSERT INTO TEST VALUES (1,1,'One');
      INSERT INTO TEST VALUES (1,2,'Two');
      INSERT INTO TEST VALUES (2,3,'Three');
      INSERT INTO TEST VALUES (2,2,'Two');
      INSERT INTO TEST VALUES (2,1,'One');
      
      
      create temp table array_t(grp int,arr varchar(100));
      
      -- create array placeholder
      insert into array_t
      select distinct  grp, sql_functions.admin.array(8) from test;
      
      -- populate the array
      update array_t a  set arr = sql_functions.admin.add_element(a.arr, b.data)
      from (select grp, row_number() over(partition by grp order by id) as rown, data
      from test) b
      where a.grp=b.grp
      and b.rown=1;
      
      update array_t a  set arr = sql_functions.admin.add_element(a.arr, b.data)
      from (select grp, row_number() over(partition by grp order by id) as rown, data
      from test) b
      where a.grp=b.grp
      and b.rown=2;
      
      update array_t a  set arr = sql_functions.admin.add_element(a.arr,b.data)
      from (select grp, row_number() over(partition by grp order by id) as rown, data
      from test) b
      where a.grp=b.grp
      and b.rown=3;
      -- Return Result
      select grp, sql_functions.admin.array_combine(arr,',')
      from array_t;
      

      【讨论】:

      • 感谢您的想法。我听说过 SQL Extensions Toolkit,但没有意识到它支持数组。由于现在无法对其进行测试,我还没有完全弄清楚为什么您分三个阶段填充数组,并且看起来您的 rown 与我的 id 相同。我希望稍后尝试您的解决方案。
      • rown 只是确保它以正确的顺序提供给array
      【解决方案3】:

      这个简单的查询似乎在做你想做的事:

      select grp, group_concat(id, ',')
      from test
      group by grp
      order by grp;
      
       GRP | GROUP_CONCAT 
      -----+--------------
         1 | 1,2,3
         2 | 1,2,3
      (2 rows)
      

      group_concat 的文档说它对项目进行排序,无论它们进入的顺序如何。顺序是字典顺序的,而不是数字的(它调用 std::list<std::string>sort() 方法)。如果要自定义排序,则需要修改其c++代码。

      【讨论】:

      • 谢谢,亚历克斯。也许我过于简化了我的例子。没错,这会起作用,但我真的想在与我连接的列不同的列上订购。我会考虑修改我的问题。
      • 我已经修改了group_concat,只是注释掉了values.sort(),所以我假设这些值是按照它们到达的顺序连接起来的。也许可以进一步修改它以接受两个参数,@987654327 @ 和 id 但这暂时超出了我的范围。
      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2019-03-09
      • 2023-03-12
      • 1970-01-01
      • 1970-01-01
      • 2017-02-03
      相关资源
      最近更新 更多