1

我有两张桌子Test1Corr_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 in a single row 。但是只更新了一个,这是为什么呢?

4

2 回答 2

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)

此外,您的数据模型的列名应该更明确一点,以便关系结构显而易见 - 我不得不猜测连接条件。

于 2012-07-02T13:02:01.807 回答
0

为什么您的查询返回两行

要了解为什么您的查询返回两行而不是您期望的那一行,它有助于理解 SQL Server 在执行查询时理论上采取的逻辑步骤。

第一个逻辑步骤是处理 FROM 子句。您的查询指定了两个表之间的交叉连接,dbo.Test1并生成一个包含 8 行dbo.corr_table的中间虚拟表(我们称之为):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、国家/地区为“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. 第一列ab中的值是从 中的两个不同行复制而来的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 流程图,解释了逻辑步骤。它被复制为下面的图像,从sqlwithmanoj.wordpress.com复制:

在此处输入图像描述

于 2012-07-02T13:41:26.167 回答