7

I have a situation where I need to have a secondary column be incremented by 1, assuming the value of another is the same.

Table schema:

CREATE TABLE [APP].[World]
(
    [UID] [uniqueidentifier] ROWGUIDCOL  NOT NULL,
    [App_ID] [bigint] NOT NULL,
    [id] [bigint] NOT NULL,
    [name] [varchar](255) NOT NULL,
    [descript] [varchar](max) NULL,
    [default_tile] [uniqueidentifier] NOT NULL,
    [active] [bit] NOT NULL,
    [inactive_date] [datetime] NULL
)

First off, I have UID which is wholly unique, no matter what App_ID is.

In my situation, I would like to have id be similar to Increment(1,1), only for the same App_ID.

Assumptions:

  1. There are 3 App_Id: 1, 2, 3

Scenario:

  • App_ID 1 has 3 worlds
  • App_ID 2 has 5 worlds
  • App_ID 3 has 1 world

Ideal outcome:

App_ID  id
1       1
2       1
3       1
1       2
2       2
1       3
2       3
2       4
2       5

Was thinking of placing the increment logic in the Insert stored procedure but wanted to see if there would be an easier or different way of producing the same result without a stored procedure.

Figure the available option(s) are triggers or stored procedure implementation but wanted to make sure there wasn't some edge-case pattern I am missing.

Update #1

Lets rethink this a little.

This is about there being a PK UID and ultimately a Partitioned Column id, over App_ID, that is incremented by 1 with each new entry for the associated App_id.

  • This would be similar to how you would do Row_Number() but without all the overhead of recalculating the value each time a new entry is inserted.
  • As well App_ID and id both have the space and potential for being BIGINT; therefore the combination number of possible combinations would be: BIGINT x BIGINT
GoldBishop
  • 2,820
  • 4
  • 47
  • 82
  • 1
    App_ID 1 has 3 worlds what is worlds? – Hazem Torab Mar 15 '16 at 05:53
  • The idea is to have bigint number of Applications with a bigint number of Worlds for each application. then apply a Unique IX on `App_ID` and `id` – GoldBishop Mar 15 '16 at 05:57
  • And what is the point of that uniqueness? Second column has no real sense, this is not an `id` that is a rowno/serialno/sort_no column. And note this column is not related to world or anything so you'll still be able do insert any duplicating values but with different `id`. I'd say there is an _extract entity_ case and actually you need 3 tables: Apps, Worlds, AppWorlds. – Ivan Starostin Mar 15 '16 at 07:36
  • @IvanStarostin the point is that there is no purpose of a Join table for this design as a World can only be associated with one App. It is not a Many-To-Many case scenario. the `id` column is only to describe a unique `bigint` limit in that App's World count, and not the Count of All Worlds in the table. Again, I have a `bigint` possible number of App's and need to be able to have an equivalent `bigint` possible number of Worlds for each App. – GoldBishop Mar 15 '16 at 14:43
  • @GoldBishop Your table is `AppWorlds` from my schema. You have more than one entity in it. _"the id column is only to describe a unique bigint limit in that App's World count"_ - no idea what is your idea here. – Ivan Starostin Mar 15 '16 at 15:00
  • 1
    Possibly related line of thought is here - [Can a sql server table have two identity columns](https://stackoverflow.com/q/349092/352349) (answer is 'no', but there are some interesting workarounds). – Anssssss Mar 15 '16 at 15:04
  • @Anssssss guess it depends on how you view the information. I am not looking for a secondary `Identity(1,1)` column just one that operates like it as it pertains to the `App_Id`. I already have a `rowguid` with the UID column, so don't need another incrementer that is managed by the Instance. – GoldBishop Mar 15 '16 at 15:10
  • @IvanStarostin that is an alternative implementation but seems to be a little overkill. As the World's would never be duplicated across Applications. – GoldBishop Mar 15 '16 at 15:28
  • @Anssssss, good link-back. Did not think about applying a Sequence column. – GoldBishop Mar 15 '16 at 15:30
  • @GoldBishop ...my last reply had no suggestions in it. – Ivan Starostin Mar 15 '16 at 15:30
  • @IvanStarostin combining your two comments into one idea. I have done Join Tables in the past in a Many to Many environment. Unfortunately, this situation does not meet the necessary criteria for a use implementation. It is a possible solution just not a viable long-term solution for this situation. – GoldBishop Mar 15 '16 at 15:31
  • @Anssssss, unfortunately the Sequence information is not reset upon a Truncation of data in the table. May require a little more work to get it working but is a possible alternative solution. – GoldBishop Mar 15 '16 at 15:46
  • Do you need the id to be populated at insertion or can you perhaps just project it out later doing something like "ROW_NUMBER() OVER (PARTITION BY App_Id ORDER BY Name)"? – Anssssss Mar 15 '16 at 16:27
  • @Anssssss, I have used Row_Number in the past for things that needed dynamic reordering, such as Project Items being reordered in a Gantt chart. It is an option but what kind of performance hit would be incurred if you performed this everytime? I am thinking it would be good as long as the World or Application count were small. But as it is designed, this would recalculate every time the RowNumber over all available App's and available Worlds – GoldBishop Mar 15 '16 at 17:36
  • @GoldBishop, yeah row_number is probably not a good option for what you're doing then. – Anssssss Mar 15 '16 at 17:43
  • @HazemTorab, sorry for the delay. The context of the Table names, is really irrelevant. Its the concept to implement a Incrementing RowGUID with an ultimately partitioned Incremented column – GoldBishop Mar 15 '16 at 17:49
  • Are you averse to the use of a TRIGGER combined with a VIEW that returns the current App_ID and MAX(id)? Then, when an INSERT occurs, the trigger can insert MAX(id) + 1 along with the other values being inserted? – Forty3 Mar 24 '16 at 18:53
  • @Forty3 I am not afraid of Database or Table triggers, if that is what you mean ;) I figure the best approach is at the point of Insertion but looking for alternative solutions. One's I may not have thought of or experienced. – GoldBishop Mar 29 '16 at 16:23
  • Agree with Simon Aronsson. However, if its open season on sanity, you could create a Sequence per App_Id :P https://learn.microsoft.com/en-us/sql/t-sql/statements/create-sequence-transact-sql – MarkD Nov 10 '17 at 21:03
  • @MarkD yeah with 2016 that is a consideration – GoldBishop Nov 14 '17 at 00:08
  • @GoldBishop Sequence objects are available from SQL Server 2012 AFAIK – MarkD Nov 14 '17 at 14:55
  • @MarkD guess I skipped that "feature" in 2012 – GoldBishop Nov 14 '17 at 15:23

3 Answers3

1

This is not possible to implement the way you are asking for. As others have pointed out in comments to your original post, your database design would be a lot better of split up in multiple tables, which all have their own identities and utilizes foreign key constraint where necessary.

However, if you are dead set on proceeding with this approach, I would make app_id an identity column and then increment the id column by first querying it for

MAX(identity)

and then increment the response by 1. This kind of logic is suitable to implement in a stored procedure, which you should implement for inserts anyway to prevent from direct sql injections and such. The query part of such a procedure could look like this:

INSERT INTO
  [db].dbo.[yourtable]
SET
  (
    app_id
    , id
  ) 
VALUES
  (
   @app_id
   , (
        SELECT
          MAX(id)
        FROM
          [db].dbo.[table]
          WHERE
            App_id = @app_id
     )
   )

The performance impact for doing so however, is up to you to assess. Also, you need to consider how to properly handle when there is no previous rows for that app_id.

simme
  • 1,514
  • 12
  • 23
  • I agree, in most situations a Join Table would be the best solution but that type of normalization is not warranted by the current needs and design and would add a layer of abstraction that would add a further layer of confusion to what will be a complex data-layer, in itself. – GoldBishop Mar 30 '16 at 12:33
  • Then how about the second suggestion? – simme Mar 30 '16 at 19:41
  • Yeah but Max has a limit on Deletes, as well as Count. I was thinking of a CTE Insert more than the nested design but both get the same result. – GoldBishop Mar 31 '16 at 13:28
0

Simplest Solution will be as below :

    /* Adding Leading 0 to [App_ID] */
[SELECT RIGHT(CONCAT('0000', (([App_ID] - (([App_ID] - 1) % 3)) / 3) + 1), 4) AS [App_ID]

I did the similar thing in my recent code, please find the below image.

enter image description here

Peter
  • 663
  • 1
  • 7
  • 15
  • Curious, why use modulus and then divide by the same factor? Padding is understandable. In comparison to your image, the `D_Tie Break` column would only increment by `1` if the `D_Time Stamp` column was the same value. Assuming `D_Time Stamp` column would be comparable to my `App_Id` – GoldBishop May 12 '16 at 01:04
  • @GoldBishop In my script I need to assign 1-5 values that's I was dividing with 5...You may have to slightly modify it as I don't know the extact values – Peter May 12 '16 at 08:25
  • Very curious solution. Very well done, maybe a little confusing in overall design but understandable. – GoldBishop May 12 '16 at 21:12
  • @peter....I have not had a chance to get back to this design....RL interjected and so hobby development has to be "back-burnered". For the most part, I have deduced that no single approach will win this design. It will have to be a #Frankenstein approach to get it the way I want. – GoldBishop Sep 22 '17 at 12:27
0

Hope the below example will help you.

Explanation part - In the below Code, used the MAX(Primary_Key Identity column) and handled first entry case with the help of ISNULL(NULL,1). In All other cases, it will add up 1 and gives unique value. Based on requirements and needs, we can made changes and use the below example code. WHILE Loop is just added to show demo(Not needed actually).

IF OBJECT_ID('dbo.Sample','U') IS NOT NULL 
  DROP TABLE dbo.Sample 

CREATE TABLE [dbo].[Sample](
    [Sample_key] [int] IDENTITY(1,1) NOT NULL PRIMARY KEY CLUSTERED,    
    [Student_Key] [int] UNIQUE NOT NULL,
    [Notes] [varchar](100) NULL,
    [Inserted_dte] [datetime] NOT NULL
)

DECLARE @A INT,@N INT
SET @A=1
SET @N=10
WHILE(@A<=@N)
BEGIN
    INSERT INTO [dbo].[Sample]([Student_Key],[Notes],[Inserted_dte])
    SELECT ISNULL((MAX([Student_Key])+1),1),'NOTES',GETDATE() FROM  [dbo].[Sample]
    SET @A+=1
END

SELECT * FROM [dbo].[Sample]
Arulmouzhi
  • 1,878
  • 17
  • 20