How to create an auto increment column that is segmented by an other column

回眸只為那壹抹淺笑 提交于 2019-12-11 00:09:13

问题


I need to create a table that will contain a incremental id, but I would like the ids be automatically segmented according to an other column. Here is what I want :

CREATE TABLE dbo.MyTable (
    myKey INT IDENTITY PRIMARY KEY,
    category INT,
    incrementalId INT
);

INSERT INTO dbo.MyTable (category) VALUES (100);
INSERT INTO dbo.MyTable (category) VALUES (200);
INSERT INTO dbo.MyTable (category) VALUES (100);
INSERT INTO dbo.MyTable (category) VALUES (100);
INSERT INTO dbo.MyTable (category) VALUES (100);
INSERT INTO dbo.MyTable (category) VALUES (200);

SELECT *
FROM dbo.MyTable;

I would like this to display something like :

myKey       category    incrementalId
----------- ----------- -------------
1           100         1
2           200         1
3           100         2
4           100         3
5           100         4
6           200         2

Meaning I want the incrementalId to be automatically incremented per category and restart from 1 for any new category inserted. I want this to be done by itself on any inserts in the table (I don't want to have to remember to do that when I insert in this table).

I think this might be done with window functions and maybe a trigger, but I just can't figure how.

EDIT:

I would like the data to be persisted to avoid incrementalId to be shifted if data deletion happens. Also, ideally the same ID would not be re-given in the event of rows deletion (the same way that sequences or IDENTITY works)

Any idea ?


回答1:


CREATE TABLE dbo.MyTable (
  myKey INT IDENTITY PRIMARY KEY,
  category INT,
  incrementalId INT
);
GO

create table dbo.nextCategoryID (
  category int,
  nextidvalue int,
  constraint PK_nextCategoryID primary key clustered( category, nextidvalue )
);
GO

create trigger numberByCategory on dbo.MyTable
after insert as 

-- Automatically add any net new category
insert into dbo.nextCategoryID ( category, nextidvalue )
    select distinct category, 1 as nextidvalue
    from inserted
    where not exists ( select * from dbo.nextCategoryID s
        where s.category = inserted.category );


-- Number the new rows in each incoming category
with numberedrows as (
    select 
        i.myKey, 
        i.category, 
        n.nextidvalue - 1 + row_number() over ( partition by i.category order by i.category ) as incrementalId
    from inserted i
    join dbo.nextCategoryID n on i.category = n.category
)
update m
    set incrementalId = n.incrementalId
from dbo.MyTable m
join inserted i on m.myKey = i.myKey
join numberedrows n on n.myKey = i.myKey;


update dbo.nextCategoryID
    set nextidvalue = 1 + ( select max( m.incrementalId ) 
        from inserted i 
        join dbo.MyTable m on i.myKey = m.myKey
        where i.category = nextCategoryID.category 
    )
where exists ( select * 
    from inserted i
    where i.category = nextCategoryID.category
);

GO

-- Test data 

INSERT INTO dbo.MyTable (category) VALUES (100);
INSERT INTO dbo.MyTable (category) VALUES (200);
INSERT INTO dbo.MyTable (category) VALUES (100);
INSERT INTO dbo.MyTable (category) VALUES (100);
INSERT INTO dbo.MyTable (category) VALUES (100);
INSERT INTO dbo.MyTable (category) VALUES (200);

insert into dbo.MyTable (category) 
values 
    ( 200 ),
    ( 200 ),
    ( 100 ),
    ( 300 ),
    ( 400 ),
    ( 400 )


SELECT *
FROM dbo.MyTable;



回答2:


You can easily achieved this via a trigger:

CREATE TRIGGER dbo.UpdateIncrementalID
ON dbo.MyTable
AFTER  INSERT
AS 
      UPDATE x 
      SET incrementalId = ROW_NUMBER() OVER(PARTITION BY category ORDER BY myKey DESC)
      FROM dbo.MyTable x



回答3:


I think you don't need to add additional column 'IncrementalID' in your table.

You can make it in your select statement.

SELECT myKey,category,ROW_NUMBER() OVER(PARTITION BY category ORDER BY myKey )incrementalId
FROM MyTable
ORDER BY myKey

sample output.

Else you can create a view from your actual table.

CREATE VIEW dbo.VIEW_MyTable 
 AS
  SELECT myKey,category,ROW_NUMBER() OVER(PARTITION BY category ORDER BY myKey )incrementalId
    FROM MyTable
    ORDER BY myKey



回答4:


You can update the same table using below Update query

;with cte as (
select mykey, category, incrementalid, row_number() over (partition by category order by mykey,category) as rn from MyTable
)
update cte
set incrementalId = rn



回答5:


Extending @Kannan's solution into a UDF that's called from a compute column:

create function dbo.fnsPartId(@mykey int)
returns int
as
begin
    declare @Ret int
    ;
    with
    enum as
    (
        select mykey, category, incrementalid, 
            row_number() over (partition by category order by mykey, category) as rn 
            from MyTable
    )
    select @Ret = rn from enum where mykey = @mykey
    return @Ret
end 

And modify the table as:

CREATE TABLE dbo.MyTable (
    myKey INT IDENTITY PRIMARY KEY,
    category INT,
    incrementalId AS ([dbo].[fnsPartId]([mykey]))
);



回答6:


Try to create a default constraint on the column. Use a function to generate next value for the row as a default row returned by the function.




回答7:


Please try this (added after insert trigger on this table)-

create trigger InsertIncrementalID
on dbo.MyTable
after insert
as
begin
    update  mt
    set     incrementalId = (select count(mt1.category) from dbo.MyTable mt1 where mt1.category = mt.category)
    from    dbo.MyTable mt
    inner join inserted i on i.myKey = mt.myKey
end

Please remember two points while using trigger - 1. We are updating table from inside trigger so if you have any other trigger(after update) on this table, that trigger will be executed too. 2. While inserting multiple rows in this table with single select query, this trigger will be executed only once.



来源:https://stackoverflow.com/questions/39498675/how-to-create-an-auto-increment-column-that-is-segmented-by-an-other-column

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!