【问题标题】:How to use array in a sql join and check if all of the array elements satisfy a condition?如何在sql连接中使用数组并检查所有数组元素是否满足条件?
【发布时间】:2021-03-02 05:38:39
【问题描述】:

我在 postgresql 中有两个表,activitycontacts

一个activity可以有多个数组形式的联系人,像这样

contact Id = {23,54,34}.

我只想在联系人表中删除该活动的所有联系人 ID 时删除该活动,并在至少一个联系人 ID 仍未删除时保留该活动。

Deleted At 是联系人表中的列,用于检查已删除的联系人。我不想使用 NOT IN。

活动表

id    contact Id 
-------------------    
16    {60,61,23}
15    {}        
5     {59}
6     {}

【问题讨论】:

  • 您使用内联数组而不是正确的外键和映射表是否有原因? (见这里:stackoverflow.com/questions/41054507/…
  • 你的上级应该已将其更改为单独的表。此时是否可以进行数据库设计更改?
  • 如果我可以将这个 NOT IN 替换为一个也会有帮助的连接
  • 这是一个非常糟糕的设计,您遇到的问题是该糟糕设计的直接结果。如果要删除活动中的行,为什么要使用 UPDATE?顺便说一句,您还应该避免使用那些可怕的引用标识符wiki.postgresql.org/wiki/…
  • 请不要破坏您的帖子。通过在 Stack Exchange 网络上发帖,您已在 CC BY-SA 4.0 license 下授予 Stack Exchange 分发该内容的不可撤销的权利(即无论您未来的选择如何)。根据 Stack Exchange 政策,帖子的非破坏版本是分发的版本,因此,任何破坏行为都将被撤销。如果您想了解更多关于删除帖子的信息,请参阅:How does deleting work?

标签: sql postgresql join


【解决方案1】:

您可以使用简单的EXISTS 谓词测试contacts 表和activity.contacts 数组:

create table activity (
  id int primary key,
  contacts int[]
)
create table contacts (
  id int primary key,
  name varchar(10),
  deleted boolean
)
insert into activity
  select 16 as id, '{1,2,3}'::int[] as contacts union all
  select 15, null union all      
  select 5, '{4}' union all
  select 6, '{6, 5}'
insert into contacts
  select 1 as id, 'q' as name, false as deleted union all
  select 2, 'w', false union all
  select 3, 'e', true union all
  select 4, 'r', false union all
  select 5, 't', true union all
  select 6, 'y', true
delete
from activity a
where not exists (
  select null
  from contacts c
  where not(c.deleted)
    and c.id = any(a.contacts)
)

2 行受影响

db小提琴here

【讨论】:

    【解决方案2】:

    使用UNNEST 将嵌套数组转换为行,然后执行反连接以查找损坏的引用:

    An anti-join 是当您使用 WHERE y.KeyColumn IS NULL 执行 x LEFT OUTER JOIN y 时 - 这与具有相同连接条件的 INNER JOIN 正好相反)。

    WITH unnested AS (
    
        SELECT
            Id AS ActivityId,
            UNNEST( ContactId ) AS ContactIdFromArray
        FROM
            crmActivity
    )
    SELECT
        u.ActivityId,
        u.ContactIdFromArray AS Missing_ContactId_in_Activity
    FROM
        unnested AS u
        LEFT OUTER JOIN contacts AS c ON
            c.ContactId = u.ContactIdFromArray
    WHERE
        c.ContactId IS NULL
    ORDER BY
        u.ActivityId
    

    我只想在联系人表中删除该活动的所有联系人 ID 时删除该活动,并在至少一个联系人 ID 仍未删除时保留该活动。

    这可以通过 DELETE FROM 使用 WHERE crmActivity.Id IN 和 CTE 来完成,该 CTE 生成正确的 bad crmActivity.Id 值集,通过带有上述查询的 GROUP BY: p>

    WITH unnested AS (
    
        SELECT
            Id AS ActivityId,
            UNNEST( ContactId ) AS ContactIdFK
        FROM
            crmActivity
    )
    WITH brokenContacts AS (
        
        SELECT
            u.ActivityId,
            u.ContactIdFK,
            c.ContactId AS ContactIdPK
            
        FROM
            unnested AS u
            LEFT OUTER JOIN contacts AS c ON
                c.ContactId = u.ContactIdFromArray
    )
    WITH counts AS (
        
        SELECT
            ActivityId,
            COUNT(*) AS ContactIdCount,
            COUNT( CASE WHEN ContactIdPK IS NULL THEN 1 END ) AS BadContactIdCount
        FROM
            brokenContacts
        GROUP BY
            ActivityId
    )
    WITH badActivityIds AS (
        
        SELECT
            ActivityId
        FROM
            counts
        WHERE
            BadContactIdCount = ContactIdCount
            AND
            ContactIdCount > 0
    )
    DELETE FROM
        crmActivity
    WHERE
        ActivityId IN ( SELECT ActivityId FROM badActivityIds );
    
    

    【讨论】:

      猜你喜欢
      • 2017-06-20
      • 2013-03-06
      • 2012-10-01
      • 2021-12-07
      • 1970-01-01
      • 1970-01-01
      • 2017-08-09
      • 2016-04-26
      • 1970-01-01
      相关资源
      最近更新 更多