Mysql SQL-多对多关系筛选选择
这些是我的桌子:Mysql SQL-多对多关系筛选选择,mysql,sql,many-to-many,inner-join,Mysql,Sql,Many To Many,Inner Join,这些是我的桌子: Cadastros (id, nome) Convenios (id, nome) Especialidades (id, nome) Facilidades (id, nome) 和联接表: cadastros_convenios cadastros_especialidades cadastros_facilidades 我要查询的表:Cadastros 我正在使用MySQL 系统将允许用户选择多个“Convenios”、“Specialidades”和“Facilid
Cadastros (id, nome)
Convenios (id, nome)
Especialidades (id, nome)
Facilidades (id, nome)
和联接表:
cadastros_convenios
cadastros_especialidades
cadastros_facilidades
我要查询的表:Cadastros
我正在使用MySQL
系统将允许用户选择多个“Convenios”、“Specialidades”和“Facilidades”。将这些表视为不同类型的“标记”。用户将能够选择每种类型的多个“标签”
我想要的是只选择与提供的3个不同表中的所有“标记”相关的地籍表中的结果。请注意,这不是“或”关系。仅当提供的每个“标记”都有匹配的链接表行时,它才应该从Cadastros返回该行
以下是我到目前为止的情况:
SELECT Cadastro.*, Convenio.* FROM Cadastros AS Cadastro
INNER JOIN cadastros_convenios AS CadastrosConvenio ON(Cadastro.id = CadastrosConvenio.cadastro_id)
INNER JOIN Convenios AS Convenio ON (CadastrosConvenio.convenio_id = Convenio.id AND Convenio.id IN(2,3))
INNER JOIN cadastros_especialidades AS CadastrosEspecialidade ON (Cadastro.id = CadastrosEspecialidade.cadastro_id)
INNER JOIN Especialidades AS Especialidade ON(CadastrosEspecialidade.especialidade_id = Especialidade.id AND Especialidade.id IN(1))
INNER JOIN cadastros_facilidades AS CadastrosFacilidade ON (Cadastro.id = CadastrosFacilidade.cadastro_id)
INNER JOIN Facilidades AS Facilidade ON(CadastrosFacilidade.facilidade_id = Facilidade.id AND Facilidade.id IN(1,2))
GROUP BY Cadastro.id
HAVING COUNT(*) = 5;
我使用HAVING子句尝试根据结果显示的次数(即成功“内部连接”的次数)过滤结果。所以在每种情况下,计数都应该等于我添加的不同过滤器的数量。因此,如果我添加3个不同的“标签”,计数应该是3。如果我添加5个不同的标签,计数应该是5,以此类推。它适用于单个关系(一对内部联接)。当我添加其他2个关系时,它开始失去控制
编辑
以下是我认为有效的方法(感谢@Tomalak通过子查询指出解决方案):
但我关心的是表现。看起来WHERE子句中的这3个子查询将被过度执行
另一种解决方案
只有在前一个连接成功时,它才会连接后续表(如果没有行与其中一个连接匹配,则下一个连接将连接一个空结果集)(感谢@DRapp)
重点矿山
“仅当提供的每个“标记”都有匹配行时,才应从Cadastros返回该行。”
“存在匹配行的位置”-使用EXISTS
可以轻松解决问题
编辑经过一些澄清,我发现使用存在
是不够的。有必要比较实际行数:
SELECT
*
FROM
Cadastros c
WHERE
(SELECT COUNT(*) FROM cadastros_facilidades WHERE cadastro_id = c.id AND id IN (2,3)) = 2
AND
(SELECT COUNT(*) FROM cadastros_especialidades WHERE cadastro_id = c.id AND id IN (1)) = 1
AND
(SELECT COUNT(*) FROM cadastros_facilidades WHERE cadastro_id = c.id AND id IN (1,2)) = 2
对于此查询,链接表上的索引应为
(cadastro_id,id)
。根据表(记录)的大小,基于子查询,对每一行运行测试会显著影响性能。我已经对它进行了重组,这可能会有更好的帮助,但只有您能够确认。这里的前提是在获得满足条件的不同ID的基础上得到第一个表,将该集合连接到下一个限定符条件。。。加入最后一组。一旦确定了,使用它连接到主表及其后续链接,以获得所需的详细信息。您还有一个按ID分组的整体组,它将消除support details表中的所有其他嵌套条目
综上所述,让我们看看这个场景。从希望具有最低结果集的表开始,以连接到下一个和下一个。如果cadastros_convenios的ID符合所有标准,包括ID 1-100,太好了,我们最多知道,我们将有100个ID
现在,这100个条目立即加入第二个资格标准。。。其中,假设它只匹配其他的。。。为了简单起见,我们现在在100个选项中有50个是匹配的
最后,根据符合条件的50项,加入第三个限定符,得到30项。因此,在这3个查询中,您现在被筛选到30个条目,所有符合条件的条目都预先处理。现在,加入地籍表,然后再加入后续表格,仅根据符合条件的30个表格获取详细信息
既然您最初的查询最终会尝试每个“ID”作为条件,为什么不先用一个查询对其进行预限定,然后只获取那些符合条件的查询,然后继续
SELECT STRAIGHT_JOIN
Cadastro.*,
Convenio.*,
Especialidade.*,
Facilidade.*
FROM
( SELECT Qualify1.cadastro_id
from
( SELECT cc1.cadastro_id
FROM cadastros_convenios cc1
WHERE cc1.convenio_id IN (1, 2, 3)
GROUP by cc1.cadastro_id
having COUNT(*) = 3 ) Qualify1
JOIN
( SELECT ce1.cadastro_id
FROM cadastros_especialidades ce1
WHERE ce1.especialidade_id IN( 3 )
GROUP by ce1.cadastro_id
having COUNT(*) = 1 ) Qualify2
ON Qualify1.cadastro_id = Qualify2.cadastro_id
JOIN
( SELECT cf1.cadastro_id
FROM cadastros_facilidades cf1
WHERE cf1.facilidade_id IN (2, 3)
GROUP BY cf1.cadastro_id
having COUNT(*) = 2 ) Qualify3
ON Qualify2.cadastro_id = Qualify3.cadastro_id ) FullSet
JOIN Cadastros AS Cadastro
ON FullSet.Cadastro_id = Cadastro.Cadastro_id
INNER JOIN cadastros_convenios AS CC
ON Cadastro.id = CC.cadastro_id
INNER JOIN Convenios AS C
ON CC.convenio_id = C.id
INNER JOIN cadastros_especialidades AS CE
ON Cadastro.id = CE.cadastro_id
INNER JOIN Especialidades AS E
ON CE.especialidade_id = E.id
INNER JOIN cadastros_facilidades AS CF
ON Cadastro.id = CF.cadastro_id
INNER JOIN Facilidades AS F
ON CF.facilidade_id = F.id
这不起作用,因为它在每个链接表->其中cadastro_id=c.id和(2,3)中的id上建立了OR关系。这意味着它可以返回,例如,来自Cadastro的一行,该行与Facilidade 2关联,而不是与Facilidade 3关联。“如果提供的每个“标记”都有一个匹配的行,那么它应该只从Cadastros返回该行。”@CaioLandau Ah!。使用子查询,如
WHERE(选择COUNT(*)FROM cadastro_facilidades,其中cadastro_id=c.id,(2,3))中的id=2
,然后我了解如何解决该问题。这是一个我以前从未想过的解决方案,但我有一个顾虑。我将处理大量的数据。特别是在专用、会议室和会议桌上。这些表中不会有太多行,但它们当然会生成更大的链接表。进行3个独立的子查询不会对性能产生很大影响吗?只需使用我为解决问题而构建的查询编辑问题。我做了一些小测试,似乎所有的东西都按预期返回了。也许现在我应该用虚拟数据填充一个非常大的数据库,并进行一些测试,看看它运行得如何。看起来我不能+1(或者类似的,这是我在这里的第一篇帖子)。好的,非常感谢@好的,除非你有15个代表,否则你不能投票。我不建议对你的每个表格做一个SELECT*
,除非你真的需要每个字段。同时,仅仅为了查询而将您拥有的每个表连接到该查询中可能不是最好的主意。
SELECT
*
FROM
Cadastros c
WHERE
(SELECT COUNT(*) FROM cadastros_facilidades WHERE cadastro_id = c.id AND id IN (2,3)) = 2
AND
(SELECT COUNT(*) FROM cadastros_especialidades WHERE cadastro_id = c.id AND id IN (1)) = 1
AND
(SELECT COUNT(*) FROM cadastros_facilidades WHERE cadastro_id = c.id AND id IN (1,2)) = 2
SELECT STRAIGHT_JOIN
Cadastro.*,
Convenio.*,
Especialidade.*,
Facilidade.*
FROM
( SELECT Qualify1.cadastro_id
from
( SELECT cc1.cadastro_id
FROM cadastros_convenios cc1
WHERE cc1.convenio_id IN (1, 2, 3)
GROUP by cc1.cadastro_id
having COUNT(*) = 3 ) Qualify1
JOIN
( SELECT ce1.cadastro_id
FROM cadastros_especialidades ce1
WHERE ce1.especialidade_id IN( 3 )
GROUP by ce1.cadastro_id
having COUNT(*) = 1 ) Qualify2
ON Qualify1.cadastro_id = Qualify2.cadastro_id
JOIN
( SELECT cf1.cadastro_id
FROM cadastros_facilidades cf1
WHERE cf1.facilidade_id IN (2, 3)
GROUP BY cf1.cadastro_id
having COUNT(*) = 2 ) Qualify3
ON Qualify2.cadastro_id = Qualify3.cadastro_id ) FullSet
JOIN Cadastros AS Cadastro
ON FullSet.Cadastro_id = Cadastro.Cadastro_id
INNER JOIN cadastros_convenios AS CC
ON Cadastro.id = CC.cadastro_id
INNER JOIN Convenios AS C
ON CC.convenio_id = C.id
INNER JOIN cadastros_especialidades AS CE
ON Cadastro.id = CE.cadastro_id
INNER JOIN Especialidades AS E
ON CE.especialidade_id = E.id
INNER JOIN cadastros_facilidades AS CF
ON Cadastro.id = CF.cadastro_id
INNER JOIN Facilidades AS F
ON CF.facilidade_id = F.id