代码之家  ›  专栏  ›  技术社区  ›  Will Rickards

在SQL Server 2005中使用XML时,为什么必须设置ARITHABORT?

  •  13
  • Will Rickards  · 技术社区  · 15 年前

    在SQL Server 2005中使用XML时,为什么必须设置ARITHABORT? 我试着研究为什么我必须设置这个,但找不到一个答案告诉我为什么。只是需要设置。

    以下是我在联机取出set arithabort时得到的特定错误消息:

    参数错误:无法分析插入列表-插入失败 因为以下设置选项的设置不正确: “算术异常”。验证set选项是否正确用于indexed 计算列和/或查询通知上的视图和/或索引 和/或XML数据类型方法。

    我的存储过程在一个环境中使用ODBC从ASP.NET调用时运行良好。然后,当我将它移到另一个存储过程时,我必须在存储过程的开头添加set arithabort。我在下面包括存储过程的相关部分。以及调用它的代码。

    CREATE PROCEDURE [dbo].[myproc]
       @ruserid             varchar(8),
       @folder_list         xml,
       @insert_list         xml
    AS
    
    SET NOCOUNT ON
    SET ARITHABORT ON
    
    DECLARE @rindex integer
    DECLARE @errormsg nvarchar(4000)
    DECLARE @folder_cnt integer
    DECLARE @insert_cnt integer
    
    
    SET @rindex = -1
    
    -- temp table to hold inserts
    CREATE TABLE #insert_list (rowidx integer IDENTITY(1,1), insertdesc varchar(96) COLLATE database_default, insertfolder integer)
    
    -- temp table to hold folders
    CREATE TABLE #folder_list (rowidx integer IDENTITY(1,1), folderdesc varchar(144) COLLATE database_default, insertfolder integer)
    
    -- insert inserts to make sure data is compatible in type
    BEGIN TRY
       INSERT INTO #insert_list (insertdesc, insertfolder)
       SELECT insert_list.listitem.value('@insertdesc', 'varchar(96)'), insert_list.listitem.value('@insertfolder', 'integer')
       FROM @insert_list.nodes('/Root/Insert') AS insert_list(listitem)
    END TRY
    BEGIN CATCH
       SET @errormsg = N'PARAMETER ERROR: INSERT LIST COULD NOT BE PARSED - ' + ERROR_MESSAGE()
       RAISERROR(@errormsg, 16, 1)
       RETURN
    END CATCH
    
    -- insert folders to make sure data is compatible in type
    BEGIN TRY
       INSERT INTO #folder_list (insertfolder, folderdesc)
       SELECT folder_list.listitem.value('@insertfolder', 'integer'), folder_list.listitem.value('@folderdesc', 'varchar(144)')
       FROM @folder_list.nodes('/Root/Folder') AS folder_list(listitem)
    END TRY
    BEGIN CATCH
       SET @errormsg = N'PARAMETER ERROR: FOLDER LIST COULD NOT BE PARSED - ' + ERROR_MESSAGE()
       RAISERROR(@errormsg, 16, 1)
       RETURN
    END CATCH
    
    -- insert rows
    BEGIN TRANSACTION
    
    BEGIN TRY
    
    INSERT INTO my_folder_request (ruserid)
    VALUES ( @ruserid )
    
    SET @rindex = SCOPE_IDENTITY()
    
    INSERT INTO my_insert_request (rindex, insertdesc, insertfolder)
    SELECT @rindex, #insert_list.insertdesc, #insert_list.insertfolder
    FROM #insert_list
    ORDER BY #insert_list.rowidx
    
    INSERT INTO my_folder_desc (rindex, insertfolder, folderdesc)
    SELECT @rindex, #folder_list.insertfolder, #folder_list.folderdesc
    FROM #folder_list
    ORDER BY #folder_list.rowidx
    
    END TRY
    BEGIN CATCH
       IF @@TRANCOUNT > 0
          ROLLBACK TRANSACTION
       SET @errormsg = N'DATA INSERTION FAILED WITH MESSAGE - ' + ERROR_MESSAGE()
       RAISERROR(@errormsg, 16, 1)
       RETURN
    END CATCH
    
    IF @@TRANCOUNT > 0
       COMMIT TRANSACTION
    
    -- return result
    SELECT @rindex AS rindex
    
    DROP TABLE #insert_list
    DROP TABLE #folder_list
    
    GO           
    

    调用代码

      ' build odbc command for inserting creation request
      intRequestIndex = 0
      cmdAddRequest = New System.Data.Odbc.OdbcCommand
      cmdAddRequest.CommandType = CommandType.StoredProcedure
      cmdAddRequest.CommandTimeout = 60
      cmdAddRequest.CommandText = "{CALL myproc ( ?, ?, ?)}"
    
      ' add parameters to odbc command
      cmdAddRequest.Parameters.Add("@ruserid", OdbcType.VarChar, 8).Value = SafeODBCParamString(m_strUID)
      cmdAddRequest.Parameters.Add("@folder_list", OdbcType.NText).Value = System.Text.Encoding.Unicode.GetString(strmFolderList.ToArray())
      cmdAddRequest.Parameters.Add("@insert_list", OdbcType.NText).Value = System.Text.Encoding.Unicode.GetString(strmInsertList.ToArray())
    
      ' run odbc command returning info about results
      cmdAddRequest.Connection = Me.ODBCConnection()
      Try
         rdrRequestData = cmdAddRequest.ExecuteReader(CommandBehavior.CloseConnection) 
    
    5 回复  |  直到 8 年前
        1
  •  6
  •   Will Rickards    15 年前

    我想这句话是从联机丛书中得到的提示:“当您在计算列或索引视图上创建或更改索引时,set arithabort必须打开。”因此nodes方法必须在内部创建索引视图或其他东西。但这只是一个有根据的猜测。

        2
  •  4
  •   darryl    12 年前

    下面是我在从.NET客户端调用带有XML输入参数的存储过程时发现的ARITHABORT问题的解决方案。

    using (var conn = new SqlConnection(dbConnectionString))  
    {  
        SqlCommand command = new SqlCommand("[stored procedure name here]", conn);  
        command.CommandType = CommandType.StoredProcedure;  
        command.Parameters.AddWithValue("@parameter_name", parameter_xml_value);  
    
        conn.Open();  
    
        SqlCommand arithabortCommand = new SqlCommand("SET ARITHABORT ON", conn);  
        arithabortCommand.ExecuteNonQuery();  
    
        command.ExecuteNonQuery();  
        conn.Close();  
    } // using (var conn = new SqlConnection(dbConnectionString))  
    
        3
  •  3
  •   John Gilmer    12 年前
        4
  •  2
  •   pilotcam    8 年前

    威尔·里卡兹和约翰·吉尔默的回答是可靠的。他们回答了“为什么”和“什么时候”。我将详细介绍一下约翰的回答:

    我刚刚遇到了同样的问题,我想知道为什么ARITHABORT设置很重要——尤其是因为我在其他几个服务器上运行的相同软件没有问题。使用两个不同的服务器,我执行了以下操作:

    SET ARITHABORT OFF
    DECLARE @message XML
    SELECT @message = (SELECT '1' As Bar FOR XML PATH('Foo'), TYPE)
    SELECT @@Version, @@Options, compatibility_level from sys.databases where name='xxxx'
    SELECT @message.exist('/Foo/Bar') -- This line fails on only one server
    

    结果发现,这两个服务器之间的区别在于数据库兼容级别:

     80: Error
    100: Okay
    
        5
  •  0
  •   SQLMenace    15 年前

    除非创建XML索引,否则不需要将arithabort设置为on。也就是说,我知道在使用ADO.NET(我认为它将ARITHABORT设置为OFF)时存在一些性能问题,这不会影响启用它,因为它将在查询执行过程中发生溢出或被零除错误时终止查询。