mysql - 将外部联接应用于复杂语句

mysql - Applying an outer join to a complex statement

我整理了一个查询来提取人们的名字,优先考虑在查看页面的人的 language/writing 脚本中写入的那个名字的版本。

我之前遇到过遗漏记录的问题,post就此提出了一个问题 here. I received an answer 建议使用 CASE 语句并应用分数根据首选 language/script。我已经得到了排名,它允许返回正确数量的记录(现在没有丢失的记录)。见下文:

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),它只是拉出每个人遇到的第一条记录。我需要能够使 "score" 起作用,并且只提取具有最高可用分数(可能是 3、2、1 或 0)的记录——每个人的最高分数记录。

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

编辑:这个 fiddle 显示了一个示例,它提取了所有记录及其分数:http://sqlfiddle.com/#!9/54ce8/13 - 我想要做的是只提取每个 [= =27=]得分最高。在这个例子中,我想要一个 table 和 Jorge 和 Alejandro 只是因为他们是两个中得分最高的名字。

我只能想到一种方法来实现这一点。在您当前的工作查询中结合使用 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;

我也在 fiddle 中测试了查询 http://sqlfiddle.com/#!9/54ce8/33

一点解释:

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

return score 重要吗?或者目标只是return一个满足score=3条件的name,如果不存在,满足条件的return一个name对于分数=2,否则...

一种方法是进行条件聚合,当满足条件时 return 值为 name,否则 return 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