TSQL选择按密钥分组的最近的非未来日期范围

时间:2012-06-19 09:53:27

标签: sql sql-server sql-server-2008 tsql

以下是PRICES的简化表格结构:

SKU      PriceType    FromDate      ToDate        Price
..............................................................
SUR40    NONMEMBER    1900-01-01    1900-01-01    1000
SUR40    RSP          1900-01-01    1900-01-01    1500
SUR40    MEMBER       2012-07-04    2012-07-04    649
SUR40    MEMBER       2012-06-15    2012-06-15    699
SUR40    MEMBER       2012-06-01    2012-06-01    599
SUR40    MEMBER       2012-03-31    2012-03-31    699
SUR40    MEMBER       1900-01-01    1900-01-01    749

PRICES表存储了所有产品的所有价格。 FromDateToDate列指定价格应生效的期间。如果在指定日期没有促销,则空日期(返回为1900-01-01)是默认价格。

鉴于SKU和日期,查询应返回适用于该日期的产品价格。例如,使用2012-06-16选择应返回:

SUR40    NONMEMBER    1900-01-01    1900-01-01    1000
SUR40    RSP          1900-01-01    1900-01-01    1500
SUR40    MEMBER       1900-01-01    1900-01-01    749

使用2012-06-15选择应返回:

SUR40    NONMEMBER    1900-01-01    1900-01-01    1000
SUR40    RSP          1900-01-01    1900-01-01    1500
SUR40    MEMBER       2012-06-15    2012-06-15    699

SQL Server是MS SQL 2008 R2。自从我写完最后一个SQL查询以来已经有一段时间了,我似乎无法理解这一点。 :(

非常感谢任何帮助。这是我到目前为止所提出的:

select SKU, PriceType, FromDate, ToDate, Price from PRICES
where SKU IN ('SUR40')
and PriceType IN ('NONMEMBER','RSP','MEMBER')
and FromDate < GETDATE()
order by PriceType, FromDate DESC

我认为某处应该有一个group by,但是使用一个在失败时不返回错误消息的Web服务并不是很有帮助:(

3 个答案:

答案 0 :(得分:2)

示例数据:

DECLARE @PRICES TABLE (
    SKU nvarchar(10),
    PriceType nvarchar(10),
    FromDate date,
    ToDate date,
    Price int
)

INSERT @PRICES VALUES 
('SUR40'  ,  'NONMEMBER' ,   '1900-01-01'  ,  '1900-01-01'  ,  1000 ) ,
('SUR40'  ,  'RSP'        ,  '1900-01-01'  ,  '1900-01-01'  ,  1500 ) ,
('SUR40'  ,  'MEMBER'    ,   '2012-07-04'  ,  '2012-07-04'  ,  649  ) ,
('SUR40'  ,  'MEMBER'   ,    '2012-06-15'  ,  '2012-06-15' ,   699  ) ,
('SUR40'  ,  'MEMBER'   ,    '2012-06-01'  ,  '2012-06-01' ,   599  ) ,
('SUR40'  ,  'MEMBER'    ,   '2012-03-31'  ,  '2012-03-31'  ,  699  ) ,
('SUR40'  ,  'MEMBER'     ,  '1900-01-01'  ,  '1900-01-01'   , 749  )

查询参数:

DECLARE @SKU nvarchar(10)
DECLARE @Date date

SET @SKU = 'SUR40'
SET @Date = '2012-06-15'

查询:

SELECT * FROM 
    (
    SELECT
        SKU,
        PriceType,
        FromDate,
        ToDate,
        Price,
        ROW_NUMBER() 
            OVER (
                PARTITION BY SKU, PriceType
                ORDER BY 
                CASE 
                    WHEN (FromDate = '1900-01-01' 
                      AND ToDate = '1900-01-01') THEN 1 
                    ELSE 0
                END ASC) rn
    FROM @PRICES
    WHERE 
        SKU = @SKU
        AND (
            (FromDate = '1900-01-01' AND ToDate = '1900-01-01')
            OR
            (FromDate <= @Date AND @Date <= ToDate)
            )
    ) Raw
WHERE rn = 1

解释,由内而外:

  • 对于每个SKU和价格类型,我们对后备行和日期匹配行感兴趣(如果有)
  • 我们希望更喜欢日期匹配行,因此我们使用ROW_NUMBER()和合适的PARTITIONORDER BY子句来排序前面的日期匹配行(如果有的话)后备行
  • 这是内部Raw查询
  • 然后我们只选择Raw中有rownumber 1的所有行。这将是它们存在的日期匹配行,或者没有日期匹配行的后备行。

答案 1 :(得分:2)

虽然您的第二个查询似乎有错误(虽然日期为`1900-01-01,但您已省略了价格为749的记录),我认为您希望始终返回1900-01-01个日期:

select SKU, PriceType, FromDate, ToDate, Price 
from PRICES
where SKU='SUR40'
and PriceType IN ('NONMEMBER','RSP','MEMBER')
and ((FromDate='1900-01-01' AND ToDate='1900-01-01')
    OR 
     (FromDate>='2012-06-15' AND ToDate<='2012-06-15')
    )
order by PriceType, FromDate DESC

当然你应该使用上面的参数而不是常数值。

这是SQL小提琴:http://sqlfiddle.com/#!3/18cf7/4

答案 2 :(得分:0)

您不需要分组,只需要在日期中添加限制:

select SKU, PriceType, FromDate, ToDate, Price from PRICES
where SKU IN ('SUR40')
and PriceType IN ('NONMEMBER','RSP','MEMBER')
and (FromDate = '1900-01-01' or @dateToProcess between FromDate and ToDate)
order by PriceType, FromDate DESC

@dateToProcess将是2012-06-16或2012-06-15等。