使用值对查找行并找到完全匹配的组

Lookup rows using pairs of values and find exact matching groups

我有这些表:

项目

id name version
1 Pete 0.0.1
2 Swag 0.0.1
3 Swag 0.0.2
4 Swag 0.0.3
5 Kale 0.0.1
6 Kale 0.0.2

id name
1 Jake
2 Skye
3 Kieth
4 Jim
5 Eliz

Person_Project

id person_id project_id
1 1 1
2 2 1
3 2 2
4 3 1
5 3 3
6 4 1
7 4 4
8 5 1
9 5 2
10 5 5

每个人都有独特的项目,这意味着没有两个人会在同一个项目上工作。

我正在写一个基于 java 的 api,我收到了一个 json 不同项目的请求,我必须 return 正在从事确切项目的人在请求中给出。

要求:

[
    {"name": "Pete", "version": "0.0.1"}
]

这应该returnJake

要求:

[
    {"name": "Pete", "version": "0.0.1"},
    {"name": "Swag", "version": "0.0.1"}
]

这应该returnSkye

要求:

[
    {"name": "Pete", "version": "0.0.1"},
    {"name": "Swag", "version": "0.0.2"}
]

这应该returnKieth

我正在为此写 SQL,但没有得到我需要的东西。

这就是我要做的

SELECT pe.id, pe.name
FROM person pe
LEFT JOIN person_project pepr on pepr.person_id = pe.id
WHERE pe.id IN (
    SELECT pepr.person_id
    FROM project pr
    LEFT JOIN person_project pepr ON pepr.project_id = pr.id
    WHERE pr.name IN ('Pete', 'Swag') AND pr.version IN ('0.0.1', '0.0.2')
    GROUP BY pepr.project_id
    HAVING COUNT(pepr.project_id) = 2
)
GROUP BY pe.id, pe.name
HAVING COUNT(pe.id) = 2

这是不对的,因为我将 IN 用于将应用于其他项目的版本。

您可以使用 SQL 关系除法逻辑,如 this answer 中所述。您对精确 division/no 余数部分感兴趣:

with project_list as (
    select id
    from project
    where exists (
        select *
        from (values
            ('pete', '0.0.1'),
            ('swag', '0.0.1')
        ) as user_input(name, version)
        where project.name = user_input.name and project.version = user_input.version
    )
), person_project_copy as (
    select person_id, case when project_list.id is not null then 1 end as is_required
    from person_project
    left join project_list on person_project.project_id = project_list.id
)
select person_id
from person_project_copy
group by person_id
having count(is_required) = (select count(*) from project_list)
and    count(*)           = (select count(*) from project_list)

DB<>Fiddle for all three examples

这是一道经典的Relational Division Without Remainder题。

首先将您的输入数据放入 table 变量或 Table 值参数或临时 table.

那么你可以使用标准关系除法答案之一

SELECT
  p.name
FROM Person p
WHERE EXISTS (SELECT 1
    FROM Person_Project pp
    LEFT JOIN @input i
        JOIN Project prj ON prj.name = i.name AND prj.version = i.version
      ON pp.project_id = prj.id
    WHERE pp.person_id = p.id
    HAVING COUNT(prj.id) = COUNT(*)
       AND COUNT(prj.id) = (SELECT COUNT(*) FROM @input)
);

db<>fiddle

它的作用如下:

  • 获取满足以下 EXISTS 子查询的所有 Person 行:
  • 对于每个 Person 取其所有 Person_Project
  • Left-join输入数据(同时加入匹配Project)。
  • 将其分组并确保来自连接的匹配项数等于子查询中的行数...
  • ... 并且数字也等于输入行的总数。

还有其他解决方案。