如何编写更好的 SQL 语句进行数据验证

Posted

技术标签:

【中文标题】如何编写更好的 SQL 语句进行数据验证【英文标题】:How can I write better SQL statement for data validation 【发布时间】:2021-07-31 04:29:17 【问题描述】:

我正在编写一个存储过程。在其中,我有使用 if 条件的数据验证。例如,如果第一个条件是成功,我将调用存储过程。如果 if 条件失败,我将调用另一个存储过程。同样,我有 20 多个条件可以在一个存储过程中运行。这会产生性能问题。对于以下场景,我该如何以更好的方式编写。

CREATE PROCEDURE Sample 
    <@Param1, sysname, @p1> <Datatype_For_Param1, , int> = <Default_Value_For_Param1, , 0>, 
    <@Param2, sysname, @p2> <Datatype_For_Param2, , int> = <Default_Value_For_Param2, , 0>
AS
BEGIN
    IF @ModeClass = 'I'
    BEGIN 
        --Validation 001
        IF EXISTS (SELECT rderNumber FROM OrderHeader WHERE OrderNumber = @OrderNumber) 
            SET @STATE_H001 = 0;
            SET @FatalCode ='H001';
            SET @FatalFlag = 'F';
            EXEC spLinkTrackOrderProcessingResult 
                                                @pross, @RunningID, @BatchNumber, @FatalFlag, @RecordType, @OrderNumber,
                                                @Sequence1, @Sequence2, @CustomerCode, @Customerabbreviation, @LoadPlanNumber, @InvoiceNumber,
                                                @JobPlaningNumber,  @CompanyCode,   @PlantAbbreviation, @ModeClass,             @FieldName,         @BeforeChangeValue,
                                                @AfterChangeValue,  @FatalCode,     @FatalMessage,      @HeaderErrorFlag,       @DetailErrorFlag,   @ProcessUser,
                                                @ProcessDate,       @SolvedUser,    @SolvedDate,        @CreateUser,            @CreateDate,        @ChangeUser, 
                                                @ChangeDate,        @ByProgram,     @ByMachine;
        ELSE
            BEGIN
                SET @STATE_H001 = 1;
            END

        --Validation 002
        IF EXISTS (SELECT top 1 1  FROM OrderDetail WHERE OrderNumber = @OrderNumber) 
            BEGIN
                SET @STATE_H004 = 1;
            END
        ELSE
            BEGIN
                SET @STATE_H004 = 0;
                SET @FatalCode ='H004';
                SET @FatalFlag = 'F';
                EXEC spLinkTrackOrderProcessingResult 
                                                    @pross,             @RunningID,     @BatchNumber,       @FatalFlag,             @RecordType,        @OrderNumber,
                                                    @Sequence1,         @Sequence2,     @CustomerCode,      @Customerabbreviation,  @LoadPlanNumber,    @InvoiceNumber,
                                                    @JobPlaningNumber,  @CompanyCode,   @PlantAbbreviation, @ModeClass,             @FieldName,         @BeforeChangeValue,
                                                    @AfterChangeValue,  @FatalCode,     @FatalMessage,      @HeaderErrorFlag,       @DetailErrorFlag,   @ProcessUser,
                                                    @ProcessDate,       @SolvedUser,    @SolvedDate,        @CreateUser,            @CreateDate,        @ChangeUser, 
                                                    @ChangeDate,        @ByProgram,     @ByMachine;
            END
      END 
END
GO

【问题讨论】:

基于集合的验证方法是编写一个一次性验证多个条件的查询。 忽略不完整和无效的sql,定义“更好”。 IMO 这种方法非常不寻常,但没有人知道您到底想要完成什么(注意 what,而不是 如何)。此外,除了您之外,没有人知道您的“性能条件”是什么意思。但我猜你以 RBAR 方式使用验证逻辑(作为 ETL 过程的一部分),这就是你的性能问题的根源。 如果你需要为每个条件运行一个存储过程,那么你会有很大的性能问题。可能有一种更全面的方法,根本不使用存储过程。这只是一个建议,因为您的问题没有足够的信息来说明更多信息。 【参考方案1】:

性能问题是因为您的执行计划很复杂。重新组织您的验证,仅使用您的验证结果调用记录器 SP 一次,并且不要使用 else 子句。

基于您的代码的示例:

CREATE PROCEDURE Sample 
    <@Param1, sysname, @p1> <Datatype_For_Param1, , int> = <Default_Value_For_Param1, , 0>, 
    <@Param2, sysname, @p2> <Datatype_For_Param2, , int> = <Default_Value_For_Param2, , 0>
AS
BEGIN
    IF @ModeClass = 'I'
    BEGIN 
        SET @STATE_H001 = 1;
        SET @STATE_H004 = 1;
        --Validation 001
        IF EXISTS (SELECT rderNumber FROM OrderHeader WHERE OrderNumber = @OrderNumber) 
        BEGIN
            SET @STATE_H001 = 0;
            SET @FatalCode ='H001';
            SET @FatalFlag = 'F';
        END;

        --Validation 002
        IF @STATE_H001 = 1 and NOT EXISTS (SELECT top 1 1  FROM OrderDetail WHERE OrderNumber = @OrderNumber) 
        BEGIN
                SET @STATE_H004 = 0;
                SET @FatalCode ='H004';
                SET @FatalFlag = 'F';
        END;
    END 
      
    if @STATE_H001 = 0 or @STATE_H004 = 0
        EXEC spLinkTrackOrderProcessingResult 
            @pross, @RunningID, @BatchNumber, @FatalFlag, @RecordType, @OrderNumber,
            @Sequence1, @Sequence2, @CustomerCode, @Customerabbreviation, @LoadPlanNumber, @InvoiceNumber,
            @JobPlaningNumber,  @CompanyCode,   @PlantAbbreviation, @ModeClass,             @FieldName,         @BeforeChangeValue,
            @AfterChangeValue,  @FatalCode,     @FatalMessage,      @HeaderErrorFlag,       @DetailErrorFlag,   @ProcessUser,
            @ProcessDate,       @SolvedUser,    @SolvedDate,        @CreateUser,            @CreateDate,        @ChangeUser, 
            @ChangeDate,        @ByProgram,     @ByMachine;
    
END
GO

【讨论】:

以上是关于如何编写更好的 SQL 语句进行数据验证的主要内容,如果未能解决你的问题,请参考以下文章

如何编写更好的SQL查询:终极指南-第二部分

如何在 WHERE 子句中编写带有 SELECT 语句的 SQL DELETE 语句?

如何使用FOR 和DO循环语句编写1*2*3*4....*10总和

如何使用sql编写查询语句 用于查询学生的 各科成绩

外键SQL语句的编写

怎样编写SQL语句求平均成绩