我主要使用javascript,对sql Server来说很新。我应该在表中插入两条记录。我在开发环境中做了尽职调查并且我的脚本正常工作,但是dba说它已经准备好投入生产,但是我的dba家伙已经离开了一段时间。那么如何调整跟踪数据脚本以进行生产:是否有可用的通用模板?
use tpaApp
go
IF NOT EXISTS(SELECT 1 FROM AppVersion WITH (NOLOCK) WHERE key1 = 401 and key2 = 800)
INSERT INTO AppVersion (key1, key2, name, value, timestamp, changed, disabled, delete)
SELECT 401, 800, 'AndroidVersion', '1.0.1', GETDATE(), 0, 0, 0
IF NOT EXISTS(SELECT 1 From AppVersion WITH (NOLOCK) WHERE key1 = 401 and key2 = 900)
INSERT INTO AppVersion (key1, key2, name, value, timestamp, changed, disabled, delete)
SELECT 401, 900, 'IosVersion', '1.0.1', GETDATE(), 0, 0, 0
- 回滚脚本:
IF EXISTS(SELECT 1 FROM AppVersion WITH (NOLOCK) WHERE key1 = 401 AND key2 = 800)
DELETE FROM AppVersion WHERE key1=401 and key2 =800
IF EXISTS(SELECT 1 FROM AppVersion WITH (NOLOCK) WHERE key1 = 401 AND key2 = 900)
DELETE FROM AppVersion WHERE key1=401 and key2 =900
答案 0 :(得分:0)
这是一个粗略的模板。但是,您的组织可能已制定了与此不同的规则和标准。
--No point slowing things down by calling GETDATE() twice
DECLARE @dtm DATETIME = GETDATE();
--Remove the (NOLOCK ) you need to be sure the SELECT statement returns 100% accurate results.
--Perform inside a single Transaction to ensure nothing changes between IF statement and INSERT statement
BEGIN TRY
BEGIN TRANSACTION
IF NOT EXISTS(SELECT 1 FROM AppVersion WHERE key1 = 401 and key2 = 800)
INSERT INTO AppVersion (key1, key2, name, value, timestamp, changed, disabled, delete)
SELECT 401, 800, 'AndroidVersion', '1.0.1',@dtm, 0, 0, 0
IF NOT EXISTS(SELECT 1 From AppVersion WHERE key1 = 401 and key2 = 900)
INSERT INTO AppVersion (key1, key2, name, value, timestamp, changed, disabled, delete)
SELECT 401, 900, 'IosVersion', '1.0.1', @dtm, 0, 0, 0
COMMIT
END TRY
BEGIN CATCH
-- Determine if an error occurred.
IF @@TRANCOUNT > 0
ROLLBACK
--PUT YOUR ERROR HANDLING HERE i.e Log Error
END CATCH;
答案 1 :(得分:0)
短版
另一方面,发布的查询存在严重缺陷,可能导致数据错误。您可以在不需要过多锁定的单个安全语句中插入所有行:
INSERT INTO AppVersion (key1, key2, name, value, timestamp, changed, disabled, [delete])
SELECT
newrows.key1, newrows.key2, newrows.name, newrows.value,
newrows.timestamp, newrows.changed, newrows.disabled, newrows.[delete]
FROM ( VALUES
(401, 800, 'AndroidVersion', '1.0.1', GETDATE(), 0, 0, 0),
(401, 900, 'IosVersion', '1.0.1', GETDATE(), 0, 0, 0)
) newrows (key1, key2, name, value, timestamp, changed, disabled, [delete])
LEFT OUTER JOIN AppVersion
ON newrows.key1=AAppversion.key1 and newrows.key2=Appversion.key2
WHERE Appversion.key1 is null and #Appversion.key2 is null
<强>解释强>
首先,这些查询会出现几个严重的问题。 NOLOCK
提示和尝试使用DELETE
而不是事务是一个非常强烈的迹象表明存在阻塞问题。试图掩盖它们虽然无济于事,但实际上它会让事情变得更糟。例如,NOLOCK
并不代表don't take locks
。这意味着don't respect locks, ie read dirty data while taking excessive locks yourself
。这意味着如果某个其他事务插入相同的数据并将其删除,您的查询仍可能会看到它们。
我怀疑缺少索引或编写错误的查询会导致阻塞。也许长时间运行的交易最终会相互锁定?请记住,SQL Server是最快的数据库之一。它可以在没有这些技巧的情况下每小时处理数百万条数据。这些问题应该修复,而不是掩盖。例如,缺少索引意味着SELECT查询可能必须在尝试查找符合其条件的行时锁定 lot 行。使用适当的索引,它可能只需要锁定1行。
INSERT .. SELECT
用于插入查询结果,而不是特定值。要插入特定值,您应该使用VALUES
子句。您可以在单个语句中添加多行,例如:
INSERT INTO AppVersion (key1, key2, name, value, timestamp, changed, disabled, delete)
VALUES
(401, 800, 'AndroidVersion', '1.0.1', GETDATE(), 0, 0, 0),
(401, 900, 'IosVersion', '1.0.1', GETDATE(), 0, 0, 0)
单个INSERT,DELETE,UPDATE语句是原子的,即如果它们失败,它们将自动回滚。他们不需要显式交易。在任何情况下,删除行不与回滚相同。如果存在严重问题,则可能永远不会调用DELETE
子句,从而在数据库中保留ghost条目。
如果只想插入新条目,可以将值与目标表连接起来,只插入新的值。如果您要比较源表和目标表,请编写:
INSERT into Target (ID,a,b,c)
SELECT source.ID,source.a,source.b,source.c
FROM source
LEFT OUTER JOIN target on source.ID = target.ID
where target.ID is null
这将仅选择没有匹配目标键的源行并插入它们。
您可以将值视为表格,将它们括在括号中并提供表格和列名称,例如:
FROM ( VALUES
(401, 800, 'AndroidVersion', '1.0.1', GETDATE(), 0, 0, 0),
(401, 900, 'IosVersion', '1.0.1', GETDATE(), 0, 0, 0)
) newrows (key1, key2, name, value, timestamp, changed, disabled, [delete])
这样您只能插入新行:
INSERT INTO AppVersion (key1, key2, name, value, timestamp, changed, disabled, [delete])
SELECT
newrows.key1, newrows.key2, newrows.name, newrows.value,
newrows.timestamp, newrows.changed, newrows.disabled, newrows.[delete]
FROM ( VALUES
(401, 800, 'AndroidVersion', '1.0.1', GETDATE(), 0, 0, 0),
(401, 900, 'IosVersion', '1.0.1', GETDATE(), 0, 0, 0)
) newrows (key1, key2, name, value, timestamp, changed, disabled, [delete])
LEFT OUTER JOIN AppVersion
ON newrows.key1=AAppversion.key1 and newrows.key2=Appversion.key2
WHERE Appversion.key1 is null and #Appversion.key2 is null
减少阻止
对于隐含的阻塞问题,您应该消除长时间运行的事务和不必要的查询,删除NOLOCK并确保所有表都具有正确的索引。如果AppVersion
具有以key1
和key2
列开头的主键,或者如果存在以它们开头的索引,则锁定将是最小的。
另一个考虑因素是在数据库级别使用Snapshot isolation,尤其是READ_COMMITTED_SNAPSHOT
。这将允许读者即使作者开始修改它们也能读取现有数据。当编写者更改旧行时,服务器将开始制作旧行的副本,确保读者仍然可以在不阻塞编写器的情况下读取数据。
在某种程度上,这是你尝试(但失败)与NOLOCK
提示有关的内容。