【发布时间】:2018-08-12 21:46:26
【问题描述】:
在过去的几个月里,我一直在研究我的单页应用程序,在尝试改进后端的一些东西的同时,我开始更深入地研究插入/更新和一般数据库的过程。我使用 SQL Server 2008 并且数据库是使用 Primary Keys 和 Foreign Keys 以及 Indexes 设计的,以便在表之间建立强大而可靠的连接。我说的是这些元素,因为我们当前的系统有一个设计很差的数据库。 deadlocks 有很多问题,而且性能很慢。我们的系统是全州范围的产品,具有大量数据交易,尤其是在乞讨或学年结束等时间段。然而,我的新应用程序在未来应该会有相同甚至更大的转换,因此我的数据库设计和插入/更新过程必须改进并防止死锁。我的问题是关于应该在这种系统中使用的 SQL Server 标准。例如,我最近开始更多地研究存储过程,并且似乎很多 DBA 专家都建议在反复使用相同查询的情况下采用这种方式。这应该可以防止一些安全风险并提高效率。以下是我目前如何在我的系统中处理 Insert 和 Update 事务的示例:
<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 EXIST 和IF 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 和良好实践的信息。正如我所提到的,这个项目对数据交易很重,对我来说选择正确的方法非常重要。如果有人可以提供一些想法,信息或示例,将不胜感激。
【问题讨论】:
-
数据库的结构是什么?您能否解释一下为什么需要检查和更新数据是否存在?
-
迄今为止最慢的方法是循环一个普通的 CFQUERY 标签,我强烈建议不要这样做。
-
是的。这是另一种方式,将循环放在 CFQUERY 中,但是,正如我所说,我们对在 CFQUERY 之外循环的结果感到惊讶,但仅使用 EXECUTE SP。我会尝试挖掘代码。那是几年前的事了……
-
这是我的看法。 MERGE 让 SQL Server 找出执行操作的最佳方法。我相信它胜过构建 if/else SQL 语句。
-
@espresso_coffee - 您提到了死锁,但没有说您是否绝对确定地确定了哪些语句是死锁的 - 以及所涉及的表上存在哪些索引。与其“猜测”什么可能解决问题,您应该首先跟踪死锁以确定导致它们的原因......只有这样您才能确定如何解决它们。
标签: sql-server sql-server-2008 stored-procedures coldfusion sql-merge