如何创建由其他列分段的自动增量列
How to create an auto increment column that is segmented by an other column
我需要创建一个包含递增 ID 的 table,但我希望 ID 能够根据其他列自动分段。这就是我想要的:
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;
我希望它显示如下内容:
myKey category incrementalId
----------- ----------- -------------
1 100 1
2 200 1
3 100 2
4 100 3
5 100 4
6 200 2
意思是我希望 incrementalId
每个类别自动递增,并从 1 重新开始插入任何新类别。我希望在 table 中的任何插入上自行完成此操作(我不想在插入此 table 时必须记住这样做)。
我认为这可以通过 window 函数和触发器完成,但我不知道如何实现。
编辑:
我希望持久化数据以避免在发生数据删除时移动 incrementalId。此外,理想情况下,在删除行的情况下不会重新给出相同的 ID(与序列或 IDENTITY 的工作方式相同)
有什么想法吗?
我认为您不需要在 table 中添加额外的列 'IncrementalID'。
你可以在你的 select 声明中做到这一点。
SELECT myKey,category,ROW_NUMBER() OVER(PARTITION BY category ORDER BY myKey )incrementalId
FROM MyTable
ORDER BY myKey
示例输出。
否则您可以根据实际 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
请试试这个(在此 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
使用触发器时请记住两点——
1. 我们正在从触发器内部更新 table,因此如果您在此 table 上有任何其他触发器(更新后),该触发器也将被执行。
2. 当使用单个 select 查询在此 table 中插入多行时,此触发器将只执行一次。
您可以通过触发器轻松实现:
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
您可以使用下面的更新查询
更新相同的table
;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
将@Kannan 的解决方案扩展到从计算列调用的 UDF:
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
并将table修改为:
CREATE TABLE dbo.MyTable (
myKey INT IDENTITY PRIMARY KEY,
category INT,
incrementalId AS ([dbo].[fnsPartId]([mykey]))
);
尝试在列上创建默认约束。使用函数为行生成下一个值作为函数返回的默认行。
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;
我需要创建一个包含递增 ID 的 table,但我希望 ID 能够根据其他列自动分段。这就是我想要的:
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;
我希望它显示如下内容:
myKey category incrementalId
----------- ----------- -------------
1 100 1
2 200 1
3 100 2
4 100 3
5 100 4
6 200 2
意思是我希望 incrementalId
每个类别自动递增,并从 1 重新开始插入任何新类别。我希望在 table 中的任何插入上自行完成此操作(我不想在插入此 table 时必须记住这样做)。
我认为这可以通过 window 函数和触发器完成,但我不知道如何实现。
编辑:
我希望持久化数据以避免在发生数据删除时移动 incrementalId。此外,理想情况下,在删除行的情况下不会重新给出相同的 ID(与序列或 IDENTITY 的工作方式相同)
有什么想法吗?
我认为您不需要在 table 中添加额外的列 'IncrementalID'。
你可以在你的 select 声明中做到这一点。
SELECT myKey,category,ROW_NUMBER() OVER(PARTITION BY category ORDER BY myKey )incrementalId
FROM MyTable
ORDER BY myKey
示例输出。
否则您可以根据实际 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
请试试这个(在此 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
使用触发器时请记住两点—— 1. 我们正在从触发器内部更新 table,因此如果您在此 table 上有任何其他触发器(更新后),该触发器也将被执行。 2. 当使用单个 select 查询在此 table 中插入多行时,此触发器将只执行一次。
您可以通过触发器轻松实现:
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
您可以使用下面的更新查询
更新相同的table;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
将@Kannan 的解决方案扩展到从计算列调用的 UDF:
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
并将table修改为:
CREATE TABLE dbo.MyTable (
myKey INT IDENTITY PRIMARY KEY,
category INT,
incrementalId AS ([dbo].[fnsPartId]([mykey]))
);
尝试在列上创建默认约束。使用函数为行生成下一个值作为函数返回的默认行。
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;