2008-12-18 94 views
4

如何在具有稀疏日期数的表格和具有详尽数量日期的另一个表格之间进行联接,以便稀疏日期之间的差距取前一个稀疏日期的值?需要SQL中复杂联接语句的帮助

说明性的例子:

PRICE table (sparse dates): 
date  itemid price 
2008-12-04 1  $1 
2008-12-11 1  $3 
2008-12-15 1  $7 


VOLUME table (exhaustive dates): 
date   itemid volume_amt 
2008-12-04 1  12345 
2008-12-05 1  23456 
2008-12-08 1  34567 
2008-12-09 1  ... 
2008-12-10 1 
2008-12-11 1 
2008-12-12 1 
2008-12-15 1 
2008-12-16 1 
2008-12-17 1 
2008-12-18 1 

期望的结果:

date  price volume_amt 
2008-12-04 $1  12345 
2008-12-05 $1  23456 
2008-12-08 $1  34567 
2008-12-09 $1  ... 
2008-12-10 $1 
2008-12-11 $3 
2008-12-12 $3 
2008-12-15 $7 
2008-12-16 $7 
2008-12-17 $7 
2008-12-18 $7 

更新:

一对夫妇的人建议相关子查询是实现期望的结果。 (相关子查询=包含对外部查询引用的子查询。)

这将工作;然而,我应该注意到我使用的平台是MySQL,相关子查询的优化程度很差。任何不使用相关子查询的方法?

+0

你可以编辑标题或删除并重新开始? – 2008-12-18 17:23:03

+0

看起来像别人做的。是的,没有在那里注意。 – 2008-12-18 17:26:50

+0

需要输入的列名和所需的输出。现在这个问题的措辞是不明确的 – 2008-12-18 17:26:51

回答

7

这并不像一个LEFT OUTER JOIN到疏表那么简单,因为你想通过外留下的NULL加入到充满最近的价格。

EXPLAIN SELECT v.`date`, v.volume_amt, p1.item_id, p1.price 
FROM Volume v JOIN Price p1 
    ON (v.`date` >= p1.`date` AND v.item_id = p1.item_id) 
LEFT OUTER JOIN Price p2 
    ON (v.`date` >= p2.`date` AND v.item_id = p2.item_id 
    AND p1.`date` < p2.`date`) 
WHERE p2.item_id IS NULL; 

此查询将Volume匹配到Price中较早的所有行,然后使用另一个连接来确保我们只找到最近的价格。

我在MySQL 5.0.51上测试了这个。它既不使用相关的子查询也不使用group by。

编辑:已更新查询以匹配item_id以及日期。这似乎也起作用。我在(date)上创建了一个索引,在(date, item_id)上创建了一个索引,并且EXPLAIN计划是相同的。在这种情况下,(item_id, date)上的索引可能会更好。这里的解释输出为:

+----+-------------+-------+------+---------------+---------+---------+-----------------+------+--------------------------------------+ 
| id | select_type | table | type | possible_keys | key  | key_len | ref    | rows | Extra        | 
+----+-------------+-------+------+---------------+---------+---------+-----------------+------+--------------------------------------+ 
| 1 | SIMPLE  | p1 | ALL | item_id  | NULL | NULL | NULL   | 6 |          | 
| 1 | SIMPLE  | v  | ref | item_id  | item_id | 22  | test.p1.item_id | 3 | Using where       | 
| 1 | SIMPLE  | p2 | ref | item_id  | item_id | 22  | test.v.item_id | 1 | Using where; Using index; Not exists | 
+----+-------------+-------+------+---------------+---------+---------+-----------------+------+--------------------------------------+ 

但我有一个非常小的数据集,并且优化可能取决于较大的数据集。您应该尝试使用更大的数据集来分析优化。

编辑:我之前粘贴了错误的EXPLAIN输出。上面的一个已更正,并更好地使用(item_id, date)索引。

2
SELECT v.date, p.price, v.volume 
FROM volume v 
LEFT JOIN Price p ON p.itemID=v.itemID 
    AND p.[date] = (
        SELECT MAX([date]) 
        FROM price p2 
        WHERE p2.[date] <= v.[date] AND p2.itemid= v.itemid 
        GROUP BY p2.[date] 
        ) 
+0

将无法​​正常工作 - 将仅在有新价格时返回数量。期望的输出是该卷应当返回当天的价格或者具有新价格的最近的前一天。 – 2008-12-18 17:35:34

0
SELECT Volume.date, volume.itemid, price.price, volume.volume_amt 
FROM Volume 
LEFT OUTER JOIN Price 
ON Volume.date = Price.date 

可能。我的SQL福弱

3

假设有每个日期只有1价格/为itemid:

select v.date, v.itemid, p.price 
from volume v 
join price p on p.itemid = v.item_id 
where p.date = (select max(p2.date) from price p2 
       where p2.itemid = v.itemid 
       and p2.date <= v.date); 
0

这种方法在甲骨文工作。不知道其他数据库,你没有指定。如果这个确切的语法在你的数据库中不起作用,我猜想有类似的技术。

dev> select * from price; 

AS_OF    ID  AMOUNT 
----------- ---------- ---------- 
04-Dec-2008   1   1 
11-Dec-2008   1   2 
15-Dec-2008   1   3 

dev> select * from volume; 

DAY     ID  VOLUME 
----------- ---------- ---------- 
05-Dec-2008   1   1 
06-Dec-2008   1   2 
07-Dec-2008   1   3 
08-Dec-2008   1   4 
09-Dec-2008   1   5 
10-Dec-2008   1   6 
11-Dec-2008   1   7 
12-Dec-2008   1   8 
13-Dec-2008   1   9 
14-Dec-2008   1   10 
15-Dec-2008   1   11 
16-Dec-2008   1   12 
17-Dec-2008   1   13 
18-Dec-2008   1   14 
19-Dec-2008   1   15 
20-Dec-2008   1   16 
21-Dec-2008   1   17 
22-Dec-2008   1   18 
23-Dec-2008   1   19 

dev> select day, volume, amount from (
    2 select day, volume, (select max(as_of) from price p where p.id = v.id and as_of <= day) price_as_of 
    3  from volume v 
    4 ) 
    5 join price on as_of = price_as_of 
    6 order by day; 

DAY    VOLUME  AMOUNT 
----------- ---------- ---------- 
05-Dec-2008   1   1 
06-Dec-2008   2   1 
07-Dec-2008   3   1 
08-Dec-2008   4   1 
09-Dec-2008   5   1 
10-Dec-2008   6   1 
11-Dec-2008   7   2 
12-Dec-2008   8   2 
13-Dec-2008   9   2 
14-Dec-2008   10   2 
15-Dec-2008   11   3 
16-Dec-2008   12   3 
17-Dec-2008   13   3 
18-Dec-2008   14   3 
19-Dec-2008   15   3 
20-Dec-2008   16   3 
21-Dec-2008   17   3 
22-Dec-2008   18   3 
23-Dec-2008   19   3