【问题标题】:Why do these join differently based on size?为什么这些连接根据大小不同?
【发布时间】:2014-06-13 22:35:29
【问题描述】:

在 Postgresql 中,如果您取消嵌套两个相同大小的数组,它们会将一个数组中的每个值与另一个数组中的一个值对齐,但如果两个数组的大小不同,它将一个值与每个值连接起来来自另一个。

select unnest(ARRAY[1, 2, 3, 4, 5]::bigint[]) as id,
unnest(ARRAY['a', 'b', 'c', 'd', 'e']) as value

会回来

1 | "a"
2 | "b"
3 | "c"
4 | "d"
5 | "e"

但是

select unnest(ARRAY[1, 2, 3, 4, 5]::bigint[]) as id, -- 5 elements
unnest(ARRAY['a', 'b', 'c', 'd']) as value -- 4 elements
order by id

会回来

1 | "a"
1 | "b"
1 | "c"
1 | "d"
2 | "b"
2 | "a"
2 | "c"
2 | "d"
3 | "b"
3 | "d"
3 | "a"
3 | "c"
4 | "d"
4 | "a"
4 | "c"
4 | "b"
5 | "d"
5 | "c"
5 | "b"
5 | "a"

这是为什么?我假设正在使用某种隐式规则,我想知道我是否可以明确地做到这一点(例如,当我有匹配的数组大小时我想要第二种样式,或者我想要一个数组中的缺失值是视为 NULL)。

【问题讨论】:

    标签: arrays postgresql


    【解决方案1】:

    SELECT 中对 set-returning 函数的支持是 PostgreSQL 扩展,也是 IMO 非常奇怪的扩展。它被广泛认为已弃用,最好尽可能避免。

    尽可能避免使用 SRF-in-SELECT

    现在LATERAL 在 9.3 中得到支持,两个主要用途之一已经不复存在。如果您想将一个 SRF 的输出用作另一个 SRF 的输入,则以前必须在 SELECT 中使用 set-returning 函数; LATERAL 不再需要它。

    其他用途将在 9.4 中被替换,当添加 WITH ORDINALITY 时,允许您保留 set-returning 函数的输出顺序。这是目前主要的剩余用途:将两个 SRF 的输出压缩到匹配值对的行集中。 WITH ORDINALITY 最受期待 unnest 的使用,但也适用于任何其他 SRF。

    为什么会有奇怪的输出?

    PostgreSQL 在这里使用的逻辑(无论 IMO 出于什么疯狂的原因,它最初是在古代历史中引入的)是:每当任一函数产生输出时,发出一行。如果只有一个函数产生了输出,请再次扫描另一个函数的输出以获取所需的行。如果两者都不产生输出,则停止发射行。

    generate_series 更容易查看。

    regress=> SELECT generate_series(1,2), generate_series(1,2);
     generate_series | generate_series 
    -----------------+-----------------
                   1 |               1
                   2 |               2
    (2 rows)
    
    regress=> SELECT generate_series(1,2), generate_series(1,3);
     generate_series | generate_series 
    -----------------+-----------------
                   1 |               1
                   2 |               2
                   1 |               3
                   2 |               1
                   1 |               2
                   2 |               3
    (6 rows)
    
    regress=> SELECT generate_series(1,2), generate_series(1,4);
     generate_series | generate_series 
    -----------------+-----------------
                   1 |               1
                   2 |               2
                   1 |               3
                   2 |               4
    (4 rows)
    

    在大多数情况下,您真正​​想要的是两者的简单交叉连接,这要明智得多。

    regress=> SELECT a, b FROM generate_series(1,2) a, generate_series(1,2) b;
     a | b 
    ---+---
     1 | 1
     1 | 2
     2 | 1
     2 | 2
    (4 rows)
    
    regress=> SELECT a, b FROM generate_series(1,2) a, generate_series(1,3) b;
     a | b 
    ---+---
     1 | 1
     1 | 2
     1 | 3
     2 | 1
     2 | 2
     2 | 3
    (6 rows)
    
    regress=> SELECT a, b FROM generate_series(1,2) a, generate_series(1,4) b;
     a | b 
    ---+---
     1 | 1
     1 | 2
     1 | 3
     1 | 4
     2 | 1
     2 | 2
     2 | 3
     2 | 4
    (8 rows)
    

    当前主要的例外是当您想要以锁步方式运行多个函数时,成对运行(如zip),您目前无法使用连接来执行此操作。

    WITH ORDINALITY

    这将在 9.4 中使用WITH ORDINALITY 进行改进,虽然它会比 SELECT 中的多个 SRF 扫描效率稍低(除非添加了优化器改进),但它会更加明智。

    假设您想将1..310..40 与多余元素的空值配对。使用 with ordinality 就可以了(仅限 PostgreSQL 9.4):

    regress=# SELECT aval, bval 
               FROM generate_series(1,3) WITH ORDINALITY a(aval,apos) 
               RIGHT OUTER JOIN generate_series(1,4) WITH ORDINALITY b(bval, bpos) 
               ON (apos=bpos);
    
     aval | bval 
    ------+------
        1 |    1
        2 |    2
        3 |    3
          |    4
    (4 rows)
    

    而 srf-in-from 将改为返回:

    regress=# SELECT generate_series(1,3) aval, generate_series(1,4) bval;
     aval | bval 
    ------+------
        1 |    1
        2 |    2
        3 |    3
        1 |    4
        2 |    1
        3 |    2
        1 |    3
        2 |    4
        3 |    1
        1 |    2
        2 |    3
        3 |    4
    (12 rows)
    

    【讨论】:

    • 可能值得一提的是 LATERAL 在这里是隐含的,尤其是因为这个例子没有使用来自其他 from 子句的术语作为横向允许的。同样,在添加横向之前,您可以将 srf 作为 from 子句,对吧?
    猜你喜欢
    • 2016-09-14
    • 2020-12-05
    • 2019-12-05
    • 1970-01-01
    • 2013-04-05
    • 2015-04-18
    • 1970-01-01
    • 2021-06-23
    • 1970-01-01
    相关资源
    最近更新 更多