将输入排序为聚合函数

时间:2014-06-25 18:57:35

标签: sql netezza

在我们公司的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之后只能应用于结果集中出现的术语。

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

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无关。外部选择忽略了内部选择的顺序,如下面的代码片段所示:

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

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

所以我的问题是:

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

PS:我应该如何在我的问题中包含和格式化结果集?我看不出如何制作一张桌子。

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

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

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

3 个答案:

答案 0 :(得分:2)

编辑:更好的解决方案。

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;

答案 1 :(得分:1)

如果安装了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;

答案 2 :(得分:0)

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

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的文档说明它会对项目进行排序,而不管它们的顺序如何。顺序是词典,而不是数字(它调用sort()方法std::list<std::string>)。如果您想要自定义排序,则需要修改其c ++代码。