复杂的SQL语句:选择匹配多个表

时间:2013-07-06 19:40:15

标签: sql

我有桌子:

  • Candidates
  • CandidateLanguages
  • CandidateSkills

每位候选人可能拥有1种以上的语言和1种以上的技能

因此对于Candidate“FRED”,他在CandidateLanguages中的记录可能是

FRED - ENGLISH
FRED - FRENCH

他在CandidateSkills的记录可能是

FRED - RUNNING
FRED - JUMPING

Candidate“JIM”他在CandidateLanguages的记录可能是

JIM - ENGLISH

他在CandidateSkills的记录可能是

JIM - RUNNING

我的查询需要选择符合多种技能和语言的候选人。

例如英文:

选择所有选定语言的所有候选人,并拥有所有选定的技能......

或换句话说......

SELECT ALL candidates WHERE 
(language = 'FRENCH' AND language is 'ENGLISH') AND 
(skill = 'RUNNING' AND skill = 'JUMPING') 

在上述两位候选人中,这应该只返回“FRED”

我理解问题是尝试从语言和技能表中选择多个记录,我认为可能需要连接,但现在我迷路了...

6 个答案:

答案 0 :(得分:4)

您正在解决的问题称为关系部门

请参阅此文: Divided We Stand: The SQL of Relational Division 以及以下几个方法来解决问题: How to filter SQL results in a has-many-through relation

解决问题的一种方法(一般来说 - 效率最高):

SELECT ALL c.candidate
FROM Candidates c
  JOIN CandidateLanguages lang1
    ON  lang1.candidate = c.candidate 
    AND lang1.language = 'English'
  JOIN CandidateLanguages lang2
    ON  lang2.candidate = c.candidate 
    AND lang2.language = 'French'
  JOIN CandidateSkills sk1
    ON  sk1.candidate = candidate 
    AND sk1.skill = 'Running'
  JOIN CandidateSkills sk2
    ON  sk2.candidate = candidate 
    AND sk2.skill = 'Jumping' ;

另一种似乎更容易编写的方法,特别是如果涉及很多语言和技能,则在每个方法中使用两个派生表GROUP BY

SELECT ALL c.candidate
FROM Candidates c
  JOIN 
    ( SELECT candidate
      FROM CandidateLanguages
      WHERE language IN ('English', 'French')
      GROUP BY candidate
      HAVING COUNT(*) = 2                     -- the number of languages
    ) AS lang
      ON  lang.candidate = c.candidate 
  JOIN 
    ( SELECT candidate
      FROM CandidateSkills 
      WHERE skill IN ('Running', 'Jumping')
      GROUP BY candidate
      HAVING COUNT(*) = 2                     -- the number of skills
    ) AS sk
      ON  sk.candidate = c.candidate ;

答案 1 :(得分:1)

如果您想要所有技能所有语言,只需计算乘法就足够了。

select c.id
from candidate c 
join candidateLanguage cl on c.id = cl.candidateId
join language l on cl.languageId = l.id
join candidateSkill cs on c.id = cd.candidateId
join skill s on s.id = cs.skillId
group by c.id
having count(*) = 4

having条件可以表示为

having count(*) = 
    (select count(*) from skill) * (select count(*) from language)

我该怎么办?

  • 列出所有可能的候选语言技能三元组
  • 按候选人分组
  • 如果计数等于(技能数)*(语言数),那么此候选人所有组合都存在

修改

如果您只需要语言和技能的子集,则可以对其进行过滤:

select c.id
from candidate c 
join candidateLanguage cl on c.id = cl.candidateId
join language l on cl.languageId = l.id
join candidateSkill cs on c.id = cd.candidateId
join skill s on s.id = cs.skillId
where l.name in ('English', 'French')
  and s.name in ('RUNNING', 'JUMPING')
group by c.id
having count(*) = 4

这里的不同之处在于,您只能计算符合条件的技能和语言。

答案 2 :(得分:0)

不优雅,但效率很高。

SELECT 
  *
FROM
  Candidates c
WHERE 
  (SELECT COUNT(*) 
   FROM   CandidateLanguages cl 
   WHERE  cl.candidateId = c.candidateId AND cl.language in ('FRENCH', 'ENGLISH')
  ) = 2
  AND
  (SELECT COUNT(*) 
   FROM   CandidateSkills cs 
   WHERE  cs.candidateId = c.candidateId AND cs.skill in ('RUNNING', 'JUMPING')
  ) = 2

答案 3 :(得分:0)

如果您的数据查询可以写成“给我所有具有我们在表格中列出的所有已知技能的候选人,以及我们在不同表格中列出的所有已知语言”,而不仅仅是“所有候选人”英语和法语,以及跳跃和跑步“,你可以使用这些数据驱动的查询之一:

select
    *
from
    Candidates as C
where
    (select count(*) from CandidateLanguages where CandidateName = C.Name) = (select count(*) from Languages)
and (select count(*) from CandidateSkills where CandidateName = C.Name) = (select count(*) from Skills)
go

select
    *
from
    Candidates
where
    Name not in (
        select 
            C.Name
        from
            (Candidates as C cross join Languages as L)
            left join CandidateLanguages as CL on C.Name = CL.CandidateName and L.Name = CL.LanguageName
        where
            CL.CandidateName is null
    )
and Name not in (
        select
            C.Name
        from
            (Candidates as C cross join Skills as S)
            left join CandidateSkills as CS on C.Name = CS.CandidateName and S.Name = CS.LanguageName
        where
            CS.CandidateName is null
    )
go

可以在LINQPad中测试的完整示例代码是available here(您可能必须创建空数据库)

答案 4 :(得分:-1)

 select candidate-name,resulttblskills1.sumCOLRATIOSKILLS,resulttbllanguages1.sumCOLRATIOLanguages from candidates candidates1

    join (select  count(*) as sumCOLRATIOskills
          from candidateskills skills1
          where skills1.requiredskills in ('jumping','canoeing','mostlygoofing'
          group by id
          ) as resulttblskills1 on resulttblskills1.id = candidates1.id 

    join (select  count(*) as sumCOLRATIOLANGUAGES
          from candidatelanguages languages1     
          where languages1.requiredlanguages in ('French','english','esparanto')
          group by id ) as resulttbllanguages1 on resulttbllanguages1.id = candidates1.id

    where resulttblskills1.sumCOLRATIOSKILLS > 1
          and resulttbllanguages1.sumCOLRATIOLANGUAGES > 1

答案 5 :(得分:-2)

您正在描述候选人和技能之间以及候选人和语言之间的多对多关系。希望您的数据库具有必需的表。查询类似于:

select yourfields
from candidate c join candidateLanguage cl on c.id = cl.candidateId
join languages l on cl.languageId = l.id
join candidateSkill cs on c.id = cd.candidateId
join skill s on s.id = cs.skillId
where l.language in ('FRENCH', 'ENGLISH')
and s.skill in ('RUNNING', 'JUMPING')