Use UNNEST
to convert the nested-array to rows, then do an anti-join to look for broken references:
(An anti-join is when you perform a x LEFT OUTER JOIN y
with a WHERE y.KeyColumn IS NULL
- this gives you the opposite of an INNER JOIN
with the same join criteria).
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
I want to delete a activity only if all the contact Ids of that activity are deleted in contacts table and keep the activity if at least one one contact id is still not deleted.
This can be done with a DELETE FROM
using WHERE crmActivity.Id IN
with a CTE that generates the correct set of bad crmActivity.Id
values, via a GROUP BY
with the above query:
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 );