使用SQL计算连续的重复记录

时间:2009-08-10 12:00:19

标签: sql sql-server-2005

我有一个数据分析问题,我可以通过一些T-SQL或一些脚本轻松解决,但我想知道是否有一个聪明的SQL解决方案。问题是它稍微混淆了SQL的行独立性假设。

我有一个表,其中包含与用户关联的名称 - 值对,并按提交顺序排列,例如:

ID      USERID  VARIABLE        VALUE   SUBMITTED
3115    2287    votech05    2   2009-02-02 15:34:00
3116    2287    comcol05    1   2009-02-02 15:34:00
3117    2287    fouryr05    1   2009-02-02 15:35:00
3118    2287    none05          2   2009-02-02 15:35:00
3119    2287    ocol1_05    2   2009-02-02 15:44:00
3120    2287    disnone         2   2009-02-02 15:45:00
3121    2287    dissense    2   2009-02-02 15:49:00
3122    2287    dismobil    3   2009-02-02 15:51:00
3123    2287    dislearn    3   2009-02-02 15:51:00
3124    2287    disment         3   2009-02-02 15:52:00
3125    2287    disother    2   2009-02-02 15:55:00
3126    2287    disrefus    7   2009-02-02 15:58:00

我希望能够确定最大的相同值组的值和计数(当数据被命令为ID主键时)。所以,对于上面的例子,因为我有4个值= 2按顺序出现,只有3个值= 3,我想报告:

USERID     VALUE      COUNT
2287       2          4

给定用户。

同样,这可以使用其他工具相当快速地完成,但由于数据集非常大(大约7500万条记录)并经常更改,因此能够通过查询解决此问题会很好。我正在使用SQL Server 2005。

3 个答案:

答案 0 :(得分:3)

(评论后编辑)

您可以通过为每组连续值分配“头”编号来实现。之后,选择每行的头编号,并按人头聚合。

这是一个例子,CTE的可读性:

WITH
OrderedTable as (
    select value, rownr = row_number() over (order by userid, id)
    from YourTable
    where userid = 2287
),
Heads as (
    select cur.rownr, CurValue = cur.value
    ,   headnr = row_number() over (order by cur.rownr)
    from OrderedTable cur
    left join OrderedTable prev on cur.rownr = prev.rownr+1 
    where IsNull(prev.value,-1) != cur.value
),
ValuesWithHead as (
    select value
    ,   HeadNr = (select max(headnr) 
                from Heads 
                where Heads.rownr <= data.rownr)
    from OrderedTable data
)
select Value, [Count] = count(*)
from ValuesWithHead
group by HeadNr, value
order by count(*) desc

这将输出:

Value   Count
2       4
3       3
1       2
2       1
2       1
7       1

使用“top 1”仅选择第一行。

这是我创建测试数据的查询:

create table YourTable (
    id int primary key,
    userid int,
    variable varchar(25),
    value int
)
insert into YourTable (id, userid, variable, value) values (3115, 2287, 'votech05', 2)
insert into YourTable (id, userid, variable, value) values (3116, 2287, 'comcol05', 1)
insert into YourTable (id, userid, variable, value) values (3117, 2287, 'fouryr05', 1)
insert into YourTable (id, userid, variable, value) values (3118, 2287, 'none05', 2)
insert into YourTable (id, userid, variable, value) values (3119, 2287, 'ocol1_05', 2)
insert into YourTable (id, userid, variable, value) values (3120, 2287, 'disnone', 2)
insert into YourTable (id, userid, variable, value) values (3121, 2287, 'dissense', 2)
insert into YourTable (id, userid, variable, value) values (3122, 2287, 'dismobil', 3)
insert into YourTable (id, userid, variable, value) values (3123, 2287, 'dislearn', 3)
insert into YourTable (id, userid, variable, value) values (3124, 2287, 'disment', 3)
insert into YourTable (id, userid, variable, value) values (3125, 2287, 'disother', 2)
insert into YourTable (id, userid, variable, value) values (3126, 2287, 'disrefus', 7)

答案 1 :(得分:2)

这可能是游标最好解决的问题之一。试一试。它应该是接近的,但它没有经过测试,因为你没有提供带有样本数据的CREATE TABLE和INSERT语句来使这很容易。

declare @userid int
set @userid = 2287;
declare C cursor fast_forward for
select VALUE from T
where USERID = @userid
order by ID;

declare @value int, @prevvalue int;
declare @runcount int, @runlongest int;
set @runlongest = 0;
declare @valuelongest int;
open C;
fetch next from C into @value;
while @@fetch_status = 0 begin
  if @value = @prevvalue set @runcount = @runcount + 1 else set @runcount = 1;
  if @runcount > @runlongest begin
    set @runlongest = @runcount;
    set @valuelongest = @value;
  end;
  set @prevvalue = @value;
  fetch next from C into @value;
end;
select @userid as USERID, @valuelongest as VALUE, @runlongest as [COUNT];

close C;
deallocate C;

75M行不会很快,但也可能不会太慢​​。如果您的运行时间非常长,并且您拥有正确的索引,则可以通过在临时表中使用row_number对行进行编号,然后使用一次跳过运行的WHILE循环来做得更好。如果您认为值得关注,请告诉我(如果可以,请使用示例数据发布CREATE TABLE和INSERT语句)。

答案 2 :(得分:0)

没有测试它我认为以下应该有效:

Row_number()over(按用户ID划分,按id划分值)

完成此操作后,只需选择row_nunber最高的那个

请告诉我这是否有效!!

谢谢,Edi