Изменение времени документов для выстраивания их на временной осиИзменение времени документов от времени начала до времени окончания для выстраивания их на временной оси используя хранимую процедуру 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
|