在我们公司的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");
我想要以下输出:
以下是我希望做的事情:
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;
内部选择按预期对结果进行排序,但外部选择会任意重新排序(据我所知)。
所以我的问题是:
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)。答案 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 ++代码。