3

I have client numbers that are three characters. '001' to '999'. Sometimes there will be gaps that can be reused. I am trying to fill this gaps. So I am searching for a way to find first available gap.

CREATE TABLE co
( co_clno varchar(3));

INSERT INTO co
VALUES 
('001'),
('002'),
('003'),
('005'),
('006'),
('007'),
('008');

The available gap here is '004'

I have tried to first create a list of available number with no sucess:

WITH numbers AS
     (SELECT to_char(generate_series(1,9),'000') num)
SELECT num FROM numbers 
     WHERE num NOT IN(SELECT co_clno FROM co)

The final code should be something like:

WITH numbers AS
     (SELECT to_char(generate_series(1,9),'000') num)
SELECT min(num) FROM numbers 
     WHERE num NOT IN(SELECT co_clno FROM co)

SQLFiddle: http://sqlfiddle.com/#!15/1e48d/1

Thanks in advance for any clue.

3 Answers 3

3
select substring(to_char(n,'000') from 2) as num
from generate_series(1,9) gs(n)

except

select co_clno
from co

order by 1
limit 1
Sign up to request clarification or add additional context in comments.

1 Comment

Thank you! Simple and clean. It worked like expected.
1

@GordonLinoff's idea is right, but I not sure about realization.

So here is my version of the query:

with 
  t(n) as (
    values
      ('001'),
      ('002'),
      ('005'),
      ('003'),
      ('006'),
      ('009'),
      ('010'),
      ('012')),
  t1 as (
    select
      n,
      lag(n, -1) over (order by n)::int - n::int - 1 as cnt
    from t)
select 
  to_char(generate_series(n::int+1, n::int+cnt), '000') as gap
from
  t1
where 
  cnt > 0;

And result is:

 gap  
------
  004
  007
  008
  011
(4 rows)

To solve problem with missing first value just use

select '000' union all <your data>

Comments

1

You can use lead() to find where the gap starts:

select n.*
from (select n.*, lead(co_clno) over (order by co_clno) as next_num
      from co n
     ) n
where next_num is null or 
      n. co_clno::int <> (next_num::int) - 1
order by co_clno
limit 1;

You can get the next value with:

select to_char((n. co_clno::int) + 1, '000')
from (select n.*, lead(co_clno) over (order by co_clno) as next_num
      from co n
     ) n
where next_num is null or 
      n. co_clno::int <> next_num::int
order by co_clno
limit 1;

The only problem with this is that it won't get the first value if missing. Hmmm . . .

select (case when first_num <> '001' then '001'
             else min(to_char((n. co_clno::int) + 1, '000'))
        end)
from (select n.*, lead(co_clno) over (order by co_clno) as next_num,
             min(co_clno) over () as first_num
      from co n
     ) n
where next_num is null or 
      n. co_clno::int <> (next_num::int) - 1
group by first_num;

5 Comments

It will find '009' and not the first available number '004'. sqlfiddle.com/#!15/1e48d/10
The second query will give '002' as available. sqlfiddle.com/#!15/1e48d/11
@sibert . . . The query works fine. Your SQL Fiddle is wrong: sqlfiddle.com/#!15/1e48d/10. I don't know where your CTE came from.
Without CTE (AFAIK): ERROR: relation "numbers" does not exist?
@sibert . . . I just used the naming conventions from your select . . . I didn't realize the table had other names.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.