使用 PHP 运行使用 SQL Server 中存储的过程和事务的查询会导致神秘的错误


Using PHP to run queries that use stored procs and transactions in SQL Server is causing mysterious errors

这是

最奇怪的事情,我不确定为什么或发生了什么。 一切在SQL Server中运行良好,但在PHP中运行时会导致错误。

在 PHP 中,我构建一个包含一对多查询或语句的动态语句。 它看起来像这样:

begin try 
    begin transaction  
    -- statement 1
    UPDATE table_name SET status = 1 WHERE things='stuff';  
    -- dynamic: to run after inserts
    exec [dbo].[SP_TEST_2]; 
    exec [dbo].[SP_TEST_3]; 
    exec [dbo].[SP_TEST_9]; 
    exec [dbo].[SP_TEST_14];
    commit 
    select 'successful' as for_php_success_message 
end try 
begin catch  
      rollback
      select   error_number()     as for_php_error_number
              ,error_severity()   as for_php_error_severity
              ,error_state()      as for_php_error_state
              ,error_procedure()  as for_php_error_procedure
              ,error_line()       as for_php_error_line
              ,error_message()    as for_php_error_message; 
end catch

今天早上,有人来找我,因为所有这些的头版都在向他们抛出错误。 Warning: mssql_query() [function.mssql-query]: message: The ROLLBACK TRANSACTION request has no corresponding BEGIN TRANSACTION. 这段代码几个月来一直没有动过,没有任何问题。 从那时起,存储过程中的代码可能已更改。

我将代码复制到SQL Server Management Studio进行测试,通过直接复制和粘贴,一切运行良好。 没有错误,没有警告,只是成功。

接下来,我在线查找交易并将rollback更改为if @@trancount>0 rollback,这确实修复了交易错误; 但是 - 我有一个来自 PHP 的新错误:

Array
(
    [for_php_error_number] => 50000
    [for_php_error_severity] => 16
    [for_php_error_state] => 1
    [for_php_error_procedure] => SP_TEST_Record
    [for_php_error_line] => 247
    [for_php_error_message] => spTEST_Record: 515: Cannot insert the value NULL into column 'TEST_DATA', table 'tempdb.dbo.#IDs________________________________________________________________________________________________________________00000001D27E'; column does not allow nulls. INS
)

(不过,提醒一下:如果我在SQL Server Mgmt Studio中运行确切的代码,则不会返回任何错误(

该错误涉及在 PHP 构建的动态查询中列出的每个SP_TEST_#过程结束时调用的 SP。 我无法将 SP 代码复制到这里,因为这是工作内容,我没有编写它们,所以我真的不想解释它们,因为它们很大而且格式不好。 但是,我将显示发生错误的SP_TEST_RECORD顶部(不在第 247 行(:

ALTER PROCEDURE [dbo].[SP_TEST_Record] (
    @Test_ID real=0, @debug int=1, @create_entry int=0, @autoclose bit=0, @autodelete bit=0
)
AS
BEGIN 
    SET NOCOUNT ON;  
declare @vtest_id real; set @vtest_id=@Test_ID  
declare @vdebug int; set @vdebug=@debug     
declare @vcreate_entry bit; set @vcreate_entry=@create_entry  
declare @vautoclose bit; set @vautoclose=@autoclose 
declare @vautodelete bit; set @vautodelete=@autodelete  
declare @test_date datetime; set @test_date=getutcdate()
declare @ct int;
begin try
begin tran
if object_id('tempdb..#IDs') is not null drop table #IDs
CREATE TABLE #IDs (TYC_ID int not null, TYC_TYPE_ID int not null, TYC_ENV_ID int not null, TEST_DATA nvarchar(2000) null) ON [PRIMARY]
ALTER TABLE #IDs ADD PRIMARY KEY NONCLUSTERED (TYC_ID, TYC_TYPE_ID, TYC_ENV_ID)
insert into #IDs(TYC_ID, TYC_TYPE_ID, TYC_ENV_ID, TEST_DATA) 
select TYC_ID, TYC_TYPE_ID, TYC_ENV_ID, TEST_DATA from SR_TESTING where TEST_ID=@vTest_ID

所以 - 我知道的一件事...不知何故,我的 PHP 事务被动态语句调用的存储过程中的某个东西结束了,这是事务问题的原因。 这些存储过程中存在事务。

  • 但是,为什么当我运行完全相同的代码时,我在 SQL Server 中看不到空插入错误?如果存在空插入,则存在空插入...那么,如果它是否从 PHP 调用会有所不同呢?

  • 不太重要的是,我的事务在开始时是如何被其中一个存储过程结束的?

  • 为什么当代码在PHP中运行时,事务由SP结束,而在SQL Server中运行时没有?

  • 是否有一些执行-并发-操作顺序-事务层次结构-发生了什么?

我不想直接删除这个问题,因为这至少对我来说是一次很棒的学习经历。 然而,这是一个非常本地化的问题。 我前段时间想出了答案,所以我不记得具体细节了......

这肯定是由于在下行调用的某个存储过程中发生了某些事情。 我很惊讶地发现错误以这种方式冒泡,尤其是对于 PHP。 我想错误范围在其中发挥了作用...PHP 接受了它看到的第一个错误,但在 SQL Server 中运行代码允许它更优雅地失败(所以我没有在那里看到错误(。 邓诺。 故障 排除。。。当有疑问时 - 更深入,哈哈...