Необходимо вычислить дубликаты элементов в столбце и отделить элементы, имеющие некоторые условия в SQL. - PullRequest
3 голосов
/ 07 июля 2019

Я новичок в мире ORACLE SQL.Я пытаюсь найти способ рассчитать что-то, как показано ниже.

Допустим, у меня есть следующие данные, доступные в таблице «А».Статус создается по другой логике, просто для простоты я беру пример статуса.

Name               Status
---------------------------
John                 N
John                 Y
Smith                N
Karrie               Y
Walter               N
Smith                Y
Walter               N
John                 N
Walter               N
Karrie               Y

Теперь я хочу вывести что-то, как показано ниже:

Name                Status               Flag
-----------------------------------------------
John                 N                     S
John                 Y                     S
John                 N                     S
Smith                N                     S
Smith                Y                     S
Karrie               Y                     Y
Karrie               Y                     Y
Walter               N                     N
Walter               N                     N
Walter               N                     N

Это простопример данных и пример выходных данных.

В основном, давайте возьмем пример Джона:

  • Если доступно 3 Джона с несколькими N и хотя бы одним Y', тогда я хочу, чтобы флаг был' S '.
  • Если доступно 3 Джона, имеющих несколько' N 'и Нет ' Y ', тогда я хочу, чтобы флаг был'N '.
  • Если доступно 3 Джона с несколькими буквами' Y 'и Нет ' N ', тогда я хочу, чтобы флаг был' Y '.

Я попробовал это, добавив дополнительный столбец в качестве количества *, который разбивает таблицу на Имя и подсчитывает все имена в ней.Что в конечном итоге скажет мне «Сколько« Y »и« N »может быть там!».

Но, даже если я посчитаю все эти Имена, я не смогу разделить «Y» и «N»)..

Не уверен, как мне добиться этого с помощью чистого SQL.

Я знаю, что

If COUNT('N') = COUNT('John') then 
flag = 'N' 
else if COUNT('N') < COUNT('John') then 
flag = 'S' 
else flag = 'Y'.

Это то, что я пробовал:

SELECT a.NAME
       , a.status
       , COUNT(NAME) OVER (PARTITION BY NAME ORDER BY NAME) COUNT 
FROM A a;

Я застрял после этого.

Не могли бы вы мне помочь?

Это мой ожидаемый результат:

Name                Status               Flag

-----------------------------------------------
John                 N                     S
John                 Y                     S
John                 N                     S
Smith                N                     S
Smith                Y                     S
Karrie               Y                     Y
Karrie               Y                     Y
Walter               N                     N
Walter               N                     N
Walter               N                     N

Ответы [ 5 ]

1 голос
/ 07 июля 2019

В этом случае вы можете сделать это без подсчета, просто используя max() и min():

select t.*,
       (case when max(status) over (partition by name) = min(status) over (partition by name)
             then status
             else 'S'
        end) as flag
from t;

Это реализует следующую логику:

  • Еслистатусы одинаковы для имени, присвойте этот статус.
  • В противном случае присвойте 'S'.

Эта логика представляет собой несколько более простой способ выражения ваших условий.

Здесь - это дБ <> скрипка.

Я не уверен, является ли получение строк в указанном порядке частью вопроса. Если это так, вы можете добавить:

order by (case when flag = 'S' then 1 when flag = 'Y' then 2 else 3 end),
         name
1 голос
/ 07 июля 2019

Кажется, правило просто:

  • если все статусы одинаковы, показать статус
  • еще покажи S

То есть COUNT DISTINCT или сравните MIN и MAX, чтобы определить, существует ли только один статус для имени.

SELECT
  a.name,
  a.status,
  CASE WHEN COUNT(DISTINCT status) OVER (PARTITION BY name) = 1
       THEN status
       ELSE 'S'
  END AS flag
FROM a;

или

SELECT
  a.name,
  a.status,
  CASE WHEN MIN(status) OVER (PARTITION BY name) = MAX(status) OVER (PARTITION BY name)
       THEN status
       ELSE 'S'
  END AS flag
FROM a;

Если status может быть НЕДЕЙСТВИТЕЛЕН, я бы рекомендовал DECODE вместо CASE WHEN, потому что CASE WHEN станет менее читабельным.

0 голосов
/ 07 июля 2019

Один вариант будет использовать дважды count() в качестве аналитической функции окна:

with A( Name, Status ) as
(
 select 'John'  ,'N'  from dual union all
 select 'John'  ,'Y'  from dual union all
 select 'Smith' ,'N'  from dual union all
 select 'Karrie','Y'  from dual union all
 select 'Walter','N'  from dual union all
 select 'Smith' ,'Y'  from dual union all
 select 'Walter','N'  from dual union all
 select 'John'  ,'N'  from dual union all
 select 'Walter','N'  from dual union all
 select 'Karrie','Y'  from dual       
), A2 as
(
 select a.Name, a.status,
        count(*) over ( partition by Status, name order by name ) count1,
        count(*) over ( partition by name order by name ) count2       
   from A
)
select a.Name, a.Status, 
       case when count1 > count2 or count2 > count1 then 
      -- the above comparison is needed to make sure "Name" has multiple "N" or "S"
            'S'
       else
            status
       end as flag
  from A2 a
 order by decode(flag,'S',0,1), a.Name;

Демо

0 голосов
/ 07 июля 2019

Вы можете попробовать использовать объединение в подзапросе с условным агрегированием

select a.name, a.status
    ,  case when  t.tot_status_n > 1 and  t.tot_status_y >= 1 then 'S'
            when  t.tot_status_n > 1 and  t.tot_status_y =0  then 'N'
            when t.tot_status_y > 1 then 'Y' 
            else '?' end flag
from table_a
INNER JOIN (
    select name
       , sum( case when status = N then 1  else 0 end) tot_status_n , 
       , sum( case when status = Y then 1  else 0 end) tot_status_y
    from table_a 
    group by name 
) t on a.name  = t.name 
0 голосов
/ 07 июля 2019

Вы можете посчитать различные статусы для каждого имени с условной агрегацией:

select name,
  sum(case status when 'N' then 1 else 0 end) ncounter,
  sum(case status when 'Y' then 1 else 0 end) ycounter
from tablename
group by name

и затем присоединить этот запрос к таблице:

select t.*,
  case 
    when c.ncounter = 0 then 'Y'
    when c.ycounter = 0 then 'N'
    else 'S' 
  end Flag
from tablename t inner join (
  select name,
    sum(case status when 'N' then 1 else 0 end) ncounter,
    sum(case status when 'Y' then 1 else 0 end) ycounter
  from tablename
  group by name
) c on c.name = t.name 

См. Демонстрационную версию .Результаты:

> NAME   | STATUS | FLAG
> :----- | :----- | :---
> John   | N      | S   
> John   | Y      | S   
> John   | N      | S   
> Walter | N      | N   
> Walter | N      | N   
> Walter | N      | N   
> Smith  | Y      | S   
> Smith  | N      | S   
> Karrie | Y      | Y   
> Karrie | Y      | Y  
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...