【问题标题】:Avoid multiple duplicate records due to JOIN needed for filtering避免由于过滤需要 JOIN 而出现多个重复记录
【发布时间】:2023-03-30 23:40:01
【问题描述】:

所以我有三个简单的表

create table user (
    id SERIAL PRIMARY KEY,
    name TEXT
);

create table item (
    user_id INTEGER REFERENCES user(id),
    key TEXT,
    count INTEGER
);

create table foob (
    user_id INTEGER REFERENCES user(id),
    value_f TEXT,
    value_t TEXT
);

我需要获取与某些项目和 foobs 匹配的所有用户:

select id, name
from user
left join item on item.user_id = user.id
left join foob on foob.user_id = user.id
where item.key = 'my_key' and foob.value_t = 'vtz'

但这会导致相同结果的多个副本,因为item 和/或foob 有多行引用同一用户。由于我只使用其他两个表进行过滤,不需要在我的select 中使用任何一个,我显然不需要多个复制结果。我怎样才能避免这种情况?

在我的真实代码中,我还将json_build_object() 与我的select 一起使用

【问题讨论】:

    标签: sql postgresql join left-join postgresql-9.5


    【解决方案1】:

    您可以只使用GROUP BY "user".id(因为它是其表的主键,其表中的所有其他列在功能上都依赖于它)。

    select    id, name
    from      "user"
    left join item on item.user_id = "user".id
    left join foob on foob.user_id = "user".id
    where     item.key = 'my_key' and foob.value_t = 'vtz'
    group by  "user".id
    

    顺便说一句,我不确定这些是否是您真实表格的名称。 Funny things happen if you select * from user.

    【讨论】:

    • 在这种情况下,group byselect distinct 有何不同?
    • @MarkusMeskanen 在这个上下文中没有太大区别,除了你可以使用json_build_object(),因为不需要比较整个输出行。 DISTINCT ON might also be used,但GROUP BY 是这里更标准的解决方案。
    【解决方案2】:

    你大概可以使用ROW_NUMBER()这样的函数

    select * from (
    select id, name, ROW_NUMBER () OVER (ORDER BY id) as rn
    from user
    left join item on item.user_id = user.id and item.key = 'my_key'
    left join foob on foob.user_id = user.id
    and foob.value_t = 'vtz' )xxx
    where rn = 1;
    

    【讨论】:

      【解决方案3】:

      最快的解决方法可能是SELECT DISTINCT

      select distinct id, name
      from user
      left join item on item.user_id = user.id
      left join foob on foob.user_id = user.id
      where item.key = 'my_key' and foob.value_t = 'vtz'
      

      另一种选择是在子查询中查找匹配的用户:

      with cte as (
          select t1.user_id
          from item t1
          inner join foob t2
              on t1.user_id = t2.user_id
          where t1.key = 'my_key' and t2.value_t = 'vtz'
      )
      select u.id, u.name
      from user u
      where u.id in (select user_id from cte)
      

      【讨论】:

      • 嗨,很抱歉最初没有包含它,但这不起作用,因为我实际上使用的是 select json_build_object()distinct 不适用于它:/
      • @ClodoaldoNeto 1 小时前编辑 stackoverflow.com/posts/42668847/revisions
      【解决方案4】:

      没有JOIN的另一个选项:

      select id, name
      from user 
      where user_id in (
          select user_id from item where key = 'my_key'
          UNION ALL
          select user_id form foob where value_t = 'vtz'
      )
      

      【讨论】:

        【解决方案5】:

        我认为您可能遇到了称为“鸿沟陷阱”的数据模型问题。

        【讨论】:

        • 我没有投反对票,但我可能应该投反对票。这没有回答我的问题,没有解释什么是鸿沟陷阱以及如何避免它,甚至没有提供任何关于鸿沟陷阱的文章的链接。如果这是评论而不是答案,您可能会获得支持。
        猜你喜欢
        • 1970-01-01
        • 2018-02-22
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2018-08-29
        • 2021-03-23
        • 1970-01-01
        • 2013-10-17
        相关资源
        最近更新 更多