sql - 用于插入脚本的生产就绪模板

标签 sql sql-server database

我主要使用 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

最佳答案

精简版

另一方面,发布的查询存在严重缺陷,可能导致数据错误。您可以在不占用过多锁的单个安全语句中插入所有行:

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 并不意味着不要锁。这意味着不要尊重锁,即读取脏数据时自己使用过多的锁。这意味着如果其他一些事务插入相同的数据并将其删除,您的查询可能仍会看到它们。

我怀疑缺少索引或编写错误的查询导致阻塞。也许长期运行的事务最终会相互锁定?请记住,SQL Server 是最快的数据库之一。如果没有这些技巧,它每小时可以处理数 TB 数据的数百万笔交易。。这些问题应该得到解决,而不是被掩盖。例如,缺少索引意味着 SELECT 查询在尝试查找符合其条件的行时可能必须锁定很多行。使用适当的索引,它可能只需要锁定 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 子句,从而在数据库中留下幽灵条目。

如果您只想插入新条目,您可以将值与目标表左连接并仅插入新条目。如果您要比较源表和目标表,您可以这样写:

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 具有以 key1key2 列开头的主键,或者如果有以它们开头的索引,则锁定将是最小的.

另一个考虑是使用 Snapshot isolation ,尤其是数据库级别的 READ_COMMITTED_SNAPSHOT。这将允许读者读取现有数据,即使作者开始修改它们也是如此。服务器将在写入者更改旧行时开始复制它们,以确保读取者仍然可以读取数据而不会阻塞写入者。

在某种程度上,这就是您尝试(但失败了)使用 NOLOCK 提示进行的操作。

关于sql - 用于插入脚本的生产就绪模板,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/50518430/

相关文章:

mysql - 简化 mysql 查询

mysql - 如何删除除最新的 50 行之外的所有行

php - mysql_query和php语法错误问题

mysql - 在两个表中搜索 MySQL

c# - 在 SQL Server 中插入 100 万行的最快方法

c# - IEnumerable<T>、Parallel.ForEach 和内存管理

sql-server - 为什么在 Where 子句中使用 Case

sql - ORACLE 从表组中选择 ID,错误 "not a GROUP BY expression"

mysql - MySQL 中的 SQL 拆分没有自己的函数

php sql 注入(inject)