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

Можете ли вы отредактировать заголовок или удалить и начать заново?

Tad Donaghe 18.12.2008 20:23

похоже, это сделал кто-то другой. да, не обращал на это внимания.

ʞɔıu 18.12.2008 20:26

нужны имена столбцов ввода и желаемого вывода. Сейчас формулировка вопроса довольно неоднозначна.

Martijn Laarman 18.12.2008 20:26

добавлены имена столбцов, желаемый результат уже присутствовал

ʞɔıu 18.12.2008 20:32
ReactJs | Supabase | Добавление данных в базу данных
ReactJs | Supabase | Добавление данных в базу данных
Это и есть ваш редактор таблиц в supabase.👇
Понимание Python и переход к SQL
Понимание Python и переход к SQL
Перед нами лабораторная работа по BloodOath:
3
4
2 799
5
Перейти к ответу Данный вопрос помечен как решенный

Ответы 5

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]
                   )

не сработает - вернет объем только в те дни, когда будет новая цена. желаемый результат - объем должен быть возвращен по цене того дня или ближайшего предыдущего дня, для которого есть новая цена.

ʞɔıu 18.12.2008 20:35
 SELECT  Volume.date, volume.itemid, price.price, volume.volume_amt
 FROM Volume 
 LEFT OUTER JOIN Price
 ON Volume.date = Price.date

Наверное. Мой SQL-фу слабый

Нет, это даст ему пробелы по цене в строках, которые не имеют точных совпадений.

Dave Costa 18.12.2008 20:35

Ах, попался. Мы проводим торги акциями или что-то в этом роде, и нам нужна цена последней сделки. <думает>

The Archetypal Paul 18.12.2008 20:37

Предполагая, что существует только 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);

Тот же ответ, что и мой, но я признаю, что вы получили его быстрее, даже если мой показывает, что я опубликовал раньше.

Joel Coehoorn 18.12.2008 20:39

на самом деле я считаю, что это содержит ошибку: условие p.date принадлежит условию соединения, а не предложению where.

ʞɔıu 18.12.2008 20:49

Не в этом случае, но было бы, если бы вы выполняли внешнее соединение.

Bill Karwin 18.12.2008 20:55

мой комментарий о сравнении p.date, относящемся к пунктам where или on, был неправильным; это не имеет значения.

ʞɔıu 18.12.2008 21:06

Этот метод работает в 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)?

ʞɔıu 18.12.2008 21:14

Другие вопросы по теме