处理具有大量数据交易的应用程序的插入/更新过程的最佳方法?

时间:2018-08-12 21:46:27

标签: sql-server sql-server-2008 stored-procedures coldfusion sql-merge

过去的几个月中,我一直在开发单页应用程序,在尝试改进后端的某些功能时,我开始更深入地研究我的插入/更新和数据库过程。我使用SQL Server 2008,并且使用Primary KeysForeign Keys以及Indexes设计数据库,以便在表之间建立牢固可靠的连接。我正在谈论这些元素,因为我们当前的系统设计的数据库非常差。 deadlocks存在很多问题,并且性能下降。我们的系统是全州范围的产品,具有大量的数据交易,尤其是在诸如乞讨或学年结束之类的时间段内。但是,我的新应用程序将来可能会期望相同甚至更大的翻译,因此必须改进数据库设计和“插入/更新”过程并防止死锁。我的问题是有关在此类系统中应使用的SQL Server标准。例如,我最近开始更多地研究存储过程,并且似乎很多DBA专家建议在反复使用相同查询的情况下采用这种方式。那应该防止一些安全风险并提高效率。以下是有关我当前如何在系统中处理InsertUpdate交易的示例:

<cftransaction action="begin">
    <cftry>
        <cfquery name="qrySaveDictionary" datasource="#dsn#">
            DECLARE @Status BIT = <cfqueryparam cfsqltype="cf_sql_bit" value="#trim(arguments.frm_status)#">;
            DECLARE @Name VARCHAR(50) = <cfqueryparam cfsqltype="cf_sql_varchar" maxlength="50" value="#trim(arguments.frm_name)#">;
            DECLARE @Code CHAR(2) = UPPER(<cfqueryparam cfsqltype="cf_sql_char" maxlength="2" value="#trim(arguments.frm_code)#">);
            DECLARE @ActionDate DATETIME = CURRENT_TIMESTAMP;
            DECLARE @ActionID UNIQUEIDENTIFIER = <cfqueryparam cfsqltype="cf_sql_idstamp" value="#SESSION.AccountID#">;

            <cfif len(trim(arguments.frm_recordid))>
                DECLARE @RecordID INT = <cfqueryparam cfsqltype="cf_sql_integer" value="#trim(arguments.frm_recordid)#">;
                IF EXISTS(SELECT RecID FROM Dictionary WHERE RecID = @RecordID)
                BEGIN
                    UPDATE Dictionary
                    SET
                        Status = @Status,
                        Name = @Name,
                        Code = @Code,
                        ActionDt = @ActionDate,
                        ActionID = @ActionID
                    WHERE RecID = @RecordID
                    SELECT @RecordID AS RecID
                END
            <cfelse>
                BEGIN
                    IF NOT EXISTS(SELECT 1 FROM Dictionary WHERE Code = @Code)
                    INSERT INTO Dictionary (
                        Status,Name,Code,ActionDt,ActionID
                    ) VALUES (
                        @Status,@Name,@Code,@ActionDate,@ActionID
                    )
                    SELECT SCOPE_IDENTITY() AS RecID
                END
            </cfif>
        </cfquery>

        <cfset local.fnResults = {status : "200", message : "Record successully saved!", RecID : qrySaveDictionary.RecID}>

        <cfcatch type="any">
            <cftransaction action="rollback" />
                <cfset local.fnResults = {status : "400", class : "alert-danger", message : "Error! Please contact your administrator."}>
            </cfcatch>
        </cftry>
    </cftransaction>

在上面的示例中,我使用ColdFusion检查在函数参数中传递的现有记录。另外,我在ColdFusion中使用事务处理,以防止出现某些错误情况时出现不良数据。另外,我在查询中声明了所有参数,然后运行Update或Insert语句。我读了很多关于IF EXISTIF NOT EXIST的博客,有人说它们对性能不利,并可能导致死锁。我的问题是,进行此类陈述的最佳方法是什么?

1)像这样的东西:

begin tran
if exists (select * from table with (updlock,serializable) where key = @key)
begin
   update table set ...
   where key = @key
end
else
begin
   insert into table (key, ...)
   values (@key, ...)
end
commit tran

2)

begin tran
   update table with (serializable) set ...
   where key = @key

   if @@rowcount = 0
   begin
      insert into table (key, ...) values (@key,..)
   end
commit tran

3)也许使用MERGE,我没有找到一个很好的例子。我也想知道这应该是存储过程函数吗?同样,我自己一个人从事这个项目,我们公司没有DBA,而且我的资源有限。我正在尝试在Internet上进行研究,以获取有关SQL Server和良好实践的信息。正如我所提到的,这个项目非常重视数据事务,因此选择正确的方法对我来说非常重要。如果有人可以提供一些想法,信息或示例,将不胜感激。

1 个答案:

答案 0 :(得分:-1)

您不应该猜测您的用户想要做什么。您的系统应该为每个用户操作使用不同的按钮/表单/功能来限制/限制/验证用户操作。

在写作时阅读也介绍了读写器死锁https://www.red-gate.com/simple-talk/sql/performance/sql-server-deadlocks-by-example/

所有这些都是为什么我建议您需要将qrySaveDictionary分为2的原因:

<cftransaction action="begin">
    <cftry>
        <cfquery name="qryInsertDictionary" datasource="#dsn#">
            DECLARE @Status BIT = <cfqueryparam cfsqltype="cf_sql_bit" value="#trim(arguments.frm_status)#">;
            DECLARE @Name VARCHAR(50) = <cfqueryparam cfsqltype="cf_sql_varchar" maxlength="50" value="#trim(arguments.frm_name)#">;
            DECLARE @Code CHAR(2) = UPPER(<cfqueryparam cfsqltype="cf_sql_char" maxlength="2" value="#trim(arguments.frm_code)#">);
            DECLARE @ActionDate DATETIME = CURRENT_TIMESTAMP;
            DECLARE @ActionID UNIQUEIDENTIFIER = <cfqueryparam cfsqltype="cf_sql_idstamp" value="#SESSION.AccountID#">;

                BEGIN
                     INSERT INTO Dictionary (
                        Status,Name,Code,ActionDt,ActionID
                    ) VALUES (
                        @Status,@Name,@Code,@ActionDate,@ActionID
                    )
                    SELECT SCOPE_IDENTITY() AS RecID
                END
        </cfquery>

        <cfset local.fnResults = {status : "200", message : "Record successully saved!", RecID : qrySaveDictionary.RecID}>

        <cfcatch type="any">
            <cftransaction action="rollback" />
                <cfset local.fnResults = {status : "400", class : "alert-danger", message : "Error! Please contact your administrator."}>
            </cfcatch>
        </cftry>
    </cftransaction>

<cftransaction action="begin">
    <cftry>
        <cfquery name="qryUpdateDictionary" datasource="#dsn#">
            DECLARE @Status BIT = <cfqueryparam cfsqltype="cf_sql_bit" value="#trim(arguments.frm_status)#">;
            DECLARE @Name VARCHAR(50) = <cfqueryparam cfsqltype="cf_sql_varchar" maxlength="50" value="#trim(arguments.frm_name)#">;
            DECLARE @Code CHAR(2) = UPPER(<cfqueryparam cfsqltype="cf_sql_char" maxlength="2" value="#trim(arguments.frm_code)#">);
            DECLARE @ActionDate DATETIME = CURRENT_TIMESTAMP;
            DECLARE @ActionID UNIQUEIDENTIFIER = <cfqueryparam cfsqltype="cf_sql_idstamp" value="#SESSION.AccountID#">;

                BEGIN
                    UPDATE Dictionary
                    SET
                        Status = @Status,
                        Name = @Name,
                        Code = @Code,
                        ActionDt = @ActionDate,
                        ActionID = @ActionID
                    WHERE RecID = @RecordID
                    SELECT @RecordID AS RecID
                END

        </cfquery>

        <cfset local.fnResults = {status : "200", message : "Record successully saved!", RecID : qrySaveDictionary.RecID}>

        <cfcatch type="any">
            <cftransaction action="rollback" />
                <cfset local.fnResults = {status : "400", class : "alert-danger", message : "Error! Please contact your administrator."}>
            </cfcatch>
        </cftry>
    </cftransaction>

然后让前端决定用户选择操作时何时插入或更新。