通过多个查找表子句查找数据

y53ybaqx  于 2022-09-18  发布在  Java
关注(0)|答案(2)|浏览(415)
declare @Character table (id int, [name] varchar(12));

insert into @Character (id, [name])
values
(1, 'tom'),
(2, 'jerry'),
(3, 'dog');

declare @NameToCharacter table (id int, nameId int, characterId int);

insert into @NameToCharacter (id, nameId, characterId)
values
(1, 1, 1),
(2, 1, 3),
(3, 1, 2),
(4, 2, 1);

名称表不止1、2、3个,要解析的列表是动态的

名称表

id  | name
----------
1      foo
2      bar
3      steak

字符表

id | name
---------
1     tom
2     jerry
3     dog

名称到字符表格

id | nameId | characterId
1     1           1
2     1           3
3     1           2
4     2           1

我正在寻找一个查询,将返回一个字符,有两个名字。例如,使用上面的数据,只会返回“tom”。

SELECT * 
FROM nameToCharacterTable
WHERE nameId in (1,2)

In子句将返回所有包含1或3的行。我只想返回同时包含1和3的行。

我被难住了,我已经尝试了我所知道的一切,不想求助于动态SQL。任何帮助都是最好的

本例中的1,3将是一个动态的整数列表。例如,它可以是1,3,4,5,.....

yrdbyhpb

yrdbyhpb1#

过滤出字符在与您提供的列表匹配的CharacterToName表中出现的次数(我假设您可以将其转换为表变量或临时表)。

declare @Character table (id int, [name] varchar(12));

insert into @Character (id, [name])
values
(1, 'tom'),
(2, 'jerry'),
(3, 'dog');

declare @NameToCharacter table (id int, nameId int, characterId int);

insert into @NameToCharacter (id, nameId, characterId)
values
(1, 1, 1),
(2, 1, 3),
(3, 1, 2),
(4, 2, 1);

declare @RequiredNames table (nameId int);

insert into @RequiredNames (nameId)
values
(1),
(2);

select *
from @Character C
where (
    select count(*)
    from @NameToCharacter NC
    where NC.characterId = c.id
    and NC.nameId in (select nameId from @RequiredNames)
) = 2;

返回:

Id|名称

1|Tom

注意:提供这里所示的DDL+DML使人们更容易帮助您。

6tqwzwtp

6tqwzwtp2#

这是经典的Relational Division加上余数。**

有许多不同的解决方案。@Dalek给了你一个很好的答案:内部连接所有东西,然后检查每一盘都有合适的数量。这通常是最快的解决方案。

如果希望确保它可以处理动态数量的行数,只需将最后一行更改为

) = (SELECT COUNT(*) FROM @RequiredNames);

还有另外两种常见的解决方案。

  • LEFT-JOIN并检查是否已连接所有行
SELECT *
FROM @Character c
WHERE EXISTS (SELECT 1
    FROM @RequiredNames rn
    LEFT JOIN @NameToCharacter nc ON nc.nameId = rn.nameId AND nc.characterId = c.id
    HAVING COUNT(*) = COUNT(nc.nameId)  -- all rows are joined
);
  • 双重反联接,也就是说:没有“不在集合中”的“必需的”
SELECT *
FROM @Character c
WHERE NOT EXISTS (SELECT 1
    FROM @RequiredNames rn
    WHERE NOT EXISTS (SELECT 1
        FROM @NameToCharacter nc
        WHERE nc.nameId = rn.nameId AND nc.characterId = c.id
    )
);

另一个答案的变体使用窗口聚合而不是子查询。我不认为这是性能上的,但它在某些情况下可能有用处。

SELECT *
FROM @Character c
WHERE EXISTS (SELECT 1
    FROM (
      SELECT *, COUNT(*) OVER () AS cnt
      FROM @RequiredNames
    ) rn
    JOIN @NameToCharacter nc ON nc.nameId = rn.nameId AND nc.characterId = c.id
    HAVING COUNT(*) = MIN(rn.cnt)
);

db<>fiddle

相关问题