Строковые операции
Есть таблица в которой хранятся события и пользователи совершившие это событие плюс дата события.
Мне необходимо вытащить количество событий для каждого пользователя, за промежуток дат и разбить это на промежутки дат(желательно и часов).Возможно можно решить datediff ,но пока не могу никак реализовать это.Чтобы выглядело как-то так:
пользователь 20101101 20101102 20101103 ............ Всего
ххх 2 3 7 ............ 12
Вот что получилось(к сожалению выводит только общее количество):
DECLARE @charACT VARCHAR(25)
DECLARE @dateDATE_START DATETIME
DECLARE @dateDATE_END DATETIME
SET @charACT= 'ADJUST'
SET @dateDATE_START = '20101101'
SET @dateDATE_END = '20101110'
select
distinct UserID,
count (action) as [NUMBER]
from rf_log_all where Action = @charACT
AND DATE_TIME between @dateDATE_START and @dateDATE_END
group by userid
Типа такого:
declare @rf_log_all table(UserID int, Action varchar(25), date_time datetime)
insert into @rf_log_all select 1, 'ADJUST', '20101001'
insert into @rf_log_all select 1, 'ADJUST', '20101102'
insert into @rf_log_all select 1, 'ADJUST', '20101103'
insert into @rf_log_all select 1, 'ADJUST', '20101104'
insert into @rf_log_all select 2, 'ADJUST', '20101105'
insert into @rf_log_all select 2, 'ADJUST', '20101106'
insert into @rf_log_all select 2, 'ADJUST', '20101107'
insert into @rf_log_all select 1, 'ADJUST', '20101108'
insert into @rf_log_all select 3, 'ADJUST', '20101109'
insert into @rf_log_all select 2, 'ADJUST', '20101110'
insert into @rf_log_all select 3, 'ADJUST', '20101111'
-- запрос
DECLARE @charACT VARCHAR(25)
DECLARE @dateDATE_START DATETIME
DECLARE @dateDATE_END DATETIME
SET @charACT= 'ADJUST'
SET @dateDATE_START = '20101101'
SET @dateDATE_END = '20101110'
select
UserID,
[period: 2010.11.01 - 2010.11.03] = sum (case when date_time between '20101101' and '20101103' then 1 else 0 end),
[period: 2010.11.04 - 2010.11.07] = sum (case when date_time between '20101104' and '20101107' then 1 else 0 end),
[period: 2010.11.08 - 2010.11.10] = sum (case when date_time between '20101108' and '20101110' then 1 else 0 end),
count (action) as [NUMBER]
from
@rf_log_all
where
Action = @charACT
AND DATE_TIME between @dateDATE_START and @dateDATE_END
group by
userid
А периоды уже там сами сообразите какие вам нужны, по часам или по датам...
Принцип понятен я думаю.
п.с.
кстати непонятно, как название темы, коррелирует с ее содержанием =)
А возможно ли создание case автоматически из диапазона дат? дело в том что переменные date_start и date_end передаются из другой системы, соотвественно диапазон может меняться и очень часто. И еще вопрос данные в исходной таблице для date_time хранятся ввиде datetime, возможно ли перевести это просто в date, не потеряв результат запроса(когда например несколько событий в один день)?
А по поводу названия темы, я просто подумывал решить это с помощью datediff.)))))
Если у вас сам интервал динамический, но кол-во диапазонов фиксированное, то вы можете соотв-но высчитать эти даты (границы диапазонов) и записать кейс с переменными типа
Если у вас само число диапазонов может меняться - т.е. например передали интервал "месяц" - это 4 диапазона. передали 2 месяца - 8 диапазонов. В таком случае - число столбцов будет переменным - а это достигается только динамическим sql (даже pivot не поможет).
перевести в date - это всмысле отбросить время? есть несколько способов вот например один:
можно писать короче
а сервер сам преобразует в дату при вычислениях в запросе.
Если у вас сам интервал динамический, но кол-во диапазонов фиксированное, то вы можете соотв-но высчитать эти даты (границы диапазонов) и записать кейс с переменными типа
Если у вас само число диапазонов может меняться - т.е. например передали интервал "месяц" - это 4 диапазона. передали 2 месяца - 8 диапазонов. В таком случае - число столбцов будет переменным - а это достигается только динамическим sql (даже pivot не поможет).
перевести в date - это всмысле отбросить время? есть несколько способов вот например один:
можно писать короче
а сервер сам преобразует в дату при вычислениях в запросе.
Диапазон всегда один, а вот интервал меняется, в этом и проблема.
Хотелось бы чтобы он автоматом заполнял верхнюю строку датами из диапазона с интервалом 1 день,а он ведь будет меняться.Тоесть указывать 1-10 число ,либо даже 1,2,3 не подходит. DATESTART может и с середины месяца начинаться а DATEEND быть в конце следующего месяца.
А хотелось бы всетаки получить что-то типа такого 1.jpg, там исходная и необходимая таблица.
Pivot,это да. Вот только он берет значения дат только с событиями а мне нужны даты даже без событий.Плюс он еще требует явно указывать столбцы.
- pivot это нет =) я же вам так сразу и написал чтоб не тратили время. он по сути ничего не "берет", он разворачивает то, что вы ему укажете. Но если кол-во столбцов может меняться, то он не подойдет.
Так все-таки дипазон, т.е. количество дней, постоянное? Меняется только интервал? Т.е. это получается как скользящее по календарю окно фиксированного размера (например размер - 30 дней)? Тогда без проблем, а если размер окна меняется - динамика.
Так можно выбрать несколько диапазонов в виде строк, а потом развернуть их в колонки pivot-ом.
- pivot это нет =) я же вам так сразу и написал чтоб не тратили время. он по сути ничего не "берет", он разворачивает то, что вы ему укажете. Но если кол-во столбцов может меняться, то он не подойдет.
Так все-таки дипазон, т.е. количество дней, постоянное? Меняется только интервал? Т.е. это получается как скользящее по календарю окно фиксированного размера (например размер - 30 дней)? Тогда без проблем, а если размер окна меняется - динамика.
Наверно я всетаки вас не так понял. Да , действительно это будет окно скользящее по календарю не фиксированого размера.
А подскажите пожалуйста как можно это организовать в динамике?
К сожалению динамические запросы пока не осилил.
pivot можно динамически использовать, столбцы будут сами генериться из набора строк (данные)
в скрипте в зависимости от интервала высчитываются колонки и потом события разносятся по тим колонкам по совпадающим датам, без учета времени (вроде про это вы говорили? сами там уже допилите под ваши нужды если что не так.
скрипт работает вроде правильно, но понятно что писал быстро и на разных данных не тестировал, это вы у сами.
Разбирайтесь =)
if object_id('tempdb..#rf_log_all') is not null drop table #rf_log_all
create table #rf_log_all (UserID int, Action varchar(25), date_time datetime)
insert into #rf_log_all select 1, 'ADJUST', '20101001 00:15:08'
insert into #rf_log_all select 1, 'ADJUST', '20101102 01:15:08'
insert into #rf_log_all select 1, 'ADJUST', '20101103 02:10:08'
insert into #rf_log_all select 1, 'ADJUST', '20101104 03:15:08'
insert into #rf_log_all select 2, 'ADJUST', '20101105 04:19:08'
insert into #rf_log_all select 2, 'ADJUST', '20101105 07:19:08'
insert into #rf_log_all select 2, 'ADJUST', '20101105 19:19:08'
insert into #rf_log_all select 2, 'ADJUST', '20101106 05:15:08'
insert into #rf_log_all select 3, 'ADJUST', '20101106 10:14:08'
insert into #rf_log_all select 2, 'ADJUST', '20101107 06:15:08'
insert into #rf_log_all select 1, 'ADJUST', '20101108 07:15:08'
insert into #rf_log_all select 3, 'ADJUST', '20101109 23:15:08'
insert into #rf_log_all select 3, 'ADJUST', '20101109 17:15:08'
insert into #rf_log_all select 2, 'ADJUST', '20101110 09:15:08'
insert into #rf_log_all select 3, 'ADJUST', '20101111 10:15:08'
DECLARE @charACT VARCHAR(25)
DECLARE @DateStart DATETIME
DECLARE @DateEnd DATETIME
SET @charACT= 'ADJUST'
SET @DateStart = '20101105'
SET @DateEnd = '20101120'
declare @debug bit
set @debug = 1
-- сформруем костяк динамического запроса, куда потом вместо
-- {...} подставим части скрипта сформированного "на лету"
declare @sql nvarchar(max)
set @sql = N'
select
UserID,
{Periods}
count (action) as [NUMBER]
from
#rf_log_all
where
Action = ''{charACT}''
AND DATE_TIME between ''{DateStart}'' and ''{DateEnd}''
group by
userid'
-- счетчик цикла
declare @i int
set @i = 0
-- посчитаем кол-во дней между начальным интервалом и конечным
declare @days int
set @days = datediff(dd, @DateStart, @DateEnd)
--проверим что интревал верный
-- (менее 1 дня - нельзя т.к. не будет ни одной колонки, более 4096-2 элементоd mssql не поддерживает
if @days not between 1 and 4094 begin
raiserror ('Интервал дат задан неверно',11,1)
return
end
-- переменные для хренения даты, и скрипта в котором перечисляются периоды
declare @sql_periods varchar(max)
declare @sql_period varchar(max)
set @sql_periods = ''
set @sql_period = ''
-- цикло по дням
while @i <= @days begin
-- высчитаем дату которая соотв.-ет колонке, как: начало интервала + смещение
set @sql_period = convert(char(8),dateadd (dd,@i,@DateStart),112)
-- формируем кусочек скрипта в котором проверим что дата события date_time (без времени, для этого конвертом отбросим его)
-- совпадает с датой колоник, если это так, включаем это значение в сумму
set @sql_periods = @sql_periods + char(13) +
'['+ @sql_period +'] = sum (case when convert(char(8),date_time,112) = ''' + @sql_period + ''' then 1 else 0 end),'
set @i = @i + 1
end
-- посмотрим что получилось c периодами
if @debug = 1 print @sql_periods
-- подставим параметры и сформированные ранее кусочки скрипта в итоговый
set @sql = replace (@sql, '{Periods}', @sql_periods)
set @sql = replace (@sql, '{charACT}', @charACT)
set @sql = replace (@sql, '{DateStart}', convert(char(8),@DateStart,112))
set @sql = replace (@sql, '{DateEnd}', convert(char(8),@DateEnd,112))
-- посмотрим что получилось c финальным скриптом
if @debug = 1 print @sql
-- отдаем скрипт на выполнение
exec sp_executesql @sql
-- удаляем тестовые данные
drop table #rf_log_all
можно ту же самую динамику реализовать с pivot, тогда скрипт примет несколько другой вид. принцип тут поймете, а дальше уже сами решите как будет лучше. только замечу с кейсом будет работать и на 2000 (с пивотом нет).
Уважаемый, вы чем слушали, точнее читали? Я разве говорил что нельзя? Я говорил
[QUOTE=SomewherSomehow]
Я так и не понял, вам-таки требуется переменное число столбцов? Если да, то выход один использовать динамику. я же вам так сразу и написал чтоб не тратили время. он по сути ничего не "берет", он разворачивает то, что вы ему укажете. Но если кол-во столбцов может меняться, то он не подойдет.[/QUOTE]
Динамика, это динамика, что уж там использовать, дело личных предпочтений и ситуации, про это я не говорил. Я говорил про то что в обычном, не динамическом sql, pivot не поможет, т.к. там надо явно указывать список столбцов. Читайте пожалуйста тему внимательно, прежде чем писать, особенно кого-то поправлять.
А вы пробовали прогнать скрипт?
Колонки высчитываются исходя из переданного интервала (а не исходя из данных в наборе строк), а по ним насчитываются события. Если событий на какую-то дату нет, то колонка сама по себе будет, но там будут нули...
Колонки высчитываются исходя из переданного интервала (а не исходя из данных в наборе строк), а по ним насчитываются события. Если событий на какую-то дату нет, то колонка сама по себе будет, но там будут нули...
Сорри, не указал кому. Это не вам.
С вашим сейчас только пытаюсь разобраться.
в скрипте в зависимости от интервала высчитываются колонки и потом события разносятся по тим колонкам по совпадающим датам, без учета времени (вроде про это вы говорили? сами там уже допилите под ваши нужды если что не так.
скрипт работает вроде правильно, но понятно что писал быстро и на разных данных не тестировал, это вы у сами.
Разбирайтесь =)
if object_id('tempdb..#rf_log_all') is not null drop table #rf_log_all
create table #rf_log_all (UserID int, Action varchar(25), date_time datetime)
insert into #rf_log_all select 1, 'ADJUST', '20101001 00:15:08'
insert into #rf_log_all select 1, 'ADJUST', '20101102 01:15:08'
insert into #rf_log_all select 1, 'ADJUST', '20101103 02:10:08'
insert into #rf_log_all select 1, 'ADJUST', '20101104 03:15:08'
insert into #rf_log_all select 2, 'ADJUST', '20101105 04:19:08'
insert into #rf_log_all select 2, 'ADJUST', '20101105 07:19:08'
insert into #rf_log_all select 2, 'ADJUST', '20101105 19:19:08'
insert into #rf_log_all select 2, 'ADJUST', '20101106 05:15:08'
insert into #rf_log_all select 3, 'ADJUST', '20101106 10:14:08'
insert into #rf_log_all select 2, 'ADJUST', '20101107 06:15:08'
insert into #rf_log_all select 1, 'ADJUST', '20101108 07:15:08'
insert into #rf_log_all select 3, 'ADJUST', '20101109 23:15:08'
insert into #rf_log_all select 3, 'ADJUST', '20101109 17:15:08'
insert into #rf_log_all select 2, 'ADJUST', '20101110 09:15:08'
insert into #rf_log_all select 3, 'ADJUST', '20101111 10:15:08'
DECLARE @charACT VARCHAR(25)
DECLARE @DateStart DATETIME
DECLARE @DateEnd DATETIME
SET @charACT= 'ADJUST'
SET @DateStart = '20101105'
SET @DateEnd = '20101120'
declare @debug bit
set @debug = 1
-- сформруем костяк динамического запроса, куда потом вместо
-- {...} подставим части скрипта сформированного "на лету"
declare @sql nvarchar(max)
set @sql = N'
select
UserID,
{Periods}
count (action) as [NUMBER]
from
#rf_log_all
where
Action = ''{charACT}''
AND DATE_TIME between ''{DateStart}'' and ''{DateEnd}''
group by
userid'
-- счетчик цикла
declare @i int
set @i = 0
-- посчитаем кол-во дней между начальным интервалом и конечным
declare @days int
set @days = datediff(dd, @DateStart, @DateEnd)
--проверим что интревал верный
-- (менее 1 дня - нельзя т.к. не будет ни одной колонки, более 4096-2 элементоd mssql не поддерживает
if @days not between 1 and 4094 begin
raiserror ('Интервал дат задан неверно',11,1)
return
end
-- переменные для хренения даты, и скрипта в котором перечисляются периоды
declare @sql_periods varchar(max)
declare @sql_period varchar(max)
set @sql_periods = ''
set @sql_period = ''
-- цикло по дням
while @i <= @days begin
-- высчитаем дату которая соотв.-ет колонке, как: начало интервала + смещение
set @sql_period = convert(char(8),dateadd (dd,@i,@DateStart),112)
-- формируем кусочек скрипта в котором проверим что дата события date_time (без времени, для этого конвертом отбросим его)
-- совпадает с датой колоник, если это так, включаем это значение в сумму
set @sql_periods = @sql_periods + char(13) +
'['+ @sql_period +'] = sum (case when convert(char(8),date_time,112) = ''' + @sql_period + ''' then 1 else 0 end),'
set @i = @i + 1
end
-- посмотрим что получилось c периодами
if @debug = 1 print @sql_periods
-- подставим параметры и сформированные ранее кусочки скрипта в итоговый
set @sql = replace (@sql, '{Periods}', @sql_periods)
set @sql = replace (@sql, '{charACT}', @charACT)
set @sql = replace (@sql, '{DateStart}', convert(char(8),@DateStart,112))
set @sql = replace (@sql, '{DateEnd}', convert(char(8),@DateEnd,112))
-- посмотрим что получилось c финальным скриптом
if @debug = 1 print @sql
-- отдаем скрипт на выполнение
exec sp_executesql @sql
-- удаляем тестовые данные
drop table #rf_log_all
можно ту же самую динамику реализовать с pivot, тогда скрипт примет несколько другой вид. принцип тут поймете, а дальше уже сами решите как будет лучше. только замечу с кейсом будет работать и на 2000 (с пивотом нет).
А под @sql обязательно выделять max ? С max работать отказывается напрочь .
И еще при подсчете событий попадает ли оно в дату происходит :
[COLOR="Red"][20101105] = sum (case when convert(char(8),date_time,112) = '20101105' then 1 else 0 end),
[20101106] = sum (case when convert
select
UserID,
[20101105] = sum (case when convert(char(8),date_time,112) = '20101105' then 1 else 0 end),
[20101106] =
Msg 170, Level 15, State 1, Line 4
Line 4: Incorrect syntax near '='.[/COLOR]
Так происходит потому что вы ограничили длину переменной varchar, видно скрипт у вас обрезается.
Так происходит потому что вы ограничили длину переменной varchar, видно скрипт у вас обрезается.
Спасибо большое, доделал под себя , все отлично работает.
А по поводу nvarchar(4000), стоит SQL2005 , но ставить больше 4000 не дает. От чего это может зависить?
declare @b varchar(8000)
-- либо вот так
declare @a varchar(max)
--а вот так нельзя
--declare @c varchar(8001)
для nvarchar соотв.но пополам 4000
больше и не должно проходить, это связно с тем как sql server хранит данные.
просто типы varchar(8000) и varchar(max), по сути разные типы и хранятся по разному. в 2000-м они и назывались по разному и работать с ними приходилось по разному. в новой версии МС постаралась максимально сократить отличия этих типов в использовании, но хранятся они по прежнему по-разному. лень сейчас искать пруф в документации, но по-моему так.
непонятно другое, почему у вас max "не проходит" как вы говорите. т.к. ошибку сервера вы при этом не приводите, ничего вам по этому поводу сказать не могу.
declare @b varchar(8000)
-- либо вот так
declare @a varchar(max)
--а вот так нельзя
--declare @c varchar(8001)
для nvarchar соотв.но пополам 4000
больше и не должно проходить, это связно с тем как sql server хранит данные.
просто типы varchar(8000) и varchar(max), по сути разные типы и хранятся по разному. в 2000-м они и назывались по разному и работать с ними приходилось по разному. в новой версии МС постаралась максимально сократить отличия этих типов в использовании, но хранятся они по прежнему по-разному. лень сейчас искать пруф в документации, но по-моему так.
непонятно другое, почему у вас max "не проходит" как вы говорите. т.к. ошибку сервера вы при этом не приводите, ничего вам по этому поводу сказать не могу.
Ошибка такая:
[COLOR="red"]Incorrect syntax near 'max'
[/COLOR]
Но я думаю это не так принципиально, 4000 вполне хватает для диапазона месяц.
declare @a varchar(max)
и выполнить, то выдаст
Incorrect syntax near 'max'
?
Если так, то что-то не то у вас с сервером, так только 2000 ругался, попробуйте
выполнить на сервере
select @@version
и выложите результат сюда чтоль, интересно...
а еще может база была перенесена с версии 2000 до 2005 (тогда EXEC sp_dbcmptlevel 'dbname', 90) хотя вообще-то тут вряд ли такое, раз просто не дает определять переменные
такое ощущение, что действительно экземпляр это 2000 сервера
кстати интересную темку нашел о 2005м
а еще может база была перенесена с версии 2000 до 2005 (тогда EXEC sp_dbcmptlevel 'dbname', 90) хотя вообще-то тут вряд ли такое, раз просто не дает определять переменные
такое ощущение, что действительно экземпляр это 2000 сервера
кстати интересную темку нашел о 2005м
Ага, даже если уровень совместимости 80, переменную дает определять. Вощем хз, либо сервер 2000, либо 2005 какой-то сырой, вот на этих версиях работает без проблем
Microsoft SQL Server 2005 - 9.00.4266.00 (Intel X86) Oct 7 2009 16:41:15 Copyright (c) 1988-2005 Microsoft Corporation Standard Edition on Windows NT 5.2 (Build 3790: Service Pack 2)