6

I have the tables:

  • Candidates
  • CandidateLanguages
  • CandidateSkills

Each candidate may have more than 1 language and more than 1 skill

So for Candidate "FRED", his records in CandidateLanguages may be

FRED - ENGLISH
FRED - FRENCH

and his records in CandidateSkills may be

FRED - RUNNING
FRED - JUMPING

and for Candidate "JIM" his records in CandidateLanguages may be

JIM - ENGLISH

and his records in CandidateSkills may be

JIM - RUNNING

My query needs to select candidates that match multiple skills and languages.

So for example in English:

Select all of the candidates who speak ALL of the selected languages and have ALL of the selected skills...

Or put another way....

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

Of the two candidates above, this should only return "FRED"

I understand that the problem is with trying to select multiple records from the Language and Skills table and I think that joins may be required, but now I am lost...

4

6 回答 6

4

您正在解决的问题称为Relational Division

请参阅这篇文章:Divided We Stand: The SQL of Relational Division和这个问题的一些解决方法:How to filter SQL results in a has-many-through relationship

解决它的一种方法(通常是最有效的):

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 ;
于 2013-07-06T20:03:29.263 回答
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

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

于 2013-07-06T20:03:43.930 回答
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中测试的完整示例代码可在此处获得(您可能必须创建空数据库)

于 2013-07-06T20:10:24.483 回答
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
于 2013-07-06T20:05:02.700 回答
-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
于 2013-07-06T20:16:18.167 回答
-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')
于 2013-07-06T19:57:30.110 回答