Страница 1 из 1

Вопрос по SQL

СообщениеДобавлено: 04.10.2007 17:54:08
v-t-l
Понимаю что вопрос скорее на sql.ru, но может здесь кто знает?
Вполне жизненная ситуация - имеется таблица:
Код: Выделить всё
Дата    |Цена
=============
01.08.07|1000
14.09.07|1234
22.10.07|1500

Как должен выглядеть запрос, чтобы получить
Код: Выделить всё
Цена|ДатаС   |ДатаПо
======================
1000|01.08.07|14.09.07
1234|14.09.07|22.10.07
1500|22.10.07|

Если важно, то SQL - Firebird 2.0

Заранее спасибо.

СообщениеДобавлено: 04.10.2007 18:18:31
Attid
вопрос скорее на sql.ru,

угу

Если важно, то SQL - Firebird 2.0

угу, на разных серверах по разному будет.

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

СообщениеДобавлено: 04.10.2007 18:27:03
v-t-l
JOIN - я как раз и думал об этом, но никак не придумаю условие связи?

СообщениеДобавлено: 04.10.2007 19:16:20
shade
Можно попробовать что-то типа этого
Код: Выделить всё
SELECT t1.*, min(t2.Дата)
FROM t as t1, t as t2
WHERE  t1.Дата < t2.Дата
GROUP BY t1.Дата

Правда потеряется последняя строчка - там уж сами подумайте как замутить join

СообщениеДобавлено: 04.10.2007 22:29:37
v-t-l
Нашел вариант:
Код: Выделить всё
SELECT
  t1.FPrice,
  t1.FDate,
(SELECT MIN(FDate) FROM TBL_PRICE AS t2 WHERE  t1.FDate < t2.FDate)
FROM TBL_PRICE as t1


Не проверял, правда, на больших объемах. Есть подозрение, что будет тормозить. Если у кого-нибудь есть еще идеи - welcome :)

СообщениеДобавлено: 04.10.2007 22:43:49
alexs
v-t-l писал(а):Нашел вариант:

вот этот вариант наиболее хорош
чтобы не тормозил - необходим индекс
MIN кажется умеет использовать в птице индекс - надо просто посмотреть в документации и что выдаст на это IBExpert в статистике запросов

СообщениеДобавлено: 05.10.2007 18:28:14
v-t-l
Код: Выделить всё
SELECT
  t1.FPrice,
  t1.FDate,
(SELECT MIN(FDate) FROM TBL_PRICE AS t2 WHERE  t1.FDate < t2.FDate)
FROM TBL_PRICE as t1


Проверил на больших объемах. Сгенерировал миллион записей случайных данных, создал индексы по обоим полям. Тормозит - минимум 10 минут на выборку. Пробовал по интервалу
Код: Выделить всё
SELECT
  t1.FPrice,
  t1.FDate,
(SELECT MIN(FDate) FROM TBL_PRICE AS t2 WHERE  t1.FDate < t2.FDate)
FROM TBL_PRICE as t1
WHERE t1.FDate BETWEEN '01.01.2007' AND '01.09.2007'

Выборка возвращает около 30000 записей, но выполняется еще дольше на пару минут.
Я так думаю, у огнептицы "план" неправильный (PLAN)
:lol:
Где взять лучший, в смысле как правильно указать ему последовательность операций?

СообщениеДобавлено: 05.10.2007 19:25:51
Brainenjii
может я не прав, но можно ввести суррогатный ключ ID, и генератором его заполнять, и тогда выборка будет примерно такая
Код: Выделить всё
SELECT FDATE, FPRICE, (SELECT FDATE FROM TEST AS T1 WHERE T1.ID = T2.ID + 1) FROM TEST AS T2

СообщениеДобавлено: 05.10.2007 21:21:40
Сергей Смирнов
Вам тестирование по SQL надо пройти или чтобы работало нормально? Во втором случае изготовление нехитрой ХП даст гарантию построения выборки за один проход правильно отсортированной таблицы. Прирост скорости тут возможен на 1-3 порядка по сравнению с запросом в зависимости от степени его кривизны.

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

СообщениеДобавлено: 06.10.2007 14:50:17
v-t-l
Мне нужно, чтобы работало быстро с выборкой по периоду.
ХП не подходит, т.к. это будет использоваться в редактируемых представлениях (updatable views).
Практическая польза - получение длительности действия каждого значения (цены в данном случае) для дальнейших расчетов.
Что касается метода, предложенного Brainenjii, то не факт, что даты будут вставляться последовательно.
Код: Выделить всё
SELECT
  t1.FPrice,
  t1.FDate,
(SELECT MIN(FDate) FROM TBL_PRICE AS t2 WHERE  t1.FDate < t2.FDate)
FROM TBL_PRICE as t1
WHERE t1.FDate BETWEEN '01.01.2007' AND '01.09.2007'

Здесь наверняка все дело в порядке выполнения запроса - нужно заставить огнептицу сначала ограничить выборку по периоду (WHERE t1.FDate BETWEEN '01.01.2007' AND '01.09.2007'), а уж затем приступать к выборке результирующих полей. Для этого, как я понимаю, есть (по крайней мере в 2.0+) ключевое слово PLAN, вот только как им пользоваться - непонятно.

Вот какой план показывает IBExpert:
Код: Выделить всё
План
PLAN (T2 ORDER TBL_PRICE_IDX2 INDEX (TBL_PRICE_IDX2))
PLAN (T1 INDEX (TBL_PRICE_IDX2))

Адаптированный план
PLAN (T2 ORDER TBL_PRICE_IDX2 INDEX (TBL_PRICE_IDX2)) PLAN (T1 INDEX (TBL_PRICE_IDX2))

СообщениеДобавлено: 06.10.2007 17:10:28
Attid
v-t-l
если нужно чтоб быстро работало добавь поле, дабавь тригер который будет его обновлять, пройдись один раз update и будет тебе счастье, зачем городить огород ?

СообщениеДобавлено: 07.10.2007 02:20:38
debi12345
Можно попробовать нечто типа :

select
t1.fprice,
t1.fdate as fdatebegin,
min(t2.fdate) as fdateend
from
t as t1,
t as t2
where
t2.fdate > t1.fdate
group by
t1.fprice,
t1.fdate;


Если даты в таблице все время возрастают, а не повторяются - проиндексировать. Если не повторяются только в пределах цены - не индексировать, или проиндексировать составным индексом "цена+дата".

СообщениеДобавлено: 08.10.2007 16:55:08
alexs
Вот смотри пример:
Код: Выделить всё
select
  tb_price1.tb_price_id,
  tb_price1.spr_3_id,
  tb_price1.tb_price_date,
  tb_price1.tb_price_price1,
  (select
     max(tb_price2.tb_price_date)
   from
     tb_price tb_price2
   where
       tb_price2.tb_price_date < tb_price1.tb_price_date
     and
       tb_price2.spr_3_id = tb_price1.spr_3_id
  )
from
  tb_price tb_price1

где
tb_price - таблица с ценами (порядка милиона записей)
tb_price.spr_3_id - код товарной карточки

на таблице tb_price есть индекс по полям SPR_3_ID,TB_PRICE_DATE
тормозов не наблюдаю.
тем более в таком виде (без ограничения по товару) в живую запрос никто не делает - обычно дополнительно основной запрос ограничивается по коду товара, т.е.:
Код: Выделить всё
select
  tb_price1.tb_price_id,
  tb_price1.spr_3_id,
  tb_price1.tb_price_date,
  tb_price1.tb_price_price1,
  (select
     max(tb_price2.tb_price_date)
   from
     tb_price tb_price2
   where
       tb_price2.tb_price_date < tb_price1.tb_price_date
     and
       tb_price2.spr_3_id = tb_price1.spr_3_id
  )
from
  tb_price tb_price1
where
  tb_price1.spr_3_id = :spr_3_id