【问题标题】:Int Identity Column with Descriminator带鉴别器的 Int 标识列
【发布时间】:2022-01-23 18:10:38
【问题描述】:

我有一个表,其中有一列应该自动递增,但是它应该在另一个描述符列上递增

例如:

Id Filenumber Descriminator More Columns...
2AA15D5E-F158-45AE-902C-CD49644846BC 1 A
FE6B95EE-DFF4-48D4-9BD7-7DB4187A2D6D 2 A
2A132492-447A-485D-A546-2FB9158AE71B 1 B

因此,如果我输入另一个带有描述符“A”的条目,我会得到文件编号 3。但是通过添加一个带有“B”的条目,我会得到 2,因为它是第 2 个“B”行。

我知道的唯一方法是更新触发器,但是否有更简单的解决方案,或者如果它是唯一的方法,它在流量大的情况下是否可靠(具有许多用户的负载平衡系统)?

【问题讨论】:

  • 视图中定义您的文件编号并查询视图。
  • 不是 timestamp、@FlorianSchmidinger ,这是 rowversion 的已弃用同义词,但某种始终递增的值(IDENTITY 或日期和时间值)会起作用。
  • “因为这个表经常改变。” 因为定义改变太频繁了?老实说,如果是这样的话,这听起来像是个问题。表的定义可以更改,是的,但不应该经常更改。
  • 如果 Descriminator 是静态的,您可以使用 AFTER INSERT 触发器来计算新记录。但由于它可以更改,您可能应该将计算留给报告。使用 ROW_NUMBER 或 DENSE_RANK 函数很容易。
  • 表架构更改过于频繁可能暗示架构未规范化。例如。您可以将受架构更改影响的属性移动到两个新表中。一个表 property 唯一标识每个属性,一个连接表 main_property 具有该属性的 main_idproperty_idvalue 列。

标签: sql sql-server tsql


【解决方案1】:

我知道的唯一方法是更新触发器,但是否有更简单的解决方案,或者如果它是唯一的方法,它在流量大的情况下是否可靠(具有许多用户的负载平衡系统)?

实际上没有任何方法可以在不牺牲可扩展性的情况下做到这一点。

UpdateTrigger 与 Try Catch 结合使用这些列上的唯一索引可能吗?

是的,这是可能的,但我不建议这样做。您最终会遇到阻塞,甚至可能是死锁,因此需要仔细编码、索引设计和测试。在最好的情况下,您的 DML 将比它可能的成本高得多,因为它必须查询同一鉴别器中的所有其他行,并且会话根本无法在同一鉴别器中同时运行 DML。

如果您确实想走这条路,我会维护一个单独的表,其中 Discriminator 作为 PK,LastFileNumber。这样您就不必扫描很多行来找出下一个 FileNumber。

比如:

create table Discriminator(Discriminator varchar(10) not null primary key, LastFileNumber int)

然后在您的触发器或存储过程中为每一行:

set nocount on
declare @discriminator varchar(10) = 'a';
declare @fn int
declare @fnt table(fn int)

merge discriminator with (updlock, holdlock)  as t 
using (select 1 fn) as s
on t.Discriminator = @discriminator
when matched then update set t.LastFileNumber = t.LastFileNumber + 1
when not matched then insert (Discriminator,LastFileNumber) values (@discriminator,1)
output inserted.LastFileNumber
into @fnt;

set @fn = (select fn from @fnt)

但最简单且性能最佳的解决方案是放弃 FileNumber 在每个“鉴别器”中是连续的“要求”,而只需使用 SEQUENCE 或 IDENTITY 来生成它们。所以你得到

Id Filenumber Descriminator More Columns...
2AA15D5E-F158-45AE-902C-CD49644846BC 1 A
FE6B95EE-DFF4-48D4-9BD7-7DB4187A2D6D 2 A
2A132492-447A-485D-A546-2FB9158AE71B 3 B

如果你想显示每个鉴别器中的序数,你可以像这样查询它

select
    Id,
    row_number() over (partition by Discriminator order by FileNumber) FileOrdinal,
    Discriminator
from T

【讨论】:

  • UpdateTrigger 与 Try Catch 结合使用这些列上的唯一索引可能吗?
  • 试试看,谢谢。只是另一个想法:......如果我有这个简单的标识列,分区行号的计算将是确定性的,对吗? ...然后一个持久的计算列可以解决问题吗?
  • 没有。因为你可以删除一行。
  • 这是我们不做的一件事......除非服务器在内部这样做?
  • 但是你可以,所以计算列是不确定的。如果你只想在插入时计算一个值,你需要一个触发器。
【解决方案2】:

@Larnu 已经在 cmets 中提到了这一点,但我认为值得向您展示这种事情是多么简单,并且使用正确的索引计算鉴别器应该足够快!

简而言之,您的表设计可以更改为在file_number 上有一个复合聚集主键和一个额外的递增字段,例如revision_date

CREATE TABLE your_table (
   file_number   int      NOT NULL
 , revision_date datetime NOT NULL CONSTRAINT df_your_table_revision_date (getutcdate())
 , CONSTRAINT pk_your_table PRIMARY KEY (file_number, revision_date)
);

然后,您可以在表格顶部创建一个视图,为简单起见,该视图可用于满足您的所有数据访问需求:

CREATE VIEW your_data_access_view
  AS
SELECT filenumber
     , revision_date
     , Row_Number() OVER (PARTITION BY filenumber ORDER BY revision_date) AS descriminator
FROM   your_table
;

展示它的实际效果:

INSERT INTO your_table (file_number) VALUES (1);
WAITFOR DELAY '00:00:01';
INSERT INTO your_table (file_number) VALUES (2);
WAITFOR DELAY '00:00:01';
INSERT INTO your_table (file_number) VALUES (1);

SELECT filenumber
     , revision_date
     , descriminator
FROM   your_data_access_view
ORDER
    BY filenumber
     , descriminator
;

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2022-01-16
    • 1970-01-01
    相关资源
    最近更新 更多