zoukankan      html  css  js  c++  java
  • SqlServer2005连接之前的行

      Common Table Expressions (CTEs)和Row_number()函数提供了我们一种优雅的解决方案.下面我们来假设一个常见的场景.零售商一张产品(Items)表,还有一张历史价格(PriceHistory)表.数据如下:

    产品表

    CREATE TABLE [dbo].[Items](
        [ItemId] [int] NOT NULL,
        [Item] [varchar](100) NOT NULL,
     CONSTRAINT [PK_Items] PRIMARY KEY CLUSTERED 
    (
        [ItemId] ASC
    ))
    GO
     
    CREATE TABLE [dbo].[PriceHistory](
        [ItemId] [int] NOT NULL,
        [PriceStartDate] [datetime] NOT NULL,
        [Price] [decimal](10, 2) NOT NULL,
     CONSTRAINT [PK_PriceHistory] PRIMARY KEY CLUSTERED 
    (
        [ItemId] ASC,
        [PriceStartDate] ASC
    ))
    GO
    ALTER TABLE [dbo].[PriceHistory]  WITH CHECK ADD  CONSTRAINT [FK_PriceHistory_Items] FOREIGN KEY([ItemId])
    REFERENCES [dbo].[Items] ([ItemId])
     
    测试数据:
    GO
    INSERT INTO Items VALUES (1, 'vacuum cleaner')
    INSERT INTO Items VALUES (2, 'washing machine')
    INSERT INTO Items VALUES (3, 'toothbrush')
    INSERT INTO PriceHistory VALUES (1,'2004-03-01',250)
    INSERT INTO PriceHistory VALUES (1,'2005-06-15',219.99)
    INSERT INTO PriceHistory VALUES (1,'2007-01-03',189.99)
    INSERT INTO PriceHistory VALUES (1,'2007-02-03',200.00)
    INSERT INTO PriceHistory VALUES (2,'2006-07-12',650.00)
    INSERT INTO PriceHistory VALUES (2,'2007-01-03',550.00)
    INSERT INTO PriceHistory VALUES (3,'2005-01-01',1.99)
    INSERT INTO PriceHistory VALUES (3,'2006-01-01',1.79)
    INSERT INTO PriceHistory VALUES (3,'2007-01-01',1.59)
    INSERT INTO PriceHistory VALUES (3,'2008-01-01',1.49)

    ItemId          PriceStartDate         Price
    1    2004-03-01 00:00:00.000    250.00
    1    2005-06-15 00:00:00.000    219.99
    1    2007-01-03 00:00:00.000    189.99
    1    2007-02-03 00:00:00.000    200.00
    2    2006-07-12 00:00:00.000    650.00
    2    2007-01-03 00:00:00.000    550.00
    3    2005-01-01 00:00:00.000    1.99
    3    2006-01-01 00:00:00.000    1.79
    3    2007-01-01 00:00:00.000    1.59
    3    2008-01-01 00:00:00.000    1.49

    现在用户希望出一张这样的报表:

    Item Old Price Range Price Start Date End Date
    vacuum cleaner   250.00 2004-03-01 2005-06-15
    vacuum cleaner 250.00 219.99 2005-06-15 2007-01-03
    vacuum cleaner 219.99 189.99 2007-01-03 2007-02-03
    vacuum cleaner 189.99 200.00 2007-02-03  
    washing machine   650.00 2006-07-12 2007-01-03
    washing machine 650.00 550.00 2007-01-03  
    toothbrush   1.99 2005-01-01 2006-01-01
    toothbrush 1.99 1.79 2006-01-01 2007-01-01
    toothbrush 1.79 1.59 2007-01-01 2008-01-01
    toothbrush 1.59 1.49 2008-01-01  

    第一步,我们首先用CTE来实现,按时间排名产品价格视图:

    ;WITH PriceCompare AS (
    SELECT i.Item, ph.ItemId, ph.PriceStartDate, ph.Price,
    ROW_NUMBER() OVER (Partition BY ph.ItemId ORDER BY PriceStartDate) AS rownum 
    FROM Items i INNER JOIN PriceHistory ph 
    ON i.ItemId = ph.ItemId) 
     
    SELECT * FROM PriceCompare 
     
    结果是:
    Item ItemId PriceStartDate Price rownum
    vacuum cleaner 1 a2004-03-01 250.00 1
    vacuum cleaner 1 2005-06-15 219.99 2
    vacuum cleaner 1 2007-01-03 189.99 3
    vacuum cleaner 1 2007-02-03 200.00 4
    washing machine 2 2006-07-12 650.00 1
    washing machine 2 2007-01-03 550.00 2
    toothbrush 3 2005-01-01 1.99 1
    toothbrush 3 2006-01-01 1.79 2
    toothbrush 3 2007-01-01 1.59 3
    toothbrush 3 2008-01-01 1.49 4

    然后增加Select主句:

    SELECT currow.Item, prevrow.Price AS OldPrice, currow.Price AS RangePrice, currow.PriceStartDate AS StartDate, nextrow.PriceStartDate AS EndDate 
    FROM PriceCompare currow 
    LEFT JOIN PriceCompare nextrow 
            ON currow.rownum = nextrow.rownum - 1
            AND currow.ItemId = nextrow.ItemId
    LEFT JOIN PriceCompare prevrow
            ON currow.rownum = prevrow.rownum + 1
            AND currow.ItemId = prevrow.ItemId

    请注意,我使用的第一行Left Join 为一个项有没有以前的行,就像最后一行没有下一行。

    最后我们把写成一个完整的视图吧:

    CREATE VIEW [dbo].[PriceCompare] AS
    WITH PriceCompare AS 
    (
    SELECT i.Item, ph.ItemId, ph.PriceStartDate, ph.Price, 
    ROW_NUMBER() OVER (Partition BY ph.ItemId ORDER BY PriceStartDate) AS rownum 
    FROM 
            Items i 
    INNER JOIN 
            PriceHistory ph 
    ON i.ItemId = ph.ItemId
    )
     
     SELECT
            currow.Item, 
            prevrow.Price AS OldPrice, 
            currow.Price AS RangePrice, 
            currow.PriceStartDate AS StartDate, 
            nextrow.PriceStartDate AS EndDate 
    FROM 
            PriceCompare currow 
    LEFT JOIN PriceCompare nextrow 
            ON currow.rownum = nextrow.rownum - 1 AND currow.ItemId = nextrow.ItemId 
    LEFT JOIN PriceCompare prevrow 
            ON currow.rownum = prevrow.rownum + 1 AND currow.ItemId = prevrow.ItemId

    实现CTE后让T-SQL如此简洁.这项的场景很多的,例如我们需要存储某一个值历史记录,也可以用这种DB设计方式来实现.
    相比传统建立历史表还需要一个结束时间,要少至少一个字段.

    希望对您的开发有帮助!


    作者:Petter Liu
    出处:http://www.cnblogs.com/wintersun/
    本文版权归作者和博客园共有,欢迎转载,但未经作者同意必须保留此段声明,且在文章页面明显位置给出原文连接,否则保留追究法律责任的权利。
    该文章也同时发布在我的独立博客中-Petter Liu Blog

  • 相关阅读:
    云原生生态周报 Vol. 16 | CNCF 归档 rkt,容器运行时“上古”之战老兵凋零
    Knative 基本功能深入剖析:Knative Eventing 之 Sequence 介绍
    基于 K8s 做应用发布的工具那么多, 阿里为啥选择灰姑娘般的 Tekton ?
    Serverless 的喧哗与骚动(一)附Serverless行业发展回顾
    239. Sliding Window Maximum
    237. Delete Node in a Linked List
    146. LRU Cache
    140. Word Break II
    165. Compare Version Numbers
    258. Add Digits
  • 原文地址:https://www.cnblogs.com/wintersun/p/1824542.html
Copyright © 2011-2022 走看看