Skip to main content
Commonmark migration
Source Link

A simple and fast variant:

SELECT min(number) AS first_number, count(*) AS ct_free
FROM (
    SELECT *, number - row_number() OVER (PARTITION BY id_set ORDER BY number) AS grp
    FROM   tbl
    WHERE  status = 'FREE'
    ) x
GROUP  BY grp
HAVING count(*) >= 3  -- minimum length of sequence only goes here
ORDER  BY grp
LIMIT  1;
  • Requires a gapless sequence of numbers in number (as provided in the question).

  • Works for any number of possible values in status besides 'FREE', even with NULL.

  • The major feature is to subtract row_number() from number after eliminating non-qualifying rows. Consecutive numbers end up in the same grp - and grp is also guaranteed to be in ascending order.

  • Then you can GROUP BY grp and count the members. Since you seem to want the first occurrence, ORDER BY grp LIMIT 1 and you get starting position and length of the sequence (can be >= n).

Set of rows

###Set of rows ToTo get an actual set of numbers, don't look up the table another time. Much cheaper with generate_series():

SELECT generate_series(first_number, first_number + ct_free - 1)
    -- generate_series(first_number, first_number + 3 - 1) -- only 3
FROM  (
   SELECT min(number) AS first_number, count(*) AS ct_free
   FROM  (
      SELECT *, number - row_number() OVER (PARTITION BY id_set ORDER BY number) AS grp
      FROM   tbl
      WHERE  status = 'FREE'
      ) x
   GROUP  BY grp
   HAVING count(*) >= 3
   ORDER  BY grp
   LIMIT  1
   ) y;

If you actually want a string with leading zeros like you display in your example values, use to_char() with the FM (fill mode) modifier:

SELECT to_char(generate_series(8, 11), 'FM000000')

SQL Fiddle with extended test case and both queries.

Closely related answer:

A simple and fast variant:

SELECT min(number) AS first_number, count(*) AS ct_free
FROM (
    SELECT *, number - row_number() OVER (PARTITION BY id_set ORDER BY number) AS grp
    FROM   tbl
    WHERE  status = 'FREE'
    ) x
GROUP  BY grp
HAVING count(*) >= 3  -- minimum length of sequence only goes here
ORDER  BY grp
LIMIT  1;
  • Requires a gapless sequence of numbers in number (as provided in the question).

  • Works for any number of possible values in status besides 'FREE', even with NULL.

  • The major feature is to subtract row_number() from number after eliminating non-qualifying rows. Consecutive numbers end up in the same grp - and grp is also guaranteed to be in ascending order.

  • Then you can GROUP BY grp and count the members. Since you seem to want the first occurrence, ORDER BY grp LIMIT 1 and you get starting position and length of the sequence (can be >= n).

###Set of rows To get an actual set of numbers, don't look up the table another time. Much cheaper with generate_series():

SELECT generate_series(first_number, first_number + ct_free - 1)
    -- generate_series(first_number, first_number + 3 - 1) -- only 3
FROM  (
   SELECT min(number) AS first_number, count(*) AS ct_free
   FROM  (
      SELECT *, number - row_number() OVER (PARTITION BY id_set ORDER BY number) AS grp
      FROM   tbl
      WHERE  status = 'FREE'
      ) x
   GROUP  BY grp
   HAVING count(*) >= 3
   ORDER  BY grp
   LIMIT  1
   ) y;

If you actually want a string with leading zeros like you display in your example values, use to_char() with the FM (fill mode) modifier:

SELECT to_char(generate_series(8, 11), 'FM000000')

SQL Fiddle with extended test case and both queries.

Closely related answer:

A simple and fast variant:

SELECT min(number) AS first_number, count(*) AS ct_free
FROM (
    SELECT *, number - row_number() OVER (PARTITION BY id_set ORDER BY number) AS grp
    FROM   tbl
    WHERE  status = 'FREE'
    ) x
GROUP  BY grp
HAVING count(*) >= 3  -- minimum length of sequence only goes here
ORDER  BY grp
LIMIT  1;
  • Requires a gapless sequence of numbers in number (as provided in the question).

  • Works for any number of possible values in status besides 'FREE', even with NULL.

  • The major feature is to subtract row_number() from number after eliminating non-qualifying rows. Consecutive numbers end up in the same grp - and grp is also guaranteed to be in ascending order.

  • Then you can GROUP BY grp and count the members. Since you seem to want the first occurrence, ORDER BY grp LIMIT 1 and you get starting position and length of the sequence (can be >= n).

Set of rows

To get an actual set of numbers, don't look up the table another time. Much cheaper with generate_series():

SELECT generate_series(first_number, first_number + ct_free - 1)
    -- generate_series(first_number, first_number + 3 - 1) -- only 3
FROM  (
   SELECT min(number) AS first_number, count(*) AS ct_free
   FROM  (
      SELECT *, number - row_number() OVER (PARTITION BY id_set ORDER BY number) AS grp
      FROM   tbl
      WHERE  status = 'FREE'
      ) x
   GROUP  BY grp
   HAVING count(*) >= 3
   ORDER  BY grp
   LIMIT  1
   ) y;

If you actually want a string with leading zeros like you display in your example values, use to_char() with the FM (fill mode) modifier:

SELECT to_char(generate_series(8, 11), 'FM000000')

SQL Fiddle with extended test case and both queries.

Closely related answer:

replaced http://dba.stackexchange.com/ with https://dba.stackexchange.com/
Source Link

A simple and fast variant:

SELECT min(number) AS first_number, count(*) AS ct_free
FROM (
    SELECT *, number - row_number() OVER (PARTITION BY id_set ORDER BY number) AS grp
    FROM   tbl
    WHERE  status = 'FREE'
    ) x
GROUP  BY grp
HAVING count(*) >= 3  -- minimum length of sequence only goes here
ORDER  BY grp
LIMIT  1;
  • Requires a gapless sequence of numbers in number (as provided in the question).

  • Works for any number of possible values in status besides 'FREE', even with NULL.

  • The major feature is to subtract row_number() from number after eliminating non-qualifying rows. Consecutive numbers end up in the same grp - and grp is also guaranteed to be in ascending order.

  • Then you can GROUP BY grp and count the members. Since you seem to want the first occurrence, ORDER BY grp LIMIT 1 and you get starting position and length of the sequence (can be >= n).

###Set of rows To get an actual set of numbers, don't look up the table another time. Much cheaper with generate_series():

SELECT generate_series(first_number, first_number + ct_free - 1)
    -- generate_series(first_number, first_number + 3 - 1) -- only 3
FROM  (
   SELECT min(number) AS first_number, count(*) AS ct_free
   FROM  (
      SELECT *, number - row_number() OVER (PARTITION BY id_set ORDER BY number) AS grp
      FROM   tbl
      WHERE  status = 'FREE'
      ) x
   GROUP  BY grp
   HAVING count(*) >= 3
   ORDER  BY grp
   LIMIT  1
   ) y;

If you actually want a string with leading zeros like you display in your example values, use to_char() with the FM (fill mode) modifier:

SELECT to_char(generate_series(8, 11), 'FM000000')

SQL Fiddle with extended test case and both queries.

Closely related answer:

A simple and fast variant:

SELECT min(number) AS first_number, count(*) AS ct_free
FROM (
    SELECT *, number - row_number() OVER (PARTITION BY id_set ORDER BY number) AS grp
    FROM   tbl
    WHERE  status = 'FREE'
    ) x
GROUP  BY grp
HAVING count(*) >= 3  -- minimum length of sequence only goes here
ORDER  BY grp
LIMIT  1;
  • Requires a gapless sequence of numbers in number (as provided in the question).

  • Works for any number of possible values in status besides 'FREE', even with NULL.

  • The major feature is to subtract row_number() from number after eliminating non-qualifying rows. Consecutive numbers end up in the same grp - and grp is also guaranteed to be in ascending order.

  • Then you can GROUP BY grp and count the members. Since you seem to want the first occurrence, ORDER BY grp LIMIT 1 and you get starting position and length of the sequence (can be >= n).

###Set of rows To get an actual set of numbers, don't look up the table another time. Much cheaper with generate_series():

SELECT generate_series(first_number, first_number + ct_free - 1)
    -- generate_series(first_number, first_number + 3 - 1) -- only 3
FROM  (
   SELECT min(number) AS first_number, count(*) AS ct_free
   FROM  (
      SELECT *, number - row_number() OVER (PARTITION BY id_set ORDER BY number) AS grp
      FROM   tbl
      WHERE  status = 'FREE'
      ) x
   GROUP  BY grp
   HAVING count(*) >= 3
   ORDER  BY grp
   LIMIT  1
   ) y;

If you actually want a string with leading zeros like you display in your example values, use to_char() with the FM (fill mode) modifier:

SELECT to_char(generate_series(8, 11), 'FM000000')

SQL Fiddle with extended test case and both queries.

Closely related answer:

A simple and fast variant:

SELECT min(number) AS first_number, count(*) AS ct_free
FROM (
    SELECT *, number - row_number() OVER (PARTITION BY id_set ORDER BY number) AS grp
    FROM   tbl
    WHERE  status = 'FREE'
    ) x
GROUP  BY grp
HAVING count(*) >= 3  -- minimum length of sequence only goes here
ORDER  BY grp
LIMIT  1;
  • Requires a gapless sequence of numbers in number (as provided in the question).

  • Works for any number of possible values in status besides 'FREE', even with NULL.

  • The major feature is to subtract row_number() from number after eliminating non-qualifying rows. Consecutive numbers end up in the same grp - and grp is also guaranteed to be in ascending order.

  • Then you can GROUP BY grp and count the members. Since you seem to want the first occurrence, ORDER BY grp LIMIT 1 and you get starting position and length of the sequence (can be >= n).

###Set of rows To get an actual set of numbers, don't look up the table another time. Much cheaper with generate_series():

SELECT generate_series(first_number, first_number + ct_free - 1)
    -- generate_series(first_number, first_number + 3 - 1) -- only 3
FROM  (
   SELECT min(number) AS first_number, count(*) AS ct_free
   FROM  (
      SELECT *, number - row_number() OVER (PARTITION BY id_set ORDER BY number) AS grp
      FROM   tbl
      WHERE  status = 'FREE'
      ) x
   GROUP  BY grp
   HAVING count(*) >= 3
   ORDER  BY grp
   LIMIT  1
   ) y;

If you actually want a string with leading zeros like you display in your example values, use to_char() with the FM (fill mode) modifier:

SELECT to_char(generate_series(8, 11), 'FM000000')

SQL Fiddle with extended test case and both queries.

Closely related answer:

update fiddle
Source Link
Erwin Brandstetter
  • 182.2k
  • 28
  • 457
  • 620

A simple and fast variant:

SELECT min(number) AS first_number, count(*) AS ct_free
FROM (
    SELECT *, number - row_number() OVER (PARTITION BY id_set ORDER BY number) AS grp
    FROM   tbl
    WHERE  status = 'FREE'
    ) x
GROUP  BY grp
HAVING count(*) >= 3  -- minimum length of sequence only goes here
ORDER  BY grp
LIMIT  1;
  • Requires a gapless sequence of numbers in number (as provided in the question).

  • Works for any number of possible values in status besides 'FREE', even with NULL.

  • The major feature is to subtract row_number() from number after eliminating non-qualifying rows. Consecutive numbers end up in the same grp - and grp is also guaranteed to be in ascending order.

  • Then you can GROUP BY grp and count the members. Since you seem to want the first occurrence, ORDER BY grp LIMIT 1 and you get starting position and length of the sequence (can be >= n).

###Set of rows To get an actual set of numbers, don't look up the table another time. Much cheaper with generate_series():

SELECT generate_series(first_number, first_number + ct_free - 1)
    -- generate_series(first_number, first_number + 3 - 1) -- only 3
FROM  (
   SELECT min(number) AS first_number, count(*) AS ct_free
   FROM  (
      SELECT *, number - row_number() OVER (PARTITION BY id_set ORDER BY number) AS grp
      FROM   tbl
      WHERE  status = 'FREE'
      ) x
   GROUP  BY grp
   HAVING count(*) >= 3
   ORDER  BY grp
   LIMIT  1
   ) y;

If you actually want a string with leading zeros like you display in your example values, use to_char() with the FM (fill mode) modifier:

SELECT to_char(generate_series(8, 11), 'FM000000')

SQL FiddleSQL Fiddle with extended test case and both queries.

Closely related answer:

A simple and fast variant:

SELECT min(number) AS first_number, count(*) AS ct_free
FROM (
    SELECT *, number - row_number() OVER (PARTITION BY id_set ORDER BY number) AS grp
    FROM   tbl
    WHERE  status = 'FREE'
    ) x
GROUP  BY grp
HAVING count(*) >= 3  -- minimum length of sequence only goes here
ORDER  BY grp
LIMIT  1;
  • Requires a gapless sequence of numbers in number (as provided in the question).

  • Works for any number of possible values in status besides 'FREE', even with NULL.

  • The major feature is to subtract row_number() from number after eliminating non-qualifying rows. Consecutive numbers end up in the same grp - and grp is also guaranteed to be in ascending order.

  • Then you can GROUP BY grp and count the members. Since you seem to want the first occurrence, ORDER BY grp LIMIT 1 and you get starting position and length of the sequence (can be >= n).

###Set of rows To get an actual set of numbers, don't look up the table another time. Much cheaper with generate_series():

SELECT generate_series(first_number, first_number + ct_free - 1)
    -- generate_series(first_number, first_number + 3 - 1) -- only 3
FROM  (
   SELECT min(number) AS first_number, count(*) AS ct_free
   FROM  (
      SELECT *, number - row_number() OVER (PARTITION BY id_set ORDER BY number) AS grp
      FROM   tbl
      WHERE  status = 'FREE'
      ) x
   GROUP  BY grp
   HAVING count(*) >= 3
   ORDER  BY grp
   LIMIT  1
   ) y;

If you actually want a string with leading zeros like you display in your example values, use to_char() with the FM (fill mode) modifier:

SELECT to_char(generate_series(8, 11), 'FM000000')

SQL Fiddle with extended test case and both queries.

Closely related answer:

A simple and fast variant:

SELECT min(number) AS first_number, count(*) AS ct_free
FROM (
    SELECT *, number - row_number() OVER (PARTITION BY id_set ORDER BY number) AS grp
    FROM   tbl
    WHERE  status = 'FREE'
    ) x
GROUP  BY grp
HAVING count(*) >= 3  -- minimum length of sequence only goes here
ORDER  BY grp
LIMIT  1;
  • Requires a gapless sequence of numbers in number (as provided in the question).

  • Works for any number of possible values in status besides 'FREE', even with NULL.

  • The major feature is to subtract row_number() from number after eliminating non-qualifying rows. Consecutive numbers end up in the same grp - and grp is also guaranteed to be in ascending order.

  • Then you can GROUP BY grp and count the members. Since you seem to want the first occurrence, ORDER BY grp LIMIT 1 and you get starting position and length of the sequence (can be >= n).

###Set of rows To get an actual set of numbers, don't look up the table another time. Much cheaper with generate_series():

SELECT generate_series(first_number, first_number + ct_free - 1)
    -- generate_series(first_number, first_number + 3 - 1) -- only 3
FROM  (
   SELECT min(number) AS first_number, count(*) AS ct_free
   FROM  (
      SELECT *, number - row_number() OVER (PARTITION BY id_set ORDER BY number) AS grp
      FROM   tbl
      WHERE  status = 'FREE'
      ) x
   GROUP  BY grp
   HAVING count(*) >= 3
   ORDER  BY grp
   LIMIT  1
   ) y;

If you actually want a string with leading zeros like you display in your example values, use to_char() with the FM (fill mode) modifier:

SELECT to_char(generate_series(8, 11), 'FM000000')

SQL Fiddle with extended test case and both queries.

Closely related answer:

clarify
Source Link
Erwin Brandstetter
  • 182.2k
  • 28
  • 457
  • 620
Loading
fix bug
Source Link
Erwin Brandstetter
  • 182.2k
  • 28
  • 457
  • 620
Loading
add variant
Source Link
Erwin Brandstetter
  • 182.2k
  • 28
  • 457
  • 620
Loading
add link
Source Link
Erwin Brandstetter
  • 182.2k
  • 28
  • 457
  • 620
Loading
Source Link
Erwin Brandstetter
  • 182.2k
  • 28
  • 457
  • 620
Loading