| Изменение времени документов для выстраивания их на временной оси| Изменение времени документов от времени начала до времени окончания для выстраивания их на временной оси используя хранимую процедуру MSSQL |  | Автор статьи: mikecool | Редакторы: Последняя редакция №2 от 23.06.06 | История
 URL: http://kb.mista.ru/article.php?id=260
 |  | 
 Ключевые слова: Документ, время,хранимая, процедура, SQL
 
 
 Однажды столкнулся с ситуацией: пользователь, обладающий административными правами в 1С, перенес документы за предыдущий день, время которых было в районе 23:51. Остальные пользователи, активно работающие с базой и создавая документы, успели внести порядка 500 документов пока не закончился день по меркам 1С. Это случилось в 13.15 дня. Естессно, пришлось быстро писать обработку меняющую время документов. Все это(написание обработки и исправление) заняло порядка получаса времени. Довольно много для розничной торговли с высоким товарооборотом. Поэтому подумал и решил, что если у меня будет процедурка, которая будет исправлять время документов и выстраивать их по новой с интервалом в 1 секунду - то это решение возникшей у меня проблемы.
 
 Возникла единственная проблема: после использования процедуры атрибут ВремяДок содержит время 00:00:00
 Пока эту проблему не решил. Может кто поможет...
 Далее привожу текст процедуры(сильно ее здесь не описываю, ибо снабдил некоторыми комментариями, будут вопросы - обращайтесь)
 
 SET QUOTED_IDENTIFIER ON
 GO
 SET ANSI_NULLS ON
 GO
 /*
 Процедура выполняет 'выстраивание' документов по времени, начиная
 с даты @StartD и времени @StartT и заканчивая датой @EndD и
 временем @EndT
 */
 ALTER      PROCEDURE ChangeTimeDocs (@StartD char(10) = '', @StartT char(8) = '',
 @EndD char(10) = '', @EndT char(8) = '')
 /*
 Параметры
 @StartD, @EndD - дата начала/конца правки, формат 'ГГГГ-ММ-ДД'
 @StartT, @EndT - Время начала/конца правки, формат 'ЧЧ:ММ:СС'
 */
 
 AS
 
 SET NOCOUNT ON
 
 BEGIN
 
 IF @StartD = ''
 SELECT @StartD = Left(GetDate(), 10)
 
 IF @StartT = ''
 SELECT @StartT = '00:00:00'
 
 IF @EndD = ''
 SELECT @EndD = @StartD
 
 IF @EndT = ''
 SELECT @EndT = '23:59:59'
 
 /*
 @StartTime, @EndTime - символьное представление времени(ЧЧ:ММ:СС)
 @StartTimeI, @EndTimeI - время в секундах
 @Start, @End - строки для ограничения выборки из _1sjourn
 @CharDate - представление даты выборки в формате ГГГГММДД
 */
 DECLARE @Year INT, @Month INT, @Day INT,
 @YearC CHAR(4), @MonthC CHAR(2), @DayC CHAR(2),
 @StartTime CHAR(8), @EndTime CHAR(8),
 @StartTimeI INT, @EndTimeI INT,
 @Start CHAR(17), @End CHAR(17), @CharDate CHAR(8)
 -- для хождения циклом по диапазону дат показалось удобным иметь
 -- следующие переменные
 DECLARE @StartDate_Date DATETIME, @EndDate_Date DATETIME
 -- переменные для рассчитываемых значений даты и времени
 DECLARE @TempDate DATETIME, @TempTime INT
 
 -- переменные для значений из курсора документов
 DECLARE @Id_doc CHAR(9), @Id_DocDef INT, @DTIOld CHAR(23), @DTINew CHAR(23)
 -- Всего документов, значение для увеличения времени
 DECLARE @DocsCount INT, @Multiplier INT
 -- количество секунд в дне
 DECLARE @SecInDay INT
 
 -- начальная инициализация
 SELECT @StartDate_Date = CAST((@StartD+' '+@StartT) AS DATETIME)
 SELECT @EndDate_Date = CAST((@EndD+' '+@EndT) AS DATETIME)
 SELECT @TempDate = @StartDate_Date
 SELECT @SecInDay = 23*3600+59*60+59
 
 /*
 Теперь пробегаемся от даты старта до даты конца с интервалом в день
 и внутри каждого дня выстраиваем документы, присваивая каждому новое
 время, начиная с времени первого документа
 В зависимости от количества документов, интервал секунда или доли секунды
 */
 WHILE @TempDate <= @EndDate_Date
 BEGIN
 -- взяли составляющие части 'рассчетной' даты
 SELECT @Year             = DATEPART(YEAR, @TempDate)
 SELECT @Month         = DATEPART(MONTH, @TempDate)
 SELECT @Day             = DATEPART(DAY, @TempDate)
 
 -- время начала дня '00:00:00' или переданное время старта
 IF DATEDIFF(DAY, @TempDate, @StartDate_Date)=0
 SELECT @StartTimeI =    CAST(LEFT(@StartT, 2) AS INT)*3600 +
 CAST(SUBSTRING(@StartT, 4, 2) AS INT)*60 +
 CAST(RIGHT(@StartT, 2) AS INT)
 ELSE
 SELECT @StartTimeI = 1
 
 IF @StartTimeI = 0
 SELECT @StartTimeI = 1
 
 -- получаем время в 36-ом формате
 SELECT @StartTime = dbo.Convert10to36(@StartTimeI)
 
 -- время конца дня '23:59:59' или переданное время окончания
 IF DATEDIFF(DAY, @TempDate, @EndDate_Date)=0
 SELECT @EndTimeI = (CAST(LEFT(@EndT, 2) AS INT)*3600 +
 CAST(SUBSTRING(@EndT, 4, 2) AS INT)*60 +
 CAST(RIGHT(@EndT, 2) AS INT))*10000
 ELSE
 SELECT @EndTimeI = @SecInDay * 10000
 
 -- получаем время в 36-ом формате
 SELECT @EndTime        = dbo.Convert10to36(@EndTimeI)
 -- значение рассчетного времени, которое будет использоваться для установки
 -- нового времени
 SELECT @TempTime    = @StartTimeI * 10000
 
 -- преобразуем момент начала-конца в символьный вид
 SELECT @YearC = CAST(@Year AS CHAR)
 SELECT @MonthC = CAST(@Month AS CHAR)
 IF LEN(@MonthC) = 1
 SELECT @MonthC = '0' + @MonthC
 SELECT @DayC = CAST(@Day AS CHAR)
 IF LEN(@DayC) = 1
 SELECT @DayC = '0' + @DayC
 
 SELECT @CharDate     = @YearC+@MonthC+@DayC
 SELECT @Start         = RTRIM(@CharDate + @StartTime)
 SELECT @End                = RTRIM(@CharDate + @EndTime)
 
 -- Выбираем документы за период
 -- курсор объявлен статическим, чтобы имелся слепок данных до обработки
 -- хотя может статика тут и не нужна...
 DECLARE Docs CURSOR STATIC FOR
 SELECT j.IDDOC AS IDDOC, j.IDDocDef AS IDDOCDEF,
 j.Date_Time_IDDOC AS DTI_Old
 FROM _1sjourn as j (NOLOCK)
 WHERE (j.Date_Time_IdDoc >= @Start) AND
 (j.Date_Time_IdDoc <= @End)
 
 OPEN Docs
 
 -- это количество документов, попавших в выборку
 SELECT @DocsCount = @@CURSOR_ROWS
 
 -- если мы упорядочиваем доки в пределах дня, то и число секунд
 -- берем между временем начала и временем конца
 IF DATEDIFF(DAY, @StartDate_Date, @EndDate_Date) = 0
 SELECT @SecInDay = @EndTimeI/10000 - @StartTime/10000
 
 -- вот на это значение будем домножать результирующие дату-время документа
 -- для расположения документа на временной оси
 -- отношение @SecInDay / @DocsCount ввел для универсальности - вдруг документов
 -- окажется более 84000 в день...
 SELECT @Multiplier = 10000
 IF @DocsCount > @SecInDay
 SELECT @Multiplier = @Multiplier * @SecInDay / @DocsCount
 
 FETCH NEXT FROM Docs
 INTO @Id_doc, @Id_DocDef, @DTIOld
 
 WHILE @@FETCH_STATUS = 0
 BEGIN
 SELECT @DTINew    = LEFT(@DTIOld, 8) +
 dbo.Convert10to36(@TempTime)+ RIGHT(@DTIOld, 9)
 UPDATE _1sjourn
 SET Date_Time_IDDOC = @DTINew
 WHERE IDDOC = @Id_doc AND IDDocDef = @Id_DocDef
 
 -- увеличим время на одну секунду(или долю секунды)
 SELECT @TempTime = @TempTime + 1 * @Multiplier
 
 -- выбираем след. документ
 FETCH NEXT FROM Docs
 INTO @Id_doc, @Id_DocDef, @DTIOld
 
 END
 
 CLOSE Docs
 
 -- переходим в следующий день
 SELECT @TempDate = DATEADD(DAY, 1, @TempDate)
 END
 
 DEALLOCATE Docs
 
 END
 
 GO
 SET QUOTED_IDENTIFIER OFF
 GO
 SET ANSI_NULLS ON
 GO
 
 Используемые процедурой функции(правленные процедуры, заимствованные из http://www.sinor.ru/~my1c/knowhow/SQLcnvID.html):
 
 -- 1-я функция
 SET QUOTED_IDENTIFIER ON
 GO
 SET ANSI_NULLS ON
 GO
 
 ALTER    FUNCTION Convert10to36
 (@Deci int )
 RETURNS char(6)
 AS
 BEGIN
 --SET NOCOUNT ON
 DECLARE @j INT
 declare @Res36 CHAR(9)
 DECLARE @Arr36 CHAR(36)
 SELECT @Arr36 = '0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ'
 SELECT @Res36 = ''
 SELECT @j = LOG(@Deci)/LOG(36) +1
 while @j>0
 begin
 SELECT @Res36 = LTRIM(RTRIM(@Res36)) + SUBSTRING(@Arr36, @Deci/POWER(36,@j-1) +1 ,1)
 SELECT @Deci = @Deci%POWER(36,@j-1)
 SELECT @j =@j-1
 end
 return @Res36
 END
 
 GO
 SET QUOTED_IDENTIFIER OFF
 GO
 SET ANSI_NULLS ON
 GO
 
 -- 2-я функция
 SET QUOTED_IDENTIFIER ON
 GO
 SET ANSI_NULLS ON
 GO
 
 CREATE FUNCTION Convert36to10f
 (@Res36 char(9) )
 RETURNS int
 AS
 BEGIN
 --SET NOCOUNT ON
 DECLARE @j INT
 declare @Deci int
 DECLARE @Arr36 CHAR(36)
 SELECT @Arr36 = '0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ'
 SELECT @Deci = 0
 SELECT @j = 1
 while @j <= LEN(LTRIM(RTRIM(@Res36)))
 begin
 if @j <> 1
 SELECT @Deci = @Deci*36
 SELECT @Deci = @Deci + CHARINDEX(SUBSTRING(LTRIM(RTRIM(@Res36)), @j,1),@Arr36) -1
 SELECT @j = @j+1
 end
 return @Deci
 END
 
 GO
 SET QUOTED_IDENTIFIER OFF
 GO
 SET ANSI_NULLS ON
 GO
 
 
 |