过去的几个月中,我一直在开发单页应用程序,在尝试改进后端的某些功能时,我开始更深入地研究我的插入/更新和数据库过程。我使用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和良好实践的信息。正如我所提到的,这个项目非常重视数据事务,因此选择正确的方法对我来说非常重要。如果有人可以提供一些想法,信息或示例,将不胜感激。
答案 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>
然后让前端决定用户选择操作时何时插入或更新。