Entry tags:
Помогите с SQL
У меня есть таблица с курсами ценных бумаг. Скажем, колонки 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
no subject
no subject
(
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
В качестве наводки: кроме функций мин и макс (которые очень тяжелые, т.к. статистические) есть еще прием order by и взять первую строку.
А вот для попытки реализации этой идеи гони инсерты :)
no subject
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
no subject
no subject
(Anonymous) 2006-08-08 04:22 pm (UTC)(link)no subject
no subject
no subject
Но твое решение (твой монстр) неверно, т.к. не учитывает факта, что интересует последовательная смена (то есть не просто минимальное, а минимальное из непосредственной близости к текущему).
Выполни своего монстра на 1,3 и поймешь, о чем я.
no subject
Без рекурсии тоже делается, но циклом: список бумаг, у которых курс сегодня равен вчерашнему, а потом по этому списку запрос типа минимум даты, на которой count(курсы между изначальной датой и этой) без фильтра равен count(того же самого) с фильтром "курс тот же, что вчера". Но я хочу ещё и без
единого гвоздяцикла :-)no subject
Список - правильное решение, но сложное, потому что много count, будет тормозить.
Самое правильное, на мой взгляд, решение - это завести триггер на апдейт и каждый раз сохранять эту дату в какой-нибудь статистической табличке, откуда потом выдирать одним э-ле-мен-тар-ным селектом. Но мы ведь не ищем простых путей, как я поняла? :)
no subject
no subject
Ладно, время не ждёт, начинаю писать процедуру (если что, так и скажу "les gens d'Oracle m'ont dit..."). Но если появятся какие идеи - говори, ага?
P.S. Я только что понял, в какую сторону смотри зверёк на твоём userpic :-))) Раньше мне там виделась часть морды, направленной влево вниз, т.е. вообще все пиксели - это мех, мех, мех %-)
no subject
Я не уверен, что прочитал внимательно :)
Это оно? :
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
no subject
no subject
Может ему индексов добавить? Или микрософту?
no subject
И как, что сделал ты?
no subject
А какие индексы у тебя уже есть?
no subject
no subject
DARR+VAL_COD
PTF_COD+VAL_COD+DARR
Но мне же нужен только DARR, т.е. любой их первых двух прокатывает.
Я в итоге решил даже процедуру не писать, а сделать, как ты предложила - в момент смены курса просчитывать дату и писать её куда-нибудь уже готовенькую.
По поводу "микрософту" - это работали мы с
no subject
no subject
no subject
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
Кстати, не знал, что можно в таком случае сделать alias одной instance таблицы position, а вторую оставить без alias. Век живи, век учись :-)
no subject
no subject
Для сравнения, мой показывал что-то в районе 500 :-)
no subject
no subject