按SQL子集对记录进行分组
我有一个数据库,里面有许可证持有人(PermitNum=PK)和每个许可证持有人的详细设施。在tblPermitDetails表中有两列按SQL子集对记录进行分组,sql,ms-access,Sql,Ms Access,我有一个数据库,里面有许可证持有人(PermitNum=PK)和每个许可证持有人的详细设施。在tblPermitDetails表中有两列 PermitNum(外键) FacilityID(设施表的整数外键查找) 许可证持有人的许可证上可以有1-29个项目,即许可证50可以有船坞(FacID 4)、铺砌人行道(FacID 17)和挡土墙(FacID 20)等。我需要一个SQL过滤器/显示任何内容,所有许可证上只有FacID 19、20或28,而不是那些有FacID 19、20或28的许可证加上“x
哦,是的,我确实知道I.e.和e.g.之间的区别。因为我40多岁了,写了3000多页的考古现场报告和文学硕士论文,但我在这里真的因为与SQL的斗争而感到压力很大,在请求帮助之前,我根本不在乎查阅《芝加哥风格手册》。所以,不要对我的合成错误害羞!谢谢!未经测试,但像这样的东西怎么样
SELECT DISTINCT p.PermitNum
FROM tblPermitDetails p
WHERE EXISTS
(SELECT '+'
FROM tblFacility f
WHERE p.FacilityID = f.FacilityID
AND f.facilityID = 19 )
AND EXISTS
(SELECT '+'
FROM tblFacility f
WHERE p.FacilityID = f.FacilityID
AND f.facilityID = 20 )
AND EXISTS
(SELECT '+'
FROM tblFacility f
WHERE p.FacilityID = f.FacilityID
AND f.facilityID = 28 )
AND NOT EXISTS
(SELECT '+'
FROM tblFacility f
WHERE p.FacilityID = f.FacilityID
AND f.facilityID NOT IN (19,20,28) )
快速的方法可能是只查看正好有三个匹配项的匹配项(使用内部查询),然后在这些匹配项中只包括有19、20和28个匹配项的匹配项
当然,这是一种蛮力方法,不是很优雅。但它有一点好处,那就是易于理解。我能想到的任何方法都不容易自定义为各种其他值集。好的,一开始我似乎不理解这个问题。因此,再一次:
SELECT PermitNum
FROM tblPermitDetails
WHERE FacilityID IN (19, 20, 28)
GROUP BY PermitNum
HAVING COUNT(PermitNum)=3
我将在这里重新创建Stacy的示例:
DECLARE @PermitHolders TABLE
(PermitNum INT NOT NULL,
PermitHolder VARCHAR(20))
DECLARE @tblPermitDetails TABLE
(PermitNum INT,
FacilityID INT)
INSERT INTO @PermitHolders VALUES (1, 'John Doe')
INSERT INTO @PermitHolders VALUES (2, 'Sus Brown')
INSERT INTO @PermitHolders VALUES (3, 'Steve Toni')
INSERT INTO @PermitHolders VALUES (4, 'Jill Jack')
INSERT INTO @tblPermitDetails VALUES (1, 1)
INSERT INTO @tblPermitDetails VALUES (1, 4)
INSERT INTO @tblPermitDetails VALUES (1, 7)
INSERT INTO @tblPermitDetails VALUES (1, 19)
INSERT INTO @tblPermitDetails VALUES (2, 19)
INSERT INTO @tblPermitDetails VALUES (3, 1)
INSERT INTO @tblPermitDetails VALUES (3, 4)
INSERT INTO @tblPermitDetails VALUES (3, 7)
INSERT INTO @tblPermitDetails VALUES (3, 20)
INSERT INTO @tblPermitDetails VALUES (4, 19)
INSERT INTO @tblPermitDetails VALUES (4, 20)
这就是解决方案:
SELECT * FROM @PermitHolders
WHERE (PermitNum IN (SELECT PermitNum FROM @tblPermitDetails WHERE FacilityID IN (19, 20, 28)))
AND (PermitNum NOT IN (SELECT PermitNum FROM @tblPermitDetails WHERE FacilityID NOT IN (19, 20, 28)))
我有一个观察:
您没有提到tblPermitDetails的任何PK。如果不存在,这可能对性能不好。我建议您同时使用PermitNum和FacilityID(复合密钥)创建PK因为这将作为您的主键和预期查询的有用索引。我不确定您是否想要全部19,20,28或任何19,20,28…此外,这是未经测试的,但如果您想要任何解决方案,它应该相当接近
Select
allowed.PermitNum
from
DetailedFacilties allowed
join DetailedFacilities disallowed on allowed.PermitNum != disallowed.PermitNum
where
allowed.FacilityID in (19, 20, 28)
and disallowed.FacilityID not in (19, 20, 28)
或者,以散文形式,获取具有任何请求的许可证编号的许可证列表,只要该许可证不在请求的列表中就不存在行
同一查询的更优化版本如下所示:
SELECT PermitNum FROM (SELECT DISTINCT PermitNum FROM tblPermitDetails
WHERE FacilityID IN (19, 20, 28)) AS t1
WHERE NOT EXISTS (SELECT 1 FROM tblPermitDetails t2
WHERE t2.PermitNum = t1.PermitNum
AND FacilityID NOT IN (19, 20, 28));
阅读起来有点困难,但通过先执行“DISTINCT”部分,它将涉及更少的“notexists”子查询
更新:
David-W-Fenton提到,出于优化原因,应该避免使用NOT EXISTS。对于一个小表,这可能不会有多大影响,但如果需要避免NOT EXISTS,也可以使用COUNT(*)进行查询:
SELECT DISTINCT PermitNum FROM tblPermitDetails t1
WHERE (SELECT COUNT(*) FROM tblPermitDetails t2
WHERE t1.PermitNum = t2.PermitNum
AND FacilityID IN (19, 20, 28))
=
(SELECT COUNT(*) FROM tblPermitDetails t3
WHERE t1.PermitNum = t3.PermitNum)
那(未经测试)呢
i、 e.我们找到所有与您的标准不匹配的许可证(它们至少有一个详细信息不在您列出的范围内),然后通过左连接和where标准找到所有剩余的许可证
如果索引设置正确,这应该很快。你想要那些只有三个面板的?还是有三个面板的任意组合的?什么数据库服务器?MS、Oracle、MySql?@Stacy-你的问题不太容易理解,这就是为什么你得不到回答的原因。你能编辑它并添加da的示例吗ta和您想要的输出?如果我们能看到这一点,人们将更容易理解您需要什么。例如,=拉丁语id est,意思是“那就是”;例如,=拉丁语exempli gratia,意思是“例如”。“我只是想提一下。”斯泰西,如果我理解正确的话:你需要所有至少有19、20或28项设施的许可证,但不包括其他设施的许可证。正确吗?这假设与Permitholder关联的每个设施中只能有一个无法工作。PermitNum只能是给定记录的一个值,因此您的查询永远不会返回任何结果。我认为这一个可以完成任务。如果它完成任务,为什么您不接受它作为答案?避免Access中不存在是一件好事,因为它的优化不可靠,并且通常不会在比较的两侧使用索引。请参阅更新的答案。对于较小的表,即使使用NOT EXISTS进行表扫描而不是索引查找,也可能不会产生任何明显的差异,但如果需要,还有其他方法可以解决此问题(尽管我怀疑,考虑到所有的子查询,这个会更快——但它可能是提出更优化的东西的起点)。
SELECT DISTINCT PermitNum FROM tblPermitDetails t1
WHERE (SELECT COUNT(*) FROM tblPermitDetails t2
WHERE t1.PermitNum = t2.PermitNum
AND FacilityID IN (19, 20, 28))
=
(SELECT COUNT(*) FROM tblPermitDetails t3
WHERE t1.PermitNum = t3.PermitNum)
select permitnum
from tblPermitDetails t1
left outer join
(Select distinct permitnum from tblPermitDetails where facilityId not in (19, 20, or 28)) t2
on t1.permitnum=t2.permitnum
where t2.permitnum is null