RedShift GROUP BY 常量列给出不一致的结果

Posted

技术标签:

【中文标题】RedShift GROUP BY 常量列给出不一致的结果【英文标题】:RedShift GROUP BY Constant Column Gives Inconsistent Results 【发布时间】:2017-03-21 00:08:16 【问题描述】:

我想知道是否有人可以帮助解释为什么在 VARCHAR 的常量列上使用 GROUP BYINTEGER 的常量列会产生不同的行为。

这是我的最小工作示例。下表模拟了我发现此问题的真实数据:

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_1app_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)

这是不正确的,因为我实际上期望的是使用整数值 12 代替 VARCHARapp_1app_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 常量列给出不一致的结果的主要内容,如果未能解决你的问题,请参考以下文章

Redshift GROUP BY 时间间隔

H2 抱怨语法错误,MySQL 接受它 - 但是,错误的语法给出了正确的结果:列 ... 必须在 GROUP BY 列表中;

在 Amazon Redshift 中使用窗口函数时需要 GROUP BY 聚合

Redshift 中的 GROUP BY 后不必要的 DS_BCAST_INNER

为啥一个 group by 的聚合这么慢?

linq group by 和 select inside group by 给出错误 EFcore