Abh*_*bhi 4 sql-server sql-server-2016
我运行了一份报告,在该报告中,我遇到了一种情况,即基于基本上是键或 Id 的列值,我需要从映射 Id 表中获取相应的值。像下面这样:
SELECT
(case when [column1='A'] then (select value from Table1)
when [column1='B'] then (select value from Table2)
when [column1='C'] then (select value from Table3)
and so on uptil 35 more 'when' conditions ...
ELSE column1 end) Value
from Table1
Run Code Online (Sandbox Code Playgroud)
更确切地说:
SELECT
(case when [A.column1='1']
then (select value from B where B.clientId=100 and A.column1=B.Id)
when [A.column1='2']
then (select value from C where C.clientId=100 and A.column1=C.Id)
when [A.column1='3']
then (select value from D where D.clientId=100 and A.column1=D.Id)
...
and so on uptil 30 more 'when' conditions
...
ELSE column1 end)
FROM A
Run Code Online (Sandbox Code Playgroud)
在表 B、C、D.. 等中,我们维护所有客户的数据。每个客户端都有一个特定的ClientId
,这些表 B、C、D 等都有索引Id
和ClientId
列。
在 SQL Server 中是否有一种有效的方法来处理这个问题?
我假设您在子查询中的表上有适当的索引。我模拟了一些快速测试数据并在 table 中放入了 1000 万行A
。我不是创建 30 个表的游戏,所以我只为CASE
表达式创建了 3 个。我认为 3 足以显示一般原则。
DROP TABLE IF EXISTS dbo.B;
CREATE TABLE dbo.B (
ClientID INT NOT NULL,
Id VARCHAR(20) NOT NULL,
[Value] VARCHAR(100),
PRIMARY KEY (ClientID, Id)
);
INSERT INTO B VALUES (100, '1', 'TABLE B');
DROP TABLE IF EXISTS dbo.C;
CREATE TABLE dbo.C (
ClientID INT NOT NULL,
Id VARCHAR(20) NOT NULL,
[Value] VARCHAR(100),
PRIMARY KEY (ClientID, Id)
);
INSERT INTO C VALUES (100, '2', 'TABLE C');
DROP TABLE IF EXISTS dbo.D;
CREATE TABLE dbo.D (
ClientID INT NOT NULL,
Id VARCHAR(20) NOT NULL,
[Value] VARCHAR(100),
PRIMARY KEY (ClientID, Id)
);
INSERT INTO D VALUES (100, '3', 'TABLE D');
DROP TABLE IF EXISTS dbo.A;
CREATE TABLE dbo.A (
column1 VARCHAR(20) NOT NULL
);
INSERT INTO dbo.A WITH (TABLOCK)
SELECT CAST(1 + t.RN % 3 AS VARCHAR(20))
FROM
(
SELECT TOP (5000000) ROW_NUMBER() OVER (ORDER BY (SELECT NULL)) RN
FROM master..spt_values t1
CROSS JOIN master..spt_values t2
) t;
Run Code Online (Sandbox Code Playgroud)
我禁用了结果集并在 SSMS 中运行了以下查询:
SELECT A.column1
FROM A;
Run Code Online (Sandbox Code Playgroud)
大约需要 0.723 秒。我正在做非常不科学的测试,因为我对你的数据一无所知。无论如何,对于串行查询,我们不能期望比 0.7 秒更好的结果。那是我们的基线。
编写此查询的最有效方法是根本不使用连接。关键是,如果CASE
表达式找到匹配项,它只会返回 3(或 30)个唯一值。您可以将结果保存到局部变量中,然后在查询中使用它们。下面的查询在大约 1.044 秒内完成:
DECLARE @B_VALUE VARCHAR(100) = (select value from B where B.clientId=100 and B.Id = '1');
DECLARE @C_VALUE VARCHAR(100) = (select value from C where C.clientId=100 and C.Id = '2');
DECLARE @D_VALUE VARCHAR(100) = (select value from D where D.clientId=100 and D.Id = '3');
SELECT
(case when A.column1='1' then @B_VALUE
when A.column1='2' then @C_VALUE
when A.column1='3' then @D_VALUE
-- omitted other columns
else column1 end)
FROM A;
Run Code Online (Sandbox Code Playgroud)
计划非常简单:
对于另一个选项,您可以使用连接编写查询(我们可以CASE
使用 以更紧凑的形式重写表达式COALESCE()
。这在大约 2.314 秒内完成:
SELECT
COALESCE(B.column1, C.column1, D.column1, -- omitted other columns
A.column1)
-- (case A.column1
-- when '1' then B.value
-- when '2' then C.value
-- when '3' then D.value
-- -- omitted other columns
-- else A.column1 end)
FROM A
LEFT JOIN B ON B.clientId=100 and B.Id = '1'
LEFT JOIN C ON C.clientId=100 and C.Id = '2'
LEFT JOIN D ON D.clientId=100 and D.Id = '3';
Run Code Online (Sandbox Code Playgroud)
这是计划:
通过编写这样的查询,您可以获得几乎相同的运行时和查询计划:
SELECT
(case A.column1
when '1' then (select value from B where B.clientId=100 and '1'=B.Id)
when '2' then (select value from C where C.clientId=100 and '2'=C.Id)
when '3' then (select value from D where D.clientId=100 and '3'=D.Id)
-- omitted other columns
else column1 end)
FROM A;
Run Code Online (Sandbox Code Playgroud)
问题中的原始查询有一个问题:SQL Server 在嵌套循环连接之前进行了无用的排序。该查询在我的机器上大约 5.838 秒内完成。
跟踪标志 8690 消除了排序和表线轴。查询在大约 7.479 秒内运行,跟踪标志为 8690,所以我认为假脱机对这个查询有帮助。
归档时间: |
|
查看次数: |
51089 次 |
最近记录: |