Готовый готовый шаблон для скрипта вставки - PullRequest
0 голосов
/ 25 мая 2018

Я в основном работаю в 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

Ответы [ 2 ]

0 голосов
/ 25 мая 2018

Короткая версия

Опубликованные запросы, с другой стороны, имеют серьезные недостатки и могут привести к получению неверных данных.Вы можете вставить все строки в один безопасный оператор, который не требует чрезмерных блокировок:

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 может никогда не вызываться, оставляя записи-призраки в базе данных.

Если вы хотите вставить только новые записи, вы можете слева соединить значения с целевой таблицей и вставить только новые.Если вы сравнивали исходную и целевую таблицы, вы могли бы написать:

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, или если существует индекс, начинающийся с них, блокировка будет минимальной.

Другим соображением является использование Изоляция снимков , особенно READ_COMMITTED_SNAPSHOT на уровне базы данных.Это позволит читателям читать существующие данные, даже если автор начал их изменять.Сервер начнет делать копии старых строк, когда авторы изменяют их, гарантируя, что читатели все еще могут читать данные, не блокируя авторов.

В некотором смысле это то, что вы пытались (но не смогли) сделать с подсказкой NOLOCK.

0 голосов
/ 25 мая 2018

Вот грубый шаблон.Но ваша организация может установить правила и стандарты, которые отличаются от этого.

    --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;  
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...