【问题标题】:mysql - Applying an outer join to a complex statementmysql - 将外部联接应用于复杂语句
【发布时间】:2019-12-04 22:47:39
【问题描述】:

我整理了一个查询以提取人名,并优先考虑以查看该页面的人的语言/书写脚本编写的该名称的版本。

我之前在丢失记录时遇到了一些麻烦,并发布了一个关于 here 的问题。我收到了一个answer,它建议使用 CASE 语句并应用分数来根据首选语言/脚本对名称进行排名。我已经获得了排名,它允许返回正确数量的记录(现在没有丢失记录)。见下文:

SELECT 
    people.person_id,
    names.name,
CASE 
    WHEN people.person_default_name_id=names.name_id AND language_scripts.script_id = :user_script_id THEN 3
    WHEN names.language_id = :user_language_id THEN 2
    WHEN language_scripts.script_id = :user_script_id THEN 1
    ELSE 0
    END as score
FROM 
    `people` 
LEFT JOIN
    `names` ON names.person_id=people.person_id
LEFT JOIN
    `languages` ON names.language_id = languages.language_id
LEFT JOIN
    `language_scripts` ON languages.language_id = language_scripts.language_id
GROUP BY 
    people.person_id 
ORDER BY 
    names.name ASC

我现在遇到的麻烦是,因为我只需要每人一个结果(因此是 GROUP BY),它只是提取每人遇到的第一条记录。我需要能够使“分数”发挥作用,并且只提取具有最高可用分数(可能是 3、2、1 或 0)的记录——每人获得最高分数的一条记录。

原帖中的建议是使用外部查询,但我不确定如何在此处应用。有什么想法吗?

编辑:这个小提琴显示了一个提取所有记录及其分数的示例:http://sqlfiddle.com/#!9/54ce8/13 - 我想要做的就是为每个 person_id 提取一个得分最高的记录。在这个例子中,我想要一张只有 Jorge 和 Alejandro 的表格,因为他们是两者中得分最高的名字。

【问题讨论】:

  • CASE 表达式括在MAX() 聚合函数中,即MAX(CASE WHEN ... END) AS max_score
  • 感谢您的回复,@spencer7593。我试过了,但它似乎只是显示每条记录的最大可能分数(即 3),但不一定是与该分数对应的名称。
  • 我在sqlfiddle.com/#!9/54ce8/13 上整理了一个小提琴,这显示了所有被提取的记录及其相应的分数。我想要做的是只为每个 person_id 抽取一条得分最高的记录。在这个例子中,我想要一张只有 Jorge 和 Alejandro 的表格,因为他们是两人中得分最高的名字。
  • 嗨@tcadidot0,是的,这是正确的。结果应该是 Jorge 和 Alejandro 因为他们的得分最高。如果姓名表中没有 Jorge 和 Alejandro,那么得分为 2 的 George 和 Alexander 将是结果,因为他们是下一个最高分。
  • 对不起,我删除了我的评论,因为我没有阅读您的完整问题。我认为您应该将上面的查询作为没有任何 GROUP BY 的子查询,然后将其与您的 names 表连接起来。那应该给你正确的名字

标签: mysql case outer-join


【解决方案1】:

我只能想到一种方法来实现这一点。在您当前的工作查询中结合GROUP_CONCATSUBSTRING_INDEX。下面的示例查询:

SELECT    person_id,
          SUBSTRING_INDEX(GROUP_CONCAT(a.name ORDER BY a.score DESC),',',1) sname, 
          SUBSTRING_INDEX(GROUP_CONCAT(a.score ORDER BY a.score DESC),',',1) max_score FROM 
(SELECT   people.person_id,
          names.name,
          CASE 
          WHEN people.person_default_name_id=names.name_id AND language_scripts.script_id = '1' THEN 3
          WHEN names.language_id = '1' THEN 2
          WHEN language_scripts.script_id = '1' THEN 1
          ELSE 0
          END AS score
FROM      `people` 
LEFT JOIN `names` ON names.person_id=people.person_id
LEFT JOIN `languages` ON names.language_id = languages.language_id
LEFT JOIN `language_scripts` ON languages.language_id = language_scripts.language_id) a 
GROUP BY person_id;

我也测试了小提琴中的查询http://sqlfiddle.com/#!9/54ce8/33

一点解释:

  1. GROUP_CONCAT 在名称和分数上加上 ORDER BY 条件 - 请注意 ORDER BY 在两个 GROUP_CONCAT 中必须相同。
  2. SUBSTRING_INDEX 获取字段中以逗号 (,) 分隔的第一个值。

【讨论】:

    【解决方案2】:

    返回score 重要吗?或者目标是只返回满足 score=3 条件的name,如果不存在,则返回满足 score=2 条件的name,否则......

    一种方法是进行条件聚合,当满足条件时返回name的值,否则返回NULL,然后用MAX()(或MIN())挑选非NULL值聚合函数。

    然后我们可以使用COALESCE 函数为我们提供第一个非NULL 值,检查我们是否得到score=3 行,然后是score=2 行,等等。

    SELECT p.person_id
         , COALESCE(
             MAX(CASE
                   WHEN p.person_default_name_id = n.name_id AND s.script_id = '1'  
                   THEN n.name
                   ELSE NULL 
             END)
           , MAX(CASE
                   WHEN n.language_id = '1'
                   THEN n.name
                   ELSE NULL 
             END)
           , MAX(CASE
                    WHEN s.script_id = '1'
                    THEN n.name
                    ELSE NULL
              END)
           , MAX(n.name)
           ) AS `name`
    
           -- additional expressions so we can see what is happening
         , MAX(CASE
                 WHEN p.person_default_name_id = n.name_id AND s.script_id = '1'  
                 THEN n.name
                 ELSE NULL 
           END) AS s3_name
         , MAX(CASE
                 WHEN n.language_id = '1'
                 THEN n.name
                 ELSE NULL 
                 END
           ) AS s2_name
         , MAX(CASE
              WHEN s.script_id = '1'
              THEN n.name
              ELSE NULL 
              END) AS s1_name
         , MAX(n.name) AS s0_name
    
      FROM `people` p
      LEFT
      JOIN `names` n
        ON n.person_id = p.person_id
      LEFT
      JOIN `languages` l
        ON l.language_id = n.language_id
      LEFT
      JOIN `language_scripts` s
        ON s.language_id = l.language_id
     GROUP
        BY p.person_id
     ORDER
        BY ...
    

    在这里演示:http://sqlfiddle.com/#!9/54ce8/37

    【讨论】:

    • 返回分数并不重要——它只是获取最相关名称的一种方式,正如您在第一段中所指出的那样。这段代码也很好用。非常感谢!
    猜你喜欢
    • 1970-01-01
    • 2016-07-01
    • 1970-01-01
    • 1970-01-01
    • 2018-11-23
    • 2011-10-23
    • 1970-01-01
    • 1970-01-01
    • 2012-04-29
    相关资源
    最近更新 更多