按SQL子集对记录进行分组

按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

我有一个数据库,里面有许可证持有人(PermitNum=PK)和每个许可证持有人的详细设施。在tblPermitDetails表中有两列

  • PermitNum(外键)
  • FacilityID(设施表的整数外键查找) 许可证持有人的许可证上可以有1-29个项目,即许可证50可以有船坞(FacID 4)、铺砌人行道(FacID 17)和挡土墙(FacID 20)等。我需要一个SQL过滤器/显示任何内容,所有许可证上只有FacID 19、20或28,而不是那些有FacID 19、20或28的许可证加上“x”其他的许可证。仅此子集。我已经做了4天了,有人能帮我吗?我已经张贴到其他BB,但没有收到任何有益的建议

    正如奥德所建议的,这里有更多的细节。 tblPermitDetails表没有PK

    假设我们有1-10个许可证持有人;许可证1是John Doe,他有一个船坞(FacID 1)、一条走道(FacID 4)、一个浮标(FacID 7)和一条底冲(FacID 19)……这是许可证1的3条记录。许可证2是Sus Brown,她只有下护板(FacID 19),许可证3是Steve Toni,他有一个船坞(FacID 1),一条走道(FacID 4),一个浮标(FacID 7)和一道挡土墙(FacID 20)。许可证4是Jill Jack,她有下护板(FacID 19)和挡土墙(FacID 20)。我可以继续,但我希望你跟着我。我想要一个SQL(用于MS Access),它将显示只允许2和4,因为它们具有Facid 19和20的组合[两者或一个或另一个],但不包括任何其他内容,如允许1,它具有#19,但也具有4和7

    我希望这有帮助,如果没有,请说


    哦,是的,我确实知道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