3

таблица T1SELECT MIN Дата (с помощью функции WINDOW) и MAX Даты (с соответствующим полем с использованием JOIN) вместе

+----+------------+------------+--------+------+ 
| ID | Sdate | Edate | Reason | Type | 
+----+------------+------------+--------+------+ 
| 1 | 5/30/2016 |   |  | A | 
| 1 | 1/19/2016 | 12/15/2016 | USD | B | 
| 1 | 11/20/2016 | 10/1/2016 | IT  | B | 
| 2 | 10/25/2016 |   |  | A | 
| 2 | 9/22/2016 | 7/11/2016 | SD  | B | 
| 2 | 2/13/2016 |   |  | A | 
| 2 | 1/1/2016 | 4/3/2016 | IT  | B | 
+----+------------+------------+--------+------+ 

Для каждого ID, нужно выбрать минимальный SDATE для Type = A и максимальной EDATE для Тип = B, наряду с причиной разряда, связанной с типом = B. Есть больше, чем просто два типов, но я показал A и B в качестве примера

Желаемая Выход:

+-------------+-----------+------------+------------------+ 
| Customer ID | Startdate | Enddate | Discharge Reason | 
+-------------+-----------+------------+------------------+ 
|   1 | 5/30/2016 | 12/15/2016 | USD    | 
|   2 | 2/13/2016 | 7/11/2016 | SD    | 
+-------------+-----------+------------+------------------+ 

Попытка:

Это дает мне пустой столбец STARTDATE поскольку INNER JOIN на внешнем связанном подзапросе, который используется для получения Max Edate и связанного с ним Discharge Reason, заставляет запрос игнорировать Type=A и, следовательно, нет начальных данных. Как я могу объединить эти работы?

+0

измените ваш вопрос, чтобы включить эту ** критическую деталь ** «* В исходной таблице, типы намного больше. .. * "in * Vladimer's * ответ. Потому что в вашем вопросе вы конкретно указываете Type = [A, B], как будто это единственные варианты. – Stephen

ответ

0

вы можете попробовать это:

SELECT 
    t.ID 
    ,t.SDate 
    ,t.EDate 
    ,Reason 
FROM (
SELECT ID, MIN(CASE WHEN Type='A' THEN SDate END) as SDate , max(CASE WHEN Type='B' THEN EDate END) as EDate 
FROM T1 
group by ID) t 
INNER JOIN T1 on t.ID = T1.id and t.EDate = T1.Edate and type = 'B' 

ИЛИ

WITH CTE AS 
(
SELECT ID, MIN(CASE WHEN Type='A' THEN SDate END) as SDate , max(CASE WHEN Type='B' THEN EDate END) as EDate 
FROM T1 
group by ID 
) 
SELECT 
    CTE.ID AS [Customer ID] 
    ,CTE.SDate AS StartDate 
    ,CTE.EDate AS Enddate 
    ,T1.Reason AS [Discharge Reason] 

FROM CTE 
INNER JOIN T1 
    ON CTE.ID=T1.ID AND CTE.Edate=T1.EDAte and T1.type = 'B' 

при условии наличия не дубликатом макс не относится к типу B. если есть, то вам нужно настроить его немного.

1

Это классическая проблема top-n-per-group, которую вы должны сделать дважды.

Выборочные данные

DECLARE @T TABLE (ID int, Sdate date, Edate date, Reason varchar(50), Type char(1)); 
INSERT INTO @T (ID, Sdate, Edate, Reason, Type) VALUES 
(1, '2016-05-30', NULL   , NULL , 'A'), 
(1, '2016-01-19', '2016-12-15' , 'USD', 'B'), 
(1, '2016-11-20', '2016-10-01' , 'IT' , 'B'), 
(2, '2016-10-25', NULL   , NULL , 'A'), 
(2, '2016-09-22', '2016-07-11' , 'SD' , 'B'), 
(2, '2016-02-13', NULL   , NULL , 'A'), 
(2, '2016-01-01', '2016-04-03' , 'IT' , 'B'); 

Количество строк в два раза.

PARTITION BY ID ORDER BY Type, Sdate даст номер строки 1 для каждого ID и наименьший Sdate и Type=A.

PARTITION BY ID ORDER BY Type DESC, Edate DESC предоставит номер строки 1 для каждого ID и самый большой Edate и Type=B.

SELECT 
    ID 
    ,Sdate 
    ,Edate 
    ,Reason 
    ,Type 
    ,ROW_NUMBER() OVER (PARTITION BY ID ORDER BY Type, Sdate) AS rnA 
    ,ROW_NUMBER() OVER (PARTITION BY ID ORDER BY Type DESC, Edate DESC) AS rnB 
FROM @T 
ORDER BY ID, Type, Sdate 
; 

Промежуточный результат

+----+------------+------------+--------+------+-----+-----+ 
| ID | Sdate | Edate | Reason | Type | rnA | rnB | 
+----+------------+------------+--------+------+-----+-----+ 
| 1 | 2016-05-30 | NULL  | NULL | A | 1 | 3 | 
| 1 | 2016-01-19 | 2016-12-15 | USD | B | 2 | 1 | 
| 1 | 2016-11-20 | 2016-10-01 | IT  | B | 3 | 2 | 
| 2 | 2016-02-13 | NULL  | NULL | A | 1 | 3 | 
| 2 | 2016-10-25 | NULL  | NULL | A | 2 | 4 | 
| 2 | 2016-01-01 | 2016-04-03 | IT  | B | 3 | 2 | 
| 2 | 2016-09-22 | 2016-07-11 | SD  | B | 4 | 1 | 
+----+------------+------------+--------+------+-----+-----+ 

Теперь нам нужно получить строки с rnA=1 и rnB=1 и положить их вместе присоединения к нему ID.

Запрос

WITH 
CTE 
AS 
(
    SELECT 
     ID 
     ,Sdate 
     ,Edate 
     ,Reason 
     ,Type 
     ,ROW_NUMBER() OVER (PARTITION BY ID ORDER BY Type, Sdate) AS rnA 
     ,ROW_NUMBER() OVER (PARTITION BY ID ORDER BY Type DESC, Edate DESC) AS rnB 
    FROM @T 
) 
,CTE_A 
AS 
(
    SELECT 
     ID 
     ,Sdate 
    FROM CTE 
    WHERE rnA = 1 
) 
,CTE_B 
AS 
(
    SELECT 
     ID 
     ,Edate 
     ,Reason 
    FROM CTE 
    WHERE rnB = 1 
) 
SELECT 
    CTE_A.ID 
    ,CTE_A.Sdate 
    ,CTE_B.Edate 
    ,CTE_B.Reason 
FROM 
    CTE_A 
    INNER JOIN CTE_B ON CTE_B.ID = CTE_A.ID 
ORDER BY ID 
; 

Результат

+----+------------+------------+--------+ 
| ID | Sdate | Edate | Reason | 
+----+------------+------------+--------+ 
| 1 | 2016-05-30 | 2016-12-15 | USD | 
| 2 | 2016-02-13 | 2016-07-11 | SD  | 
+----+------------+------------+--------+ 
+0

Как вы думаете, может ли быть альтернативный способ выбора типа = A или B, кроме того, чтобы упорядочить по алфавиту и определить номер строки? В исходной таблице типы намного больше, сложнее и динамичнее. Или мне придется неохотно жестко закодировать Type в фильтрах для чисел строк? – AS91

+1

@ AS91, это зависит от того, насколько сложна логика «Тип». В конце вы должны иметь возможность разделить строки с типом 'A' с' B'. Вышеуказанный запрос сокращает некоторые углы, вычисляя два набора номеров строк в одном «SELECT». Если это сложнее, используйте два 'SELECT' с соответствующими фильтрами, которые оставляют только строки' A' или 'B'. И в итоге присоедините результаты к 'ID'. –

Смежные вопросы