从组记录中选择前N个

时间:2015-07-01 23:46:26

标签: sql sql-server

我需要在价格变化时列出frist记录。使用下面的例子。

发票表记录(按客户分组价格并按日期排序)

Index  Customer Date         Price
01     A        May-01-2016  $12.00
02     A        May-11-2016  $12.00

03     A        May-21-2016  $13.00
04     A        May-22-2016  $13.00
05     A        May-23-2016  $13.00
06     A        May-24-2016  $13.00

07     A        Jun-01-2016  $14.00
08     A        Jun-11-2016  $14.00
09     A        Jun-21-2016  $14.00
10     A        Jun-25-2016  $14.00

11     B        May-02-2016  $12.50
12     B        May-12-2016  $12.50

13     B        May-22-2016  $13.50

14     B        May-24-2016  $13.80
15     B        May-26-2016  $13.80
16     B        May-28-2016  $13.80

17     B        Jun-02-2016  $14.60
18     B        Jun-12-2016  $14.60
19     B        Jun-22-2016  $14.60
20     B        Jun-26-2016  $14.60

我需要在价格变化时获得第一条记录。结果将是:

01     A        May-01-2016  $12.00

03     A        May-21-2016  $13.00

07     A        Jun-01-2016  $14.00

11     B        May-02-2016  $12.50

13     B        May-22-2016  $13.50

14     B        May-24-2016  $13.80

17     B        Jun-02-2016  $14.60

除了查询之外,我可以将结果限制为每个客户的最新2条记录吗?结果将是:

03     A        May-21-2016  $13.00

07     A        Jun-01-2016  $14.00

14     B        May-24-2016  $13.80

17     B        Jun-02-2016  $14.60

谢谢。

3 个答案:

答案 0 :(得分:1)

第一部分:

SELECT id, customer, dte, price
  FROM (SELECT id, customer, dte, price
              ,COALESCE(LAG(price) OVER(PARTITION BY customer ORDER BY id), price - 1) AS prev_price
          FROM invoice) x
  WHERE x.price <> x.prev_price
  ORDER BY x.id

第二部分:

SELECT id, customer, dte, price
  FROM (SELECT id, customer, dte, price
              ,ROW_NUMBER() OVER (PARTITION BY customer ORDER BY id DESC) AS row_num
          FROM (SELECT id, customer, dte, price
                      ,COALESCE(LAG(price) OVER (PARTITION BY customer ORDER BY id), price - 1) AS prev_price
                  FROM invoice) x
          WHERE x.price <> x.prev_price) y
  WHERE row_num <= 2
  ORDER BY id

答案 1 :(得分:1)

;WITH CTE AS (
SELECT * , 
  CAST(SUBSTRING([Date], 5,2) + '-' 
                + LEFT([Date] , 3) 
                + '-' + RIGHT([Date] ,4) AS DATE) AS DateDt
FROM Invoice)
,CTE2 AS 
(
SELECT *
     ,ROW_NUMBER() OVER (PARTITION BY Price , Customer ORDER BY DateDt ASC) rn
FROM CTE
)
SELECT [Index] ,Customer, [Date] ,Price
FROM CTE2
where rn = 1
ORDER BY Customer , DateDt

结果集

╔═══════╦══════════╦═════════════╦═══════╗
║ Index ║ Customer ║    Date     ║ Price ║
╠═══════╬══════════╬═════════════╬═══════╣
║    01 ║ A        ║ May-01-2016 ║ 12.00 ║
║    03 ║ A        ║ May-21-2016 ║ 13.00 ║
║    07 ║ A        ║ Jun-01-2016 ║ 14.00 ║
║    11 ║ B        ║ May-02-2016 ║ 12.50 ║
║    13 ║ B        ║ May-22-2016 ║ 13.50 ║
║    14 ║ B        ║ May-24-2016 ║ 13.80 ║
║    17 ║ B        ║ Jun-02-2016 ║ 14.60 ║
╚═══════╩══════════╩═════════════╩═══════╝

答案 2 :(得分:0)

假设日期列在某个有效的日期格式中(否则你应该将其转换),这是另一种方法:

;with cte as (
    select 
       Invoice.[index], Invoice.Customer, Invoice.Date, Invoice.Price, 
       rn = row_number() over (partition by Invoice.customer order by date desc) 
    from Invoice
    join (
       select Customer, price, min(date) min_date 
       from Invoice 
       group by Customer, price
    ) t1 on Invoice.Customer = t1.Customer and Invoice.Date = t1.min_date
)
select [index], Customer, date, price 
from cte where rn <= 2 order by [index]

此外, index 是一个保留关键字,因此对于列名称来说是一个糟糕的选择。

使用您的样本数据,输出将是:

index   Customer    date        price
3       A           2016-05-21  $13.00
7       A           2016-06-01  $14.00
14      B           2016-05-24  $13.80
17      B           2016-06-02  $14.60

Sample SQL Fiddle

如果您正在使用SQL Server 2012+,则可以使用窗口化聚合函数并执行此查询,这可能更高效(或不):

select [index], Customer, date, price 
from (
    select *, rn = dense_rank() over (partition by customer order by min_date desc) 
    from (select *, min(date) over (partition by customer, price) min_date from Invoice) a
) b where rn <= 2 and Date = min_date order by [index]