table1 name createdate a 2011-03-01 10:00:00 a 2011-03-01 11:00:00 a 2011-03-01 14:00:00 b 2011-03-01 13:00:00 b 2011-03-01 13:20:00 b 2011-03-01 14:00:00 查询结果为 name createdate count a 2011-03-01 10:00:00 2 a 2011-03-01 14:00:00 1 b 2011-03-01 13:00:00 3 就相当于是统计name字段中的值在第一次出现后的2小时内,总共出现了几次?
这个是网上的解答:
-
declare @table1
table(
name
nvarchar,createdate smalldatetime)
-
-
insert
into @table1
-
select
'a',
'2011-03-01 10:00:00'
-
union all
select
'a',
'2011-03-01 11:00:00'
-
union all
select
'a',
'2011-03-01 14:00:00'
-
union all
select
'b',
'2011-03-01 13:00:00'
-
union all
select
'b',
'2011-03-01 13:20:00'
-
union all
select
'b',
'2011-03-01 14:00:00'
-
-
select
name,
-
createdate,
-
(
select
count(createdate)
-
from @table1 b
-
where a.name=b.name
and
-
a.createdate<=b.createdate
and
-
dateadd(hh,
2,a.createdate) >= b.createdate
-
)
as
count
-
-
from @table1 a
-
where
not
exists
-
(
select
1
from
-
@table1 b
-
where a.name=b.name
and
-
a.createdate>b.createdate
and
-
a.createdate<
dateadd(hh,
2,b.createdate))
-
group
by
name,createdate
但是这个解答其实是有问题的,当把临时表中的第3条数据的createdate改为'2011-03-01 12:00:00',那么显示的结果是:
name createdate count
a 2011-03-01 10:00:00 3
b 2011-03-01 13:00:00 3
在其中没有包括createdate为'2011-03-01 12:00:00'的记录,因为这个时间到为'2011-03-01 10:00:00'是超过2个小时了,也就是说为'2011-03-01 10:00:00'是第一个出现时间,到为'2011-03-01 11:59:59'为止,接下来应该是从'2011-03-01 12:00:00'开始的下个区间了,而这里显然是有问题的。
以下是我写的解法,虽然效率不是太高,但是能解决这个问题:
-
declare @table1
table(
name
nvarchar,createdate smalldatetime)
-
-
insert
into @table1
-
select
'a',
'2011-03-01 10:00:00'
-
union all
select
'a',
'2011-03-01 11:00:00'
-
union all
select
'a',
'2011-03-01 12:00:00'
-
union all
select
'b',
'2011-03-01 13:10:00'
-
union all
select
'b',
'2011-03-01 13:20:00'
-
union all
select
'b',
'2011-03-01 14:30:00'
-
union all
select
'b',
'2011-03-01 15:15:00'
-
union all
select
'b',
'2011-03-01 16:00:00'
-
union all
select
'b',
'2011-03-01 17:00:00'
-
-
-
-
;with aa
--按照name分区,同时按照createdate排序编号
-
as
-
(
-
select
name,
-
createdate,
-
ROW_NUMBER()
over(
partition
by
name
-
order
by createdate)
as k1
-
from @table1
-
),
-
-
r
-
as
-
(
-
select v.name,
-
starts=v.k1,
--区间开始的编号
-
-
ends=
isnull(
-
min(
case
when v.k1<a.k1
-
and
DATEADD(
hour,
2,v.createdate) <= a.createdate
-
then a.k1
-
else
null
-
end)
-1,
-
-
max(
case
when v.k1<a.k1
-
then a.k1
-
else v.k1
-
end)
-
),
--区间结尾的编号
-
-
isnull(
-
min(
case
when v.k1<a.k1
-
and
DATEADD(
hour,
2,v.createdate) <= a.createdate
-
then a.k1
-
else
null
-
end)
-1,
-
-
max(
case
when v.k1<a.k1
-
then a.k1
-
else v.k1
-
end)
-
) - v.k1
as diff
--区间结尾编号与区间开始编号之间的差值
-
-
from aa v
-
inner
join aa a
-
on v.name = a.name
--只关联name相等的
-
group
by v.name,
-
v.k1
-
having
isnull(
-
min(
case
when v.k1<a.k1
-
and
DATEADD(
hour,
2,v.createdate) <= a.createdate
-
then a.k1
-
else
null
-
end)
-1,
-
-
max(
case
when v.k1<a.k1
-
then a.k1
-
else v.k1
-
end)
-
) >=v.k1
-
and
-
isnull(
-
max(
case
when v.k1>a.k1
and
-
DATEADD(
hour,
-2,v.createdate) >= a.createdate
-
then v.k1 -
1
-
else
null
-
end) +
1,
-
-
min(
case
when v.k1>a.k1
-
then a.k1
-
else v.k1
-
end)
-
) = v.k1
-
)
-
-
--select * from r
-
-
-
select aa.name,
-
aa.createdate,
-
diff +
1
-
from r
-
inner
join aa
-
on aa.name = r.name
-
and aa.k1 =r.starts
-
where
not
exists
-
(
select
1
-
from r rr
-
where rr.name = r.name
and
-
rr.starts <> r.starts
and
-
rr.starts < r.starts
and
-
(rr.ends = r.ends
or
-
rr.ends = r.starts)
-
)
不过发现我的这个解法也是有问题的,最大的问题在与不能准确的确定上限在那里,还得继续考虑问题的解法。
下面这个也是有问题的:
-
declare @table1
table(
name
nvarchar,createdate smalldatetime)
-
-
insert
into @table1
-
select
'a',
'2011-03-01 10:00:00'
-
union all
select
'a',
'2011-03-01 11:00:00'
-
union all
select
'a',
'2011-03-01 12:00:00'
-
-
--union all select 'b','2011-03-01 13:10:00'
-
--union all select 'b','2011-03-01 13:20:00'
-
--union all select 'b','2011-03-01 14:30:00'
-
--union all select 'b','2011-03-01 15:15:00'
-
--union all select 'b','2011-03-01 16:00:00'
-
--union all select 'b','2011-03-01 17:15:00'
-
-
-
-
;with a
--按照name分区,同时按照createdate排序编号
-
as
-
(
-
select
name,
-
createdate,
-
ROW_NUMBER()
over(
partition
by
name
-
order
by createdate)
as k1
-
from @table1
-
),
-
-
c
-
as
-
(
-
select a1.name,
-
a1.createdate,
-
a1.k1,
-
MIN(a2.createdate)
as nextCreatedate,
-
MIN(a2.k1)
as nextK1
-
from a a1
-
inner
join a a2
-
on a1.name = a2.name
-
and a2.createdate <
DATEADD(
hour,
2,a1.createdate)
-
and a2.createdate >= a1.createdate
-
and a1.k1 <= a2.k1
-
group
by a1.name,
-
a1.createdate,
-
a1.k1
-
),
-
-
w
-
as
-
(
-
select
name,
-
createdate,
-
k1
-
-
--null,
-
--null,
-
--null
-
from a
-
where k1 =
1
-
-
union all
-
-
select c.name,
-
c.nextCreatedate,
-
c.nextK1
-
-
from W
-
inner
join a
-
on a.name = w.name
-
and
dateadd(
hour,
2,w.createdate) <= a.createdate
-
and w.k1 <= a.k1
-
and w.createdate <>
'2011-03-01 12:00:00'
-
inner
join c
-
on w.name = c.name
-
and w.createdate = c.createdate
-
and w.k1 = c.k1
-
where w.k1 <=
3
-
)
-
-
SELECT *
-
FROM w
下面的解法是正确的,不过用的是T-SQL,不是纯sql了:
-
declare @table1
table(
name
nvarchar(
100),createdate smalldatetime)
-
-
declare @table2
table(
name
nvarchar(
100),createdate smalldatetime,rnum
bigint)
-
-
declare @temp
table(
name
nvarchar(
100),createdate smalldatetime,rnum
bigint)
-
-
declare @i
int =
1;
-
-
insert
into @table1
-
select
'a',
'2011-03-01 10:00:00'
-
union all
select
'a',
'2011-03-01 11:00:00'
-
union all
select
'a',
'2011-03-01 12:00:00'
-
-
union all
select
'b',
'2011-03-01 13:10:00'
-
union all
select
'b',
'2011-03-01 13:20:00'
-
union all
select
'b',
'2011-03-01 14:30:00'
-
union all
select
'b',
'2011-03-01 15:15:00'
-
union all
select
'b',
'2011-03-01 16:00:00'
-
union all
select
'b',
'2011-03-01 17:16:00'
-
union all
select
'b',
'2011-03-01 17:15:00'
-
-
-
;with a
--按照name分区,同时按照createdate排序编号
-
as
-
(
-
select
name,
-
createdate,
-
ROW_NUMBER()
over(
partition
by
name
-
order
by createdate)
as k1
-
from @table1
-
)
-
-
insert
into @table2
-
select *
from a
-
-
insert
into @temp
-
select
name,
-
createdate,
-
rnum
-
from @table2
-
where rnum =
1
-
-
--select * from @temp
-
-
-
while @i <= (
select
MAX(rnum)
from @table2)
-
begin
-
insert
into @temp
-
-
select t2.name,
-
min(t2.createdate),
-
@i +
1
-
from @temp t1
-
inner
join @table2 t2
-
on t1.name = t2.name
-
and t2.createdate >=
dateadd(
hour,
2,t1.createdate)
-
where t1.rnum = @i
-
group
by t2.name
-
-
set @i = @i +
1
-
end
-
-
;with r
-
as
-
(
-
select
name,
-
createdate
-
from @temp
-
group
by
name,
-
createdate
-
)
-
-
select r.name,
-
r.createdate,
-
COUNT(
1)
-
from r
-
inner
join @table1 t
-
on t.name = r.name
-
and t.createdate >= r.createdate
-
and t.createdate <
DATEADD(
HOUR,
2,r.createdate)
-
group
by r.name,
-
r.createdate
其实这个问题是个递归问题,由上一个找到下一个,但是得构造一下:
-
declare @table1
table(
name
nvarchar,createdate smalldatetime)
-
-
insert
into @table1
-
select
'a',
'2011-03-01 10:00:00'
-
union all
select
'a',
'2011-03-01 11:00:00'
-
union all
select
'a',
'2011-03-01 12:00:00'
-
union all
select
'a',
'2011-03-01 12:20:00'
-
-
union all
select
'b',
'2011-03-01 13:10:00'
-
union all
select
'b',
'2011-03-01 13:20:00'
-
union all
select
'b',
'2011-03-01 14:30:00'
-
union all
select
'b',
'2011-03-01 15:15:00'
-
union all
select
'b',
'2011-03-01 16:00:00'
-
union all
select
'b',
'2011-03-01 17:20:00'
-
union all
select
'b',
'2011-03-01 17:15:00'
-
union all
select
'b',
'2011-03-01 19:16:00'
-
union all
select
'b',
'2011-03-01 17:15:00'
-
-
-
;with a
--按照name分区,同时按照createdate排序编号
-
as
-
(
-
select
name,
-
createdate,
-
ROW_NUMBER()
over(
partition
by
name
-
order
by createdate)
as k1
-
from @table1
-
),
-
-
c
--对于每个时间,找到大于这个时间2小时的时间中最小那个时间
-
as
-
(
-
select a1.name,
-
a1.createdate,
-
a1.k1,
-
MIN(a2.createdate)
as nextCreatedate,
-
MIN(a2.k1)
as nextK1
-
from a a1
-
inner
join a a2
-
on a1.name = a2.name
-
and a2.createdate >=
DATEADD(
hour,
2,a1.createdate)
-
-
group
by a1.name,
-
a1.createdate,
-
a1.k1
-
-
union all
-
-
select a.name,
null,
null,a.createdate,
1
--构造递归运行时需要的层级
-
from a
-
where k1 =
1
-
),
-
-
w
--递归查询
-
as
-
(
-
select c.name,
-
c.createdate,
-
c.k1,
-
c.nextCreatedate,
-
c.nextK1,
-
1
as lev
-
from c
-
where createdate
is
null
-
and k1
is
null
-
-
union all
-
-
select c.name,
-
c.createdate,
-
c.k1,
-
c.nextCreatedate,
-
c.nextK1,
-
lev +
1
-
from W
-
inner
join c
-
on w.name = c.name
-
and w.nextCreatedate = c.createdate
-
-
)
-
-
SELECT
distinct
name,
-
nextCreatedate,
-
nextK1
-
FROM w