Warning: file_get_contents(/data/phpspider/zhask/data//catemap/5/sql/72.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
Sql 如何优化3个表上的m:n关系查询_Sql_Database Design_Relation - Fatal编程技术网

Sql 如何优化3个表上的m:n关系查询

Sql 如何优化3个表上的m:n关系查询,sql,database-design,relation,Sql,Database Design,Relation,这是我的sql问题-共有3个表: Names Lists ListHasNames Id Name Id Desc ListsId NamesId =-------- ------------ ---------------- 1 Paul 1 Football 1 1 2 Joe 2 Basketball

这是我的sql问题-共有3个表:

Names Lists ListHasNames Id Name Id Desc ListsId NamesId =-------- ------------ ---------------- 1 Paul 1 Football 1 1 2 Joe 2 Basketball 1 2 3 Jenny 3 Ping Pong 2 1 4 Tina 4 Breakfast Club 2 3 5 Midnight Club 3 2 3 3 4 1 4 2 4 3 5 1 5 2 5 3 5 4 这意味着保罗的Id=1和乔的Id=2在足球队名单上。Id=1,保罗和珍妮在篮球队,等等

现在我需要一个SQL语句,它返回特定名称组合的Lists.Id: 在哪些名单中,保罗、乔和珍妮是唯一的成员?答案列表。Id=4早餐俱乐部-但不是5午夜俱乐部,因为蒂娜也在该列表中

我尝试过内部联接和子查询:

SELECT Q1.Lists_id FROM ( SELECT Lists_Id FROM names as T1, listhasnames as T2 WHERE (T1.Name='Paul') and (T1.Id=T2.Names_ID) and ( ( SELECT count(*) FROM listhasnames as Z1 where (Z1.lists_id = T2.lists_Id) ) = 3) ) AS Q1 INNER JOIN ( SELECT Lists_Id FROM names as T1, listhasnames as T2 WHERE (T1.Name='Joe') and (T1.Id=T2.Names_ID) and ( (SELECT count(*) FROM listhasnames as Z1 WHERE (Z1.Lists_id = T2.lists_id) ) = 3) ) AS Q2 ON (Q1.Lists_id=Q2.Lists_id) INNER JOIN ( SELECT Lists_Id FROM names as T1, listhasnames as T2 WHERE (T1.Name='Jenny') and (T1.Id=T2.Names_ID) and ( (SELECT count(*) FROM listhasnames as Z1 WHERE (Z1.Lists_id = T2.lists_id) ) = 3) ) AS Q3 ON (Q1.Lists_id=Q3.Lists_id) 看起来有点复杂,嗯?如何优化? 我只需要列表.Id,其中有特定的名字,只有这些名字,没有其他人。也许是选入

问候,, 丹尼斯更新:

select a.ListsId from
(
    --lists with three names only
    select lhn.ListsId, count(*) as count
    from ListHasNames  lhn
    inner join Names n on lhn.NamesId = n.Id 
    group by lhn.ListsId
    having count(*) = 3
) a
where a.ListsId in (select ListsId from ListHasNames lhn where NamesId = (select NamesId from names where Name = 'Paul'))
and a.ListsId in (select ListsId from ListHasNames lhn where NamesId = (select NamesId from names where Name = 'Joe'))
and a.ListsId in (select ListsId from ListHasNames lhn where NamesId = (select NamesId from names where Name = 'Jenny'))
更新:

select a.ListsId from
(
    --lists with three names only
    select lhn.ListsId, count(*) as count
    from ListHasNames  lhn
    inner join Names n on lhn.NamesId = n.Id 
    group by lhn.ListsId
    having count(*) = 3
) a
where a.ListsId in (select ListsId from ListHasNames lhn where NamesId = (select NamesId from names where Name = 'Paul'))
and a.ListsId in (select ListsId from ListHasNames lhn where NamesId = (select NamesId from names where Name = 'Joe'))
and a.ListsId in (select ListsId from ListHasNames lhn where NamesId = (select NamesId from names where Name = 'Jenny'))
编辑:由于Chris Gow的评论而更正;子选择是排除包含其他人的列表所必需的。 由于丹尼斯的评论,编辑2更正了表名

编辑:由于Chris Gow的评论而更正;子选择是排除包含其他人的列表所必需的。
编辑2由于丹尼斯的评论更正了表名

我最近刚刚解决了一个可能也适用于您的案例的问题。这可能太过分了

我采用的方法是创建可能是正确解决方案的候选关联列表,然后使用游标或队列表遍历可能正确的解决方案以进行完整验证

在我的例子中,这是通过像这样做来实现的

select
ParentId
count(*) as ChildCount
checksum_agg(checksum(child.*) as ChildAggCrc
from parent join child on parent.parentId = child.parentId
然后,您可以将计数和聚合校验和与查找数据(即要检查的3个名称)进行比较。如果没有行匹配,则保证没有匹配项。如果有任何行匹配,则可以遍历并连接该特定的ParentId,以验证行集之间是否存在任何差异


清澈如泥

我最近刚刚解决了一个问题,这个问题可能也适用于您的案例。这可能太过分了

我采用的方法是创建可能是正确解决方案的候选关联列表,然后使用游标或队列表遍历可能正确的解决方案以进行完整验证

在我的例子中,这是通过像这样做来实现的

select
ParentId
count(*) as ChildCount
checksum_agg(checksum(child.*) as ChildAggCrc
from parent join child on parent.parentId = child.parentId
然后,您可以将计数和聚合校验和与查找数据(即要检查的3个名称)进行比较。如果没有行匹配,则保证没有匹配项。如果有任何行匹配,则可以遍历并连接该特定的ParentId,以验证行集之间是否存在任何差异


清澈如泥

以卡尔·马纳斯特的解决方案为出发点,我想到了:

SELECT listsid 
FROM listhasnames 
GROUP BY listsid HAVING COUNT(*) = 3
INTERSECT
SELECT x.listsid 
FROM listhasnames x, names n 
WHERE n.name IN('Paul', 'Joe', 'Jenny') 
AND n.id = x.namesid

以卡尔·马纳斯特的解决方案为出发点,我想到:

SELECT listsid 
FROM listhasnames 
GROUP BY listsid HAVING COUNT(*) = 3
INTERSECT
SELECT x.listsid 
FROM listhasnames x, names n 
WHERE n.name IN('Paul', 'Joe', 'Jenny') 
AND n.id = x.namesid

也许你应该告诉他Count=3是因为他的请求有3个名字,因为名字列表是固定的,Count总是3,我会从选择列表中删除最后两列,改为使用COUNT*=3。也许你应该告诉他COUNT=3是因为他请求使用3个名称。由于名称列表是固定的,COUNT将始终为3,我会从选择列表中删除最后两列,改为使用COUNT*=3。这是一条注释,没有答案。我有点好奇。为什么表名使用复数?我们通常使用单数名称来命名表中的一行。如果要使用复数,为什么第三个表的名称不更合适?我还可以建议将第三个表命名为Membership或ListMembership。Carl Manaster的答案返回您指定的结果集。是否为复数:第三个表名为ListHasNames,因为如果您选择n:m关系,MySQL Workbench默认分配该名称。这只是一个例子……这是一个评论,不是答案。我有点好奇。为什么表名使用复数?我们通常使用单数名称来命名表中的一行。如果要使用复数,为什么第三个表的名称不更合适?我还可以建议将第三个表命名为Membership或ListMembership。Carl Manaster的答案返回您指定的结果集。是否为复数:第三个表名为ListHasNames,因为如果您选择n:m关系,MySQL Workbench默认分配该名称。这只是一个例子……这在postgreSQL中似乎不起作用。我试过了,4号和5号都回来了。OP只希望返回的列表ID只包含这三个人,没有其他人。你说得对——它在任何DBMS中都不起作用;我添加了一个子选择来纠正这个问题。谢谢。从列表中选择*SID b应该是从列表中选择*但是我还是得到了它-这在postgreSQL中似乎不起作用。我试过了,4号和5号都回来了。OP只希望返回的列表ID只包含这三个人,没有其他人。你说得对——它在任何DBMS中都不起作用;我添加了一个子选择来纠正这个问题。谢谢。选择*fro
m ListsId b应该从ListHasNames b中读取SELECT*,但我还是得到了它-这是另一个很好的方法来排除有额外个人的列表。从未听说过INTERSECT,看起来它是MSSQLS2005的新功能。然而,这个查询实际上不返回错误的结果吗?第一个查询返回具有3个名称的所有列表,第二个查询返回包含Paul、Joe或Jenny的所有列表。如果有一个列表总共包含3个成员,但其中只有1或2个成员是Paul、Joe或Jenny,则此查询将返回该列表,根据原始问题,该查询是不正确的。这是另一个排除包含额外个人的列表的好方法。从未听说过INTERSECT,看起来它是MSSQLSQL2005的新成员。然而,这个查询实际上不返回错误的结果吗?第一个查询返回具有3个名称的所有列表,第二个查询返回包含Paul、Joe或Jenny的所有列表。如果有一个列表包含总共3个成员,但其中只有1或2个成员是Paul、Joe或Jenny,则该查询将返回该列表,根据原始问题,该查询是不正确的。