【问题标题】:Determine source on COALESCE fields确定 COALESCE 字段的来源
【发布时间】:2016-08-15 17:10:59
【问题描述】:

我有两个表table,它们的结构相同,但属于不同的模式(模式AB)。所有有问题的行都将始终出现在A.table 中,但可能会出现也可能不会出现在B.table 中。 B.table 本质上是对 A.table 中默认值的覆盖。

因此,我的查询在每个字段上使用了一个 COALESCE,类似于:

SELECT COALESCE(B.id, A.id) as id,
       COALESCE(B.foo, A.foo) as foo,
       COALESCE(B.bar, A.bar) as bar
FROM A.table LEFT JOIN B.table ON (A.id = B.id)
WHERE A.id in (1, 2, 3)

这很好用,但我还想添加数据源。在上面的示例中,假设 id=2 存在于 B.table 但不存在于 1 或 3 中,我想包括一些指示,表明 A 是 1 和 3 的来源,而 B 是 2 的来源。

所以数据可能如下所示

+---------------------------------+
|  id  |  foo  |  bar  |  source  |
+---------------------------------+
|   1  |    a  |    b  |       A  |
|   2  |    c  |    d  |       B  |
|   3  |    e  |    f  |       A  |
+---------------------------------+

我并不关心source的值是多少,只要我能区分A和B。

我不是 pgsql 专家(绝对不是),但我已经修改了 EXISTS 和子查询,但到目前为止还没有运气。

【问题讨论】:

    标签: sql postgresql left-join coalesce


    【解决方案1】:

    由于显示默认值的记录(来自 A.table)的 B.id 为 NULL,因此您只需将此列规范添加到查询中:

    CASE WHEN B.id IS NULL THEN 'A' ELSE 'B' END AS Source
    

    【讨论】:

    • 美丽。如此简单却让我无法理解。谢谢。
    • 使用简单的COALESCE 表达式,如果B.foo 可以为NULL(不排除),foo 的来源仍然可以是A,即使B.id IS NOT NULL
    【解决方案2】:

    USING 子句将简化您的查询:

    SELECT id
         , COALESCE(B.foo, A.foo) AS foo
         , COALESCE(B.bar, A.bar) AS bar
         , CASE WHEN b.id IS NULL THEN 'A' ELSE 'B' END AS source  -- like @Terje provided
    FROM   a
    LEFT   JOIN b USING (id)
    WHERE  a.id IN (1, 2, 3);

    但通常,这个替代查询应该为您提供更好的服务:

    SELECT x.*  --  or list columns of your choice
    FROM  (VALUES (1), (2), (3)) t (id)
         , LATERAL (
       SELECT *, 'B' AS source FROM b WHERE id = t.id
       UNION ALL
       SELECT *, 'A'           FROM a WHERE id = t.id
       LIMIT 1
       ) x
    ORDER  BY x.id;
    

    优点:

    • 您不必为要添加到结果中的每一列都添加另一个 COALESCE 构造。
    • 相同的查询适用于ab 中的任意数量的列。
    • 即使列名不相同,查询也能正常工作。只有列的数量和数据类型必须匹配。 当然,您也可以随时列出选定的兼容列:

      SELECT *  --  or list columns of your choice
      FROM  (VALUES (1), (2), (3)) t (id)
           , LATERAL (
         SELECT foo, bar, 'B' AS source FROM b WHERE id = t.id
         UNION ALL
         SELECT foo2, bar17, 'A'        FROM a WHERE id = t.id
         LIMIT 1
         ) x
      ORDER  BY x.id;

      第一个SELECT 确定名称、数据类型和列数。

    • 如果b 中的列未定义NOT NULL,则此查询不会中断。
      COALESCE 无法区分 b.foo IS NULLno row with匹配id 中的b。因此,任何结果列的来源(id 除外)仍然可以是“A”,即使结果行显示“B” - 如果b 中的任何相关列可以是NULL
      如果行存在,我的替代方法会从 b 返回 all 值 - 包括 NULL 值。因此,如果b 中的列可以是NULL,则结果可能会有所不同。需要哪种行为取决于您的要求。

    任何一个查询都假定id 被定义为主键(因此每个给定的id 值恰好有1 行或0 行)。

    相关:

    【讨论】:

    • 感谢您的回答和彻底的细分。事实证明,这些表的列数不同,所以我猜第二个行不通。有趣的阅​​读无论如何。
    • @sberry:第二个也适用于此。您只是不能在 LATERAL 查询中使用 SELECT * 的语法快捷方式。我添加了一个示例和更多解释。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2011-08-25
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多