SQL - 如果数据不可用,则获取默认 NULL 值
SQL - get default NULL value if data is not available
我得到了一个table数据如下:
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
我想 select 6 ROW_NUMs 每个 TYPE_ID,如果缺少数据我需要 return CREATED_DT 的 NULL 值和最终结果集应如下所示:
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 以简单的方式完成,但不确定如何让它工作。在这方面有什么可以帮助我的吗?
请参考其他答案,了解如何使用 CROSS JOIN
执行此操作 - 这非常简洁。或者,我们可以利用 MS-SQL 中可用的编程逻辑来实现所需的结果。以下方法将不同的 ID
和 TYPE_ID
组合存储在 SQL 游标中。然后它遍历游标条目以确保将适当数量的数据存储到临时 table 中。最后,对临时 table 执行 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 ...
.
- 假设 Row_Num 至少有记录至少有所有 6 行... tbl 中的 1,2,3,4,5,6 并且没有分数或 0 或负数...
- 我们得到了所有不同类型 ID 的列表。 (别名 A)
- 然后我们得到一个不同的行号小于 7 的列表(给我们 6 条记录)
- 我们交叉连接这些以确保每个 ID 和 Type_ID 具有全部 6 行。
- 然后我们在基础集合 (tbl) 中离开加入以获取所有需要的日期;存在此类日期的地方。由于我们使用左连接行 w/o 日期仍然存在。
.
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 测试员:Example
这还假设您希望 type_id 和 ID 的每个组合都使用 1-6。如果 ID 无关紧要,则只需将其从连接条件中排除。我将它包括在内,因为它是一个 ID,而且它似乎是密钥的一部分。
创建一个包含系列的 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
我得到了一个table数据如下:
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
我想 select 6 ROW_NUMs 每个 TYPE_ID,如果缺少数据我需要 return CREATED_DT 的 NULL 值和最终结果集应如下所示:
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 以简单的方式完成,但不确定如何让它工作。在这方面有什么可以帮助我的吗?
请参考其他答案,了解如何使用 CROSS JOIN
执行此操作 - 这非常简洁。或者,我们可以利用 MS-SQL 中可用的编程逻辑来实现所需的结果。以下方法将不同的 ID
和 TYPE_ID
组合存储在 SQL 游标中。然后它遍历游标条目以确保将适当数量的数据存储到临时 table 中。最后,对临时 table 执行 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 ...
.
- 假设 Row_Num 至少有记录至少有所有 6 行... tbl 中的 1,2,3,4,5,6 并且没有分数或 0 或负数...
- 我们得到了所有不同类型 ID 的列表。 (别名 A)
- 然后我们得到一个不同的行号小于 7 的列表(给我们 6 条记录)
- 我们交叉连接这些以确保每个 ID 和 Type_ID 具有全部 6 行。
- 然后我们在基础集合 (tbl) 中离开加入以获取所有需要的日期;存在此类日期的地方。由于我们使用左连接行 w/o 日期仍然存在。
.
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 测试员:Example
这还假设您希望 type_id 和 ID 的每个组合都使用 1-6。如果 ID 无关紧要,则只需将其从连接条件中排除。我将它包括在内,因为它是一个 ID,而且它似乎是密钥的一部分。
创建一个包含系列的 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