Recent PostsRecent Posts Popular TopicsPopular Topics
 Home Search Members Calendar Who's On

 Finding Gaps in a Sequential Number Sequence Rate Topic Display Mode Topic Options
Author
 Message
 Posted Tuesday, January 5, 2010 8:46 PM
 Forum Newbie Group: General Forum Members Last Login: Wednesday, February 23, 2011 8:25 PM Points: 7, Visits: 33
 Thanks for your all excellent solutions. Much appreciated!
Post #842529
 Posted Saturday, July 2, 2011 11:35 AM
 Forum Newbie Group: General Forum Members Last Login: Tuesday, March 5, 2013 1:45 AM Points: 3, Visits: 23
 Mike Arney (4/3/2006)If you just need to identify IF a list of numbers is in sequence, but not where the gaps are, I believe you can do it in a single pass of the table. (Warning -- to understand this algorithm, some high school math is required!)Or some grade school one:SELECT CASE WHEN COUNT(Id) = MAX(Id) - MIN(Id) + 1 THEN 'In sequence' ELSE 'Not sequence' END FROM yourtable
Post #1135534
 Posted Saturday, July 2, 2011 11:50 AM
 Forum Newbie Group: General Forum Members Last Login: Tuesday, March 5, 2013 1:45 AM Points: 3, Visits: 23
 Here is another thing i found and modified a little.Takes under a second for 1 millon rows and returns gaps size sorted Change tables and fields surrounded by []----------------------------------------SELECT LastSysId + 1 AS GapFrom, NextSysId - 1 AS GapTo, NextSysId - (LastSysId + 1) AS GapSizeFROM ( SELECT ( SELECT TOP 1 [SysId] FROM [yourtable] WHERE [SysId] < a.[SysId] ORDER BY [SysId] DESC ) AS LastSysId, a.[SysId] AS NextSysId FROM [yourtable] AS a LEFT JOIN [yourtable] AS b ON a.[SysId] = b.[SysId] + 1 WHERE b.[SysId] IS NULL) AS aWHERE LastSysId IS NOT NULLORDER BY 3 DESC
Post #1135537
 Posted Saturday, July 2, 2011 10:35 PM
 SSC-Forever Group: General Forum Members Last Login: Today @ 5:09 PM Points: 42,074, Visits: 39,457
 Martin Grape (7/2/2011)Here is another thing i found and modified a little.Takes under a second for 1 millon rows and returns gaps size sorted Change tables and fields surrounded by []----------------------------------------SELECT LastSysId + 1 AS GapFrom, NextSysId - 1 AS GapTo, NextSysId - (LastSysId + 1) AS GapSizeFROM ( SELECT ( SELECT TOP 1 [SysId] FROM [yourtable] WHERE [SysId] < a.[SysId] ORDER BY [SysId] DESC ) AS LastSysId, a.[SysId] AS NextSysId FROM [yourtable] AS a LEFT JOIN [kicks_medlem_20110630] AS b ON a.[SysId] = b.[SysId] + 1 WHERE b.[SysId] IS NULL) AS aWHERE LastSysId IS NOT NULLORDER BY 3 DESCNicely done and I can verify the speed. The only problem is that the code doesn't recognize missing "SysID" of 1. In fact, it doesn't recognize any gap from 1 to x-1 if all the rows between 1 to x-1 are missing. That could be OK for some folks. For me, it's usually not. It's neither wrong nor right. "It Depends" on what it's being used for. --Jeff Moden"RBAR is pronounced "ree-bar" and is a "Modenism" for "Row-By-Agonizing-Row".First step towards the paradigm shift of writing Set Based code: Stop thinking about what you want to do to a row... think, instead, of what you want to do to a column." Helpful Links:How to post code problemsHow to post performance problems
Post #1135582
 Posted Sunday, July 3, 2011 8:24 AM
 Forum Newbie Group: General Forum Members Last Login: Tuesday, March 5, 2013 1:45 AM Points: 3, Visits: 23
@Jeff

Here is a little stored procedure i made out of this that will return a list of gaps including missing 1-x (or 0-x or whatever-x) or
return the first available number i a sequence. Njoy

`-- Drop procedure if it existsIF EXISTS (SELECT * FROM [sys].[objects] WHERE OBJECT_ID = OBJECT_ID(N'[dbo].[FindGap]') AND [type] IN (N'P', N'PC')) DROP PROCEDURE [dbo].[FindGap]GO-- Create procedureCREATE PROCEDURE [dbo].[FindGap]   @strFindGapTable         VARCHAR(254),   @strFindGapField         VARCHAR(254),   @binFindGapLower         BIGINT = 1,   @intFindGapMode          INT = 0   AS-- +---------------------------------------------------------------------------+-- |                                                                           |-- | Type    : SQL Procedure                                                   |-- | Name    : FindGap                                                         |-- | Version : 1.1                                                             |-- | Creator : Martin Grape, ActionBase, +46 73 391 88 89                      |-- | Info    : Returns first available numer in a field of sequence or         |-- |         : displays a list of available gaps and number of gaps.           |-- | Input   : @strFindGapTable -> Name of table to run on                     |-- |         : @strFindGapField -> Name of field                               |-- |         : @strFindGapLower -> Lower value to find gap from                |-- |         : @strFindGapMode -> 0=All gaps, 1=Next available number          |-- | Example : EXEC FindGap 'yourtable', 'sysid', 1, 1                         |-- | History : 2011-07-03 Created v1.0                                         |-- |         : 2011-07-03 Added check for all numbers in sequence v1.1         |-- |         :                                                                 |-- |                                                                           |-- +---------------------------------------------------------------------------+-- Declare variablesDECLARE @strFindGapSQL    AS NVARCHAR(2000);DECLARE @strFindGapPar    AS NVARCHAR(2000);DECLARE @binFindGapMin    AS BIGINT;DECLARE @binFindGapMax    AS BIGINT;DECLARE @binFindGapCount  AS BIGINT;DECLARE @binFindGapFirst  AS BIGINT;-- Make SQL to find min and max valuesSET @strFindGapSQL = N'SELECT @binMinVal = MIN(' + @strFindGapField + '), @binMaxVal = MAX(' + @strFindGapField + '), @binCount = COUNT(*) FROM ' + @strFindGapTable;SET @strFindGapPar = N'@binMinVal BIGINT OUTPUT, @binMaxVal BIGINT OUTPUT, @binCount BIGINT OUTPUT';-- Get values into variablesEXEC SP_EXECUTESQL  @strFindGapSQL, @strFindGapPar, @binFindGapMin OUTPUT, @binFindGapMax OUTPUT, @binFindGapCount OUTPUT;-- Check modeIF @intFindGapMode = 1   -- Next available sequence number   BEGIN      -- Check if min is bigger than supplied lower, then take that value      IF @binFindGapMin > @binFindGapLower          BEGIN               RETURN @binFindGapLower;         END      -- Check if max is smaller than supplied lower, then take that value      ELSE IF @binFindGapMax < @binFindGapLower          BEGIN            RETURN @binFindGapLower;         END      -- Check if max is the same as supplied lower, then return + 1      ELSE IF @binFindGapMax = @binFindGapLower          BEGIN            RETURN @binFindGapLower + 1;         END      -- Check if no existing gaps, return higest + 1      ELSE IF @binFindGapCount = @binFindGapMax - @binFindGapMin + 1         BEGIN            RETURN @binFindGapMax + 1         END      -- Get first available number         ELSE               BEGIN                  -- Create SQL to get next value             SET @strFindGapSQL = N'SELECT TOP 1 @binMinVal = LastSysId + 1 FROM (SELECT (SELECT TOP 1 ' + @strFindGapField + ' FROM ' + @strFindGapTable + ' WHERE ' + @strFindGapField + ' < a.' + @strFindGapField + ' ORDER BY ' + @strFindGapField + ' DESC) AS LastSysId, a.' + @strFindGapField + ' AS NextSysId FROM ' + @strFindGapTable + ' AS a LEFT JOIN ' + @strFindGapTable + ' AS b ON a.' + @strFindGapField + ' = b.' + @strFindGapField + ' + 1 WHERE b.' + @strFindGapField + ' IS NULL) AS a WHERE LastSysId IS NOT NULL ORDER BY 1';            SET @strFindGapPar = N'@binMinVal BIGINT OUTPUT';            -- Get value into variable            EXEC SP_EXECUTESQL  @strFindGapSQL, @strFindGapPar, @binFindGapFirst OUTPUT;            -- Return value            RETURN @binFindGapFirst;         END      END ELSE   -- All available sequence numbers as a list   BEGIN      -- Check if first value is bigger than supplied lower, then add a row      IF @binFindGapLower < @binFindGapMin         BEGIN            -- Add a union from supplied lower to the first actual value            SET @strFindGapSQL = N'SELECT ' + CAST(@binFindGapLower AS NVARCHAR) + ' AS GapFrom, ' + CAST(@binFindGapMin - 1 AS NVARCHAR) + ' AS GapTo, ' + CAST(@binFindGapMin - @binFindGapLower AS NVARCHAR) + ' AS GapSize UNION ';         END         ELSE         BEGIN         -- Reset variable         SET @strFindGapSQL = '';      END                        -- Check if to add       SET @strFindGapSQL = @strFindGapSQL + 'SELECT LastSysId + 1 AS GapFrom, NextSysId - 1 AS GapTo, NextSysId - (LastSysId + 1) AS GapSize FROM (SELECT (SELECT TOP 1 ' + @strFindGapField + ' FROM ' + @strFindGapTable + ' WHERE ' + @strFindGapField + ' < a.' + @strFindGapField + ' ORDER BY ' + @strFindGapField + ' DESC) AS LastSysId, a.' + @strFindGapField + ' AS NextSysId FROM ' + @strFindGapTable + ' AS a LEFT JOIN ' + @strFindGapTable + ' AS b ON a.' + @strFindGapField + ' = b.SysId + 1 WHERE b.' + @strFindGapField + ' IS NULL) AS a WHERE LastSysId IS NOT NULL ORDER BY 1';      -- Run the select      EXEC (@strFindGapSQL);      -- Return also the rowcount      RETURN @@ROWCOUNT;   END-- +------------------+-- | END OF PROCEDURE |-- +------------------+GO-- +---------+-- | Testing |-- +---------+-- Drop test table if it existstIF EXISTS (SELECT * FROM [sys].[objects] WHERE OBJECT_ID = OBJECT_ID(N'[dbo].[yourtable]') AND [type] IN (N'U')) DROP TABLE [dbo].[yourtable]GO-- Add dummy dataSELECT 2 AS SysId INTO yourtable UNIONSELECT 6 AS SysId UNIONSELECT 8 AS SysId UNIONSELECT 9 AS SysId UNIONSELECT 11 AS SysId;-- Example 1: Adds a row with next available number starting on 0 , displays the result table and selects next available numberDECLARE @binFindGapReturn BIGINT;EXEC @binFindGapReturn = FindGap 'yourtable', 'sysid', 0, 1;INSERT INTO yourtable (SysId) SELECT @binFindGapReturnSELECT * FROM yourtable ORDER BY 1EXEC @binFindGapReturn = FindGap 'yourtable', 'sysid', 0, 1;SELECT @binFindGapReturn AS NextValue;GO-- Example 2: Returns all the gaps starting on 1 as a resultset and returns number of gapsDECLARE @binFindGapReturn BIGINT;EXEC @binFindGapReturn = FindGap 'yourtable', 'sysid', 1, 2;SELECT @binFindGapReturn AS NoOfGaps;GO-- Example 3: Fill missing sequences to get max + 1INSERT INTO yourtable SELECT 1INSERT INTO yourtable SELECT 3INSERT INTO yourtable SELECT 4INSERT INTO yourtable SELECT 5INSERT INTO yourtable SELECT 7INSERT INTO yourtable SELECT 10DECLARE @binFindGapReturn BIGINT;EXEC @binFindGapReturn = FindGap 'yourtable', 'sysid', 0, 1;SELECT @binFindGapReturn AS NextValue;GO-- Drop test table if it existstIF EXISTS (SELECT * FROM [sys].[objects] WHERE OBJECT_ID = OBJECT_ID(N'[dbo].[yourtable]') AND [type] IN (N'U')) DROP TABLE [dbo].[yourtable]GO`

Post Attachments
 FindGap.sql.txt (18 views, 7.35 KB)
Post #1135616
 Posted Tuesday, September 11, 2012 5:10 AM
 Forum Newbie Group: General Forum Members Last Login: Wednesday, October 10, 2012 3:37 AM Points: 1, Visits: 2
 Dear Sir, The query what u have posted helped me exactly the way i want the result. But i am completeley unaware of how it get executed. Kindly help.
Post #1357319
 Posted Thursday, August 29, 2013 4:21 PM
 Forum Newbie Group: General Forum Members Last Login: Thursday, March 10, 2016 12:57 PM Points: 1, Visits: 77
 n
Post #1489950
 Posted Friday, November 20, 2015 7:13 PM
 Forum Newbie Group: General Forum Members Last Login: Friday, November 20, 2015 7:04 PM Points: 2, Visits: 34
 Hey!Mike Arney this is a good try. I have tried various algorithms in my experience and have never come across anything as efficient as this. However, the Math behind this was not adding up for me. Therefore i went to http://mathforum.org/dr.math/ & Thanks To Dr Peterson who confirmed that this formula is invalid.Try to use your algorithm for any of the below sequence. 1, 14Or, if you prefer, 1, 3, 5, 6Cheers!Chinkit
Post #1738772

 Permissions