【问题标题】:SQL Select rows on different tables that have the same FK valuesSQL 选择具有相同 FK 值的不同表上的行
【发布时间】:2021-09-21 16:15:47
【问题描述】:

我需要创建一个查询,列出所有能够在一个国家/地区使用所有必需语言的人。我有 3 个表(国家 / 语言 / 人) 问题是我在国家和人表之间没有链接,但每个表都有一个外键“Language_Id”。我的目标是列出每个拥有与 Country 一样多的语言的人。 这是一个例子:

餐桌人:

     ID   Name    Language_ID  
1.   1    Paul    1
2.   2    Paul    2
3.   3    Max     1
4.   4    Ben     2
5.   5    Paul    3
6.   6    Ben     3

表格语言:

     ID   Name
1.   1    English
2.   2    Dutch
3.   3    French

餐桌国家:

     ID   Name         Language_ID
1.   1    France       3
2.   2    Netherlands  1
3.   3    Netherlands  2
4.   4    Belgium      2
5.   5    Belgium      3

结果表:

       Name    Country_Name
1.     Paul    France
2.     Paul    Netherlands
3.     Paul    Belgium
4.     Ben     Belgium
5.     Ben     France

因此,Max 被排除在结果之外,因为他没有所有必需的语言。

我正在运行 SQL Server 2016 标准版。

你能帮我找到解决办法吗?

谢谢!

【问题讨论】:

  • 为什么 Ben 只出现在比利时而不是法国,他拥有法国所需的所有语言?
  • 我的错...你明白了,我只是编辑帖子。抱歉弄错了
  • 你能标记你正在使用的特定 SQL 风格吗?
  • 我正在开发 2016 SQL server Std 版本

标签: sql sql-server join group-by foreign-keys


【解决方案1】:

您可以使用join 和聚合。一个棘手的部分是计算一个国家/地区的语言数量,以确保一个人会说所有这些语言:

select p.name, c.name, c.num_languages
from people p join
     (select c.*, count(*) over (partition by name) as num_languages
      from country c
     ) c
     on p.language_id = c.language_id
group by p.name, c.name, c.num_languages
having count(*) = c.num_languages;

【讨论】:

    【解决方案2】:

    一种方法可以使用 CTE 计算每个国家/地区的语言,然后每个人说的语言,并过滤每个人说的语言至少是每个国家/地区所需的语言:

    with c as (
        select name Country_Name, language_id, 
          Count(*) over(partition by name) LanguageCount
        from country
    ), p as (
        select p.name, c.Country_Name, c.LanguageCount,
          Count(*) over(partition by p.name, c.Country_Name)Languages
        from c join people p on p.Language_ID=c.language_id
    )
    select distinct p.name, p.Country_Name 
    from p
    where Languages>=LanguageCount;
    

    example fiddle

    【讨论】:

    • 感谢您的回答!
    【解决方案3】:

    如果您构建每个人所说的每种语言组合并将其与每个国家/地区的要求相匹配,您就可以得到您想要的。

    With build as (
        Select Name, cast(language_id as varchar) as languages, language_id as last_lang
        From People
    Union All
        Select a.Name, a.languages + ';' + cast(language_id as varchar) as languages
              , b.language_id as last_lang
        From build a Inner Join People b On a.Name=b.Name
        Where a.last_lang<b.language_id
    )
    Select P.Name, C.Name
    From (
        Select Name,
              String_Agg(cast(language_id as varchar), ';') Within Group (Order By language_id) as Language_Reqs
        From Country
        Group By Name
    ) C Inner Join build P on P.Languages=C.Language_Reqs
    

    还可以修改此技术以识别个人可能缺乏需求的地方。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2016-11-11
      • 2014-07-02
      • 1970-01-01
      • 2014-07-14
      • 1970-01-01
      • 1970-01-01
      • 2012-03-03
      • 2014-12-08
      相关资源
      最近更新 更多