SQL加入挑战

时间:2016-08-10 16:29:45

标签: sql sqlite join subquery

好的,所以我已经坚持了2天!我从语义的角度来解决它,但查询可能需要10分钟才能执行。我选择的数据库是SQLite(由于我不想在此详细说明),但我尝试在SQL Server 2012上运行相同的东西,但它在性能方面没有太大差别。

所以,问题是我有2个表

  • prices(product_id INT,for_date DATE,value INT)
  • events(START_on DATE,ends_on DATE NULLABLE)

我在价格表中有大约500K行,在事件表中有大约100行。

现在我需要编写一个查询来执行以下操作。

伪代码是:

  • 每个活动:
    • 如果事件具有ends_on值,则获取具有匹配的for_date的所有product_id,对于不匹配的产品,然后获取最后一个for_date,该for_date小于ends_on值但大于该事件的starts_on。 / LI>
    • ELSE如果事件的ends_on日期为NULL,那么获取所有具有与starts_on匹配的for_date的product_id,对于那些不匹配的产品,获取最后一个小于starts_on值的for_date。

我在SQL Server 2012中编写的查询是

SELECT    
    sp.for_date, sp.value
FROM 
    prices sp
INNER JOIN 
    events ev ON (((ev.ends_on IS NOT NULL AND
                   (sp.for_date = (SELECT for_date
                                   FROM prices
                                   WHERE for_date <= ev.ends_on 
                                      AND for_date > ev.starts_on
                                   ORDER BY for_date DESC
                                      OFFSET 0 ROWS
                                      FETCH NEXT 1 ROWS ONLY))))
          OR
          ((ev.ends_on is null 
          and
             (sp.for_date = (SELECT for_date
                                FROM prices
                                WHERE 
                                    for_date <= ev.starts_on_j
                                    AND for_date > dateadd(day, -14,   ev.starts_on)
                                order by for_date desc
                                offset 0 rows
                                    fetch next 1 row only))))

                                    );

顺便说一下,我还尝试使用部分数据创建临时表,并对它们执行相同的操作。它只是卡住了。

奇怪的是,如果我单独运行2个“OR”条件,响应时间就完美了!

更新

示例数据集和预期结果

价格条目

Product ID, ForDt, Value
1, 25-01-2010, 123
1, 26-01-2010, 112
1, 29-01-2010, 334
1, 02-02-2010, 512
1, 03-02-2010, 765
1, 04-02-2010, 632
1, 05-02-2010, 311
1, 06-02-2010, 555
2, 03-02-2010, 854
2, 04-02-2010, 625
2, 05-02-2010, 919
3, 20-01-2010, 777
3, 06-02-2010, 877
3, 10-03-2010, 444
3, 11-03-2010, 888

活动参赛作品(为了使其更易理解,我也添加了一个活动ID)

Event ID, StartsOn, EndsOn
22, 27-01-2010, NULL
33, 02-02-2010, 06-02-2010
44, 01-03-2010, 13-03-2010

预期结果集

Event ID, Product ID, ForDt, Value
22, 1, 26-01-2010, 112
33, 1, 06-02-2010, 311
44, 1, 06-02-2010, 311

33, 2, 05-02-2010, 919
44, 2, 05-02-2010, 919

22, 3, 20-01-2010, 777
33, 3, 06-02-2010, 877
44, 3, 11-03-2010, 888

1 个答案:

答案 0 :(得分:1)

好的,既然您已经将预期结果显示为事件列表和相关产品,那么这个问题就有意义了。您的查询仅选择日期和值。

您正在寻找每个活动的最佳产品价格记录。这可以通过分析函数轻松完成,但SQLite不支持它们。所以我们必须写一个更复杂的查询。

让我们首先看一下ends_on null的事件。以下是如何找到最佳产品价格(即starts_on之前的最后一次):

select e.event_id, p.product_id, max(for_date) as best_for_date
from events e
join prices p on p.for_date < e.starts_on
where e.ends_on is null
group by e.event_id, p.product_id;

我们扩展此查询以查找具有ends_on的事件的最佳产品价格,然后再次访问products表格,以便我们获得包含值的完整记录:

select ep.event_id, p.product_id, p.for_date, p.value
from
(
  select e.event_id, p.product_id, max(for_date) as best_for_date
  from events e
  join prices p on (e.ends_on is null and p.for_date < e.starts_on)
                or (e.ends_on is not null and p.for_date between e.starts_on and e.ends_on)
  group by e.event_id, p.product_id
) ep
join prices p on p.product_id = ep.product_id and p.for_date = ep.best_for_date;

(顺便说一句:你在这里描述了一个非常特殊的案例。到目前为止我看到的数据库会将ends_on null视为无限制或“仍然有效”。因此要检索的价格对于这样的事件,不会是 starts_on之前的最后一个,而是 starts_on之后或之后的最新。)