【问题标题】:Query to Return One Row per Person查询每人返回一行
【发布时间】:2013-06-14 22:52:21
【问题描述】:

给定一个名为 Answers like 的(简化的)表格

Id  Person      Answer      Priority
1   Tom         France      Low       
2   Tom         Germany     High      
3   Fred        England     Low       
4   Amy         Italy       High      

我想编写一个 SQL 查询,每人返回一行,表示他们的最高优先级答案。我的想法是使用自加入

SELECT *
FROM Answers aLow
LEFT OUTER JOIN Answers aHigh 
    ON aLow.Person = aHigh.Person 
       AND aLow.Priority = 'Low' 
       AND aHigh.Priority = 'High'

然后在代码中检查哪些优先级列是非空的,但会为 Tom 返回一个额外的行

Id  Person      Answer      Priority    Id      Person  Answer  Priority
1   Tom         France      Low         2       Tom     Germany High      
2   Tom         Germany     High        NULL    NULL    NULL    NULL
3   Fred        England     Low         NULL    NULL    NULL    NULL
4   Amy         Italy       High        NULL    NULL    NULL    NULL

使用这种方法,所需的输出将是

Id  Person      Answer      Priority    Id      Person  Answer  Priority
1   Tom         France      Low         2       Tom     Germany High      
3   Fred        England     Low         NULL    NULL    NULL    NULL
4   Amy         Italy       High        NULL    NULL    NULL    NULL

我确定我一定遗漏了一些简单的东西,但我不能指望它。

我错过了什么?有没有更好的方法来解决这个问题?

【问题讨论】:

  • 使用 DISTINCT 语句?

标签: sql sql-server


【解决方案1】:

这是一种方法:

with priorityRank as
(
  select *
    , priorityRank = row_number() over (partition by Person
        order by case Priority when 'High' then 1 when 'Low' then 2 end
          , Id)
  from
  Answers
)
select Id
  , Person
  , Answer
  , Priority
from priorityRank
where priorityRank = 1

SQL Fiddle with demo.

【讨论】:

    【解决方案2】:

    您可以将公用表表达式与ROW_NUMBER 窗口函数一起使用:

    WITH cte 
         AS (SELECT [id], 
                    [person], 
                    [answer], 
                    [priority], 
                    RN = Row_number() 
                           OVER ( 
                             partition BY person 
                             ORDER BY CASE WHEN priority = 'High' THEN 0 ELSE 1 END 
                           ASC) 
             FROM   dbo.answers) 
    SELECT [id], 
           [person], 
           [answer], 
           [priority] 
    FROM   cte 
    WHERE  rn = 1 
    

    DEMO

    ID  PERSON  ANSWER   PRIORITY
    4   Amy     Italy    High
    3   Fred    England  Low
    2   Tom     Germany  High
    

    Ranking Functions

    【讨论】:

      【解决方案3】:

      您是否尝试过像这样使用 DISTINCT:

      SELECT DISTINCT id, Person,Answer, Priority
      FROM Answers aLow
      LEFT OUTER JOIN Answers aHigh 
      ON aLow.Person = aHigh.Person 
         AND aLow.Priority = 'Low' 
         AND aHigh.Priority = 'High'
      

      【讨论】:

      • 返回额外的、不需要的行。此外,它不会执行,因为列名不明确(我用 * 代替了测试)。
      • 这就是为什么我在列上是明确的,这样它就不会模棱两可,因此用星号代替它会使它模棱两可。
      【解决方案4】:

      如果我对你的理解正确,你应该得到想要的结果

      SELECT *
      FROM (SELECT * FROM Answers WHERE Priority = 'Low') aLow
      FULL JOIN (SELECT * FROM Answers WHERE Priority = 'High') aHigh 
          ON aLow.Person = aHigh.Person 
      

      【讨论】:

      • 不,只返回左侧优先级为“低”的两行。此外,必须在 SQL Server 中指定连接方。我猜你的意思是LEFT OUTER JOIN
      • 实际上我的意思是完全加入 - 抱歉,已更正。我没有意识到每人可以有超过一个高或低的条目。
      猜你喜欢
      • 2013-11-06
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多