在选择不同的行时统一来自不同表的列

Posted

技术标签:

【中文标题】在选择不同的行时统一来自不同表的列【英文标题】:Unify columns from different tables while selecting distinct rows 【发布时间】:2021-03-23 17:22:11 【问题描述】:

表格

用户

id name email is_active
1 john john@albert.com FALSE
2 mike mike@ss.com TRUE
3 monica monica@dunno.com TRUE
4 joey joey@as.com FALSE
5 ross ross@boss.com FALSE

订阅

id house_id plan name status
1 1 A banana a month inactive
2 2 An apple a month active
3 3 A pear a month active

房子

id name
1 John's House
2 Mike's House
3 Monica's House
4 Joey's House
5 Ross's House

House_Contact(旧表)

id house_id is_primary
1 1 TRUE
2 2 FALSE
2 3 TRUE

House_User(新表)

id house_id is_owner user_id
1 2 FALSE 2
2 4 FALSE 4
3 5 FALSE 5

预期结果

结果表应包括以下内容:

无论状态如何,用户是否都有订阅?如果是,请包括,如果不是,请忽略。 从用户表中获取email & is_active(如果他们有订阅) 获取is_primaryis_owner(如果他们有订阅) 结果应该是不同的(没有重复的用户)
house_id email is_owner is_active
1 john@albert.com TRUE FALSE
2 mike@ss.com FALSE TRUE
3 monica@dunno.com TRUE TRUE

我尝试了什么

SELECT
    u.email AS "email",
    u.is_active AS "is_active",
    h.id AS "house_id",
    is_owner
FROM
    house c
    INNER JOIN (
        SELECT
            house_id,
            user_id
        FROM
            house_user) hu ON h.id = hu.house_id
    INNER JOIN (
        SELECT
            id,
            email,
            is_active
        FROM
            USER) u ON hu.user_id = u.id
    INNER JOIN (
        SELECT
            id,
            email,
            is_primary
        FROM
            house_contact) hc ON u.email = ch.email
    INNER JOIN (
        SELECT
            house_id,
            is_primary is_owner
        FROM
            house_contact
    UNION
    SELECT
        house_id,
        is_owner is_owner
    FROM
        house_user) t ON u.id = t.house_id)
ORDER BY
    u.email

结果是我删除 INNER JOINUNION 语句的一半。不知道如何继续。

我对统一列和可能的重复感到特别困惑。

【问题讨论】:

你能用英语解释一下结果吗?逻辑不明显。还不清楚这些表是如何相互关联的。它们都有 id,但什么 id 对应什么? 你能为这个问题创建一个fiddle 吗? 你为什么要做....INNER JOIN (SELECT ..... table ) ON.... 而不是更简单的..INNER JOIN table ON .... ??? 当数据中没有真值时,is_owner 如何变为“真”? 这不是您尝试的代码。 “用户”是保留字。 FROM USER 会引发错误。您是否简化了表名?最后是无与伦比的)。另外:如果is_primaryis_owner 同时存在,那么哪个优先?我们需要知道显示所有约束(特别是 PK、FK 和 NOT NULL)的实际表定义才能确定。并且总是你的 Postgres 版本,以防万一。 【参考方案1】:

我有根据的猜测:

SELECT DISTINCT ON (u.id)
      u.id, u.email, u.is_active, h.house_id, h.is_primary
FROM  "user" u
LEFT  JOIN (
   SELECT hu.user_id, hu.house_id
        , GREATEST(hc.is_primary, hu.is_owner) AS is_primary
   FROM   house_user hu
   LEFT   JOIN house_contact hc USING (house_id)
   WHERE  EXISTS (SELECT FROM subscription WHERE house_id = hu.house_id)
   ) h ON h.user_id = u.id
ORDER  BY u.id, h.is_primary DESC NULLS LAST, h.house_id;

我们在查询中根本不需要表house

我看到三个可能的冲突来源:

    house_contact.is_primaryhouse_user.is_owner。两者的意思似乎相同。 DB设计在这方面被打破了。取两者的GREATEST(),这意味着true,如果其中一个是true

    我们不关心subscription.status,所以只要确保房子至少有一个订阅EXISTS,从而避免可能的重复。

    一个用户可以住在多个房子里。我们希望每个用户只有 一个 行。因此,如果有的话,用is_primary(最小的house_id)显示第一个房子。如果没有房子,也没有订阅。但是外部LEFT JOIN 将用户保留在结果中。更改为JOIN 以跳过未订阅的用户。

关于DISTINCT ON

Select first row in each GROUP BY group?

关于排序布尔值:

Sorting null values after all others, except special Sort NULL values to the end of a table

【讨论】:

【参考方案2】:

您可以按如下方式使用joins

Select distinct hu.house_id, u.email, hu.is_owner, hc.is_primary
  From user u join house_user hu on u.id = hu.user_id
  Join subscriptions s on s.house_id = hu.house_id
  Join house_contract hc on hc.house_id = s.house_id;

如果表中有多个数据匹配条件,我已使用distinct 删除重复项。如果不需要,您可以将其删除。

【讨论】:

【参考方案3】:

据我所知,您希望从这样的查询开始:

select s.house_id, u.email, hu.is_owner, u.is_active      
from subscriptions s left join
     house_user hu
     on s.house_id = hu.house_id left join
     users u
     on hu.user_id = u.id;

这不会返回您想要的结果,但不清楚您的结果是如何得出的。

【讨论】:

这就是我要解决的问题。一些用户在house_contact 旧表中,一些用户在house_user 新表中。它们可能重复或仅包含在一张表中。所以我需要查询两者的结果并将is_primary列与is_owner统一起来。

以上是关于在选择不同的行时统一来自不同表的列的主要内容,如果未能解决你的问题,请参考以下文章

JOOQ:在通用界面中对来自不同表的列进行逻辑分组

Oracle 11g 文本:复合域索引 - 对来自不同表的列进行过滤

在 s-s-rS Report Builder Execute(@Query) 中对列标题使用动态 SQL - 使用来自 1 个表的值作为来自不同表的值的列标题

在选择不同的行时提供额外的数据

使用数据透视表(熊猫)中的小计行时保留索引部分(不同的列)

将来自同一表的单独查询组合为单独的列