如何从SQL中提取最后一条数据加1后重新存入数据库(C# WPF实现)
实现方案(C# WPF + SQL 订单编号自增生成)
注意:不建议直接在C#端单独查询最后一条数据再加1,高并发场景下会出现订单号重复问题,优先推荐SQL端原子操作方案,保证数据一致性。
前置依赖
首先安装对应NuGet包,优先用新版SQL客户端:
- 包名:
Microsoft.Data.SqlClient - 若使用.NET Framework环境可以用旧版
System.Data.SqlClient
方案1:SQL端原子操作(推荐,支持高并发)
该方案将查询、自增、返回逻辑封装在单条SQL语句中,全程原子性,不会出现重复编号问题。
假设业务表结构如下:
- 表名:
DeliveryOrders - 需自增字段:
OrderNo(订单号,示例格式为202405200001,前8位为日期,后4位为当日流水号)
核心代码示例:
using Microsoft.Data.SqlClient; using System; // 订单号生成方法 public string GenerateNewOrderNo() { // 替换为自身业务的数据库连接字符串 string connString = "Server=你的数据库地址;Database=你的库名;Uid=账号;Pwd=密码;TrustServerCertificate=True;"; string newOrderNo = string.Empty; // SQL原子逻辑:查询当日最大订单号→无则默认首单→有则流水号+1→返回新编号 string sql = @" DECLARE @datePrefix VARCHAR(8) = CONVERT(VARCHAR(8), GETDATE(), 112); DECLARE @maxOrderNo VARCHAR(20); DECLARE @newOrderNo VARCHAR(20); SELECT @maxOrderNo = MAX(OrderNo) FROM DeliveryOrders WHERE OrderNo LIKE @datePrefix + '%'; IF @maxOrderNo IS NULL SET @newOrderNo = @datePrefix + '0001'; ELSE SET @newOrderNo = @datePrefix + RIGHT('0000' + CAST(CAST(RIGHT(@maxOrderNo, 4) AS INT) + 1 AS VARCHAR(4)), 4); -- 若需要直接插入完整订单数据,可在此处补充INSERT语句,进一步保证原子性 -- INSERT INTO DeliveryOrders (OrderNo, ReceiverName, CreateTime) VALUES (@newOrderNo, @receiver, GETDATE()); SELECT @newOrderNo; "; using (SqlConnection conn = new SqlConnection(connString)) { conn.Open(); using (SqlCommand cmd = new SqlCommand(sql, conn)) { newOrderNo = cmd.ExecuteScalar()?.ToString(); } } return newOrderNo; }
方案2:C#端查改分离(仅低并发单实例场景使用)
如果业务并发量极低,不存在同时生成订单的场景,可以使用该更直观的方案:
public string GenerateNewOrderNoLowConcurrency() { string connString = "你的数据库连接字符串"; string datePrefix = DateTime.Now.ToString("yyyyMMdd"); int newSerial = 1; using (SqlConnection conn = new SqlConnection(connString)) { conn.Open(); // 第一步:查询当日最后一条订单号 string querySql = @"SELECT TOP 1 OrderNo FROM DeliveryOrders WHERE OrderNo LIKE @prefix + '%' ORDER BY OrderNo DESC"; using (SqlCommand queryCmd = new SqlCommand(querySql, conn)) { queryCmd.Parameters.AddWithValue("@prefix", datePrefix); var result = queryCmd.ExecuteScalar(); if (result != null) { string lastOrderNo = result.ToString(); // 截取流水号加1 string lastSerialStr = lastOrderNo.Substring(lastOrderNo.Length - 4, 4); if (int.TryParse(lastSerialStr, out int lastSerial)) { newSerial = lastSerial + 1; } } } // 生成新订单号 string newOrderNo = $"{datePrefix}{newSerial.ToString().PadLeft(4, '0')}"; // 第二步:插入新订单数据到数据库 string insertSql = @"INSERT INTO DeliveryOrders (OrderNo, CreateTime, ReceiverName) VALUES (@OrderNo, @CreateTime, @ReceiverName)"; using (SqlCommand insertCmd = new SqlCommand(insertSql, conn)) { insertCmd.Parameters.AddWithValue("@OrderNo", newOrderNo); insertCmd.Parameters.AddWithValue("@CreateTime", DateTime.Now); insertCmd.Parameters.AddWithValue("@ReceiverName", "收件人信息"); insertCmd.ExecuteNonQuery(); } return newOrderNo; } }
WPF UI绑定示例
生成的订单号可以直接赋值到WPF控件显示:
// 按钮点击生成订单号示例 private void BtnCreateOrder_Click(object sender, RoutedEventArgs e) { string newOrderNo = GenerateNewOrderNo(); // 赋值给前端文本框显示 TxtOrderNo.Text = newOrderNo; }
优化建议
- 若业务并发量很高,可以单独维护一张序号配置表,专门存储各业务线的当前最大序号,更新时加行锁,性能比每次查询全订单表更高
- 流水号位数可根据单日订单量调整,超过1万单可改为5位或6位
- 所有SQL操作都使用参数化查询,避免SQL注入风险
内容的提问来源于stack exchange,提问作者Ascenal
相关产品推荐
相关产品推荐

