两个 SELECT 查询的 ISNULL 而不是一个标量值
ISNULL for two SELECT queries instead one scalar value
我想做这样的事情:
SELECT ISNULL_QUERY((SELECT TOP 1 A.Id, A.Value FROM A), (SELECT TOP 1 B.Id, B.Value FROM B))
它应该 return 包含 Id 和 Value 列的行,例如:
Id
Value
1
TestValue
我不想 运行 两个 select 查询然后选择一个,因为它们太“重”了。
我不能使用:
SELECT ISNULL((SELECT TOP 1 1 [Id], 'test' [Value]), (SELECT TOP 1 2 [Id], 'test2' [Value]))
因为会报错:
消息 116,级别 16,状态 1,第 1 行
当子查询不使用EXISTS引入时,select列表中只能指定一个表达式。
我想要两列而不是一列
使用基于集合的方法来执行此操作,使用 UNION 运算符,如下所示:
SELECT TOP 1 A.Id, A.Value FROM A
UNION ALL
SELECT TOP 1 B.Id, B.Value FROM B
WHERE NOT EXIST(SELECT TOP 1 A.Id, A.Value FROM A)
补充测试....
0 - 初始设置
CREATE TABLE T_A (CA INT PRIMARY KEY);
CREATE TABLE T_B (CB INT PRIMARY KEY);
SET STATISTICS IO ON;
-- rewrited query :
WITH
T1 AS (SELECT TOP 1 CA FROM T_A ORDER BY CA),
T2 AS (SELECT TOP 1 CB FROM T_B ORDER BY CB)
SELECT * FROM T1
UNION ALL
SELECT * FROM T2
WHERE NOT EXISTS(SELECT * FROM T1);
1 - 所有 table 都已填充
INSERT INTO T_A VALUES (1), (2), (3);
INSERT INTO T_B VALUES (9), (8), (7);
Table'T_A'。扫描计数 2,逻辑读取 4
Table 'T_B'。扫描计数 1,逻辑读取 2
tableT_A读两遍
2 - B 为空,A 为满
DELETE FROM T_B;
Table'T_A'。扫描计数 1,逻辑读取 2
Table 'T_B'。扫描计数 1,逻辑读取 2
所有table阅读一次
3 - A空,B满
INSERT INTO T_B VALUES (9), (8), (7);
DELETE FROM T_A;
Table'T_A'。扫描计数 2,逻辑读取 4
Table 'T_B'。扫描计数 1,逻辑读取 2
Table一个读两遍
现在两个 table 填充了 1,000,000 行
DELETE FROM T_A;
WITH
TN AS (SELECT 0 AS I
UNION ALL
SELECT I + 1 FROM TN
WHERE I < 9)
INSERT INTO T_A
SELECT T1.I + 10*T2.I + 100*T3.I + 1000*T4.I + 10000*T5.I + 100000*T6.I
FROM TN AS T1
CROSS JOIN TN AS T2
CROSS JOIN TN AS T3
CROSS JOIN TN AS T4
CROSS JOIN TN AS T5
CROSS JOIN TN AS T6;
DELETE FROM T_B;
INSERT INTO T_B
SELECT * FROM T_A;
4 - 两个 table 填充了 100 万行
Table'T_A'。扫描计数 2,逻辑读取 8
Table 'T_B'。扫描计数 1,逻辑读取 3
5 - table A 空,table B 100 万行
Table'T_A'。扫描计数 2,逻辑读取 6
Table 'T_B'。扫描计数 1,逻辑读取 3
6 - table B 空,table A 100 万行
Table'T_B'。扫描计数 1,逻辑读取 3
Table 'T_A'。扫描计数 1,逻辑读取 3
结论是:无论是否两次访问(扫描计数),因为只会读取很少的页面...
如果 SQL 服务器物化 CTE:
,这将有效
WITH a as (
SELECT TOP (1) A.Id, A.Value
FROM A
)
SELECT id, value
FROM a
UNION ALL
SELECT TOP (1) b.id, b.value
FROM b
WHERE NOT EXISTS (SELECT 1 FROM a);
但事实并非如此。如果你真的需要控制执行(并且不能信任优化器),那么你可能需要使用临时表:
SELECT TOP (1) A.Id, A.Value
INTO #A;
FROM A;
然后:
SELECT id, value
FROM #A
UNION ALL
SELECT TOP (1) b.id, b.value
FROM b
WHERE NOT EXISTS (SELECT 1 FROM #A);
甚至:
IF EXISTS (SELECT 1 FROM #A)
SELECT id, value
FROM #A
ELSE
SELECT TOP (1) id, value
FROM B;
我想做这样的事情:
SELECT ISNULL_QUERY((SELECT TOP 1 A.Id, A.Value FROM A), (SELECT TOP 1 B.Id, B.Value FROM B))
它应该 return 包含 Id 和 Value 列的行,例如:
Id | Value |
---|---|
1 | TestValue |
我不想 运行 两个 select 查询然后选择一个,因为它们太“重”了。
我不能使用:
SELECT ISNULL((SELECT TOP 1 1 [Id], 'test' [Value]), (SELECT TOP 1 2 [Id], 'test2' [Value]))
因为会报错:
消息 116,级别 16,状态 1,第 1 行 当子查询不使用EXISTS引入时,select列表中只能指定一个表达式。
我想要两列而不是一列
使用基于集合的方法来执行此操作,使用 UNION 运算符,如下所示:
SELECT TOP 1 A.Id, A.Value FROM A
UNION ALL
SELECT TOP 1 B.Id, B.Value FROM B
WHERE NOT EXIST(SELECT TOP 1 A.Id, A.Value FROM A)
补充测试....
0 - 初始设置
CREATE TABLE T_A (CA INT PRIMARY KEY);
CREATE TABLE T_B (CB INT PRIMARY KEY);
SET STATISTICS IO ON;
-- rewrited query :
WITH
T1 AS (SELECT TOP 1 CA FROM T_A ORDER BY CA),
T2 AS (SELECT TOP 1 CB FROM T_B ORDER BY CB)
SELECT * FROM T1
UNION ALL
SELECT * FROM T2
WHERE NOT EXISTS(SELECT * FROM T1);
1 - 所有 table 都已填充
INSERT INTO T_A VALUES (1), (2), (3);
INSERT INTO T_B VALUES (9), (8), (7);
Table'T_A'。扫描计数 2,逻辑读取 4 Table 'T_B'。扫描计数 1,逻辑读取 2
tableT_A读两遍
2 - B 为空,A 为满
DELETE FROM T_B;
Table'T_A'。扫描计数 1,逻辑读取 2 Table 'T_B'。扫描计数 1,逻辑读取 2
所有table阅读一次
3 - A空,B满
INSERT INTO T_B VALUES (9), (8), (7);
DELETE FROM T_A;
Table'T_A'。扫描计数 2,逻辑读取 4 Table 'T_B'。扫描计数 1,逻辑读取 2
Table一个读两遍
现在两个 table 填充了 1,000,000 行
DELETE FROM T_A;
WITH
TN AS (SELECT 0 AS I
UNION ALL
SELECT I + 1 FROM TN
WHERE I < 9)
INSERT INTO T_A
SELECT T1.I + 10*T2.I + 100*T3.I + 1000*T4.I + 10000*T5.I + 100000*T6.I
FROM TN AS T1
CROSS JOIN TN AS T2
CROSS JOIN TN AS T3
CROSS JOIN TN AS T4
CROSS JOIN TN AS T5
CROSS JOIN TN AS T6;
DELETE FROM T_B;
INSERT INTO T_B
SELECT * FROM T_A;
4 - 两个 table 填充了 100 万行
Table'T_A'。扫描计数 2,逻辑读取 8 Table 'T_B'。扫描计数 1,逻辑读取 3
5 - table A 空,table B 100 万行
Table'T_A'。扫描计数 2,逻辑读取 6 Table 'T_B'。扫描计数 1,逻辑读取 3
6 - table B 空,table A 100 万行
Table'T_B'。扫描计数 1,逻辑读取 3 Table 'T_A'。扫描计数 1,逻辑读取 3
结论是:无论是否两次访问(扫描计数),因为只会读取很少的页面...
如果 SQL 服务器物化 CTE:
,这将有效WITH a as (
SELECT TOP (1) A.Id, A.Value
FROM A
)
SELECT id, value
FROM a
UNION ALL
SELECT TOP (1) b.id, b.value
FROM b
WHERE NOT EXISTS (SELECT 1 FROM a);
但事实并非如此。如果你真的需要控制执行(并且不能信任优化器),那么你可能需要使用临时表:
SELECT TOP (1) A.Id, A.Value
INTO #A;
FROM A;
然后:
SELECT id, value
FROM #A
UNION ALL
SELECT TOP (1) b.id, b.value
FROM b
WHERE NOT EXISTS (SELECT 1 FROM #A);
甚至:
IF EXISTS (SELECT 1 FROM #A)
SELECT id, value
FROM #A
ELSE
SELECT TOP (1) id, value
FROM B;