Вернуть только одно значение для столбца в случаях, когда есть несколько строк - PullRequest
0 голосов
/ 19 сентября 2018

Я хотел бы вернуть одно произвольно выбранное значение для запроса к источнику данных, который имеет несколько строк.

Необработанные данные

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 голосов
/ 19 сентября 2018

Просто используйте функцию «Предел», если вы хотите сохранить верхнюю строку для данного набора столбцов.Аргумент рядом с функцией Limit указывает количество строк, которые должны быть возвращены в соответствии с заданным запросом.

select user_id, account, role from raw_data limit 1;

Однако, если вы хотите сохранить первую запись для заданной комбинации userid-account-role, задайте для данных заданное условие и используйте ограничение.Например, приведенный ниже патч ограничит запрос на выборку конкретной учетной записью (= 52501050) и вернет верхнюю строку.

1005 *
0 голосов
/ 19 сентября 2018

Вы можете использовать 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;
0 голосов
/ 19 сентября 2018

Чтобы буквально ответить на ваш вопрос, вам просто нужно использовать 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

В более совершенном мире у вас будет один user или account столЭто ограничено, так что это не может произойти.Затем вторая таблица user_role для 0 .. многих ролей, с которыми может быть связан пользователь / учетная запись.

 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 для iveеще один подход.* (Аналогично подходу 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.)

...