SQL Server 2012插入XML数据到临时表重复运行报错咨询
报错原因
该报错的核心诱因是SQL Server的批处理预编译机制,和你写的IF判断逻辑无关:
- 同一段SQL批处理内的所有对象创建语句会在执行前先做编译校验,哪怕逻辑上不会走到CREATE TABLE分支,编译器还是会检测到#txn临时表已在当前会话存在,直接抛出重复对象错误,不会执行IF的运行时判断逻辑。
- 另外你写的
OBJECT_ID('tempdb..dbo.#txn','u')存在冗余写法,正确的临时表检查格式为OBJECT_ID('tempdb..#txn','U'),但这不是本次报错的核心原因。
方案选择建议
方案1:继续使用临时表(绝大多数场景更推荐)
临时表自带会话隔离、会话结束自动清理的特性,不会留下冗余数据,非常适合这种单次XML解析的临时数据处理场景,仅需调整代码结构即可解决报错,有两种常用改法:
改法1:调整建表逻辑,先删再建
把建表逻辑从ELSE分支中抽出来,改成「不管表是否存在,先删再统一建表」,确保同一个批处理内仅出现一次CREATE TABLE定义即可:
DECLARE @XML AS XML, @hDoc AS INT, @SQL NVARCHAR (MAX) -- 先判断存在就删除 IF OBJECT_ID('tempdb..#txn','U') IS NOT NULL BEGIN PRINT '#temp exists! drop table' DROP TABLE #txn; END -- 统一建表 CREATE TABLE #txn ( accountcode varchar(100), tienda varchar(100), caja varchar(100), cajero varchar(100), fecha varchar(100), transaccion varchar(100), itemcode varchar(100), description varchar(100), quantity numeric(10,3), weight numeric(10,3), qty_weight numeric(10,3), unitprice numeric(15,3), totalprice numeric(15,3), vatcode varchar(100), hashcode varchar(100), anulado varchar(100) ) -- 后续查询、插入、读取逻辑保持不变 SELECT @XML = [LoadedXML] FROM [dbo].[XmlImport] EXEC sp_xml_preparedocument @hDoc OUTPUT, @XML INSERT INTO #txn (accountcode, tienda, caja, cajero, fecha, transaccion, itemcode, description, quantity, weight, qty_weight, unitprice, totalprice, vatcode, hashcode, anulado) SELECT CASE WHEN codigotienda = 1 THEN '01' END as accountcode, tienda, caja, cajero, fecha, transaccion, itemcode, description, quantity, weight, CASE WHEN quantity IS NULL THEN weight WHEN weight IS NULL THEN quantity END as qty_weight, unitprice, totalprice, CASE WHEN vatcode = 4 THEN 'V0' WHEN vatcode = 1 THEN 'V1' WHEN vatcode = 2 THEN 'V2' WHEN vatcode = 3 THEN 'V3' WHEN vatcode is NULL THEN 'V0' END AS vatcode, hashcode, anulado FROM OPENXML(@hDoc, 'tcpos-export/transactions/transaction/trans-item') WITH ( codigotienda [varchar](100) '../shop/code', tienda [varchar](100) '../shop/description', caja [varchar](100) '../till/code', cajero [varchar](100) '../cashier/code', fecha [varchar](100) '../beginning-timestamp', transaccion [varchar](100) '../trans-num', itemcode [varchar](100) 'code', description [varchar](100) 'description', quantity numeric(10,3) 'quantity', weight numeric(10,3) 'weight', unitprice numeric(15,3) 'unit-price', totalprice numeric(15,3) 'taxable-amount', vatcode [varchar](100) 'vat-code', hashcode [varchar](100) 'hash-code', anulado [varchar](100) 'delete-operator-id' ) SELECT * FROM #txn WHERE hashcode IS NOT NULL AND totalprice NOT LIKE '%-%' AND unitprice NOT LIKE '%-%' AND anulado IS NULL ORDER BY CAST(hashcode AS int) EXEC sp_xml_removedocument @hDoc -- 用完可主动删除,也可等待会话结束自动清理 DROP TABLE IF EXISTS #txn
改法2:用GO分割批处理
如果不想改动原有IF逻辑,可以在代码最开头加一段删表逻辑,用GO做批处理分隔,让删表和建表分属不同批处理,即可避开预编译校验:
IF OBJECT_ID('tempdb..#txn','U') IS NOT NULL DROP TABLE #txn GO -- 后面直接粘贴你原来的全部代码即可
方案2:使用普通实体表
仅当你需要把XML解析后的结果持久化保存、供多会话/后续多次查询使用的时候,才需要用普通实体表,使用时需要注意:
- 每次运行前要清空或重建表,避免旧数据干扰
- 要处理并发问题:如果多进程同时运行该逻辑,会出现数据互相覆盖的问题
- 数据不需要留存时要主动清理,避免占用数据库存储空间
最终建议
优先选择临时表方案,仅需调整代码结构即可解决报错,不需要额外维护实体表的生命周期,也不存在并发冲突问题,更适配这类临时XML解析处理的场景。
内容的提问来源于stack exchange,提问作者alexistkd
相关产品推荐
相关产品推荐

