需要有关 SQL 中复杂 Join 语句的帮助
Posted
技术标签:
【中文标题】需要有关 SQL 中复杂 Join 语句的帮助【英文标题】:Need help with a complex Join statement in SQL 【发布时间】:2010-09-27 13:43:23 【问题描述】:如何将一个日期数稀疏的表与另一个日期数详尽的表连接起来,以使稀疏日期之间的间隔采用前一个稀疏日期的值?
示例:
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,其相关子查询的优化很差。有什么方法可以在不使用相关子查询的情况下做到这一点?
【问题讨论】:
你可以编辑标题或删除并重新开始吗? 看起来是别人做的。是的,没注意那里。 需要输入的列名和所需的输出。现在这个问题的措辞相当模棱两可 添加了列名,所需的输出已经存在 【参考方案1】:这并不像稀疏表的单个 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 上对此进行了测试。它既不使用相关子查询也不使用分组依据。
编辑:更新了查询以匹配 item_id 和日期。这似乎也有效。我在(date)
上创建了一个索引,在(date, item_id)
上创建了一个索引,EXPLAIN 计划是相同的。在这种情况下,(item_id, date)
上的索引可能会更好。这是 EXPLAIN 的输出:
+----+-------------+-------+------+---------------+---------+---------+-----------------+------+--------------------------------------+
| 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)
索引的更好使用。
【讨论】:
到目前为止看起来不错,如果我们假设可能有多个可能的 item_id 怎么办,只需将 v.item_id=p1.item_id 和 v.item_id=p2.item_id 放在 on 子句中,对吗?另外,如果您真的愿意,最好在 (item_id, date) 或 (date, item_id) 上添加索引?【参考方案2】:假设每个日期/项目只有 1 个价格:
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);
【讨论】:
与我的答案相同,但我承认即使我的答案显示我发布得更早,您也能更快地做对。 其实我认为这包含一个错误:p.date条件属于join条件,而不是where子句。 在这种情况下不是,但如果您正在执行外部连接,它会。 我对 where 或 on 子句中的 p.date 比较的评论不正确;没什么区别。【参考方案3】: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]
)
【讨论】:
不会起作用——只会在有新价格的日子返回交易量。期望的输出是当日价格或最近的前一天有新价格时应返回的交易量。【参考方案4】: SELECT Volume.date, volume.itemid, price.price, volume.volume_amt
FROM Volume
LEFT OUTER JOIN Price
ON Volume.date = Price.date
大概吧。我的 SQL-fu 很弱
【讨论】:
不,这会让他在没有完全匹配的行中出现价格空白。 啊,明白了。我们正在做股票交易或其他事情,并且想要最后交易价格。 【参考方案5】:此方法适用于 Oracle。不了解其他数据库,您也没有指定。如果这个确切的语法在你的数据库中不起作用,我猜有类似的技术。
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
【讨论】:
以上是关于需要有关 SQL 中复杂 Join 语句的帮助的主要内容,如果未能解决你的问题,请参考以下文章