You need to enable JavaScript to run this app.
最新活动
大模型
产品
解决方案
定价
生态与合作
支持与服务
开发者
了解我们

PowerApps调用SQL Server存储过程时触发OUTPUT子句与触发器冲突错误的求助

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

火山引擎 最新活动