在有多行的情况下,仅返回列的单个值

问题描述 投票:0回答:3

我想为针对具有多行的数据源的查询返回单个任意选择的值。

原始数据

user_id   account   role
paa2013   52501050  PD/PI
paa2013   52501050  Principal Investigator

我想要的是

user_id   account   role
paa2013   52501050  PD/PI

我的查询

select distinct 
  user_id, 
  account,
  case 
    when role = 'PD/PI' then 'PD/PI'
    when role = 'Principal Investigator' then 'Principal Investigator'
  end  
from table
where account = '52501050' 
group by 
  user_id, 
  account,
  case 
    when role = 'PD/PI' then 'PD/PI'
    when role = 'Principal Investigator' then 'Principal Investigator'
  end

我得到了什么

user_id   account   role
paa2013   52501050  PD/PI
paa2013   52501050  Principal Investigator

谢谢你的帮助!

sql sql-server group-by
3个回答
1
投票

要从字面上回答你的问题,你只需要使用MAX(),因为PD来自Pr

SELECT
  user_id,
  account,
  MAX(role)   AS max_role
FROM
  table
WHERE
  account = '52501050'
GROUP BY
  user_id,
  account

更广泛地说,有很多选择。

WITH
  roles AS
(
  SELECT 1 AS rank, 'PD/PI' AS role
  UNION ALL
  SELECT 2 AS rank, 'Principal Investigator' AS role
  UNION ALL
  SELECT 3 AS rank, 'another' AS role
),
  grouped_data AS
(
  SELECT
    table.user_id,
    table.account,
    MIN(roles.rank)  AS min_role_rank
  FROM
    table
  INNER JOIN
    roles
      ON roles.role = table.role
  GROUP BY
    table.user_id,
    table.account
)
SELECT
  *
FROM
  grouped_data
INNER JOIN
  roles
    ON roles.role = grouped_data.min_role_rank

要么...

WITH
  ranked_data AS
(
  SELECT
    table.*,
    ROW_NUMBER() OVER (PARTITION BY table.user_id,
                                    table.account
                           ORDER BY role_rank.id
                      )
                         AS user_role_rank
  FROM
    table
  CROSS APPLY
  (
    SELECT
      CASE table.role
        WHEN 'PD/PI'                  THEN 1
        WHEN 'Principal Investigator' THEN 2
        WHEN 'an other'               THEN 3
                                      ELSE 4
      END
          AS id
  )
    role_rank
)
SELECT
  *
FROM
  ranked_data 
WHERE
  user_role_rank = 1

要么...

WITH
  roles AS
(
  SELECT 1 AS rank, 'PD/PI' AS role
  UNION ALL
  SELECT 2 AS rank, 'Principal Investigator' AS role
  UNION ALL
  SELECT 3 AS rank, 'another' AS role
),
  ranked_data AS
(
  SELECT
    table.*,
    ROW_NUMBER() OVER (PARTITION BY table.user_id,
                                    table.account
                           ORDER BY roles.rank
                      )
                         AS user_role_rank
  FROM
    table
  INNER JOIN
    roles
      ON roles.role = table.role
)
SELECT
  *
FROM
  ranked_data 
WHERE
  user_role_rank = 1

在一个更完美的世界中,你会有一个useraccount表,这是受限制的,所以这不会发生。然后是第二个user_role表,用于与用户/帐户关联的0..many角色。

 id | account                user_id | role_id
----+---------              ---------+---------
 11 | aaaaaaa                   11   |     1
 22 | bbbbbbb                   11   |     2
                                22   |     2
                                22   |     3

然后你会有一个role表,其中包括排名序列....

 role_id | rank | name | etc
---------+------+------+-----
     1   |  30  |  aa  | ???
     2   |  10  |  bb  | ???
     3   |  20  |  cc  | ???

然后查询变得相对简洁......

SELECT
  *
FROM
  user
CROSS APPLY
(
  SELECT TOP 1 role.*
    FROM user_role
    JOIN role ON role.id = user_role.role_id
   WHERE user_role.user_id = user.user_id
ORDER BY role.rank
)
  AS role

(这表明了不同的结构和不同的方法,其中一个或两个可能对您有所帮助)

编辑:

我也注意到SQL SERVER现在支持WITH TIES到另一种方法。 *(类似于ROW_NUMBER()方法,代码略短......

  SELECT TOP(1) WITH TIES
    table.*
  FROM
    table
  CROSS APPLY
  (
    SELECT
      CASE table.role
        WHEN 'PD/PI'                  THEN 1
        WHEN 'Principal Investigator' THEN 2
        WHEN 'an other'               THEN 3
                                      ELSE 4
      END
          AS id
  )
    role_rank
  ORDER BY
    ROW_NUMBER() OVER (PARTITION BY table.user_id,
                                    table.account
                           ORDER BY role_rank.id
                      )

起初这可能令人困惑。它根据TOP(1)选择第一行(ORDER BY)以及与之绑定的所有行。因此,它在功能上与执行WHERE ROW_NUMBER() = 1相同(但SQL Server不允许ROW_NUMBER()WHERE子句中。)


0
投票

您可以将row_number()ORDER BY子句一起使用,在该子句中为角色分配优先级。

SELECT user_id,
       account,
       role
       FROM (SELECT user_id,
                    account,
                    role,
                    row_number() OVER (PARTITION BY user_id,
                                                    account
                                       ORDER BY CASE role
                                                  WHEN 'PD/PI' THEN
                                                    1
                                                  WHEN 'Principal Investigator' THEN
                                                    2
                                                  ...
                                                END) rn
                    FROM table) x
       WHERE rn = 1;

-1
投票

如果要保留给定选择列的顶行,只需使用“限制”功能。 Limit函数旁边的参数是针对满足给定查询应返回的行数。

select user_id, account, role from raw_data limit 1;

但是,如果要保留给定userid-account-role组合的第一个条目,请将数据子集化为给定条件并使用该限制。例如,下面的补丁会将选择查询限制为特定帐户(= 52501050)并返回顶行。

select user_id, account, role from raw_data where account = '52501050' limit 1;
© www.soinside.com 2019 - 2024. All rights reserved.