RedShift GROUP BY 常量列给出不一致的结果
Posted
技术标签:
【中文标题】RedShift GROUP BY 常量列给出不一致的结果【英文标题】:RedShift GROUP BY Constant Column Gives Inconsistent Results 【发布时间】:2017-03-21 00:08:16 【问题描述】:我想知道是否有人可以帮助解释为什么在 VARCHAR
的常量列上使用 GROUP BY
与 INTEGER
的常量列会产生不同的行为。
这是我的最小工作示例。下表模拟了我发现此问题的真实数据:
CREATE TABLE test.show_bug AS
WITH integers AS (
SELECT 0 AS num
UNION SELECT 1 AS num
UNION SELECT 2 AS num
UNION SELECT 3 AS num
UNION SELECT 4 AS num
UNION SELECT 5 AS num
)
SELECT
'2017-03-16' + mod(a.num, 2) AS date_time
, CASE mod(b.num, 3)
WHEN 0 THEN 'source_a'
WHEN 1 THEN 'source_b'
WHEN 2 THEN 'source_c'
END AS user_source
, b.num || a.num || b.num || a.num || b.num AS user_id
FROM integers AS a
CROSS JOIN integers AS b
;
这看起来像:
date_time | user_source | user_id
------------+-------------+---------
2017-03-17 | source_a | 3113313
2017-03-17 | source_b | 4114414
2017-03-17 | source_b | 1111111
2017-03-16 | source_a | 0000000
2017-03-16 | source_c | 2442242
2017-03-16 | source_c | 5225525
....
(36 rows)
基本上我希望能够COUNT
(每天)用户数量、来源数量和每个来源的用户数量。但是,我有两个相同格式的不同表格,我想将结果放在一起UNION
。我可以通过为每个结果添加一个常量列来区分这些结果:app_1
和 app_2
。
为了举例,我两次使用同一个模拟表,但在实际应用程序中,我有两个不同的表,无论哪种方式,以下 sql 都应该得到我想要的结果:
SELECT
'app_1' AS app
, date_time
, COUNT(user_source)
, COUNT(DISTINCT user_source)
, COUNT(DISTINCT user_id)
FROM test.show_bug
GROUP BY 1, 2
UNION
SELECT
'app_2' AS app
, date_time
, COUNT(user_source)
, COUNT(DISTINCT user_source)
, COUNT(DISTINCT user_id)
FROM test.show_bug
GROUP BY 1, 2
这会导致
app | date_trunc | count | count1 | count2
----------+---------------------+-------+--------+--------
app_1 | 2017-03-16 00:00:00 | 2 | 1 | 0
app_1 | 2017-03-17 00:00:00 | 2 | 1 | 0
app_1 | 2017-03-19 00:00:00 | 5 | 0 | 1
app_2 | 2017-03-19 00:00:00 | 7 | 1 | 0
app_1 | 2017-03-16 00:00:00 | 0 | 1 | 0
....
(112 rows)
这是不正确的,因为我实际上期望的是使用整数值 1
和 2
代替 VARCHAR
值 app_1
和 app_2
获得的结果,即像:
SELECT
1 AS app
, date_time
, COUNT(user_source)
, COUNT(DISTINCT user_source)
, COUNT(DISTINCT user_id)
FROM test.show_bug
GROUP BY 1, 2
UNION
SELECT
2 AS app
, date_time
, COUNT(user_source)
, COUNT(DISTINCT user_source)
, COUNT(DISTINCT user_id)
FROM test.show_bug
GROUP BY 1, 2
这给了我:
app | date_trunc | count | count1 | count2
----------+---------------------+-------+--------+--------
1 | 2017-03-16 00:00:00 | 192 | 16 | 192
1 | 2017-03-17 00:00:00 | 208 | 14 | 208
1 | 2017-03-18 00:00:00 | 203 | 14 | 203
1 | 2017-03-19 00:00:00 | 203 | 14 | 203
1 | 2017-03-20 00:00:00 | 35 | 0 | 35
2 | 2017-03-16 00:00:00 | 192 | 16 | 192
2 | 2017-03-17 00:00:00 | 208 | 14 | 208
2 | 2017-03-18 00:00:00 | 203 | 14 | 203
2 | 2017-03-19 00:00:00 | 203 | 14 | 203
2 | 2017-03-20 00:00:00 | 35 | 0 | 35
如果我不使用UNION
,也会看到这种效果。
有一些明显的变通方法可以得到我想要的结果,但这里的根本问题是使用 VARCHAR
常量列而不是 INTEGER
常量列似乎存在不直观的行为差异。
如果有人可以帮助我了解这种差异是什么,我将不胜感激。
【问题讨论】:
出于某种原因,我在两周前向 AWS 支持提交了一个错误。他们回复后我会更新答案... 您收到 AWS Support 的解释了吗?我遇到了类似的问题,目前正在跳过常量值(文字)上的 GROUP BY。 【参考方案1】:我想说您在 Amazon Redshift 中发现了一个真正的错误,或者至少是一种不受欢迎的行为。
我设法将范围缩小到:
仅当VARCHAR
用作 GROUP BY 之一时,并且
使用多个COUNT(DISTINCT)
语句时
所以,这个简单的语句也产生了太多的结果:
SELECT
'1',
COUNT(DISTINCT user_source),
COUNT(DISTINCT user_id)
FROM show_bug
GROUP BY 1
不过没关系:
SELECT
'1'::INTEGER,
COUNT(DISTINCT user_source),
COUNT(DISTINCT user_id)
FROM show_bug
GROUP BY 1
删除任一 COUNT(DISTINCT)
条目也可以正常工作。
如果您订阅了 AWS Support,我建议您提交错误报告。如果您没有订阅支持,您可以通过 AWS 支持论坛提交,但他们无法保证响应时间。
【讨论】:
'1'::INTEGER
可以简化为1
将字符串文字转换为特定类型似乎可以更正结果:SELECT '1'::CHAR(10), COUNT(DISTINCT user_source), COUNT(DISTINCT user_id) FROM show_bug GROUP BY 1
【参考方案2】:
将文字转换为特定类型会改变行为,但仍会产生不一致的结果。使用 UNION ALL 可以避免掩盖一些问题,并且下面的一些查询将返回更多结果。
使用 CHAR 得到 4 行:
SELECT
'app_1'::CHAR(5) AS app
, date_time
, COUNT(user_source)
, COUNT(DISTINCT user_source)
, COUNT(DISTINCT user_id)
FROM test.show_bug
GROUP BY 1, 2
UNION
SELECT
'app_2'::CHAR(5) AS app
, date_time
, COUNT(user_source)
, COUNT(DISTINCT user_source)
, COUNT(DISTINCT user_id)
FROM test.show_bug
GROUP BY 1, 2
app | date_time | count | count1 | count2
-------+------------+-------+--------+--------
app_2 | 2017-03-16 | 18 | 3 | 18
app_1 | 2017-03-17 | 18 | 3 | 18
app_1 | 2017-03-16 | 18 | 3 | 18
app_2 | 2017-03-17 | 18 | 3 | 18
(4 rows)
VARCHAR 给出不同的结果:
SELECT
'app_1'::VARCHAR(10) AS app
, date_time
, COUNT(user_source)
, COUNT(DISTINCT user_source)
, COUNT(DISTINCT user_id)
FROM test.show_bug
GROUP BY 1, 2
UNION
SELECT
'app_2'::VARCHAR(10) AS app
, date_time
, COUNT(user_source)
, COUNT(DISTINCT user_source)
, COUNT(DISTINCT user_id)
FROM test.show_bug
GROUP BY 1, 2
app | date_time | count | count1 | count2
-------+------------+-------+--------+--------
app_1 | 2017-03-16 | 3 | 1 | 0
app_1 | 2017-03-17 | 3 | 1 | 0
app_2 | 2017-03-17 | 0 | 1 | 0
app_2 | 2017-03-16 | 3 | 1 | 0
app_2 | 2017-03-17 | 0 | 0 | 1
app_1 | 2017-03-16 | 0 | 0 | 1
app_2 | 2017-03-16 | 0 | 0 | 1
app_1 | 2017-03-17 | 0 | 1 | 0
app_2 | 2017-03-16 | 0 | 1 | 0
app_1 | 2017-03-16 | 0 | 1 | 0
app_1 | 2017-03-17 | 0 | 0 | 1
app_2 | 2017-03-17 | 3 | 1 | 0
(12 rows)
使用 INT 得到与上述 CHAR 相同的结果。
哇,这太可怕了。现在我必须查看所有使用这样的文字对结果集进行分类的查询。
【讨论】:
以上是关于RedShift GROUP BY 常量列给出不一致的结果的主要内容,如果未能解决你的问题,请参考以下文章
H2 抱怨语法错误,MySQL 接受它 - 但是,错误的语法给出了正确的结果:列 ... 必须在 GROUP BY 列表中;
在 Amazon Redshift 中使用窗口函数时需要 GROUP BY 聚合