在架构反转时,在存储过程中获取列数据

时间:2010-02-03 20:11:25

标签: sql-server sql-server-2008 stored-procedures

在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上运行。

4 个答案:

答案 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