在SQL 2008 DB中,我需要使用2个数据表来在存储过程中构建结果。表1看起来像这样。
date name hour amount price
------------------------------------------
2009-10-12 tom 12 20 15.43
2009-10-13 fred 16 -10 6.98
表2看起来像这样。请注意,每个小时都在一个单独的列中,而不是表1中单个列中的所有小时。
date name H12 H16
--------------------------------
2009-10-12 tom 15.75 0
2009-10-13 fred 0 12.54
我需要编写一个存储过程,结果看起来像这样。
date name hour amount price result actualsold
---------------------------------------------------------------------
2009-10-12 tom 12 20 15.43 positive 15.75
2009-10-13 fred 16 -10 6.98 negative 12.54
我可以获得我需要做的大部分结果。 。 。是的,简单的部分。
SELECT date,
name,
hour,
amount,
price,
case
when amount > 0 then 'positive'
when amount < 0 then 'negative'
end as result
FROM Table1
如何从表2中获取值并将其添加到actualsold列(最好不使用游标)?
我无法控制表模式。数据库正在SQL 2008 ENT上运行。
答案 0 :(得分:2)
我认为最好的方法是对UNPIVOT进行连接,或者使用CASE语句对数字进行Table2的交叉连接以拉出列。尽管UNPIVOT语法有多好,但这两种方式都不是明显的赢家。
有时,交叉联接优于UNPIVOT,有时反之亦然。这是交叉连接方法,即使在SQL 2000中也可以使用:
SELECT
A.date,
A.name,
A.hour,
A.amount,
A.price,
result =
CASE
WHEN A.amount > 0 THEN 'positive'
WHEN A.amount < 0 THEN 'negative'
END,
actualsold =
CASE A.hour
WHEN 12 THEN B.H12
WHEN 16 THEN B.H16
END
FROM
Table1 A
INNER JOIN Table2 B ON A.date = B.date and A.name = B.name
这是一个隐式的部分交叉连接,因为Table1每个日期有很多行&amp;名称,但Table2每个日期只有一行&amp;名称。因此,Table2中的每一行在连接中会重复多次,但我们会使用不同的列。
Unpivoting类似,将UNPIVOT查询放在派生表或CTE中:
SELECT
A.date,
A.name,
A.hour,
amount,
A.price,
result =
CASE
WHEN A.amount > 0 THEN 'positive'
WHEN A.amount < 0 THEN 'negative'
END,
B.actualsold
FROM
Table1 A
INNER JOIN (
SELECT *
FROM (SELECT date, name, [12] = H12, [16] = H16 FROM Table2) X
UNPIVOT (actualsold FOR hour IN ([12], [16])) U
) B ON A.date = B.date and A.name = B.name AND A.hour = B.hour
上面两个查询的执行计划非常相似,尽管交叉连接方法稍微简单一点,但是在尝试对付有很多行的完整表中的实际数据之前,这并不能证明实际性能。 UNPIVOT执行额外的LEFT LOOP JOIN,而交叉连接方法则没有。
UNPIVOT方法的一个潜在缺点是,如果要指定限制日期范围和名称的条件,您可能需要在两个位置执行此操作,因此服务器不会对整个dang表进行取消操作(尽管它可能足够聪明,不要这样做,我不知道)。虽然24大条件CASE语句很难处理,但UNPIVOT有自己的尴尬,将列名转换为数字。
以下是希望查看这些查询的人员的设置代码:
USE tempdb
CREATE TABLE Table1 (
date smalldatetime,
name varchar(10),
hour int,
amount int,
price decimal(15,2)
)
CREATE TABLE Table2 (
date smalldatetime,
name varchar(10),
H12 decimal(15,2),
H16 decimal(15,2),
)
INSERT Table1 VALUES ('20091012', 'tom', 12, 20, 15.43)
INSERT Table1 VALUES ('20091013', 'fred', 16, -10, 6.98)
INSERT Table2 VALUES ('20091012', 'tom', 15.75, 0)
INSERT Table2 VALUES ('20091013', 'fred', 0, 12.54)
答案 1 :(得分:0)
未测试:
SELECT A.date,
A.name,
A.hour,
A.amount,
A.price,
case
when A.amount > 0 then 'positive'
when A.amount < 0 then 'negative'
end as result,
(SELECT case A.hour when 1 then B.H1
when 2 then B.H2
...
when 24 then B.H24 end
FROM Table2 B
WHERE A.date = B.date AND A.name = B.name) as actualsold
FROM Table1 A
这假设你有所有小时的列(H1,...,H24)。如果您只有H12和H16,则可以减少case
列表。
答案 2 :(得分:0)
使用UNPIVOT运算符将Table2恢复为标准化形式(未经测试)
SELECT date,
name,
hour,
Amount
FROM Table2
UNPIVOT (Amount FOR [hour] IN ([H1], [H2], [H3], [H4], ... etc ...)) AS unpvt;
答案 3 :(得分:-1)
您说您可以更改表架构,但是可以创建视图吗?如果可能的话,我会创建一个视图来纠正第二个表的结构,例如:
CREATE VIEW vw_Table2 AS
SELECT date, name, 12 as hour, H12 as sold
UNION ALL
SELECT date, name, 16 as hour, H16 as sold
从那里,你可以像这样构建你的查询:
SELECT t1.date,
t1.name,
t1.hour,
t1.amount,
t1.price,
case
when t1.amount > 0 then 'positive'
when t1.amount < 0 then 'negative'
end as result,
v2.sold as actualsold
FROM Table1 as t1
JOIN vw_Table2 as v2 ON
v2.date = t1.date
AND v2.name = t1.name
AND v2.hour = t1.hour