У меня есть устаревшая таблица, которую я не могу изменить.
Значения в нем могут быть изменены из устаревшего приложения (приложение также не может быть изменено).
Из-за большого доступа к таблице из нового приложения (новое требование) я хотел бы создать временную таблицу, которая, как мы надеемся, ускорит запросы.
Фактическим требованием является подсчет количества рабочих дней от X до Y. Например, укажите все рабочие дни с 1 января 2001 года по 24 декабря 2004 года. Таблица используется для обозначения выходных дней. у разных компаний могут быть разные выходные - это не просто суббота + воскресенье)
Временная таблица будет создаваться из программы .NET, каждый раз, когда пользователь выходит на экран для этого запроса (пользователь может выполнить запрос несколько раз, с разными значениями, таблица создается один раз), поэтому я бы хотел, чтобы это было Быстро настолько, насколько это возможно. Подход, описанный ниже, работает менее чем за секунду, но я протестировал его только с небольшим набором данных, и, тем не менее, он занимает, вероятно, около полсекунды, что не очень хорошо для пользовательского интерфейса - хотя это просто накладные расходы для первого запроса.
Старая таблица выглядит следующим образом:
CREATE TABLE [business_days](
[country_code] [char](3) ,
[state_code] [varchar](4) ,
[calendar_year] [int] ,
[calendar_month] [varchar](31) ,
[calendar_month2] [varchar](31) ,
[calendar_month3] [varchar](31) ,
[calendar_month4] [varchar](31) ,
[calendar_month5] [varchar](31) ,
[calendar_month6] [varchar](31) ,
[calendar_month7] [varchar](31) ,
[calendar_month8] [varchar](31) ,
[calendar_month9] [varchar](31) ,
[calendar_month10] [varchar](31) ,
[calendar_month11] [varchar](31) ,
[calendar_month12] [varchar](31) ,
misc.
)
Каждый месяц состоит из 31 символа, а любой выходной день (суббота + воскресенье + праздничные дни) отмечается знаком X. Каждые полдня обозначается буквой «H». Например, если в четверг начинается месяц, он будет выглядеть следующим образом (четверг + рабочие дни пятницы, суббота + воскресенье, помеченные знаком X):
' XX XX ..'
Я бы хотел, чтобы новая таблица выглядела так:
create table #Temp (country varchar(3), state varchar(4), date datetime, hours int)
И я хотел бы, чтобы строки были только для дней, которые выключены (отмечены X или H из предыдущего запроса)
Что я в итоге сделал, так это:
Создайте временную промежуточную таблицу, которая выглядит следующим образом:
create table #Temp_2 (country_code varchar(3), state_code varchar(4), calendar_year int, calendar_month varchar(31), month_code int)
Для его заполнения у меня есть объединение, которое в основном объединяет calendar_month, calendar_month2, calendar_month3 и т. Д.
Если у меня есть цикл, который проходит по всем строкам в # Temp_2, после обработки каждой строки он удаляется из # Temp_2.
Для обработки строки существует цикл от 1 до 31, а подстрока (calendar_month, counter, 1) проверяется на X или H, и в этом случае происходит вставка в таблицу #Temp.
[редактировать добавленный код]
Declare @country_code char(3)
Declare @state_code varchar(4)
Declare @calendar_year int
Declare @calendar_month varchar(31)
Declare @month_code int
Declare @calendar_date datetime
Declare @day_code int
WHILE EXISTS(SELECT * From #Temp_2) -- where processed = 0)
BEGIN
Select Top 1 @country_code = t2.country_code, @state_code = t2.state_code, @calendar_year = t2.calendar_year, @calendar_month = t2.calendar_month, @month_code = t2.month_code From #Temp_2 t2 -- where processed = 0
set @day_code = 1
while @day_code <= 31
begin
if substring(@calendar_month, @day_code, 1) = 'X'
begin
set @calendar_date = convert(datetime, (cast(@month_code as varchar) + '/' + cast(@day_code as varchar) + '/' + cast(@calendar_year as varchar)))
insert into #Temp (country, state, date, hours) values (@country_code, @state_code, @calendar_date, 8)
end
if substring(@calendar_month, @day_code, 1) = 'H'
begin
set @calendar_date = convert(datetime, (cast(@month_code as varchar) + '/' + cast(@day_code as varchar) + '/' + cast(@calendar_year as varchar)))
insert into #Temp (country, state, date, hours) values (@country_code, @state_code, @calendar_date, 4)
end
set @day_code = @day_code + 1
end
delete from #Temp_2 where @country_code = country_code AND @state_code = state_code AND @calendar_year = calendar_year AND @calendar_month = calendar_month AND @month_code = month_code
--update #Temp_2 set processed = 1 where @country_code = country_code AND @state_code = state_code AND @calendar_year = calendar_year AND @calendar_month = calendar_month AND @month_code = month_code
END
Я не специалист по SQL, поэтому я хотел бы получить некоторую информацию о моем подходе, и, возможно, даже гораздо более подходящее предложение.
После того, как у меня будет временная таблица, я планирую сделать (даты будут приходить из таблицы):
select cast(convert(datetime, ('01/31/2012'), 101) -convert(datetime, ('01/17/2012'), 101) as int) - ((select sum(hours) from #Temp where date between convert(datetime, ('01/17/2012'), 101) and convert(datetime, ('01/31/2012'), 101)) / 8)
Помимо решения по нормализации таблицы, другое решение, которое я реализовал на данный момент, - это функция, которая выполняет всю эту логику получения рабочих дней путем сканирования текущей таблицы. Он работает довольно быстро, но я не решаюсь вызывать функцию, если вместо этого могу добавить более простой запрос для получения результата.
(я сейчас пытаюсь сделать это на MSSQL, но мне нужно сделать то же самое для Sybase ASE и Oracle)