PowerApps调用SQL Server存储过程时触发OUTPUT子句与触发器冲突错误的求助
你遇到的这个错误是SQL Server的硬性限制导致的——当目标表上有启用的触发器时,带有OUTPUT子句但未指定INTO子句的DML语句(比如INSERT/UPDATE/DELETE)会被阻止执行。而PowerApps的Patch函数在向SQL Server表插入新记录时,底层会自动生成带有OUTPUT子句的INSERT语句(用来返回刚插入的记录),但这个语句并没有把OUTPUT的结果存入表变量/临时表,这就和你表上的trg_RecalcType3_OnType2Change触发器触发了冲突;Blazor那边没有问题,是因为它的插入逻辑没有自动添加这个OUTPUT子句。
下面给你提供具体的解决方案:
解决方案:用自定义存储过程替代PowerApps直接Patch表
最可靠的解决方式是创建一个专门的插入存储过程,把原本PowerApps Patch里的插入逻辑移到存储过程中,这样就能完全控制SQL语句的写法,避免自动生成的OUTPUT子句带来的冲突。
步骤1:创建插入存储过程
在SQL Server中执行以下代码,创建用于插入Expense_ClaimMileage记录的存储过程:
CREATE OR ALTER PROCEDURE dbo.InsertExpenseClaimMileage ( @ClaimHeader_id INT, @Reason_visit NVARCHAR(MAX), @Active BIT, @Date DATE, @Mileage_type INT, @UserVehicle_Id INT, @From NVARCHAR(MAX), @From_postcode NVARCHAR(50), @To NVARCHAR(MAX), @To_postcode NVARCHAR(50), @Total_miles DECIMAL(18,2), @Net_miles DECIMAL(18,2), @Business_Mileage_Rate DECIMAL(18,2), @HMRC_Fuel_Cost DECIMAL(18,2), @Mileage_VAT DECIMAL(18,2), @Custom_Mileage_VAT DECIMAL(18,2), @Mileage_NET_Cost DECIMAL(18,2), @Total_Fuel_Cost DECIMAL(18,2), @Business_Mileage_Cost DECIMAL(18,2), @Business_MileageNET_Cost DECIMAL(18,2), @MileageImageAttached INT, @Last_modified_at DATETIME, @Last_modified_by NVARCHAR(255), @Start_Odometer DECIMAL(18,2) = NULL, @End_Odometer DECIMAL(18,2) = NULL, @Amount_To_Repay DECIMAL(18,2) = NULL, @Private_Miles DECIMAL(18,2) = NULL ) AS BEGIN SET NOCOUNT ON; -- 执行插入操作,这里没有使用OUTPUT子句,避免和触发器冲突 INSERT INTO dbo.Expense_ClaimMileage ( ClaimHeader_id, Reason_visit, Active, Date, Mileage_type, UserVehicle_Id, [From], From_postcode, [To], To_postcode, Total_miles, Net_miles, Business_Mileage_Rate, HMRC_Fuel_Cost, Mileage_VAT, Custom_Mileage_VAT, Mileage_NET_Cost, Total_Fuel_Cost, Business_Mileage_Cost, Business_MileageNET_Cost, MileageImageAttached, Last_modified_at, Last_modified_by, Start_Odometer, End_Odometer, Amount_To_Repay, Private_Miles ) VALUES ( @ClaimHeader_id, @Reason_visit, @Active, @Date, @Mileage_type, @UserVehicle_Id, @From, @From_postcode, @To, @To_postcode, @Total_miles, @Net_miles, @Business_Mileage_Rate, @HMRC_Fuel_Cost, @Mileage_VAT, @Custom_Mileage_VAT, @Mileage_NET_Cost, @Total_Fuel_Cost, @Business_Mileage_Cost, @Business_MileageNET_Cost, @MileageImageAttached, @Last_modified_at, @Last_modified_by, @Start_Odometer, @End_Odometer, @Amount_To_Repay, @Private_Miles ); -- 返回新插入记录的关键ID,供后续调用Recalc存储过程使用 SELECT SCOPE_IDENTITY() AS ClaimMileage_id, @ClaimHeader_id AS ClaimHeader_id; END
步骤2:修改PowerApps的逻辑,调用存储过程替代Patch
把你原来的Patch代码替换成调用上面创建的存储过程,代码如下:
UpdateContext( { varInsertResult: db_dev_test.dboInsertExpenseClaimMileage({ ClaimHeader_id: ExpenseHeaderForMileage, Reason_visit: AddMile_Reason_Input.Text, Active: true, Date: AddMile_Date_Input.SelectedDate, Mileage_type: AddMileage_MileageType_Dropdown.Selected.Id, UserVehicle_Id: AddMileage_Uservehicles_Dropdown.Selected.UserVehicle_id, From: AddMile_SPlace_Input.Text, From_postcode: AddMile_SPostCode_Input.Text, To: AddMile_LPlace_Input.Text, To_postcode: AddMile_LPostCode_Input.Text, Total_miles: Value(AddMiles_TravelledMiles_Input.Text), Net_miles: Value(AddMiles_NetMiles_Input.Text), Business_Mileage_Rate: If(AddMileage_MileageType_Dropdown.Selected.Id = 2, 0, Value(MapBusinessFuelRate.Business_Mileage_Rates)), HMRC_Fuel_Cost: If(AddMileage_MileageType_Dropdown.Selected.Id = 2, 0, Value(QuarterlyRates.FuelRate)), Mileage_VAT: If(AddMileage_MileageType_Dropdown.Selected.Id = 2, 0, Value(AddMiles_TotalFuelRate_Input.Text) - (Value(AddMiles_TotalFuelRate_Input.Text)/1.2)), Custom_Mileage_VAT: 0, Mileage_NET_Cost: If(AddMileage_MileageType_Dropdown.Selected.Id = 2, 0, Value(AddMiles_TotalFuelRate_Input.Text) / 1.2), Total_Fuel_Cost: If(AddMileage_MileageType_Dropdown.Selected.Id = 2, 0, Value(AddMiles_TotalFuelRate_Input.Text)), Business_Mileage_Cost: If( AddMileage_MileageType_Dropdown.Selected.Id = 2, 0, If( AddMileage_MileageType_Dropdown.Selected.Id <> 4, (QuarterlyRates.FuelRate * Value(AddMiles_NetMiles_Input.Text)) / 100, Value(MapBusinessFuelRate.Business_Mileage_Rates) * Value(AddMiles_NetMiles_Input.Text) ) ), Business_MileageNET_Cost: If( AddMileage_MileageType_Dropdown.Selected.Id = 2, 0, If( AddMileage_MileageType_Dropdown.Selected.Id <> 4, Value(AddMiles_TotalFuelRate_Input.Text) / 1.2, Value(MapBusinessFuelRate.Business_Mileage_Rates) * Value(AddMiles_NetMiles_Input.Text) - (Value(AddMiles_TotalFuelRate_Input.Text) - Value(AddMiles_TotalFuelRate_Input.Text) / 1.2) ) ), MileageImageAttached: If(IsBlank(UploadedMileageImage.Image), 1, 2), Last_modified_at: Now(), Last_modified_by: User().Email, Start_Odometer: If(AddMileage_MileageType_Dropdown.Selected.Id = 3, Value(AddMiles_StartOdometerMiles_Input.Text), NULL), End_Odometer: If(AddMileage_MileageType_Dropdown.Selected.Id = 3, Value(AddMiles_EndOdometerMiles_Input.Text), NULL), Amount_To_Repay: If(AddMileage_MileageType_Dropdown.Selected.Id = 3, Value((QuarterlyRates.FuelRate * TotalPrivateMiles)/100), NULL), Private_Miles: If(AddMileage_MileageType_Dropdown.Selected.Id = 3, Value(TotalPrivateMiles), NULL) }) } ); -- 继续调用Recalc存储过程,逻辑和原来一致 ClearCollect( result_RecalcType3, db_dev_test.dboRecalcType3ForHeader({ ClaimHeaderId: varInsertResult.ClaimHeader_id }) ); Navigate( Expense_Card_Screen, ScreenTransition.Fade, {ExpenseHeaderID: varInsertResult.ClaimHeader_id} );
为什么这个方案能解决问题?
通过自定义存储过程插入数据,我们完全控制了SQL语句的写法——没有使用OUTPUT子句,也就不会触发SQL Server关于触发器和OUTPUT子句的冲突限制。同时存储过程返回了我们需要的ClaimHeader_id,保证后续的RecalcType3ForHeader调用逻辑不受影响。
另外,你的触发器trg_RecalcType3_OnType2Change已经处理了嵌套触发的问题(IF TRIGGER_NESTLEVEL() > 1 RETURN;),所以存储过程插入数据时触发的触发器也能正常工作,不会出现递归调用的问题。
内容来源于stack exchange




