Как вы можете объединить таблицу с редким количеством дат и другую таблицу с исчерпывающим количеством дат, чтобы промежутки между разреженными датами принимали значения предыдущей разреженной даты?
Наглядный пример:
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, для которой коррелированные подзапросы плохо оптимизированы. Есть ли способ сделать это без использования коррелированного подзапроса?
похоже, это сделал кто-то другой. да, не обращал на это внимания.
нужны имена столбцов ввода и желаемого вывода. Сейчас формулировка вопроса довольно неоднозначна.
добавлены имена столбцов, желаемый результат уже присутствовал


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]
)
не сработает - вернет объем только в те дни, когда будет новая цена. желаемый результат - объем должен быть возвращен по цене того дня или ближайшего предыдущего дня, для которого есть новая цена.
SELECT Volume.date, volume.itemid, price.price, volume.volume_amt
FROM Volume
LEFT OUTER JOIN Price
ON Volume.date = Price.date
Наверное. Мой SQL-фу слабый
Нет, это даст ему пробелы по цене в строках, которые не имеют точных совпадений.
Ах, попался. Мы проводим торги акциями или что-то в этом роде, и нам нужна цена последней сделки. <думает>
Предполагая, что существует только 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 принадлежит условию соединения, а не предложению where.
Не в этом случае, но было бы, если бы вы выполняли внешнее соединение.
мой комментарий о сравнении p.date, относящемся к пунктам where или on, был неправильным; это не имеет значения.
Этот метод работает в 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
Это не так просто, как одиночное 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)?
Можете ли вы отредактировать заголовок или удалить и начать заново?