SQL 服务器 SELECT MAX 加 1

SQL Server SELECT MAX and Add 1

以下 C# 代码正确地与 MySQL 服务器一起工作,以从 table 中获取列的最大值,同时此查询将值加 1,如下所示:

    SqlDataReader dr = new SqlCommand("SELECT (MAX(Consec) +1) AS NextSampleID FROM Samples", Connection).ExecuteReader();  
    while (dr.Read())
    { //in case of Maximum value of Consec = 555, the expected result: A556
      txtSampleID.Text = "A" + dr["NextSampleID"].ToString();
    }

但是,将数据库从 MySQL 迁移到 SQL 服务器后,此代码不再有效,如果 MAX(Consec) = 555 与 运行 之后的结果相同,则结果相同查询是A555,在使用MySQL server.

时不会像以前那样加1

问题:获取Consec的MAX值的正确查询是什么以及如何在同一查询中将MAX的结果加“1”?

MySQL 查询是错误的,除非在只有一个用户、没有删除且没有关系的普通应用程序中,否则它不会工作:

  • 并发调用将产生相同的 MAX 值,因此会产生相同的、重复的下一个值
  • 删除记录将减少 MAX 值,导致以前的 ID 值被分配给新行。如果该 ID 值在另一个 table 中使用,则新记录将最终与它没有实际关系的行相关联。这可能非常糟糕。想象一下,一名患者的测试样本与另一名患者的测试样本混合在一起。
  • 计算 MAX 需要锁定整个 table 或索引,从而阻塞或被其他人阻塞。不过,考虑到 MySQL 的 MVCC 隔离,这不会阻止重复,因为并发 SELECT MAX 查询不会相互阻塞。

有可能 MAX+1 可以在只有一个终端生成发票编号的 POS 应用程序中工作,但是一旦添加两个 POS 终端,您就有生成重复发票的风险。

另一方面,在电子商务应用程序中,几乎可以保证即使每个月只下两个订单,它们也会在同一时刻发生,从而导致重复。

正确的MySQL解和等价的

MySQL 中的正确解决方案是使用 AUTO_INCREMENT 属性:

CREATE TABLE Samples (
     Consec INT NOT NULL AUTO_INCREMENT,
     ...
);

如果您希望发票编号包含其他数据,请使用计算列来合并递增编号和其他数据。

SQL 服务器中的等价物是 IDENTITY property :

CREATE TABLE Samples (
     Consec INT NOT NULL IDENTITY,
     ...
);

序列

SQL 服务器和其他数据库中可用的另一个选项是 SEQUENCE 对象。 SEQUENCE 可用于生成与 table 无关的递增数字。它还可以重置,非常适合在特定时间段(例如每年)后重置发票编号的会计应用程序。

由于 SEQUENCE 是一个独立的对象,您可以在使用 NEXT VALUE FOR 将任何数据插入数据库之前递增并接收新值,例如:

SELECT NEXT VALUE FOR seq_InvoiceNumber;

NEXT VALUE FOR 可以用作 table 列的默认约束,就像使用 IDENTITYAUTO_INCREMENT 一样:

Create MyTable (
...
Consec INT NOT NULL DEFAULT (NEXT VALUE FOR seq_ThatSequence)
)

多table序列

可以在多个 table中使用相同的序列。一种有用的情况是为从多个来源导入的数据分配文档 ID,这些数据存储在不同的 table 中,例如付款。

支付提供商(信用卡、银行等)使用不同的格式发送报表。显然,您不会在那里丢失任何信息,因此您需要为每个提供商使用不同的 table,但无论付款来自何处,仍然能够以相同的方式处理付款。

如果您在每个 table 上使用 IDENTITY,您最终会遇到来自不同提供商的付款 ID 冲突。例如,在 OrderPayments table 上,您必须同时记录提供商名称 ID。生成付款的单一视图最终会产生无法被自己使用的 ID 值。

通过使用单个 SEQUENCE,每条记录都将获得自己的 ID,无论 table。