powered by simpleCommunicator - 2.0.59     © 2025 Programmizd 02
Целевая тема:
Создать новую тему:
Автор:
Закрыть
Цитировать
Форумы / Проектирование БД [игнор отключен] [закрыт для гостей] / Нарастающие в обоих направлениях итоги
15 сообщений из 15, страница 1 из 1
Нарастающие в обоих направлениях итоги
    #39236208
.det
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Гость
Попалась довольно интересная задача на DB2 на магистратуре, решил ее двумя вариантами, но не знаю, какой из них лучше, и возможно я вообще изобретаю велосипед и есть другие варианты проще.

Биржа акций.

Покупатели назначают максимальную цену за акцию (limit), за которую готовы купить, и количество (buy_c).
Продавцы назначают минимальную цену за акцию (limit), за которую согласны продать, и количество (sell_c).
Необходимо составить сводную таблицу: по каждому лимиту сколько акций может быть продано и сколько может быть куплено.

Для упрощения задачи предположим, что исходные данные содержатся в следующего вида таблице
Код: plaintext
trade (buy_c, limit, sell_c)

Для примера возьмем данные:
Код: plaintext
1.
2.
3.
4.
buy_c	limit	sell_c
------	------	------
1	30$	2
3	20$	5
7	10$	11

Но эта таблица не учитывает, что при наличии покупателя, готового купить за более высокую цену, продавец может продать дороже и, следовательно, больше.
То есть: продавец за 20$ на самом деле может продать 3+1=4 товара: покупатель, готовый купить товар за 30$, купит и за 20$. Поэтому sell_c для 20$ должен быть 4: своего рода получается накопительный итог сверху вниз.
И наоборот: готовый купить за 20$ может купить не только 5 акций за эту цену, но и 11 более дешевых. Здесь накопительный итог идет снизу вверх.

Для исходных данных ответ должен быть таким:
Код: plaintext
1.
2.
3.
4.
buy_c	limit	sell_c
------	------	------
1	30	18
4	20	16
11	10	11

Вариант решения в лоб, с двумя JOIN исходной таблицы с самой собой не работает. Когда таблицы две - все ОК, но когда появляется третья, итог считается для каждой из возможных комбинаций и в итоге цифры увеличиваются в несколько раз.

Код: sql
1.
2.
3.
4.
5.
6.
7.
8.
9.
SELECT 
	sum(b.buy_c), 
	t.limit, 
	sum(s.sell_c)
FROM trade t
		LEFT JOIN trade b ON (b.limit >= t.limit) /* щедные покупатели */
		LEFT JOIN trade s ON (s.limit <= t.limit) /* щедрые продавцы */
GROUP BY t.limit
ORDER BY t.limit DESC



Рабочий вариант 1: соединить таблицу с самой собой, а JOIN-предикаты перенести в агрегатные функции
Плюсы: короткий, читабельный, отлично scale-ится

Код: sql
1.
2.
3.
4.
5.
6.
7.
SELECT 
	sum(CASE WHEN b.limit >= t.limit THEN b.buy_c ELSE 0 END), /* щедные покупатели */
	t.limit, 
	sum(CASE WHEN b.limit <= t.limit THEN b.sell_c ELSE 0 END) /* щедрые продавцы */
FROM trade t, trade b
GROUP BY t.limit
ORDER BY t.limit DESC



Рабочий вариант 2: в подзапросах посчитать отдельно количество акций, которые можно купить, и отдельно - которые можно продать, а потом объединить их FULL JOIN'ом
Плюсов особых не вижу, просто этот вариант пришел первым в голову :-)

Код: sql
1.
2.
3.
4.
5.
6.
7.
8.
9.
10.
11.
12.
13.
14.
15.
16.
17.
SELECT
	b.buy_c,
	COALESCE(b.limit, s.limit) AS limit, /* DB2: если первое значение NULL, будет подставлено второе */
	s.sell_c
FROM 
	(SELECT t.limit, sum(b.buy_c) AS buy_c
	FROM trade t LEFT JOIN trade b ON b.limit >= t.limit
	GROUP BY t.limit) AS b /* щедные покупатели */

		FULL JOIN
	  
		(SELECT t.limit, sum(s.sell_c) AS sell_c
		FROM trade t LEFT JOIN trade s ON s.limit <= t.limit 
		GROUP BY t.limit) AS s /* щедрые продавцы */
	   
			ON (b.limit = s.limit)
ORDER BY limit DESC



Исходные данные для удобства тестирования:
Код: sql
1.
2.
3.
4.
5.
6.
7.
8.
CREATE TABLE trade (buy_c INTEGER, limit INTEGER, sell_c INTEGER)

INSERT INTO trade VALUES 
	(1, 30, 2),
	(3, 20, 5),
	(7, 10, 11)

DROP TABLE trade


Заранее спасибо за ответы/советы/мнения!
...
Рейтинг: 0 / 0
Нарастающие в обоих направлениях итоги
    #39236210
.det
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Гость
Разумеется, вариант 2 можно облагородить, выделив подзапросы в common table expressions через WITH .. AS, но сути дела это не меняет.
...
Рейтинг: 0 / 0
Нарастающие в обоих направлениях итоги
    #39236216
Фотография MasterZiv
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
.det,
а с чего ты взял, что эту задачу вообще надо решать на SQL?
...
Рейтинг: 0 / 0
Нарастающие в обоих направлениях итоги
    #39236220
.det
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Гость
MasterZiv, это часть условия
...
Рейтинг: 0 / 0
Нарастающие в обоих направлениях итоги
    #39236247
Фотография Egoр
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
.det,

.detbuy_c limit sell_c
------ ------ ------
1 30$ 2
3 20$ 5
7 10$ 11Почему при снижении цены за акцию растет количество продаваемых акций?
...
Рейтинг: 0 / 0
Нарастающие в обоих направлениях итоги
    #39236267
Фотография MasterZiv
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
.detMasterZiv, это часть условия


да не, это идиотизм.
ты на свои запросы погляди.
...
Рейтинг: 0 / 0
Нарастающие в обоих направлениях итоги
    #39236408
.det
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Гость
Egoр, потому что биржа работает не в режиме реального времени. Она собирает все заявки и потом, в конце дня, производит анализ, ценообразование и распределение акций по хитрым правилам. Данная сводная таблица отражает покупательскую способность (и "продавательское желание") по ценам. Если покупатель в своей заявке указал, что он готов купить за 30$, а все покупатели готовы продать по 20$, и в конце дня будет установлена цена 20$ (допустим), то сделки пройдут по этой цене (а жадоморы продавцы, которые ставили по 30$, попросту ничего не продадут).
...
Рейтинг: 0 / 0
Нарастающие в обоих направлениях итоги
    #39236413
Фотография Egoр
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
.det,

спс за разъяснения.
на оракле задача решается за один проход без джойнов. может на DB2 тож есть какие-то аналитические функции?
...
Рейтинг: 0 / 0
Нарастающие в обоих направлениях итоги
    #39236416
Кот Матроскин
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
.detEgoр, потому что биржа работает не в режиме реального времени.

Ой.
Это какая биржа так работает?
...
Рейтинг: 0 / 0
Нарастающие в обоих направлениях итоги
    #39236417
.det
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Гость
MasterZiv, автор задания - один из разработчиков IBM DB2, семь лет работавший в силиконовой долине, профессор и продекан кафедры информатики крупного технического университета. Полный текст задания прикреплен, если я где-то неправильно объяснил или упростил.
И что не так с запросами, особенно с вариантом 1?
В целом вспоминаются слова с таблички кабинета для совещаний на прошлом месте работы: "Если специалист хочет решить проблему - он ищет способы, если не хочет - причины" .
...
Рейтинг: 0 / 0
Нарастающие в обоих направлениях итоги
    #39236421
.det
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Гость
Egoр, в том и состоит мой вопрос - есть ли способы решить задачу проще, чем я, и каковы они.
Как решить ее в Oracle без JOINов или хотя бы в сторону каких функций смотреть?
...
Рейтинг: 0 / 0
Нарастающие в обоих направлениях итоги
    #39236428
Фотография Egoр
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
.det,

Так оно решается на Oracle:
Код: sql
1.
2.
3.
4.
5.
6.
7.
with t(buy_c, limit, sell_c) as
 (select 1, 30, 2 from dual union all select 3, 20, 5 from dual union all select 7, 10, 11 from dual)
select t.*,
   sum(buy_c) over(partition by 1 order by limit desc) as buy_tot,
   sum(sell_c) over(partition by 1 order by limit) as sell_tot
  from t
 order by limit desc
...
Рейтинг: 0 / 0
Нарастающие в обоих направлениях итоги
    #39236432
.det
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Гость
Egoр, кстати для перечисленного мною примера биржей будет установлена цена в 10$ так как при ней объем сделок будет максимальным (11) - это считается идеальной ситуацией, когда цена для такого объема лишь одна. Если смотреть полное задание, то там еще есть понятие backlog, оно используется для других ситуаций, когда максимальный объем сделок возможен при разных ценовых лимитах, и отражает избыток спроса или предложения на рынке. При избытке спроса выбирается максимальная цена, при избытке предложения - минимальная. Всего возможно семь разных ситуаций.
Кот Матроскин, не уверен, как работает настоящая биржа и меня это особо не интересует - это всего лишь часть искусственного условия университетской задачи :-) Но, думаю, автор основывался на реальных событиях, когда составлял условие :-)
...
Рейтинг: 0 / 0
Нарастающие в обоих направлениях итоги
    #39236434
.det
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Гость
Egoр, долбаный partition, никогда им не пользовался, спасибо огромное!!! Надо почитать про него на досуге, в третьей части проекта будет OLAP)))
У нас почему-то в первой части проекта для одного из заданий было выделенное условие - посчитать Dense_rank и Row_number не используя partition, вот я по инерции и во вторую часть задания это условие с собой перенес...
...
Рейтинг: 0 / 0
Нарастающие в обоих направлениях итоги
    #39236855
.det
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Гость
Решил полную задачу 19178216
А есть ли какой-нибудь способ посчитать backlog-и в подзапросе, чтобы все сделать одним запросом? Я так думаю, что на производительность это не влияет, но может быть можно еще проще и читабельней?
Код: sql
1.
2.
3.
4.
5.
6.
7.
8.
9.
10.
11.
12.
13.
14.
15.
16.
17.
18.
19.
20.
21.
22.
23.
24.
25.
26.
27.
28.
29.
30.
31.
32.
33.
34.
35.
36.
37.
CREATE VIEW order_v (stock, call_volume, call_backlog, limit, put_backlog, put_volume) AS
	/* 1st: calculate volumes for each limit, but without backlogs */
	WITH no_backlogs (stock, call_volume, limit, put_volume) AS
		(SELECT DISTINCT
			o_stock AS stock,
			SUM(CASE
					WHEN o_type = 'CALL' THEN 
						o_amount
					ELSE
						0
				END) OVER(PARTITION BY o_stock ORDER BY o_limit DESC) AS call_volume, /* including call orders with higher prices */
			o_limit AS limit,
			SUM(CASE
					WHEN o_type = 'PUT' THEN 
						o_amount
					ELSE
						0
				END) OVER(PARTITION BY o_stock ORDER BY o_limit ASC) AS put_volume /* including put orders with lower prices */
		FROM orders) 

	/* 2nd: calculate backlogs */
	SELECT
		stock,
		call_volume,
		CASE 
			WHEN call_volume > put_volume
			THEN call_volume - put_volume
			ELSE 0
		END			AS call_backlog,
		limit,
		CASE 
			WHEN put_volume > call_volume
			THEN put_volume - call_volume
			ELSE 0
		END			AS put_backlog,
		put_volume
	FROM no_backlogs
...
Рейтинг: 0 / 0
15 сообщений из 15, страница 1 из 1
Форумы / Проектирование БД [игнор отключен] [закрыт для гостей] / Нарастающие в обоих направлениях итоги
Найденые пользователи ...
Разблокировать пользователей ...
Читали форум (0):
Пользователи онлайн (0):
x
x
Закрыть


Просмотр
0 / 0
Close
Debug Console [Select Text]