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 列的默认约束,就像使用 IDENTITY
或 AUTO_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。
以下 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 列的默认约束,就像使用 IDENTITY
或 AUTO_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。