insert/update操作失败原因及解决方法

本文探讨了ASP+SQLServer环境下并发插入数据时出现的数据丢失问题。分析了因用户输入错误导致的数据插入失败,并提出了解决方案,包括字段长度调整、特殊字符处理等。
    很奇怪,asp+sql server人数在300人左右的时候,插入数据的操作有数据丢失的情况。

    查了半天,数据库相关表的编号列是primary key,有identity属性,每次自动加一。代码里面显示编号是通过select max(clNum)拿出来的。这样的话,在插入数据成功之后,还需要select一把。
     这里就有个漏洞,如果同时做插入动作的用户很多,对单个用户来说,插入数据之后,可能有很多其他用户也插入数据成功。这时再去取最大编号的话,就不能保证这个号码是这个用户插入的数据。
     到sql server的book online里查了查,sql server提供了取得插入记录后identity值的功能,
   
  • IDENT_CURRENT 返回为任何会话和任何作用域中的特定表最后生成的标识值。

  • @@IDENTITY 返回为当前会话的所有作用域中的任何表最后生成的标识值。

  • SCOPE_IDENTITY 返回为当前会话和当前作用域中的任何表最后生成的标识值。

    sql server7只提供了@@IDENTITY一种,而且没有区分会话和作用域,感觉是后两种的一个合集。
    引入以上概念,修改的代码,可以运行通过。只是有另外一个疑问,如果只是号码错,insert的动作应该成功,数据也应该能查到。但是,实际上用户输入的数据在数据库里没法找到。这个问题先挂着,继续观察。

================================================    

     隔了快一年,以上的问题算是有了个结果。经过sql profiler的监测,以及用户的反馈,经过分析发现,插入数据失败主要的原因还是由于用户输入的数据有问题造成的。这个有问题有不少种情况,例如,数据库里定义的是数字整型字段,而用户输入字母; 例如,定义的字段长度是varchar(100),用户输入超长的字符串; 前一种情况,只要在页面的逻辑上控制输入内容的类型就可以了。

     后一种情况比较有特点,因为用户有时会输入特殊字符,例如单引号,双引号,尖括号等等,这些字符输入后如果要正常显示在页面上,必须要用htmlencode转换一下才可以。正是这个htmlencode出了问题,一个双引号被转换之后,一个字符变成了6个字符(&quote;)。这样如果字符串正好100个字符长度,而里面又有2,3个双引号或是其他特殊字符的话,操作数据库肯定是会失败的。

     所以弄清楚了问题所在,解决起来也就容易了。首先是扩大数据库所定义字段的长度到200,但页面上还是限制长度100。但如果用户输入100个双引号,还不够用呢。不过这个是比较极端的情况,为了节省资源,暂时定义成200的长度。 其次,在连续的数据库操作之后(指update, insert,不包括select)加上检查,如果遇到错误,将提示用户有错误,但操作不回滚。

     这里要做个说明,原本想采用transaction模式来处理操作数据库所遇到的错误。但是,写好代码之后发现,使用transaction模式的性能有很大的问题。可能是操作数据库的步骤多,相关记录被锁住,直到操作结束。这样导致,其他用户无法同时查看相关记录。我用一台机器开两个IE窗口,模拟修改和查看操作时,修改记录就导致查看要等待很久。要是300个用户同时用,那要出大问题了。另外,象这样操作失败的情况不应该作为处理逻辑的一部分,错误只要出现就应改被及时纠正,而不是用一个单独的逻辑去专门处理。所以,只是检查是否有错误,而不回滚操作。

     另外,做个备忘。要在asp页面里实现事务模式有两种,一种是靠页面的事务,一种是数据库自身的事务。页面事务,遇到失败时,把所有操作,不仅仅是数据库事务都回滚。有时会和数据库的事务冲突,而不能正常使用。 数据库事务,只是针对数据库本身的操作,不牵涉页面上其他的逻辑。

     贴两个例子:

     sql server transaction:

Dim   connas   New   ADODB.Connection  
  ...  
   
  Sub   Date_syori()    
  On   Error   GoTo   Err_syori       
          conn.BeginTrans  
          ...  
   
          conn.execute("sql1")  
          ...  
           
          conn.execute("sql2")       
          conn.CommitTrans       
          Set   conn   =   Nothing       
          Exit   Sub       
  Err_syori:       
          conn   .RollbackTrans           
          Set   conn   =   Nothing      
          Exit   Sub       
  End   Sub  

     asp transaction:

<%@ TRANSACTION=Required%>
<%
Option Explicit
On Error Resume Next
Dim oConn, oRS

Set oConn = Server.CreateObject("ADODB.Connection")
oConn.Open "Provider=SQLOleDB;server=servername;Initial Catalog=pubs;uid=sa;pwd="
if err.Number <> 0 Then
    Response.Write "<BR>Error Occurred Opening Connection...<BR>"
    Response.Write "<BR>Error Description:" & err.Description & "...<BR>"
ObjectContext.SetAbort
    Response.End
else
    Response.Write "Connection Opened Successfully...<BR>"
    ObjectContext.SetComplete
End If


oConn.Execute "Select * from Authors"
if err.Number <> 0 Then
    Response.Write "<BR>Error Occurred Executing Query...<BR>"
    Response.Write "<BR>Error Description:" & err.Description & "...<BR>"
    oConn.Close
    Set oConn = Nothing
ObjectContext.SetAbort
Response.End
else
    Response.Write "<BR>Query Completed Successfully...<BR>"
    ObjectContext.SetComplete
End If

oConn.Close
Response.Write "<BR>Connection Closed Successfully...<BR>"
set oConn = Nothing
Response.Write "<BR>Test Completed Successfully...<BR>"

Sub OnTransactionCommit()
Response.Write "<p><b>The Transaction just committed</b>."
Response.Write "This message came from the "
Response.Write "OnTransactionCommit() event handler."
End Sub

Sub OnTransactionAbort()
Response.Write "<p><b>The Transaction just aborted</b>."
Response.Write "This message came from the "
Response.Write "OnTransactionAbort() event handler."
End Sub

%>

<think>首先,用户的问题是关于SQL中实现"insert or update"操作方法。他们提供了几个引用,我应该参考这些引用来构建我的回答。 回顾用户提供的引用: - 引用[1]: 讨论了MySQL中的批量插入操作,提到了insert into values和insert into select,以及replace into和insert into on duplicate key update。这些用于插入或更新数据。 - 引用[2]: 提到了SQL命令INSERT OR UPDATE,但似乎是示例性的,可能不是标准SQL。 - 引用[3]: 解释了如何在MySQL中实现insertOrUpdate,提到了INSERT INTO ... ON DUPLICATE KEY UPDATE, REPLACE INTO, 和 INSERT IGNORE INTO。 - 引用[4]: 涵盖了SQL语句中的insertupdate操作,包括基本的语法示例。 - 引用[5]: 讨论了insert or update语句中的一些问题,比如在设置值时遇到的困难。 用户的核心问题是:"我想查找SQL中实现insert or update操作方法 请问SQL实现insert or update操作方法"。所以,我需要总结SQL中实现这种操作方法。 现在,系统指令要求: - 所有行内数学表达式必须使用$...$格式(但这里可能不涉及数学表达式,所以可能不需要)。 - 独立公式使用$$...$$(同样,不相关)。 - LaTeX语法正确(在代码块中)。 - 使用中文回答。 - 生成相关问题。 - 在回答中引用的段落末尾自然地添加引用标识,例如[^1]。 - 结构清晰,逐步解决问题。 结构我的回答: 1. **引言**:简要解释什么是"insert or update"操作,即当记录存在时更新,不存在时插入。 2. **主要方法**: - 基于引用,提到SQL中的几种方式: - **INSERT INTO ... ON DUPLICATE KEY UPDATE**(MySQL特有) - **REPLACE INTO**(MySQL特有) - **INSERT IGNORE INTO**(MySQL特有) - 其他数据库可能有类似功能,如SQLite的INSERT OR REPLACE、INSERT OR IGNORE。 - 标准SQL方式:使用MERGE或UPSERT语句(取决于数据库)。 - 在引用中提到了INSERT OR UPDATE,但引用[2]可能指的是自定义或特定实现。 - 解释每个方法的工作原理、优缺点。 3. **示例代码**:提供一些SQL示例代码块来展示用法。 4. **注意事项**:引用中提到的问题,如加锁问题(INSERT INTO SELECT)、REPLACE INTO与ON DUPLICATE UPDATE的区别(REPLACE是delete-insert,后者是update)、INSERT IGNORE的忽略冲突。 5. **推荐或总结**:或许提到引用[1]中的图书推荐(但不要过度推销)。 6. **相关问题**:生成2-3个相关问题。 使用中文回答。 添加引用标识:当引用特定段落时,在末尾添加[^n]。 步骤: - 介绍概念。 - 描述方法: - INSERT INTO ... ON DUPLICATE KEY UPDATE - REPLACE INTO - INSERT IGNORE INTO - MERGE语句(如SQL Server的MERGE) - 其他数据库的UPSERT - 给出示例。 - 讨论常见问题。 - 生成相关问题。 确保回答真实可靠:基于提供的引用和一般知识。 相关问题生成:参考示例,生成2-3个问题。 </think>以下是针对您问题的完整解答:SQL中实现"insert or update"操作(也称为UPSERT操作)的方法,是指当目标表中已存在指定唯一键(如主键或唯一索引)的记录时执行更新(UPDATE),否则执行插入(INSERT)。这种操作在数据处理中非常常见,例如处理批量导入或同步数据时。基于您提供的引用内容(特别是引用[1],[3]和[5]),我将逐步解释SQL中实现这一操作的主流方法、语法示例、注意事项及相关问题。 ### 1. SQL中实现insert or update的主要方法 SQL标准中并没有统一的UPSERT命令,但不同数据库系统提供了类似功能。以下是常见的几种方式,参考了MySQL的实现(如引用[1]和[3]所述),并附带其他数据库的注意事项。 #### (1) **INSERT INTO ... ON DUPLICATE KEY UPDATE (MySQL特有)** - **工作原理**:当插入的记录与表中唯一键冲突时,自动执行更新操作;否则,执行插入操作。这不是删除后插入,而是直接更新现有记录,因此性能更高,避免数据丢失。 - **语法示例**: ```sql INSERT INTO table_name (column1, column2, ...) VALUES (value1, value2, ...) ON DUPLICATE KEY UPDATE column1 = VALUES(column1), column2 = VALUES(column2); ``` - 示例:如果表`goods`的主键`id`冲突,则更新价格和数量;否则插入新记录。 ```sql INSERT INTO goods (id, goodsName, price, num) VALUES (1, 'apple', 12.5, 200) ON DUPLICATE KEY UPDATE price = VALUES(price), num = VALUES(num); ``` - **优点**:高效,只在冲突时更新特定字段,不影响其他列。 - **注意点**:该方法依赖于唯一键(如主键或唯一索引)。如果表中无唯一键,操作会失败(引用[3])。 #### (2) **REPLACE INTO (MySQL特有)** - **工作原理**:如果唯一键冲突,先删除现有记录,再插入新记录;如果没有冲突,则直接插入。这相当于先执行DELETE后INSERT,可能导致数据完整性问题和性能开销。 - **语法示例**: ```sql REPLACE INTO table_name (column1, column2, ...) VALUES (value1, value2, ...); ``` - 示例:在`goods`表中,如果`id=1`的记录存在,则删除旧记录并插入新值。 ```sql REPLACE INTO goods (id, goodsName, price) VALUES (1, 'pear', 13); ``` - **优点**:语法简单。 - **缺点**:删除-插入操作可能触发级联删除或影响自增ID,不适合高并发场景(引用[1])。 #### (3) **INSERT IGNORE INTO (MySQL特有)** - **工作原理**:当唯一键冲突时,忽略冲突行,不执行插入或更新;没有冲突时正常插入。这仅适用于插入场景,本质上是“插入或跳过”。 - **语法示例**: ```sql INSERT IGNORE INTO table_name (column1, column2, ...) VALUES (value1, value2, ...); ``` - 示例:向`goods`表插入记录,如果`id`冲突,则忽略该行。 ```sql INSERT IGNORE INTO goods (id, goodsName) VALUES (1, 'straws'); ``` - **优点**:避免报错,适合容忍数据丢失的场景。 - **缺点**:不能执行更新操作,只能跳过冲突,用途有限(引用[3])。 #### (4) **其他数据库的UPSERT实现** - **SQLite**:使用`INSERT OR REPLACE`或`INSERT OR IGNORE`,类似于MySQL的REPLACE和IGNORE(引用[2]提到类似功能)。 - **PostgreSQL**:使用`INSERT ... ON CONFLICT DO UPDATE`语句,语法与MySQLON DUPLICATE KEY UPDATE相似。 - **SQL Server**:使用`MERGE`语句(标准SQL支持),它允许在一条命令中处理插入、更新和删除。 - 示例: ```sql MERGE INTO target_table AS t USING source_table AS s ON t.key = s.key WHEN MATCHED THEN UPDATE SET t.column1 = s.column1 WHEN NOT MATCHED THEN INSERT (column1, column2) VALUES (s.column1, s.column2); ``` - **Oracle**:同样支持`MERGE`语句。 - **注意**:标准SQL的MERGE是最通用的方式,但语法较复杂,适合跨数据库兼容(引用[2]和[4])。 ### 2. 注意事项和常见问题 - **性能与加锁问题**:在批量操作时(如INSERT INTO SELECT),需注意加锁行为可能导致阻塞。例如,INSERT INTO SELECT可能锁定整表,影响并发性能(引用[1])。建议在事务中使用,并添加索引优化。 - **原子性与一致性**:所有方法在事务中保证原子性(要么全部成功,要么全部回滚)(引用[1])。但REPLACE INTO可能破坏事务完整性,因为它涉及删除操作。 - **值设置问题**:在ON DUPLICATE KEY UPDATE中,使用`VALUES(column)`引用插入值。如果值计算复杂(如递增计数器),可能需要先查询数据库再处理,避免语法错误(引用[5]中提到的问题)。 - **约束处理**:UPSERT操作依赖唯一键。如果表无主键或唯一索引,操作可能失败或行为不一致(引用[3])。 - **推荐方法**:优先使用ON DUPLICATE KEY UPDATE或MERGE,它们性能更好且避免数据丢失风险。REPLACE INTO和INSERT IGNORE应谨慎使用。 ### 3. 总结 SQL中实现insert or update操作的核心是通过数据库特有的语法(如MySQLON DUPLICATE KEY UPDATE)或标准MERGE语句来处理唯一键冲突。选择方法时需考虑数据库类型、性能需求和数据完整性。例如,在MySQL中,ON DUPLICATE KEY UPDATE是最佳实践,因为它高效且直接更新(引用[1])。 如果您使用的是MySQL,推荐参考《MySQL技术大全:开发、优化与运维实战》一书,它详细介绍了这些操作的优化和实战案例(引用[1])。
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值