Sql 基于分区递增计数器

Sql 基于分区递增计数器,sql,sql-server,sql-server-2008,tsql,partitioning,Sql,Sql Server,Sql Server 2008,Tsql,Partitioning,假设我有一个在SQL Server 2008中看起来有点像这样的表: UID Std_RecordID ------------------- 1 10 2 10 3 12 4 10 5 10 6 10 7 12 基本上会有一个10秒的序列,然后是一个12秒的序列。10人也可能分别在清单14、50、21、24、31和清单16、52、23、26、33中。2的增量几乎意味着一个集合的结束 每次有新的集合时,我需要增加一个计

假设我有一个在SQL Server 2008中看起来有点像这样的表:

UID    Std_RecordID
-------------------
1      10
2      10
3      12
4      10
5      10
6      10
7      12
基本上会有一个10秒的序列,然后是一个12秒的序列。10人也可能分别在清单14、50、21、24、31和清单16、52、23、26、33中。2的增量几乎意味着一个集合的结束

每次有新的集合时,我需要增加一个计数器

我知道我可以得到一个计数器来递增和重置,如下所示:

select ROW_NUMBER() over (partition by Std_RecordId order by UID) 'Ind'
,UID
from @inputTable
但这并不是我想要的,因为它会产生以下结果:

UID    Std_RecordId    Ind
---------------------------
1      10              1
2      10              2
3      12              1
4      10              1
5      10              2
6      10              3
7      12              1
我需要它来做这样的事情:

UID    Std_RecordId    Ind
------------------------------
1      10              1
2      10              1
3      12              1
4      10              2
5      10              2
6      10              2
7      12              2

一个人如何在不借助迭代的情况下实现这一点?我正在尝试摆脱我正在处理的过程中的迭代,因为迭代是过程中最慢的部分,它会加载除此之外的其他内容。

您可以将问题简化为在当前记录之前出现值为12的记录的次数,然后将其添加到结果中。要获取记录数,可以使用外部应用:

编辑

为了详细说明我在关于临时表而不是表变量的评论中所说的,在我的测试中,使用完全相同数据的完全相同的查询始终运行得更快:

我运行的脚本是:

DECLARE @InputTable TABLE (UID INT, Std_RecordId INT);

INSERT @InputTable (UID, Std_RecordId)
SELECT  TOP 50000
        ROW_NUMBER() OVER(ORDER BY a.object_id),
        CEILING(RAND(CHECKSUM(NEWID())) * 20)
FROM    sys.all_objects a
        CROSS JOIN sys.all_objects b;

CREATE TABLE #InputTable (UID INT, Std_RecordId INT);
INSERT #InputTable (UID, Std_RecordId)
SELECT  UID, Std_RecordId
FROM    @InputTable;

SET STATISTICS TIME ON;

SELECT  i.UID,
        i.Std_RecordId,
        t.Ind
FROM    @InputTable AS i
        OUTER APPLY
        (   SELECT  Ind = COUNT(*) + 1
            FROM    @InputTable AS t
            WHERE   t.Std_RecordId = 12
            AND     t.UID < i.UID
        ) AS t;

SELECT  i.UID,
        i.Std_RecordId,
        t.Ind
FROM    #InputTable AS i
        OUTER APPLY
        (   SELECT  Ind = COUNT(*) + 1
            FROM    #InputTable AS t
            WHERE   t.Std_RecordId = 12
            AND     t.UID < i.UID
        ) AS t;

SET STATISTICS TIME OFF;

DROP TABLE #InputTable;
随着样本量的增加,差距越来越大,但对于10000行,我已经厌倦了等待更多的数据,表变量始终需要7.9秒,而临时表的平均值为0.4秒。我运行了50000行,表变量用了190秒,临时表用了4.6秒,所以差别很大

另一个优点是可以为临时表编制索引,但是我发现临时表的最佳性能是创建一个新的临时表来记录标记的位置,然后使用它为原始表提供排名:

DECLARE @InputTable TABLE (UID INT, Std_RecordId INT);

INSERT @InputTable (UID, Std_RecordId)
SELECT  TOP 1000000
        ROW_NUMBER() OVER(ORDER BY a.object_id),
        CEILING(RAND(CHECKSUM(NEWID())) * 20)
FROM    sys.all_objects a
        CROSS JOIN sys.all_objects b;

DECLARE @Counter TABLE (UID INT PRIMARY KEY, Ind INT NOT NULL);
INSERT @Counter
SELECT  UID, ROW_NUMBER() OVER(ORDER BY UID) + 1
FROM    @InputTable
WHERE   Std_RecordId = 12;

SELECT  i.UID,
        i.Std_RecordId,
        Ind = ISNULL(t.Ind, 1)
FROM    @InputTable AS i
        OUTER APPLY
        (   SELECT  TOP 1 Ind
            FROM    @Counter AS t
            WHERE   t.UID < i.UID
        ) AS t
ORDER BY i.UID;

对于50000行,它始终在不到一秒钟的时间内运行,即使对于一百万行,它也在15-20秒内运行

我假设我可以用我的终端列表替换t.Std_RecordId=12,对吗?如果是这样的话,你太棒了,我感谢你,好心的先生!当我的程序运行时,我将能够测试它并将其标记为正确的:我测试了它,它似乎工作,但它相当缓慢。通过42k条记录需要10米30秒,这根本不算多:表上有什么索引?UID和其他几列,但我的表变量上没有任何索引。我确实尝试过对原始源表运行查询,该表有一个索引,这给了我10毫秒的时间。在桌上,我有大约13分钟的时间。您所采用的方法的问题是,记录越多,花费的时间就越长,因为每次进行检查时,它必须对每条记录进行计数。但是我想不出任何其他的方法。
DECLARE @InputTable TABLE (UID INT, Std_RecordId INT);

INSERT @InputTable (UID, Std_RecordId)
SELECT  TOP 1000000
        ROW_NUMBER() OVER(ORDER BY a.object_id),
        CEILING(RAND(CHECKSUM(NEWID())) * 20)
FROM    sys.all_objects a
        CROSS JOIN sys.all_objects b;

DECLARE @Counter TABLE (UID INT PRIMARY KEY, Ind INT NOT NULL);
INSERT @Counter
SELECT  UID, ROW_NUMBER() OVER(ORDER BY UID) + 1
FROM    @InputTable
WHERE   Std_RecordId = 12;

SELECT  i.UID,
        i.Std_RecordId,
        Ind = ISNULL(t.Ind, 1)
FROM    @InputTable AS i
        OUTER APPLY
        (   SELECT  TOP 1 Ind
            FROM    @Counter AS t
            WHERE   t.UID < i.UID
        ) AS t
ORDER BY i.UID;