SQL Server: SELECT ID, имеющий только одно условие

У меня есть таблица пациентов с такими подробностями, как условия, которые есть у пациента. из приведенной ниже таблицы я хочу выбрать пациентов, заявления которых имеют ТОЛЬКО одно состояние - «гипертония». Пример Пациент Б — ожидаемый результат. Пациент А не будет выбран, потому что он заявил о нескольких состояниях.

+----+---------+--------------+
| ID | ClaimID |  Condition   |
+----+---------+--------------+
| A  |   14234 | Hypertension |
| A  |   14234 | Diabetes     |
| A  |   63947 | Diabetes     |
| B  |   23853 | Hypertension |
+----+---------+--------------+

Я попытался использовать условие NOT IN, как показано ниже, но, похоже, это не помогает.

SELECT ID, ClaimID, Condition 
FROM myTable 
WHERE Condition IN ('Hypertension') 
  AND Condition NOT IN ('Diabetes') 

person shockwave    schedule 08.01.2018    source источник


Ответы (8)


Один метод использует not exists:

select t.*
from mytable t
where t.condition = 'Hypertension' and
      not exists (select 1
                  from mytable t2
                  where t2.id = t.id and t2.condition <> t.condition
                 );
person Gordon Linoff    schedule 08.01.2018
comment
В случае большого набора данных подзапрос может иметь проблемы с производительностью. - person Amit Verma; 20.06.2021
comment
@АмитВерма. . . Если производительность является проблемой, вы просто добавили бы соответствующие индексы. - person Gordon Linoff; 20.06.2021

Или вы можете сделать это так:

select 
    id,
    claim_id,
    condition
from 
    patient
where 
    id in
    (
        select
            id
        from
            patient
        group by
            id having count (distinct condition) = 1
    );

Результат:

id claim_id    condition
-- ----------- ----------------
B        23853 Hypertension

(1 rows affected)

Настраивать:

create table patient
(
    id         varchar(1),
    claim_id   int,
    condition  varchar(16)
);

insert into patient (id, claim_id, condition) values ('A', 14234, 'Hypertension');
insert into patient (id, claim_id, condition) values ('A', 14234, 'Diabetes');
insert into patient (id, claim_id, condition) values ('A', 63947, 'Diabetes');
insert into patient (id, claim_id, condition) values ('B', 23853, 'Hypertension');
person Ron Ballard    schedule 08.01.2018

Вы можете сделать это с помощью CTE.

Я настроил этот CTE с двумя параметрами, одним из которых является искомое состояние, а другим — максимальное количество комбинированных условий, которые нужно найти (в вашем случае 1).

DECLARE @myTable TABLE (Id VARCHAR(1), ClaimID INT, Condition VARCHAR(100))
INSERT INTO @myTable (Id, ClaimID, Condition)
SELECT 'A',14234,'Hypertension' UNION ALL
SELECT 'A',14234,'Diabetes' UNION ALL
SELECT 'A',63947,'Diabetes' UNION ALL
SELECT 'B',23853,'Hypertension'

DECLARE @Condition VARCHAR(100)
DECLARE @MaxConditions TINYINT

SET @Condition='Hypertension'
SET @MaxConditions=1

; WITH CTE AS
(
    SELECT *, COUNT(2) OVER(PARTITION BY ClaimID) AS CN
    FROM @myTable T1
    WHERE EXISTS (SELECT 1 FROM @myTable T2 WHERE T1.ClaimID=T2.ClaimID AND T2.Condition=@Condition)
)

SELECT *
FROM CTE
WHERE CN<=@MaxConditions

Если вас не волнует пух, и вы просто хотите, чтобы все ClaimID были только с ОДНИМ условием, независимо от того, какое это условие, используйте это.

DECLARE @myTable TABLE (Id VARCHAR(1), ClaimID INT, Condition VARCHAR(100))
INSERT INTO @myTable (Id, ClaimID, Condition)
SELECT 'A',14234,'Hypertension' UNION ALL
SELECT 'A',14234,'Diabetes' UNION ALL
SELECT 'A',63947,'Diabetes' UNION ALL
SELECT 'B',23853,'Hypertension'

DECLARE @MaxConditions TINYINT

SET @MaxConditions=1

; WITH CTE AS
(
    SELECT *, COUNT(2) OVER(PARTITION BY ClaimID) AS CN
    FROM @myTable T1
)

SELECT *
FROM CTE
WHERE CN<=@MaxConditions
person Dave Cullum    schedule 08.01.2018

Вот один метод, использующий предложение Having

SELECT t.*
FROM   mytable t
WHERE  EXISTS (SELECT 1
               FROM   mytable t2
               WHERE  t2.id = t.id
               HAVING Count(CASE WHEN condition = 'Hypertension' THEN 1 END) > 0
                      AND Count(CASE WHEN condition != 'Hypertension' THEN 1 END) = 0) 
person Pரதீப்    schedule 08.01.2018
comment
Предложите изменить последний случай на != hypertension, чтобы он мог обрабатывать другие вторичные состояния, а не только диабет. - person Dave Cullum; 08.01.2018

И еще пара способов сделать это:

    declare @TableA  table(Id char,
                           ClaimId int,
                           Condition varchar(250));
    insert into @TableA (id, claimid, condition)
                values ('A', 14234, 'Hypertension'),
                       ('A', 14234, 'Diabetes'),
                       ('A', 63947, 'Diabetes'),
                       ('B', 23853, 'Hypertension')       


    select id, claimid, condition
    from @TableA a
    where not exists(select id
                     from @TableA b
                     where a.id = b.id
                     group by b.id
                     having count(b.id) > 1)

    OR

    ;with cte as
   (
     select id, claimid, condition
     from @TableA
   )
   ,
   cte2 as
   (
     Select id, count(Id) as counts
     from cte
     group by id
     having count(id) < 2       
    )

   Select cte.id, claimid, condition
   From cte
   inner join
   cte2
   on cte.id = cte2.id
person jackstraw22    schedule 08.01.2018

Я решил пересмотреть свой ответ на соответствующий.

Простое решение вашего вопроса - подсчитывать строки вместо значений идентификатора (поскольку это не целое число).

Вот простое введение:

SELECT 
    ID 
FROM 
    #PatientTable 
GROUP BY 
    ID 
HAVING
    ID = ID AND COUNT(*) = 1 

Это вернет идентификатор B

+----+
| ID |
+----+
| B  |
+----+

Конечно, этого недостаточно, так как вы можете работать с большими данными и нуждаться в дополнительной фильтрации.

Итак, мы пойдем и будем использовать его как подзапрос.

Используя его как подзапрос, это просто:

SELECT
    ID, 
    ClaimID, 
    Condition
FROM 
    #PatientTable
WHERE 
    ID = (SELECT ID AS NumberOfClaims FROM #PatientTable GROUP BY ID HAVING ID = ID AND COUNT(*) = 1)

Это вернет

+----+---------+--------------+
| ID | ClaimID |  Condition   |
+----+---------+--------------+
| B  |   23853 | Hypertension |
+----+---------+--------------+

Пока все хорошо, но есть еще одна проблема, с которой мы можем столкнуться. Допустим, у вас есть несколько претензий от нескольких пациентов. Использование этого запроса без изменений покажет только одного пациента. Чтобы показать всех пациентов, нам нужно использовать IN, а не = в предложении WHERE.

WHERE 
    ID IN (SELECT ID AS NumberOfClaims FROM #PatientTable GROUP BY ID HAVING ID = ID AND COUNT(*) = 1)

В нем будут перечислены все пациенты, которые подпадают под это состояние.

Если вам нужно больше условий для фильтрации, вы просто добавляете их в предложение WHERE, и все готово.

person iSR5    schedule 08.01.2018
comment
sqlblog.com/blogs/aaron_bertrand/archive/2009/10/08/ - person Dave Cullum; 08.01.2018
comment
@JiggsJedi, что вы делаете, кроме как от старого таймера, который использует оба синтаксиса (новый и старый), пока он выполняет свою работу;). Но благодаря вам я нашел лучшее решение. - person iSR5; 09.01.2018

SELECT id, sum(ct)
FROM (SELECT customer_id, CASE WHEN category = 'X' THEN 0 else 1 
end ct
FROM MASTER_TABLE
) AS t1
GROUP BY id
HAVING sum(ct) = 0

id, который будет иметь sum(ct) больше 1, будет иметь несколько условий

person Aditya Raj    schedule 19.06.2021

Используйте соединения вместо подзапросов. Соединения всегда лучше по производительности. Вы можете использовать запрос ниже.

SELECT T1.id, T1.claimid, T1.Condition
FROM mytable T1
INNER JOIN 
(
    select id, count(Condition)  counter
    from mytable 
    group by id HAVING COUNT(DISTINCT CONDITION)=1
) T2 ON T1.ID=T2.ID
WHERE  T2.counter=1
person Amit Verma    schedule 20.06.2021