如何在写入SQL表前检查数据是否存在?避免主键重复致程序崩溃
嘿,这个主键冲突导致应用崩溃的问题我太熟了!给你几个实用的解决思路,分SQL层面和代码层面,你可以根据自己的场景选:
方案一:在SQL语句层面直接避免插入重复数据
这种方法从根源上减少冲突的可能,适合大部分常规场景。
写法1:用INSERT ... WHERE NOT EXISTS
把你的插入语句改成下面这样,只有当Path不存在时才执行插入:
INSERT INTO [FileTrackLog] (Date, Client, Path, DateAddedToDb) SELECT @Date, @Client, @Path, @DateAddedToDb WHERE NOT EXISTS ( SELECT 1 FROM [FileTrackLog] WHERE Path = @Path )
执行后如果数据已存在,只会返回0行受影响,不会抛出异常,应用自然不会崩溃。
写法2:用MERGE语句(更灵活)
如果后续需要对已存在的数据做更新操作,MERGE会更合适,它能同时处理插入和更新逻辑:
MERGE INTO [FileTrackLog] AS Target USING (SELECT @Date, @Client, @Path, @DateAddedToDb) AS Source (Date, Client, Path, DateAddedToDb) ON Target.Path = Source.Path WHEN NOT MATCHED THEN INSERT (Date, Client, Path, DateAddedToDb) VALUES (Source.Date, Source.Client, Source.Path, Source.DateAddedToDb);
如果不需要更新,只保留WHEN NOT MATCHED的分支就行,效果和上面的写法一致。
方案二:在C#代码中捕获异常,优雅处理
如果遇到并发场景(比如两个请求同时检查同一个Path,都判定不存在后同时插入),还是可能触发主键冲突。这时候在代码里捕获异常就能避免应用崩溃:
using (SqlConnection conn = new SqlConnection(connStr)) { conn.Open(); try { SqlCommand cmd = new SqlCommand( "INSERT INTO [FileTrackLog] (Date, Client, Path, DateAddedToDb) " + "VALUES (@Date, @Client, @Path, @DateAddedToDb)", conn); // 记得补充参数赋值,示例如下: cmd.Parameters.AddWithValue("@Date", yourDateValue); cmd.Parameters.AddWithValue("@Client", yourClientValue); cmd.Parameters.AddWithValue("@Path", yourPathValue); cmd.Parameters.AddWithValue("@DateAddedToDb", DateTime.Now); // 按你的业务逻辑调整 int rowsAffected = cmd.ExecuteNonQuery(); if (rowsAffected == 0) { // 可选:记录日志或提示数据已存在 Console.WriteLine("该路径已存在,无需重复插入"); } } catch (SqlException ex) { // *错误码2627*是SQL Server主键/唯一约束冲突的专属错误码 if (ex.Number == 2627) { // 优雅处理,比如记录日志、给用户友好提示,而不是崩溃 Console.WriteLine("数据已存在,跳过本次插入"); } else { // 其他SQL异常,按原有逻辑抛出或处理 throw; } } }
额外建议:两种方案结合使用
单独用SQL层面的判断可能扛不住高并发场景的冲突,而单独捕获异常又不够高效。把两种方案结合起来,用SQL语句过滤掉绝大多数重复请求,再用代码捕获极少数并发导致的冲突,既能保证效率,又能彻底避免应用崩溃。
内容的提问来源于stack exchange,提问作者YongaJ
相关产品推荐
相关产品推荐

