{"id":648,"date":"2010-12-30T20:20:45","date_gmt":"2010-12-31T01:20:45","guid":{"rendered":"http:\/\/anvil-of-time.com\/wordpress\/?p=648"},"modified":"2021-03-10T19:01:02","modified_gmt":"2021-03-11T00:01:02","slug":"next-key-service-for-database-tables","status":"publish","type":"post","link":"https:\/\/anvil-of-time.com\/ms-sql-server\/next-key-service-for-database-tables\/","title":{"rendered":"Next Key Service for Database tables"},"content":{"rendered":"<p>This provides for an easy way to generate key values for tables in a database when you don&#8217;t want to use a system generated value (like an IDENTITY column).  The example is for MS SQLServer 2000 but could be adapted to any database which supports stored procedures.  Note that this service assumes the key value it derives does not already exist in the database table it is passed.<\/p>\n<p>You need a table in your database.<\/p>\n<pre name=\"code\" class=\"VB\">if exists (select * from dbo.sysobjects where id = object_id(N&#039;[dbo].[cmnNextKey]&#039;) and OBJECTPROPERTY(id, N&#039;IsUserTable&#039;) = 1)\ndrop table [dbo].[cmnNextKey]\nGO\n\nCREATE TABLE [dbo].[cmnNextKey] (\n\t[tableName] [varchar] (128) NOT NULL ,\n\t[nextKey] [decimal](18, 0) NOT NULL ,\n) ON [PRIMARY]\nGO\n-- add primary key\nALTER TABLE [dbo].[cmnNextKey] WITH NOCHECK ADD \n\tCONSTRAINT [PK_cmnNextKey] PRIMARY KEY  CLUSTERED \n\t(\n\t\t[tableName]\n\t) WITH  FILLFACTOR = 90  ON [PRIMARY] \nGO\n-- permissions\nGRANT  SELECT ,  UPDATE ,  INSERT ,  DELETE  ON [dbo].[cmnNextKey]  TO [public]\nGO<\/pre>\n<p>And a stored procedure.<\/p>\n<pre name=\"code\" class=\"VB\">-- stored procedure spcmn_getNextKey\n\nSET QUOTED_IDENTIFIER ON \nGO\nSET ANSI_NULLS ON \nGO\n\nif exists (select * from dbo.sysobjects where id = object_id(N&#039;[dbo].[spcmn_getNextKey]&#039;) and OBJECTPROPERTY(id, N&#039;IsProcedure&#039;) = 1)\ndrop procedure [dbo].[spcmn_getNextKey]\nGO\n\nCREATE PROCEDURE spcmn_getNextKey\n    @isTableName varchar(128)  = null        -- table to get next key for\n\t,@oiNextKey  decimal(18,0) = null output -- outputs the next key\n   ,@iiKeys      decimal(18,0) = null        -- number of keys to get; null = 1\n\nAS\n   set nocount on\n\n-- ----------------------------------------------------------\n-- Procedure: spcmn_getNextKey\n-- Get next technical key(s) for the table name passed in.\n-- ----------------------------------------------------------\n\n   declare @lsErrorText varchar(255)\n          ,@liERROR     int\n          ,@liROWCOUNT  int\n          ,@liBeginTran int\n\n   -- initialize error handling\n   select @liERROR = @@ERROR\n   if @liERROR &lt;&gt; 0 goto ErrorHandler\n\n   -- begin transaction\n   IF @@TRANCOUNT = 0\n   begin\n      select @liBeginTran = 1\n      begin transaction\n   end\n\n   -- default values\n   select @isTableName = ltrim(rtrim(@isTableName))\n         ,@oiNextKey   = null\n\n   -- validate table name\n   if @isTableName is null\n   begin\n      select @lsErrorText = &#039;Table name not passed&#039;\n      goto ErrorHandler\n   end\n\n   -- validate number of keys requested\n   if @iiKeys &lt;= 0\n   begin\n      select @lsErrorText = &#039;Number of keys requested for table &quot;&#039; + @isTableName + &#039;&quot; must be greater than 0.&#039;\n      goto ErrorHandler\n   end\n\n   -- default number of keys requested to 1\n   select @iiKeys = isNull(@iiKeys, 1)\n\n   -- grab a lock while getting next key\n   select @oiNextKey = nextKey\n     from cmnNextKey (updlock)\n    where tableName = @isTableName\n\n   select @liERROR    = @@ERROR\n         ,@liROWCOUNT = @@ROWCOUNT\n\n   IF @liERROR &lt;&gt; 0 goto ErrorHandler\n\n   if @liROWCOUNT = 0\n   begin\n\n      select @oiNextKey = 1\n\n      -- if new table, add it\n      insert cmnNextKey\n            (tableName\n            ,nextKey)\n      values(@isTableName\n            ,@iiKeys + 1)\n   end\n   else\n   begin\n\n      -- if existing table, bump the next key\n      update cmnNextKey\n         set nextKey = @oiNextKey + @iiKeys\n       where tableName = @isTableName\n   end\n\n   select @liERROR    = @@ERROR\n         ,@liROWCOUNT = @@ROWCOUNT\n\n   if @liROWCOUNT &lt;&gt; 1 or @liERROR &lt;&gt; 0 goto ErrorHandler\n\n\n   if @liBeginTran is not null\n      commit transaction\n\n   return\n\nErrorHandler:\n\n   if @liBeginTran is not null\n      rollback transaction\n\n   select @lsErrorText = &#039;spcmn_getNextKey: &#039; + isNull(@lsErrorText, &#039;Unexpected Error Occurred!&#039;)\n   raiserror(@lsErrorText, 0, 1) WITH SETERROR\n\n\nGO\nSET QUOTED_IDENTIFIER OFF \nGO\nSET ANSI_NULLS ON \nGO\n\nGRANT  EXECUTE  ON [dbo].[spcmn_getNextKey]  TO [public]\nGO\n<\/pre>\n<p>Examples on the usage of next key procedure (run in ISQL session).<\/p>\n<pre name=\"code\" class=\"VB\">set nocount on\ngo\n\n-- ----------------------------\n-- getting next key for &quot;Table&quot;\n-- ----------------------------\ndeclare @nextKey int\nexec spcmn_getNextKey &quot;Table&quot;, @nextKey output\nselect @nextKey &quot;Get Next Key&quot;\ngo\n\n-- --------------------------------\n-- getting next 10 keys for &quot;Table&quot;\n-- --------------------------------\ndeclare @nextKey int\nexec spcmn_getNextKey &quot;Table&quot;, @nextKey output, 10\nselect @nextKey &quot;Get Next 10 Keys&quot;\ngo\n\n-- -------------------------------------------\n-- getting multiple keys and applying to table\n-- -------------------------------------------\n\ndeclare @nextKey  int\ndeclare @keyCount int\n\n-- create and populate a temp table\ncreate table #test (keyColumn int null)\ninsert #test (keyColumn) values (null)\ninsert #test (keyColumn) values (null)\ninsert #test (keyColumn) values (null)\ninsert #test (keyColumn) values (null)\n\n-- get the count\nselect @keyCount = count(*) from #test\n\n-- getting multiple keys for &quot;Table&quot;\nexec spcmn_getNextKey &quot;Table&quot;, @nextKey output, @keyCount\n\nselect @nextKey &quot;Next Key&quot;\n\n-- must subtract 1 before update\nselect @nextKey = @nextKey - 1\n\n-- update each row with a key\nupdate #test\n   set keyColumn = @nextKey\n      ,@nextKey  = @nextKey + 1\n   \n-- see the results\nselect * from #test\n\ndrop table #test\ngo\n\nset nocount off\ngo<\/pre>\n<p>Thanks to A.J. Schroeder.<\/p>\n","protected":false},"excerpt":{"rendered":"<p>This provides for an easy way to generate key values for tables in a database when you don&#8217;t want to use a system generated value (like an IDENTITY column). The example is for MS SQLServer 2000 but could be adapted to any database which supports stored procedures. Note that this service assumes the key value&hellip;<\/p>\n","protected":false},"author":2,"featured_media":0,"comment_status":"closed","ping_status":"closed","sticky":false,"template":"","format":"standard","meta":[],"categories":[8,10],"tags":[16,15],"_links":{"self":[{"href":"https:\/\/anvil-of-time.com\/wp-json\/wp\/v2\/posts\/648"}],"collection":[{"href":"https:\/\/anvil-of-time.com\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/anvil-of-time.com\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/anvil-of-time.com\/wp-json\/wp\/v2\/users\/2"}],"replies":[{"embeddable":true,"href":"https:\/\/anvil-of-time.com\/wp-json\/wp\/v2\/comments?post=648"}],"version-history":[{"count":5,"href":"https:\/\/anvil-of-time.com\/wp-json\/wp\/v2\/posts\/648\/revisions"}],"predecessor-version":[{"id":1906,"href":"https:\/\/anvil-of-time.com\/wp-json\/wp\/v2\/posts\/648\/revisions\/1906"}],"wp:attachment":[{"href":"https:\/\/anvil-of-time.com\/wp-json\/wp\/v2\/media?parent=648"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/anvil-of-time.com\/wp-json\/wp\/v2\/categories?post=648"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/anvil-of-time.com\/wp-json\/wp\/v2\/tags?post=648"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}