如果有多行,则仅返回一列的单个值

时间:2018-09-19 20:18:40

标签: sql sql-server group-by

我想返回一个任意选择的值,以查询具有多行的数据源。

原始数据

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

感谢您的帮助!

3 个答案:

答案 0 :(得分:1)

要从字面上回答您的问题,您只需要使用MAX(),因为PDPr之后。

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

(这说明了不同的结构和方法,或者对您有所帮助)

EDIT

我还注意到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子句中。)

答案 1 :(得分: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;

答案 2 :(得分:-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;
相关问题