Помогите с SQL
Aug. 8th, 2006 05:14 pm![[personal profile]](https://www.dreamwidth.org/img/silk/identity/user.png)
У меня есть таблица с курсами ценных бумаг. Скажем, колонки POS_DARR, POS_COD и POS_COURS для даты, кода бумаги и курса.
В программе, заполняющей эту таблицу могут быть баги, когда курс застывает на некотором значении, и перестаёт обновляться, т.е. начиная со дня баги курс будет равен одному и тому же значению.
Мне нужно вывести все подозрительные линии, т.е. для некоторой даты вывести все коды, курсы которых не изменились по крайней мере со вчерашнего дня (халява), а затем дату, с которой этот курс оставался неизменным.
Я написал следующего монстра (POSITION - таблица курсов, код не в одной колонке POS_COD, а в двух POS_PTF_COD и POS_VAL_COD, INTEGRATION - список всех дат базы данных):
select pos_darr, min(int_darr), pos_ptf_cod, pos_val_cod, pos_cours
from (
select pos1.pos_darr, int_darr, pos1.pos_ptf_cod, pos1.pos_val_cod, pos1.pos_cours
from position pos1, position pos2, integration
where pos1.pos_darr = '20/07/2006'
and pos1.pos_val_cod = 'FR0000108664'
and pos1.pos_ptf_cod = '1000101'
and pos1.pos_ptf_cod = pos2.pos_ptf_cod
and pos1.pos_val_cod = pos2.pos_val_cod
and int_darr < pos1.pos_darr
and pos1.pos_darr > pos2.pos_darr
and int_darr <= pos2.pos_darr
group by pos1.pos_darr, pos1.pos_ptf_cod, pos1.pos_val_cod, pos1.pos_cours, int_darr
having min(pos2.pos_cours) = max(pos2.pos_cours)
)
group by pos_darr, pos_ptf_cod, pos_val_cod, pos_cours
Монстра работает, конечно, но только когда я запрашиваю один код (как это сдалено в примере). Когда прошу много - думает тормозит. Неудивительно, впрочем. Есть какие-то идеи, как вообще такие вещи пишутся?
Можно, конечно, писать не запрос, а цикл, который пробегает по курсору... Но хочется красиво как-то.
no subject
Date: 2006-08-08 03:30 pm (UTC)no subject
Date: 2006-08-08 03:34 pm (UTC)no subject
Date: 2006-08-08 03:57 pm (UTC)create table POSITION
(
POS_DARR DATE not null,
POS_PTF_COD VARCHAR2(10) not null,
POS_VAL_COD VARCHAR2(15) not null,
POS_COURS NUMBER(17,6)
);
create table INTEGRATION
(
INT_DARR DATE not null
);
insert into INTEGRATION (INT_DARR)
values (to_date('20-07-2006', 'dd-mm-yyyy'));
insert into INTEGRATION (INT_DARR)
values (to_date('19-07-2006', 'dd-mm-yyyy'));
insert into INTEGRATION (INT_DARR)
values (to_date('18-07-2006', 'dd-mm-yyyy'));
insert into INTEGRATION (INT_DARR)
values (to_date('17-07-2006', 'dd-mm-yyyy'));
insert into POSITION (POS_DARR, POS_PTF_COD, POS_VAL_COD, POS_COURS)
values (to_date('20-07-2006', 'dd-mm-yyyy'), '1', '1', 10);
insert into POSITION (POS_DARR, POS_PTF_COD, POS_VAL_COD, POS_COURS)
values (to_date('19-07-2006', 'dd-mm-yyyy'), '1', '1', 10);
insert into POSITION (POS_DARR, POS_PTF_COD, POS_VAL_COD, POS_COURS)
values (to_date('18-07-2006', 'dd-mm-yyyy'), '1', '1', 12);
insert into POSITION (POS_DARR, POS_PTF_COD, POS_VAL_COD, POS_COURS)
values (to_date('17-07-2006', 'dd-mm-yyyy'), '1', '1', 10);
insert into POSITION (POS_DARR, POS_PTF_COD, POS_VAL_COD, POS_COURS)
values (to_date('20-07-2006', 'dd-mm-yyyy'), '1', '2', 10);
insert into POSITION (POS_DARR, POS_PTF_COD, POS_VAL_COD, POS_COURS)
values (to_date('19-07-2006', 'dd-mm-yyyy'), '1', '2', 10);
insert into POSITION (POS_DARR, POS_PTF_COD, POS_VAL_COD, POS_COURS)
values (to_date('18-07-2006', 'dd-mm-yyyy'), '1', '2', 10);
insert into POSITION (POS_DARR, POS_PTF_COD, POS_VAL_COD, POS_COURS)
values (to_date('17-07-2006', 'dd-mm-yyyy'), '1', '2', 10);
insert into POSITION (POS_DARR, POS_PTF_COD, POS_VAL_COD, POS_COURS)
values (to_date('20-07-2006', 'dd-mm-yyyy'), '1', '3', 10);
insert into POSITION (POS_DARR, POS_PTF_COD, POS_VAL_COD, POS_COURS)
values (to_date('19-07-2006', 'dd-mm-yyyy'), '1', '3', 12);
insert into POSITION (POS_DARR, POS_PTF_COD, POS_VAL_COD, POS_COURS)
values (to_date('18-07-2006', 'dd-mm-yyyy'), '1', '3', 12);
insert into POSITION (POS_DARR, POS_PTF_COD, POS_VAL_COD, POS_COURS)
values (to_date('17-07-2006', 'dd-mm-yyyy'), '1', '3', 10);
Запрос на 20/07/2006 должен выдать:
POS_PTF_COD POS_VAL_COD DATE_START
1 1 19/07/2006
1 2 17/07/2006
Пара (1, 3) в списке не должна фигурировать, поскольку курс в последний день изменился.
no subject
Date: 2006-08-08 03:49 pm (UTC)(
POS_DARR DATE not null,
POS_PTF_COD VARCHAR2(10) not null,
POS_VAL_COD VARCHAR2(15) not null,
POS_COURS NUMBER(17,6)
);
create table INTEGRATION
(
INT_DARR DATE not null
);
insert могу тоже набрать, но это уже маразм будет :-)
В двух словах, есть некая величина (курс) для некоторого набора ID (бумаги). Как найти те ID, для которых курс не менялся со вчерашнего дня, понятно. А как при этом ещё и вывести дату, с которой курс постоянен?
no subject
Date: 2006-08-08 03:55 pm (UTC)В качестве наводки: кроме функций мин и макс (которые очень тяжелые, т.к. статистические) есть еще прием order by и взять первую строку.
А вот для попытки реализации этой идеи гони инсерты :)
no subject
Date: 2006-08-08 04:01 pm (UTC)no subject
Date: 2006-08-08 04:22 pm (UTC)no subject
Date: 2006-08-08 04:23 pm (UTC)no subject
Date: 2006-08-08 04:21 pm (UTC)no subject
Date: 2006-08-08 04:24 pm (UTC)no subject
Date: 2006-08-08 04:35 pm (UTC)Но твое решение (твой монстр) неверно, т.к. не учитывает факта, что интересует последовательная смена (то есть не просто минимальное, а минимальное из непосредственной близости к текущему).
Выполни своего монстра на 1,3 и поймешь, о чем я.
no subject
Date: 2006-08-08 05:14 pm (UTC)Без рекурсии тоже делается, но циклом: список бумаг, у которых курс сегодня равен вчерашнему, а потом по этому списку запрос типа минимум даты, на которой count(курсы между изначальной датой и этой) без фильтра равен count(того же самого) с фильтром "курс тот же, что вчера". Но я хочу ещё и без
единого гвоздяцикла :-)no subject
Date: 2006-08-08 06:59 pm (UTC)Список - правильное решение, но сложное, потому что много count, будет тормозить.
Самое правильное, на мой взгляд, решение - это завести триггер на апдейт и каждый раз сохранять эту дату в какой-нибудь статистической табличке, откуда потом выдирать одним э-ле-мен-тар-ным селектом. Но мы ведь не ищем простых путей, как я поняла? :)
no subject
Date: 2006-08-08 07:53 pm (UTC)no subject
Date: 2006-08-09 07:27 am (UTC)no subject
Date: 2006-08-09 06:34 am (UTC)Ладно, время не ждёт, начинаю писать процедуру (если что, так и скажу "les gens d'Oracle m'ont dit..."). Но если появятся какие идеи - говори, ага?
P.S. Я только что понял, в какую сторону смотри зверёк на твоём userpic :-))) Раньше мне там виделась часть морды, направленной влево вниз, т.е. вообще все пиксели - это мех, мех, мех %-)
no subject
Date: 2006-08-09 07:25 am (UTC)Я не уверен, что прочитал внимательно :)
Это оно? :
create table green (product int, date int, rate int)
insert into green select 1,5,10
insert into green select 1,4,10
insert into green select 1,3,10
insert into green select 1,2,10
insert into green select 1,1,11
insert into green select 2,5,10
insert into green select 2,4,10
insert into green select 2,3,12
insert into green select 2,2,10
insert into green select 2,1,11
insert into green select 3,5,12
insert into green select 3,4,11
insert into green select 3,3,11
declare @date int
set @date = 5
select product
,min(date) mdate
from green g1
where g1.date < @date
and not exists
(select 1
from green g2
where g2.product = g1.product
and g2.date >= g1.date
and g2.date <= @date
and g2.rate != g1.rate
)
group by product
no subject
Date: 2006-08-09 07:47 am (UTC)Может ему индексов добавить? Или микрософту?
no subject
Date: 2006-08-09 08:07 am (UTC)А какие индексы у тебя уже есть?
no subject
Date: 2006-08-09 08:27 am (UTC)no subject
Date: 2006-08-09 08:33 am (UTC)no subject
Date: 2006-08-09 08:34 am (UTC)no subject
Date: 2006-08-09 08:32 am (UTC)DARR+VAL_COD
PTF_COD+VAL_COD+DARR
Но мне же нужен только DARR, т.е. любой их первых двух прокатывает.
Я в итоге решил даже процедуру не писать, а сделать, как ты предложила - в момент смены курса просчитывать дату и писать её куда-нибудь уже готовенькую.
По поводу "микрософту" - это работали мы с
no subject
Date: 2006-08-09 10:06 am (UTC)select distinct pos_ptf_cod, pos_val_cod, first_value(pos_darr) over
(partition by pos_ptf_cod, pos_val_cod order by pos_darr) mindate
from position a
where pos_darr < '20/07/2006'
and not exists
(select 1
from position
where pos_ptf_cod = a.pos_ptf_cod
and pos_val_cod = a.pos_val_cod
and pos_darr >= a.pos_darr
and pos_darr <= '20/07/2006'
and pos_cours != a.pos_cours
)
no subject
Date: 2006-08-09 11:18 am (UTC)Кстати, не знал, что можно в таком случае сделать alias одной instance таблицы position, а вторую оставить без alias. Век живи, век учись :-)
no subject
Date: 2006-08-09 11:34 am (UTC)no subject
Date: 2006-08-09 01:01 pm (UTC)Для сравнения, мой показывал что-то в районе 500 :-)
no subject
Date: 2006-08-11 06:58 pm (UTC)no subject
Date: 2006-08-12 12:23 pm (UTC)no subject
Date: 2006-08-09 07:36 am (UTC)no subject
Date: 2006-08-09 07:47 am (UTC)И как, что сделал ты?