SQL根据日期

时间:2017-01-05 06:55:11

标签: sql oracle date

我有一个包含四列的表:id,validFrom,validTo和price。 此表包含商品的价格以及该价格生效的持续时间。

| id| validFrom |  validTo  | price
|---|-----------|-----------|---------  
| 1 | 01-01-17  | 10-01-17  | 30000   
| 1 | 04-01-17  | 09-01-17  | 20000  

现在,对于我的表中的这个输入,我的查询输出应该是:

| id| validFrom | validTo  | price  
|---|-----------|----------|-------
| 1 | 01-01-17  | 03-01-17 | 30000  
| 1 | 04-01-17  | 09-01-17 | 20000  
| 1 | 10-01-17  | 10-01-17 | 30000  

我可以比较日期并检查具有相同ID的产品是否具有重叠日期,但我不知道如何将这些日期拆分为非重叠日期。我也不允许使用PL / SQL 这只能使用SQL吗?

1 个答案:

答案 0 :(得分:3)

Oracle安装程序

String[]

<强>查询

CREATE TABLE prices ( id, validFrom, validTo, price ) AS
  SELECT 1, DATE '2017-01-01', DATE '2017-01-10', 30000 FROM DUAL UNION ALL
  SELECT 1, DATE '2017-01-04', DATE '2017-01-09', 20000 FROM DUAL UNION ALL
  SELECT 1, DATE '2017-01-11', DATE '2017-01-15', 10000 FROM DUAL UNION ALL
  SELECT 1, DATE '2017-01-16', DATE '2017-01-18', 15000 FROM DUAL UNION ALL
  SELECT 1, DATE '2017-01-17', DATE '2017-01-20', 40000 FROM DUAL UNION ALL
  SELECT 1, DATE '2017-01-21', DATE '2017-01-24', 28000 FROM DUAL UNION ALL
  SELECT 1, DATE '2017-01-23', DATE '2017-01-26', 23000 FROM DUAL UNION ALL
  SELECT 1, DATE '2017-01-26', DATE '2017-01-26', 17000 FROM DUAL;

<强>输出

WITH daily_prices ( id, dt, price, duration ) AS (
  -- Unroll the price ranges to individual days
  SELECT id,
         d.COLUMN_VALUE,
         price,
         validTo - validFrom
  FROM   prices p,
         TABLE(
           CAST(
             MULTISET(
               SELECT p.validFrom + LEVEL - 1
               FROM   DUAL
               CONNECT BY p.validFrom + LEVEL - 1 <= p.validTo
             )
             AS SYS.ODCIDATELIST
           )
         ) d
),
min_daily_prices ( id, dt, price ) AS (
  -- Where a day falls between multiple ranges group them so the price
  -- is for the shortest duration offer and if there are two equally short
  -- durations then take the minimum price
  SELECT id,
         dt,
         MIN( price ) KEEP ( DENSE_RANK FIRST ORDER BY duration )
  FROM   daily_prices
  GROUP BY id, dt
),
group_changes ( id, dt, price, has_changed_group ) AS (
  -- Find when the price changes or a day is skipped which means a new price
  -- group is beginning
  SELECT id,
         dt,
         price,
         CASE WHEN dt    = LAG( dt    ) OVER ( PARTITION BY id ORDER BY dt ) + 1
              AND  price = LAG( price ) OVER ( PARTITION BY id ORDER BY dt )
              THEN 0
              ELSE 1
              END
  FROM   min_daily_prices
),
groups ( id, dt, price, grp ) AS (
  -- Calculate unique indexes (per id) for each group of price ranges
  SELECT id,
         dt,
         price,
         SUM( has_changed_group ) OVER ( PARTITION BY id ORDER BY dt )
  FROM   group_changes
)
SELECT id,
       MIN( dt ) AS validFrom,
       MAX( dt ) AS validTo,
       MIN( price ) AS price
FROM   groups
GROUP BY id, grp
ORDER BY id, validFrom;