2012-07-02 116 views
1

我有两个表Test1Corr_table奇怪的SQL查询行为

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。但只更新一个,为什么呢?

+10

其一,你**不加入**你的表在所有 - 你只是创建一个笛卡尔乘积 - 是真的想你要? [另外,你应该停止使用旧的JOIN语法](http://sqlblog.com/blogs/aaron_bertrand/archive/2009/10/08/bad-habits-to-kick-using-old -style-joins.aspx)可能会导致这样的错误 - 忘记提供任何JOIN条件并因此创建笛卡尔积 –

+1

您能展示您期望的结果吗? –

+0

你能解释你的WHERE情况吗?我不明白过滤标准。 –

回答

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) 

而且,你的数据模型应在其列名的一点更加明确,这样的关系结构是显而易见的 - 我不得不猜测连接条件。

+0

应该将id字段作为连接的一部分吗? – Sean

+0

我不认为@MSU正在尝试进行连接 - 我认为他正在尝试做某种关键点。 – podiluska

+0

因此@AaronBertrand的评论......在我们完全回答问题之前,预期的结果肯定需要澄清。我只是“回答”它清楚地显示SQL。 – bluevector

0

为什么查询返回两行

要理解为什么您的查询返回两行,而不是你所期望的一个,它有助于了解执行查询时SQL服务器理论上需要的逻辑步骤。

第一个逻辑步骤是处理FROM子句。查询指定了两个表dbo.Test1dbo.corr_table之间的交叉连接以产生中间的虚拟表(让我们称之为V1)含有8行:

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子句。您的查询指定应该根据现有列中的值计算两个新列ab,并且表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 flow chart。这是转载如下图像,复制从sqlwithmanoj.wordpress.com

enter image description here