SQL - получить значение NULL по умолчанию, если данные недоступны - PullRequest
0 голосов
/ 23 октября 2019

Я получил данные таблицы следующим образом:

ID  | TYPE_ID | CREATED_DT | ROW_NUM  
=====================================
123 |   485   | 2019-08-31 | 1
123 |   485   | 2019-05-31 | 2
123 |   485   | 2019-02-28 | 3
123 |   485   | 2018-11-30 | 4
123 |   485   | 2018-08-31 | 5
123 |   485   | 2018-05-31 | 6
123 |   487   | 2019-05-31 | 1
123 |   487   | 2018-05-31 | 2

Я хотел бы выбрать 6 ROW_NUM для каждого TYPE_ID, если отсутствуют данные, мне нужно вернуть значение NULL для CREATED_DT, и окончательный набор результатов долженвыглядит так:

ID  | TYPE_ID | CREATED_DT | ROW_NUM  
=====================================
123 |   485   | 2019-08-31 | 1
123 |   485   | 2019-05-31 | 2
123 |   485   | 2019-02-28 | 3
123 |   485   | 2018-11-30 | 4
123 |   485   | 2018-08-31 | 5
123 |   485   | 2018-05-31 | 6
123 |   487   | 2019-05-31 | 1
123 |   487   | 2018-05-31 | 2
123 |   487   | NULL       | 3
123 |   487   | NULL       | 4
123 |   487   | NULL       | 5
123 |   487   | NULL       | 6

Запрос:

SELECT 
   A.*
FROM TBL AS A 
WHERE A.ROW_NUM <= 6
UNION ALL
SELECT 
   B.*
FROM TBL AS B 
WHERE B.ROW_NUM NOT IN (SELECT ROW_NUM FROM TBL)
      AND B.ROW_NUM <= 6

Я пытался использовать UNION ALL и ISNULL для обратной засыпки данных, которые недоступны, но все равно дают мне существующие данные, но не ожидаемыерезультат. Я думаю, что это можно сделать простым способом, используя CTE, но я не уверен, как это сделать. Может ли кто-нибудь помочь мне в этом отношении.

Ответы [ 3 ]

2 голосов
/ 23 октября 2019
  • Предполагая, что Row_Num имеет хотя бы запись, содержит как минимум все 6 строк ... 1,2,3,4,5,6 в тбл и без дробей или 0 или отрицательных чисел ...
  • мы получаем список всех различных типов идентификаторов и идентификаторов. (Псевдоним A)
  • Затем мы получаем отдельный список номеров строк меньше 7 (что дает нам 6 записей)
  • мы перекрестно соединяем их, чтобы каждый ID и Type_ID имели все 6 строк.
  • затем мы оставили соединение в базовом наборе (tbl), чтобы получить все необходимые даты;где такие даты существуют. Поскольку мы используем левое соединение, строки без даты будут сохраняться.

.

SELECT A.ID, A.Type_ID, C.Created_DT, B.Row_Num
FROM (SELECT DISTINCT ID, Type_ID FROM tbl) A
CROSS JOIN (SELECT distinct row_num from tbl where Row_num < 7) B
LEFT JOIN tbl C
  on C.ID = A.ID 
 and C.Type_ID = A.Type_ID
 and C.Row_num = B.Row_num

Давать нам:

+----+-----+---------+------------+---------+
|    | ID  | Type_ID | Created_DT | Row_Num |
+----+-----+---------+------------+---------+
|  1 | 123 |     485 | 2019-08-31 |       1 |
|  2 | 123 |     485 | 2019-05-31 |       2 |
|  3 | 123 |     485 | 2019-02-28 |       3 |
|  4 | 123 |     485 | 2018-11-30 |       4 |
|  5 | 123 |     485 | 2018-08-31 |       5 |
|  6 | 123 |     485 | 2018-05-31 |       6 |
|  7 | 123 |     487 | 2019-05-31 |       1 |
|  8 | 123 |     487 | 2018-05-31 |       2 |
|  9 | 123 |     487 | NULL       |       3 |
| 10 | 123 |     487 | NULL       |       4 |
| 11 | 123 |     487 | NULL       |       5 |
| 12 | 123 |     487 | NULL       |       6 |
+----+-----+---------+------------+---------+

Rex Tester: Пример

Это также предполагает, что вы хотите 1-6 для каждой комбинации type_id и ID. Если идентификатор не имеет значения, просто исключите его из критериев объединения. Я включил его в качестве идентификатора и похоже, что это часть ключа.

2 голосов
/ 23 октября 2019

создайте cte с серией и примените кросс

   CREATE TABLE Test (
                            ID INT,
                            TYPE_ID INT,
                            CREATED_DT DATE  
                        )

    INSERT INTO Test(ID, TYPE_ID, CREATED_DT)
    VALUES
        (123,485,'2019-08-31')
        ,(123,485,'2019-05-31')
        ,(123,485,'2019-02-28')
        ,(123,485,'2018-11-30')
        ,(123,485,'2018-08-31')
        ,(123,485,'2018-05-31')
        ,(123,487,'2019-05-31')
        ,(123,487,'2018-05-31')
;


    WITH n(n) AS
    (
        SELECT 1
        UNION ALL
        SELECT n+1 FROM n WHERE n < 6
    )

    ,id_n as (
                SELECT 
                DISTINCT 
                ID
                ,TYPE_ID
                ,n

                FROM 
                Test
                cross apply n
             )

    SELECT 
        id_n.ID
        ,id_n.TYPE_ID
        ,test.CREATED_DT
        ,id_n.n row_num

    FROM 
        id_n 
        left join 
                    (
                        select 
                            ID
                            ,TYPE_ID
                            ,CREATED_DT

                            ,ROW_NUMBER() over(partition by id, type_id order by created_dt) rn 

                        from    
                        Test 

                    ) Test on Test.ID = id_n.ID and Test.TYPE_ID = id_n.TYPE_ID and id_n.n = test.rn


    drop table Test
2 голосов
/ 23 октября 2019

Пожалуйста, обратитесь к другому ответу о том, как вы можете сделать это, используя CROSS JOIN - это довольно аккуратно. В качестве альтернативы мы можем использовать логику программирования, доступную в MS-SQL, для достижения желаемых результатов. Следующий подход сохраняет различные комбинации ID и TYPE_ID внутри курсора SQL. Затем он перебирает записи курсора, чтобы убедиться, что соответствующий объем данных сохранен во временной таблице. Наконец, SELECT выполняется для временной таблицы, и курсор закрывается. Вот подтверждение концепции, которую я проверял на https://rextester.com/l/sql_server_online_compiler.

-- Create schema for testing
CREATE TABLE Test (
    ID INT,
    TYPE_ID INT,
    CREATED_DT DATE  
)

-- Populate data
INSERT INTO Test(ID, TYPE_ID, CREATED_DT)
VALUES
(123,485,'2019-08-31')
,(123,485,'2019-05-31')
,(123,485,'2019-02-28')
,(123,485,'2018-11-30')
,(123,485,'2018-08-31')
,(123,485,'2018-05-31')
,(123,487,'2019-05-31')
,(123,487,'2018-05-31');

-- Create TempTable for output
CREATE TABLE #OutputTable (
    ID INT,
    TYPE_ID INT,
    CREATED_DT DATE,
    ROW_NUM INT
)

-- Declare local variables
DECLARE @tempID INT, @tempType INT;

-- Create cursor to iterate ID and TYPE_ID
DECLARE mycursor CURSOR FOR (
    SELECT DISTINCT ID, TYPE_ID FROM Test
    );
OPEN mycursor

-- Populate cursor
FETCH NEXT FROM mycursor
INTO @tempID, @tempType;

-- Loop
WHILE @@FETCH_STATUS = 0
BEGIN
    DECLARE @count INT = (SELECT COUNT(*) FROM Test WHERE ID = @tempID AND TYPE_ID = @tempType);

    INSERT INTO #OutputTable (ID, TYPE_ID, CREATED_DT, ROW_NUM)
    SELECT ID, TYPE_ID, CREATED_DT, ROW_NUMBER() OVER(ORDER BY ID ASC) 
    FROM Test
    WHERE ID = @tempID AND TYPE_ID = @tempType;

    WHILE @count < 6
    BEGIN
        SET @count = @count + 1
        INSERT INTO #OutputTable
        VALUES (@tempID, @tempType, NULL, @count);
    END

    FETCH NEXT FROM mycursor
    INTO @tempID, @tempType;
END

-- Close cursor
CLOSE mycursor;

-- View results
SELECT * FROM #OutputTable;

Обратите внимание, если у вас есть экземпляр, в котором уникальная комбинация ID и TYPE_ID сгруппирована более 6 раз, дополнительнаягруппировки будут включены в ваш конечный результат. Если вам нужно показать только 6 групп, вы можете изменить эту часть запроса на SELECT TOP 6 ....

...