Добро пожаловать в форум, Guest  >>   Войти | Регистрация | Поиск | Правила | В избранное | Подписаться
Все форумы / Microsoft SQL Server Новый топик    Ответить
 Разбить большой период на маленькие  [new]
VeronaM
Member

Откуда:
Сообщений: 7
Всем Добрый день!
Передо мной стоит следующая задача:
Есть таблица, в которой содержится информация о товаре, типе цены и начале действия цены.

CREATE TABLE [table1] (
[id] [int] IDENTITY (1, 1) NOT NULL,
[tovar] [int] NULL ,
[type] [varchar] (10) COLLATE Cyrillic_General_CI_AS NULL ,
[datefrom] [datetime] NULL
) ON [PRIMARY]
GO

Данные заполняются при выполнении процедуры за период, например:

declare @begin datetime
declare @end datetime

set @begin = '2008-06-01'
set @end = '2008-08-01'

Заполняется она примерно такими значениями:

insert into table1 (tovar, type, datefrom)
select 111, 'type1', '2008-06-11'
union all
select 111, 'type2', '2008-07-25'
union all
select 222, 'type1', '2008-07-15'
union all
select 222, 'type3', '2008-07-24'
...

Необходимо разбить период (с table1.datefrom по @end) для каждого товара\типа цены на маленькие с определенным шагом @step. Результат должен быть такой:

declare @step int
set @step = 7 -- шаг деления периода

111 'type1' '2008-06-11' '2008-06-18'
111 'type1' '2008-06-19' '2008-06-26'
111 'type1' '2008-06-27' '2008-07-04'
111 'type1' '2008-07-05' '2008-07-12'
111 'type1' '2008-07-13' '2008-07-20'
111 'type1' '2008-07-21' '2008-07-28'
111 'type1' '2008-07-29' '2008-08-01'

111 'type2' '2008-07-25' '2008-08-01'

222 'type1' '2008-07-15' '2008-07-22'
222 'type1' '2008-07-23' '2008-07-30'
222 'type1' '2008-07-31' '2008-08-01'

222 'type3' '2008-07-24' '2008-07-31'
222 'type3' '2008-08-01' '2008-08-01'

Дело в том ,что я реализовала этот алгоритм с помощью курсора. Но так как очень часто курсор рекомендуют не использовать, переделала с циклом while. Однако выполнение этого шага с циклом заняло больше времени, чем с курсором.

Можно ли реализовать эту задачу с помощью select\insert\update ? Естесственно не в ущерб производительности. Когда в таблице table1 примерно 1500 записей, курсор отрабатывает где-то за 5 минут. Хотелось бы быстрее, ведь в таблице table1 может содержаться и большее количество записей.

Версия сервера: Microsoft SQL Server 2000 - 8.00.2050 (Intel X86) Mar 7 2008 21:29:56 Copyright (c) 1988-2003 Microsoft Corporation Enterprise Edition on Windows NT 5.2 (Build 3790: Service Pack 2)
22 авг 08, 15:33    [6099508]     Ответить | Цитировать Сообщить модератору
 Re: Разбить большой период на маленькие  [new]
VeronaM
Member

Откуда:
Сообщений: 7
Еще раз всем привет!

Я знаю ,что надо подождать, но очень странно, что пока не поступило никаких предложений... Либо вопрос не интересен, либо не так прост в решении...
22 авг 08, 16:19    [6099859]     Ответить | Цитировать Сообщить модератору
 Re: Разбить большой период на маленькие  [new]
SuperJur
Member

Откуда: Москва
Сообщений: 274
не много не понял задачу, но вот рискну:
declare @begin datetime
declare @end datetime
declare @step int

set @begin = '2008-06-01'
set @end = '2008-08-01'
set @step = 7
while @begin < @end begin
print 'c '+Rtrim( cast( @begin as char))+' по '+ Rtrim( cast(@begin + @step as char))
set @begin = @begin + @step +1
end
22 авг 08, 16:48    [6100069]     Ответить | Цитировать Сообщить модератору
 Re: Разбить большой период на маленькие  [new]
Царь, просто царь
Guest
Если бы вы оформили вопрос согласно Рекомендации по оформлению сообщений в форуме п.6., поверьте, уже получили бы ответ. А так многа букав и весь текст в конце недели да еще в конце рабочего дня как одна сплошная нечитабельная масса.
22 авг 08, 16:49    [6100081]     Ответить | Цитировать Сообщить модератору
 Re: Разбить большой период на маленькие  [new]
VeronaM
Member

Откуда:
Сообщений: 7
Да, к концу недели и вопросы оформлять правильно тяжко. Повторюсь:

Есть таблица, в которой содержится информация о товаре, типе цены и начале действия цены.
CREATE TABLE [table1] (
[id] [int] IDENTITY (1, 1) NOT NULL,
[tovar] [int] NULL ,
[type] [varchar] (10) COLLATE Cyrillic_General_CI_AS NULL ,
[datefrom] [datetime] NULL
) ON [PRIMARY]
GO

Данные заполняются при выполнении процедуры за период, например:
declare @begin datetime --начало периода
declare @end datetime --конец периода
declare @step int-- шаг деления периода

set @begin = '2008-06-01'
set @end = '2008-08-01'
set @step = 7 


Заполняется она примерно такими значениями:
insert into table1 (tovar, type, datefrom)
select 111, 'type1', '2008-06-11'
union all
select 111, 'type2', '2008-07-25'
union all
select 222, 'type1', '2008-07-15'
union all
select 222, 'type3', '2008-07-24'
...

Необходимо разбить период (с table1.datefrom по @end) для каждого товара\типа цены на маленькие с определенным шагом @step. Результат должен быть такой:


Tov Type Start End
111 'type1' '2008-06-11' '2008-06-18'
111 'type1' '2008-06-19' '2008-06-26'
111 'type1' '2008-06-27' '2008-07-04'
111 'type1' '2008-07-05' '2008-07-12'
111 'type1' '2008-07-13' '2008-07-20'
111 'type1' '2008-07-21' '2008-07-28'
111 'type1' '2008-07-29' '2008-08-01'
111 'type2' '2008-07-25' '2008-08-01'
222 'type1' '2008-07-15' '2008-07-22'
222 'type1' '2008-07-23' '2008-07-30'
222 'type1' '2008-07-31' '2008-08-01'
222 'type3' '2008-07-24' '2008-07-31'
222 'type3' '2008-08-01' '2008-08-01'


Я уже реализовала этот алгоритм с помощью курсора (DECLARE CURSOR) и цикла WHILE. Но оба варианта выполняются долго. Меня это не устраивает.

Можно ли реализовать эту задачу с помощью select\insert\update ? Естесственно не в ущерб производительности.

select @@version
Microsoft SQL Server 2000 - 8.00.2050 (Intel X86) Mar 7 2008 21:29:56 Copyright (c) 1988-2003 Microsoft Corporation Enterprise Edition on Windows NT 5.2 (Build 3790: Service Pack 2)
22 авг 08, 17:10    [6100256]     Ответить | Цитировать Сообщить модератору
 Re: Разбить большой период на маленькие  [new]
VeronaM
Member

Откуда:
Сообщений: 7
>To SuperJur

Я УЖЕ реализовала алгоритм с циклом WHILE. Он выполняется медленнее, чем курсор.
Вы удивитесь, но я выполняла фактически предложенный вами скрипт :)).
Он работает хорошо, но когда в таблице table1 находится около 1500 записей (а это не предел) и периоды table1.datefrom - @end могут достигать полугода, то деление каждой строки таблицы table1 на маленькие периоды занимает очень много времени (для решаемой задачи 10 минут много)
22 авг 08, 17:25    [6100369]     Ответить | Цитировать Сообщить модератору
 Re: Разбить большой период на маленькие  [new]
Anddros
Member

Откуда:
Сообщений: 1077
Можно довольствоваться одним SELECT :)
И уж всяко это окажется быстрее курсора.

select tovar, type, 
dateadd(dd,n*(@step+1),datefrom) as d1,
case when dateadd(dd,n*(@step+1),datefrom)+@step<@end 
then dateadd(dd,n*(@step+1),datefrom)+@step else @end end as d1
from table1 t1,
(select n3*100+n2*10+n1 as n
from
(select 0 as n3 union all select 1 union all select 2 union all select 3 
union all select 4 union all select 5 union all select 6 
union all select 7 union all select 8 union all select 9)nn3,
(select 0 as n2 union all select 1 union all select 2 union all select 3 
union all select 4 union all select 5 union all select 6 
union all select 7 union all select 8 union all select 9)nn2,
(select 0 as n1 union all select 1 union all select 2 union all select 3 
union all select 4 union all select 5 union all select 6 
union all select 7 union all select 8 union all select 9)nn1
)nn
where datefrom>=@begin and datefrom<=@end
and n*(@step+1)<=datediff(dd,@begin,@end)
and dateadd(dd,-n*(@step+1),@end)>=datefrom
order by tovar, type, datefrom
22 авг 08, 17:26    [6100382]     Ответить | Цитировать Сообщить модератору
 Re: Разбить большой период на маленькие  [new]
VeronaM
Member

Откуда:
Сообщений: 7
>To Anddros

select tovar, type, 
dateadd(dd,n*(@step+1),datefrom) as d1,
case when dateadd(dd,n*(@step+1),datefrom)+@step<@end 
then dateadd(dd,n*(@step+1),datefrom)+@step else @end end as d1
from table1 t1,
(select n3*100+n2*10+n1 as n
from
(select 0 as n3 union all select 1 union all select 2 union all select 3 
union all select 4 union all select 5 union all select 6 
union all select 7 union all select 8 union all select 9)nn3,
(select 0 as n2 union all select 1 union all select 2 union all select 3 
union all select 4 union all select 5 union all select 6 
union all select 7 union all select 8 union all select 9)nn2,
(select 0 as n1 union all select 1 union all select 2 union all select 3 
union all select 4 union all select 5 union all select 6 
union all select 7 union all select 8 union all select 9)nn1
)nn
where datefrom>=@begin and datefrom<=@end
and n*(@step+1)<=datediff(dd,@begin,@end)
and dateadd(dd,-n*(@step+1),@end)>=datefrom
order by tovar, type, datefrom
В результате выполнения предложенного скрипта выдается такая ошибка:

Server: Msg 8623, Level 16, State 1, Line 34
Internal Query Processor Error: The query processor could not produce a query plan.  Contact your primary support provider for more information.
22 авг 08, 17:39    [6100478]     Ответить | Цитировать Сообщить модератору
 Re: Разбить большой период на маленькие  [new]
Anddros
Member

Откуда:
Сообщений: 1077
Похоже, 2000-й не справляется с таким обилием констант. С 2005-м проблем нет.
Надо немного подуменьшить максимальное количество периодов. 200 хватит?

select tovar, type, 
dateadd(dd,n*(@step+1),datefrom) as d1,
case when dateadd(dd,n*(@step+1),datefrom)+@step<@end 
then dateadd(dd,n*(@step+1),datefrom)+@step else @end end as d1
from table1 t1,
(select n3*100+n2*10+n1 as n
from
(select 0 as n3 union all select 1)nn3,
(select 0 as n2 union all select 1 union all select 2 union all select 3 
union all select 4 union all select 5 union all select 6 
union all select 7 union all select 8 union all select 9)nn2,
(select 0 as n1 union all select 1 union all select 2 union all select 3 
union all select 4 union all select 5 union all select 6 
union all select 7 union all select 8 union all select 9)nn1
)nn
where datefrom>=@begin and datefrom<=@end
and n*(@step+1)<=datediff(dd,@begin,@end)
and dateadd(dd,-n*(@step+1),@end)>=datefrom
order by tovar, type, datefrom
22 авг 08, 17:58    [6100611]     Ответить | Цитировать Сообщить модератору
 Re: Разбить большой период на маленькие  [new]
Glory
Member

Откуда:
Сообщений: 104751
VeronaM
>To SuperJur

Я УЖЕ реализовала алгоритм с циклом WHILE. Он выполняется медленнее, чем курсор.
Вы удивитесь, но я выполняла фактически предложенный вами скрипт :)).
Он работает хорошо, но когда в таблице table1 находится около 1500 записей (а это не предел) и периоды table1.datefrom - @end могут достигать полугода, то деление каждой строки таблицы table1 на маленькие периоды занимает очень много времени (для решаемой задачи 10 минут много)

Заведите таблицу-календарь. Места она займет мизерное, а пользы от нее будет очень много
22 авг 08, 18:01    [6100633]     Ответить | Цитировать Сообщить модератору
 Re: Разбить большой период на маленькие  [new]
Anddros
Member

Откуда:
Сообщений: 1077
Выяснил, что львиную долю выполнения этого запроса в 2000-м занимает построение плана запроса. Причем, время построения нарастает при увеличении числа записей, которые генерятся в таблице-счетчике. Доходит до 5-6 секунд при 500 записях. На 600 уже генерит ошибку.

А вот если ему слегка подсказать, то план генерится практически мгновенно.

Так подскажем же: :)

select tovar, type, 
dateadd(dd,n*(@step+1),datefrom) as d1,
case when dateadd(dd,n*(@step+1),datefrom)+@step<@end 
then dateadd(dd,n*(@step+1),datefrom)+@step else @end end as d1
from @table1 t1
inner loop join
(select n3*100+n2*10+n1 as n
from
(select 0 as n3 union all select 1 union all select 2 union all select 3 
union all select 4 union all select 5 union all select 6 
union all select 7 union all select 8 union all select 9)nn3,
(select 0 as n2 union all select 1 union all select 2 union all select 3 
union all select 4 union all select 5 union all select 6 
union all select 7 union all select 8 union all select 9)nn2,
(select 0 as n1 union all select 1 union all select 2 union all select 3 
union all select 4 union all select 5 union all select 6 
union all select 7 union all select 8 union all select 9)nn1
)nn
on dateadd(dd,-n*(@step+1),@end)>=datefrom
where datefrom>=@begin and datefrom<=@end
and n*(@step+1)<=datediff(dd,@begin,@end)
order by tovar, type, datefrom
22 авг 08, 18:45    [6100803]     Ответить | Цитировать Сообщить модератору
 Re: Разбить большой период на маленькие  [new]
aleks2
Guest
Зачем ДЕЛИТЬ период на мелкие части?

Пока автор не ответит на это вопрос внятно - следует полагать что данная задача бред.
23 авг 08, 09:29    [6101592]     Ответить | Цитировать Сообщить модератору
 Re: Разбить большой период на маленькие  [new]
VeronaM
Member

Откуда:
Сообщений: 7
> To aleks2

Умнее ничего не могли написать в этом топике? Например, предложить свой вариант решения? ;)))

>To Anddros

Спасибо огромное! Работает супер. Обрабатывает 15000-20000 записей за 5-10 секунд. То, что надо! :)

>To Glory

Вы правы, таблицу-календарь заведу и испробую данную задачу на ней.
25 авг 08, 11:57    [6104526]     Ответить | Цитировать Сообщить модератору
Между сообщениями интервал более 1 года.
 Re: Разбить большой период на маленькие  [new]
ZeeXor
Guest
Доброго времени суток.
Не могли бы вы подсказать как тоже самое сделать в Firebird 2.5?
а то у меня ругается на UNION.
8 авг 11, 13:18    [11085356]     Ответить | Цитировать Сообщить модератору
 Re: Разбить большой период на маленькие  [new]
Glory
Member

Откуда:
Сообщений: 104751
ZeeXor
Не могли бы вы подсказать как тоже самое сделать в Firebird 2.5?

Обратится в форум по Firebird
8 авг 11, 13:19    [11085365]     Ответить | Цитировать Сообщить модератору
Все форумы / Microsoft SQL Server Ответить