我有两张表Test1
和Corr_table
Test1表创建脚本:
CREATE TABLE [dbo].[Test1](
[id] [bigint] NOT NULL,
[Country] [varchar](3) NULL,
[PeriodKey] [varchar](max) NULL,
[a] [varchar](3) NULL,
[b] [varchar](3) NULL,
[c] [varchar](3) NULL
)
Test1
数据:
id Country PeriodKey a b c
1 E 201201 1 5 9
1 E 201202 1 5 9
3 G 201203 3 7 11
4 H 201204 4 8 12
Corr_table
创建脚本:
CREATE TABLE [dbo].[corr_table](
[Country] [varchar](5) NULL,
[id] [bigint] NULL,
[Field] [varchar](10) NULL,
[Value] [varchar](50) NULL,
[Start_date] [varchar](50) NULL,
[End_date] [varchar](50) NULL
)
corr_table
数据:
Country id Field Value Start_date End_date
E 1 a 4 201201 201202
E 1 b 6 201201 201202
现在,如果我写这个查询,
select
a = case when x.Field = 'a' then x.value else a end,
b = case when x.Field = 'b' then x.value else b end,
y.*
from
dbo.Test1 y,dbo.corr_table x
where
y.id = 1
and y.Country = 'E'
and y.PeriodKey in (201201)
它给出了以下结果:
a b id Country PeriodKey a b c
4 5 1 E 201201 1 5 9
1 6 1 E 201201 1 5 9
而我期待以下结果:
a b id Country PeriodKey a b c
4 6 1 E 201201 1 5 9
为什么两列都不会在一行中更新?它一次只更新一列,但应该更新两列
即a应为4,b应为单行6。但只更新一个,为什么会这样?
答案 0 :(得分:0)
要使@ marc_s的注释更明确,结果SQL将如下所示:
select
a = case when x.Field = 'a' then x.value else a end,
b = case when x.Field = 'b' then x.value else b end,
y.*
from
dbo.Test1 y
INNER JOIN dbo.corr_table x ON y.Country = x.Country
where
y.id = 1
and y.Country = 'E'
and y.PeriodKey in (201201)
此外,您的数据模型在其列名中应该更加明确,以便关系结构很明显 - 我不得不猜测连接条件。
答案 1 :(得分:0)
为什么您的查询会返回两行
要理解为什么查询返回两行而不是您期望的行,有助于理解SQL Server在执行查询时理论上采取的逻辑步骤。
第一个逻辑步骤是处理FROM子句。您的查询指定了两个表dbo.Test1
和dbo.corr_table
之间的交叉连接,以生成包含8行的中间虚拟表(我们称之为V1
):
ID COUNTRY PERIODKEY A B C FIELD VALUE START_DATE END_DATE
1 E 201201 1 5 9 a 4 201201 201202
1 E 201202 1 5 9 a 4 201201 201202
3 G 201203 3 7 11 a 4 201201 201202
4 H 201204 4 8 12 a 4 201201 201202
1 E 201201 1 5 9 b 6 201201 201202
1 E 201202 1 5 9 b 6 201201 201202
3 G 201203 3 7 11 b 6 201201 201202
4 H 201204 4 8 12 b 6 201201 201202
第二个逻辑步骤是处理WHERE子句。您的查询指定只有ID为1,Country为'E'且PeriodKey为201201的行才能通过。中间表中的两行满足此条件以填充另一个中间表(让我们称之为V2
):
ID COUNTRY PERIODKEY A B C FIELD VALUE START_DATE END_DATE
1 E 201201 1 5 9 a 4 201201 201202
1 E 201201 1 5 9 b 6 201201 201202
第三个也是最后一个逻辑步骤是处理SELECT子句。您的查询指定应根据现有列中的值计算两个新列a
和b
,并且应该不修改表dbo.Test1
中的所有列。 SELECT子句基本上是一个列的列表,用于定义结果集的结构。它不控制结果集中有多少行。这个中间表(我们称之为V3
)与结果集相同:
a b id Country PeriodKey a b c
4 5 1 E 201201 1 5 9
1 6 1 E 201201 1 5 9
V2
中的两行都包含您希望在结果集中看到的单行元素。基表dbo.Test1
中的所有列都包含相同的值,因为这些列中的值仅从dbo.Test1
中的一行复制。第一列a
和b
中的值是从dbo.corr_table
中的两个不同的行复制的。
如果我们能够将V2
中的这两行组合在一起,我们可以制作一行符合我们预期的行。幸运的是,我们可以在SQL中轻松表达这一点。
如何生成预期的结果集
在SQL中,SELECT语句具有GROUP BY子句,该子句将多行组合成一行代表该组。逻辑上,它在WHERE子句之后和SELECT子句之前处理。
对于您的查询,我添加了一个GROUP BY子句,并为每个计算列调用了一个聚合函数。此查询产生您的预期结果:
SELECT
MAX(
CASE
WHEN dbo.corr_table.Field = 'a'
THEN dbo.corr_table.Value
END
) AS corr_a,
MAX(
CASE
WHEN dbo.corr_table.Field = 'b'
THEN dbo.corr_table.Value
END
) AS corr_b,
dbo.Test1.*
FROM dbo.Test1
INNER JOIN dbo.corr_table ON
dbo.Test1.id = dbo.corr_table.id
WHERE
dbo.Test1.id = 1
and dbo.Test1.Country = 'E'
and dbo.Test1.PeriodKey in (201201)
GROUP BY
dbo.Test1.id,
dbo.Test1.Country,
dbo.Test1.PeriodKey,
dbo.Test1.a,
dbo.Test1.b,
dbo.Test1.c;
我现在没有时间解释,但我稍后会扩展我的答案并解释为什么这样做。现在,我建议您阅读有关逻辑查询处理和SELECT语句的内容。 Itzik Ben-Gan发表了一篇有用的PDF flow chart来解释逻辑步骤。它被复制为下面的图像,复制自sqlwithmanoj.wordpress.com: