Warning: file_get_contents(/data/phpspider/zhask/data//catemap/5/sql/72.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
如何使用SQL Server存储过程声明要分配给实体的整数范围?_Sql_Sql Server_Sql Server 2008_Stored Procedures - Fatal编程技术网

如何使用SQL Server存储过程声明要分配给实体的整数范围?

如何使用SQL Server存储过程声明要分配给实体的整数范围?,sql,sql-server,sql-server-2008,stored-procedures,Sql,Sql Server,Sql Server 2008,Stored Procedures,我有一个SQL Server 2008 R2数据库,用于管理收件人列表。每个收件人列表都需要分配一组序列号,这些序列号保证在过去90天内是唯一的。序列号只是一个介于1和999999之间的整数 我不必跟踪哪个序列号与哪个收件人关联。每个列表可能有超过500K个收件人 我有以下表格: 列表作业 ListJobId int PK ListJobName varchar(64) 收件人 RecipientId int PK ListJobId int FK Na

我有一个SQL Server 2008 R2数据库,用于管理收件人列表。每个收件人列表都需要分配一组序列号,这些序列号保证在过去90天内是唯一的。序列号只是一个介于1和999999之间的整数

我不必跟踪哪个序列号与哪个收件人关联。每个列表可能有超过500K个收件人

我有以下表格:

列表作业

    ListJobId int PK
    ListJobName varchar(64)
收件人

    RecipientId int PK
    ListJobId int FK
    Name varchar(64)
ListJobSerialRange

    ListJobSerialRangeId int PK
    ListJobId int FK
    DateClaimed datetime
    SerialNumberStart int
    SerialNumberEnd int
ListJobSerialRange
表存储将分配给应用层中收件人的声明序列号范围。分配给列表作业的所有范围之和必须等于收件人数量,因为每个收件人最终将被分配一个序列号

可以经常从
列表作业中添加和删除收件人。如果我们添加收件人,我们将需要为他们申请额外的序列号。如果我们删除收件人,我们希望释放声明的序列号,以便我们可以重复使用它们,这样我们就不会在90天内浪费序列号

每个列表作业最终将有一组范围。它不会有一些在90天之前和90天之后的索赔

下面是一个简单的例子:

  • 可能的序列号为1至999999
  • ListJob A已索赔1至10000英镑
  • ListJob已索赔10501至20500英镑
现在ListJob C有1000个收件人。它需要声明两个范围才能满足所有收件人的要求:

10,001 to 10,500 
20,501 to 21,000
到目前为止,我的存储过程有以下内容:

CREATE PROCEDURE [dbo].[ClaimSerialNumbers]
    @ListJobId int,
    @NumDaysUnique int,
    @MaxSerialNumber int
as begin
    set nocount on

    declare @RecipientCount int
    declare @QuantityClaimed int
    declare @QuantityNeeded int
    declare @DateThreshold smalldatetime

    set @DateThreshold = dateadd(day, 0-@NumDaysUnique, getdate())

    select  @RecipientCount = count(*)
    from    dbo.Recipient
    where   ListJobId = @ListJobId

    select  @QuantityClaimed = sum(SerialNumberEnd - SerialNumberStart + 1)
    from    dbo.ListJobSerialRange
    where   ListJobId = @ListJobId

    set @QuantityNeeded = @QuantityClaimed - @RecipientCount

    if (@QuantityNeeded < 0) begin
        delete  dbo.ListJobSerialRange
        where   ListJobId = @ListJobId

        set @QuantityNeeded = @RecipientCount
    end

    if (@QuantityNeeded = 0) begin
        -- if we run the sproc twice and nothing has changed, then nothing to do
        return 0
    end

    -- now the hard part:
    -- i need to claim some serial numbers
    -- ???
end
创建过程[dbo]。[ClaimSerialNumber]
@ListJobId int,
@努姆达伊苏尼克国际酒店,
@MaxSerialNumber int
作为开始
不计较
声明@RecipientCount int
声明@QuantityClaimed int
声明@QuantityRequired int
声明@DateThreshold smalldatetime
set@DateThreshold=dateadd(day,0-@NumDaysUnique,getdate())
选择@RecipientCount=count(*)
来自dbo.Recipient
其中ListJobId=@ListJobId
选择@QuantityClaimed=sum(SerialNumberRend-SerialNumberStart+1)
从dbo.ListJobSerialRange
其中ListJobId=@ListJobId
设置@QuantityRequired=@QuantityClaimed-@RecipientCount
如果(@QuantityRequired<0)开始
删除dbo.ListJobSerialRange
其中ListJobId=@ListJobId
设置@QuantityRequired=@RecipientCount
结束
如果(@QuantityRequired=0)开始
--如果我们运行了两次存储过程,但没有任何更改,则无需执行任何操作
返回0
结束
--现在最难的部分是:
--我需要索取一些序列号
-- ???
结束

我将填充表格,然后只进行更新。下面是一个关于分配范围的示例

--Populate the table; only needs done once if not a table variable
DECLARE @ListJobSerialRange TABLE
(
    ListJobSerialRangeId INT IDENTITY(1,1),
    SerialNumberStart int,
    SerialNumberEnd int,
    ListJobId int,
    DateClaimed datetime
 )

DECLARE @Counter BIGINT=1
WHILE @Counter<1000000
BEGIN
  INSERT INTO @ListJobSerialRange
    SELECT @Counter,@Counter+10000,NULL,NULL

  SET @Counter=@Counter+10000
END

-- manual update to show that it will skip ranges to get contiguous ranges
UPDATE @ListJobSerialRange 
SET ListJobId = 9999,DateClaimed=GETDATE()
WHERE ListJobSerialRangeId=1

-- manual update to show that it will skip ranges to get contiguous ranges
UPDATE @ListJobSerialRange 
SET ListJobId = 9929,DateClaimed=GETDATE()
WHERE ListJobSerialRangeId=3



-- this is where my sproc would begin
DECLARE @ListJobSerialRangeIds TABLE(
  ListJobSerialRangeId INT
)


DECLARE @MaxListJobSerialRange INT
SELECT @MaxListJobSerialRange=MAX(ListJobSerialRangeID) FROM @ListJobSerialRange

DECLARE @SerialNumbersNeeded INT = 20001
DECLARE @BlocksNeeded INT= (@SerialNumbersNeeded / 10000) + (@SerialNumbersNeeded % 10000)
DECLARE @ContBlocksAvail INT=0

SET @Counter=1
WHILE @Counter<=@MaxListJobSerialRange AND @ContBlocksAvail<>@BlocksNeeded
BEGIN
    IF EXISTS (
      SELECT *
      FROM @ListJobSerialRange
      WHERE ListJobSerialRangeID = @Counter
      AND ListJobId IS NULL
    )
    BEGIN
        INSERT INTO @ListJobSerialRangeIds SELECT @Counter

        SET @ContBlocksAvail=@ContBlocksAvail+1
    END
    ELSE
    BEGIN
      SET @ContBlocksAvail=0
      DELETE @ListJobSerialRangeIds
    END

    SET @Counter=@Counter+1
END

UPDATE l
SET ListJobId = 1123,DateClaimed=GETDATE()
FROM @ListJobSerialRange l
INNER JOIN @ListJobSerialRangeIds ids on ids.ListJobSerialRangeId=l.ListJobSerialRangeId


SELECT * FROM @ListJobSerialRange
——填充表格;如果不是表变量,则只需执行一次
声明@ListJobSerialRange表
(
ListJobSerialRangeId整数标识(1,1),
SerialNumberStart int,
SerialNumberRend int,
ListJobId int,
日期声明日期时间
)
声明@Counter BIGINT=1

虽然@Counter我终于找到了一些我认为有效的方法。。首先,我获取日期阈值内所有声明的序列号范围,并按开始编号升序对它们进行排序,然后将它们转储到临时表中。下一部分是关键概念。。我可以看一下前一行和下一行。这是一个轻微的性能问题,我必须从临时表中选择3次。但真的不应该有那么多,因为我已经把范围缩小到了那些大于日期阈值的。然后,只需看看可能存在差距的情况

CREATE     PROCEDURE [dbo].[ClaimSerialNumbers]
    @ListJobId int,
    @NumDaysUnique int,
    @MaxSerialNumber int
as
SET NOCOUNT ON; 
BEGIN TRANSACTION

declare @RecipientCount int = 0
declare @QuantityClaimed int = 0
declare @QuantityNeeded int = 0
declare @Now smalldatetime = getdate()
declare @DateThreshold smalldatetime = dateadd(day, 0-@NumDaysUnique, @Now)
declare @RunningTotal int = 0
declare @UnusedRangeCount int = 0

--***************************************
-- Get number of recipients for list job
--***************************************
select  @RecipientCount = coalesce(count(*), 0)
from    dbo.Recipient with(nolock)
where   ListJobId = @ListJobId

--***************************************
-- get how many serial numbers are already claimed
--***************************************
select  @QuantityClaimed = coalesce(sum(SR.SerialNumberEnd - SR.SerialNumberStart + 1), 0)
from    dbo.ListJobSerialRange SR
where   ListJobId = @ListJobId

--***************************************
-- Determine how many we still need
--***************************************
set @QuantityNeeded = @QuantityClaimed - @RecipientCount    

--***************************************
-- if we have more claimed that number of recipients
-- we have deleted recipients and need to free up 
-- some serial numbers
--***************************************
if (@QuantityNeeded < 0) begin
    delete  dbo.ListJobSerialRange
    where   ListJobId = @ListJobId

    set @QuantityNeeded = @RecipientCount
end

--***************************************
-- if we need to claim some serial numbers
--***************************************
if (@QuantityNeeded > 0) begin
    if object_id('tempdb..#SortedRanges') is not null drop table #SortedRanges
    if object_id('tempdb..#UnusedRanges') is not null drop table #UnusedRanges
    create table #SortedRanges(RowNumber int, SerialNumberStart int, SerialNumberEnd int)
    create table #UnusedRanges(SerialNumberStart int, SerialNumberEnd int, RunningTotal int)

    --***************************************
    -- put all the existing ranges within the last N days into a temp table
    --***************************************
    insert  #SortedRanges
            (RowNumber, SerialNumberStart, SerialNumberEnd)
    select  RowNumber = row_number() over (order by SerialNumberStart)
                    , SerialNumberStart
                    , SerialNumberEnd
    from    dbo.ListJobSerialRange
    where   DateClaimed >= @DateThreshold
    order by SerialNumberStart

    --***************************************
    -- if no ranges exist in the last N days, then the whole set is available
    --***************************************
    if (@@rowcount = 0)
    begin
        insert  dbo.ListJobSerialRange
                (ListJobId, DateClaimed, SerialNumberStart, SerialNumberEnd)
        values  (@ListJobId, @Now, 1, @QuantityNeeded)
    end
    --***************************************
    -- if we have serial numbers that were used in the last n days
    -- then look for gaps in the rangesand add those gaps to the unused range temp table
    --***************************************
    else 
    begin
        ;with UnusedRangesFirstPass as (
            select  distinct
                    case 
                        -- start of range (gap at beginning)
                        when P.RowNumber is null and C.SerialNumberStart > 1 then 1
                        -- start of range (gap at end)
                        when N.RowNumber is null and C.SerialNumberEnd < @MaxSerialNumber then C.SerialNumberEnd + 1
                        -- start of range (middle row gaps)
                        else
                            case 
                                when P.SerialNumberEnd is not null and P.SerialNumberEnd < C.SerialNumberStart - 1 then P.SerialNumberEnd + 1
                                when N.SerialNumberStart is not null and N.SerialNumberStart > C.SerialNumberEnd + 1 then C.SerialNumberEnd + 1
                            end
                    end as UnusedStart,
                    case
                        -- end of range (gap at beginning)
                        when P.RowNumber is null and C.SerialNumberStart > 1 then (C.SerialNumberStart - 1)
                        -- end of range (gap at end)
                        when N.RowNumber is null and C.SerialNumberEnd < @MaxSerialNumber then @MaxSerialNumber
                        -- end of range (middle row gaps)
                        else
                            case
                                when P.SerialNumberEnd is not null and P.SerialNumberEnd < C.SerialNumberStart - 1 then C.SerialNumberStart - 1
                                when N.SerialNumberStart is not null and N.SerialNumberStart > C.SerialNumberEnd + 1 then N.SerialNumberStart - 1
                            end
                    end as UnusedEnd
            from #SortedRanges C -- current row
            left join #SortedRanges P ON P.RowNumber = C.RowNumber - 1 -- peek at previous row
            left join #SortedRanges N ON N.RowNumber = C.RowNumber + 1 -- peek at next row
        )
        insert  #UnusedRanges
                (SerialNumberStart, SerialNumberEnd, RunningTotal)
        select  UnusedStart, UnusedEnd, 0
        from    UnusedRangesFirstPass
        where   UnusedStart is not null 
                and UnusedEnd is not null

        --***************************************
        -- update all unused ranges and calculate a running total
        -- for each row
        --***************************************
        update  #UnusedRanges
        set     @RunningTotal = RunningTotal = @RunningTotal + (SerialNumberEnd - SerialNumberStart + 1)

        --***************************************
        -- claim the unused ranges.
        -- only claim the exact number we need
        --***************************************   
        insert  dbo.ListJobSerialRange
                (ListJobId, DateClaimed, SerialNumberStart, SerialNumberEnd)
        select  @ListJobId, @Now, UR1.SerialNumberStart, 
                case 
                    when UR1.RunningTotal > @QuantityNeeded then UR1.SerialNumberEnd - (UR1.RunningTotal - @QuantityNeeded)
                    else UR1.SerialNumberEnd
                end
        from    #UnusedRanges UR1
                left join #UnusedRanges UR2 on UR1.RunningTotal > UR2.RunningTotal
        group by UR1.SerialNumberStart, UR1.SerialNumberEnd, UR1.RunningTotal
        having coalesce(sum(UR2.RunningTotal),0) < @QuantityNeeded
    end
end

--***************************************
-- return results and commit
--***************************************   
select  ListJobSerialRangeId, ListJobId, DateClaimed, SerialNumberStart, SerialNumberEnd
from    dbo.ListJobSerialRange
where   ListJobId = @ListJobId

COMMIT TRANSACTION
创建过程[dbo]。[ClaimSerialNumber]
@ListJobId int,
@努姆达伊苏尼克国际酒店,
@MaxSerialNumber int
作为
不计数;
开始交易
声明@RecipientCount int=0
声明@QuantityClaimed int=0
声明@QuantityRequired int=0
声明@Now smalldatetime=getdate()
声明@DateThreshold smalldatetime=dateadd(天,0-@NumDaysUnique,@Now)
声明@RunningTotal int=0
声明@UnusedRangeCount int=0
--***************************************
--获取列表作业的收件人数
--***************************************
选择@RecipientCount=合并(计数(*),0)
来自dbo.Recipient,带有(nolock)
其中ListJobId=@ListJobId
--***************************************
--获取已声明的序列号数量
--***************************************
选择@QuantityClaimed=coalesce(总和(SR.SerialNumberRend-SR.SerialNumberStart+1),0)
来自dbo.ListJobSerialRange SR
其中ListJobId=@ListJobId
--***************************************
--确定我们还需要多少
--***************************************
设置@QuantityRequired=@QuantityClaimed-@RecipientCount
--***************************************
--如果我们有更多的人申请领取
--我们已删除收件人,需要释放
--一些序列号
--***************************************
如果(@QuantityRequired<0)开始
删除dbo.ListJobSerialRange
其中ListJobId=@ListJobId
设置@QuantityRequired=@RecipientCount
结束
--***************************************
--如果我们需要索取一些序列号
--***************************************
如果(@QuantityRequired>0)开始
如果对象id('tempdb..#SortedRanges')不为空,则放置表#SortedRanges
如果对象id('tempdb..#UnusedRanges')不为空,则删除表#UnusedRanges
创建表#SortedRanges(rownumberint、SerialNumberStart int、serialnumberrend int)
创建表#未使用的范围(SerialNumberStart int、SerialNumberRend int、RunningTotal int)
--***************************************
--将过去N天内的所有现有范围放入临时表中
--***************************************
插入#分拣机