Please start any new threads on our new site at https://forums.sqlteam.com. We've got lots of great SQL Server experts to answer whatever question you can come up with.

 All Forums
 SQL Server 2008 Forums
 Transact-SQL (2008)
 retrieve next value out of sequence

Author  Topic 

sfortner
Yak Posting Veteran

63 Posts

Posted - 2011-09-15 : 10:56:43
Hey gurus, I have a table that I inherited that contains the following pertinent fields. It's not set up well, because there's an ordered set of job numbers followed by a separate ordered set of job numbers for a separate division:


create table Job (JobNumber int, Entered datetime)
insert into Job
select '4000', '01/01/2011' union
select '4001', '01/02/2011' union
select '4001', '01/03/2011' union
select '4002', '01/09/2011' union
select '9668', '04/20/2011' union
select '9669', '04/30/2011'


There's a udf that retrieves the next available job number, in this case, it would return 4003, but it's currently taking 12 seconds to return. I need to make this more efficient. I was able to get it down to 3 seconds by adding a table variable and removing some checks for date and job number sequence (don't ask...). I also tried a while loop and just loop through the job numbers till the difference between the next job number and the previous > 1, but that's taking around 45 seconds to run. Here's what I've got now, but I'm certain this can be rewritten (with a CTE?) to be more efficient and faster:


DECLARE @result int, @reset datetime, @default int
set @reset = '2010.01.01'
set @default = 4000

declare @tvJob table (JobNumber int)
insert into @tvJob select distinct JobNumber from Job where JobNumber >= @default and Entered >= @reset

select top 1 JobNumber + 1 from
(select JobNumber,
(select top 1 JobNumber from @tvJob where /*Entered >= @reset and*/ JobNumber = j.JobNumber+1 order by JobNumber desc) NextJobNumber
from @tvJob j /*where JobNumber >= @default and Entered >= @reset*/) t
where t.NextJobNumber is null
order by JobNumber asc


--Steve

ehorn
Master Smack Fu Yak Hacker

1632 Posts

Posted - 2011-09-15 : 18:06:25
Hello Steve,

Can you add a 'modulus check' and limit the values to those within the range of @default?

Something like this?


SELECT MAX(jobNumber)+1
FROM Job
WHERE JobNumber >= @default
AND Entered >= @reset
--limit the value by evaluating a modulus
AND JobNumber % @default < 100
Go to Top of Page

sfortner
Yak Posting Veteran

63 Posts

Posted - 2011-09-16 : 08:45:55
Well, that's definitely faster : ) but it's not returning the right results as is. If I add more values to be closer to what we actually have in production with this sql statement:


declare @default int
set @default = 4003
while (@default < 5000)
begin
insert into Job select @default, getdate()
set @default = @default + 1
end


it incorrectly returns 4100. The mod check seems to be more of a "hard-coded" soln where we need to find the first non-sequential number.

quote:
Originally posted by ehorn

SELECT MAX(jobNumber)+1
FROM Job
WHERE JobNumber >= @default
AND Entered >= @reset
--limit the value by evaluating a modulus
AND JobNumber % @default < 100




--Steve
Go to Top of Page

ehorn
Master Smack Fu Yak Hacker

1632 Posts

Posted - 2011-09-16 : 09:00:35
quote:
Originally posted by sfortner

Well, that's definitely faster : ) but it's not returning the right results as is. If I add more values to be closer to what we actually have in production with this sql statement:


declare @default int
set @default = 4003
while (@default < 5000)
begin
insert into Job select @default, getdate()
set @default = @default + 1
end


it incorrectly returns 4100. The mod check seems to be more of a "hard-coded" soln where we need to find the first non-sequential number.

quote:
Originally posted by ehorn

SELECT MAX(jobNumber)+1
FROM Job
WHERE JobNumber >= @default
AND Entered >= @reset
--limit the value by evaluating a modulus
AND JobNumber % @default < 100




--Steve




Good morning Steve,

The example using 'modulus' was intended to stimulate some thought around a simpler solution. If modulus function is considered as a potential solution for filtering this result, it likely would need to be adjusted to meet the needs of the 'job numbers' series data. However, I do not have the benefit of knowing enough about the data specifics to provide a finished solution.

The value of '100' in the modulus example was only for an example. You could change it to '< 1000' and that may provide you the result you are seeking. But without the benefit of deeper understanding of the job numbers data, this may not be an adequate solution either.

Can you share more insight into your job numbers series data. For instance, what happens when the max(jobnumber) reaches 4999 and next series in a job is '5000'. Can this happen? What would be the expected result. Are the job numbers series always organized around the 000's ranges?

Go to Top of Page

sfortner
Yak Posting Veteran

63 Posts

Posted - 2011-09-16 : 09:55:07
Maybe it would help to have more info. This db is used by a job board web app and when they create a new job the GetNewJobNumber udf is called to get the next available job number. There's currently job numbers 4000 - 8677 that are in the range where we want to create the next job number. There's other job numbers in the 1 - 2344 range that are from the old system that we don't want to use, nor do we want to use any job numbers in the 2345 - 3999 range (at least not yet). There's also some other job numbers seeded from 9668 - 9999 that we don't want to use either - these are used for special contract jobs. It's something I inherited, so I can't change it.

That said, that's why we have a @default variable that gets set to 4000 so it uses the 4000 - 9999 range. All the GetNewJobNumber udf should do is get the first available job number, where it hasn't been used yet. At some point job number 5000 in our example could get created, and to confirm, when a new job number is created, it just gets the next available number. The query should determine when the job numbers are not in sequence and return the next one.

--Steve
Go to Top of Page

ehorn
Master Smack Fu Yak Hacker

1632 Posts

Posted - 2011-09-16 : 10:54:33
quote:
Originally posted by sfortner

Maybe it would help to have more info. This db is used by a job board web app and when they create a new job the GetNewJobNumber udf is called to get the next available job number. There's currently job numbers 4000 - 8677 that are in the range where we want to create the next job number. There's other job numbers in the 1 - 2344 range that are from the old system that we don't want to use, nor do we want to use any job numbers in the 2345 - 3999 range (at least not yet). There's also some other job numbers seeded from 9668 - 9999 that we don't want to use either - these are used for special contract jobs. It's something I inherited, so I can't change it.

That said, that's why we have a @default variable
that gets set to 4000 so it uses the 4000 - 9999 range. All the GetNewJobNumber udf should do is get the first available job number, where it hasn't been used yet. At some point job number 5000 in our example could get created, and to confirm, when a new job number is created, it just gets the next available number. The query should determine when the job numbers are not in sequence and return the next one.

--Steve



I see, thanks for clarifying the need. Perhaps another method would be to create and use a table of numbers and then compare the numbers table to what exists in your jobNumbers table. Then Find the minimum available number for which there is no corresponding jobnumber. Something like this;


SELECT MIN(n)
FROM dbo.numbers n
LEFT JOIN Job j ON j.jobNumber = n.n
WHERE n.n >= @default AND n.n < 9668
AND j.jobNumber IS NULL


So here, dbo.numbers contains only one int field [n] which is a primary key and contains those numbers for which jobs can be assigned. The first null value it finds when joined to your Jobs table is then the value returned.

HTH.
Go to Top of Page

sfortner
Yak Posting Veteran

63 Posts

Posted - 2011-09-16 : 14:06:38
quote:
Originally posted by ehorn


I see, thanks for clarifying the need. Perhaps another method would be to create and use a table of numbers and then compare the numbers table to what exists in your jobNumbers table. Then Find the minimum available number for which there is no corresponding jobnumber. Something like this;


SELECT MIN(n)
FROM dbo.numbers n
LEFT JOIN Job j ON j.jobNumber = n.n
WHERE n.n >= @default AND n.n < 9668
AND j.jobNumber IS NULL


So here, dbo.numbers contains only one int field [n] which is a primary key and contains those numbers for which jobs can be assigned. The first null value it finds when joined to your Jobs table is then the value returned.

HTH.



Brilliant - works perfectly - and it even does it in 0 ms vs 11333 ms for the existing soln! The old Celko numbers table to the rescue. I'm surprised that it runs in 0 ms too - wouldn't have thought that would have been possible, but these are fairly small tables and the est execution plan shows it's using the new non-clustered index I added and the pk index on the Numbers table. Great job - thanks again!

--Steve
Go to Top of Page

ehorn
Master Smack Fu Yak Hacker

1632 Posts

Posted - 2011-09-16 : 14:12:43
quote:
Originally posted by sfortner

Brilliant - works perfectly - and it even does it in 0 ms vs 11333 ms for the existing soln! The old Celko numbers table to the rescue. I'm surprised that it runs in 0 ms too - wouldn't have thought that would have been possible, but these are fairly small tables and the est execution plan shows it's using the new non-clustered index I added and the pk index on the Numbers table. Great job - thanks again!

--Steve



yvw,

Have a nice day.
Go to Top of Page
   

- Advertisement -