2016-11-23 2 views
1

Я запускаю SQL Server 2008. Я написал код, который должен быть достаточно безопасным, чтобы предотвратить удаление или обновление любых записей, но было бы намного счастливее, если бы я мог сделать это в базе данных уровень. Можно ли пометить таблицу так, чтобы после того, как была вставлена ​​строка, она никогда не может быть изменена или удалена?SQL Server предотвращает обновление или удаление всех записей

+2

Удаленный, кто ... вы? Другой пользователь?Это либо проблема с разрешениями, либо проблема с SQL-инъекцией в зависимости от того, как вы ее смотрите. – scsimon

ответ

1

Редактировать в комментариях. Кажется, вы на самом деле ищете версию, которая действительно не должна выполняться с помощью триггеров, но это может быть связано с производительностью и намного большей кодировкой.

Наиболее подходящий метод борьбы с вашей проблемой. Поддержание транзакционных резервных копий каждые X # минут, чтобы вы могли откат, если он перепутался.

Однако sql-сервер имеет 2 метода отслеживания изменений, встроенных в них, которые вы можете исследовать.

  • Изменить отслеживание - это просто указывает, была ли запись изменена и действительно полезна при синхронизации изменений с БД. В основном он увеличивает BIGINT для каждой строки для каждой операции, поэтому вам просто нужно проверить записи больше, чем предыдущий номер синхронизации.
  • Change Data Capture - это будет захватывать вставку/обновление/удаление и состояние записи (строки).

Для вашего конкретного беспокойства Change Data Capture может быть правдоподобным и способ более прост в обслуживании, чем триггеры. Я не пробовал это сам, потому что для отслеживания изменений было достаточно, потому что для отслеживания изменений было достаточно, но вот ссылка на документацию Microsoft. https://msdn.microsoft.com/en-us/library/cc645937(v=sql.110).aspx

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

CREATE TABLE TblName (
    PrimaryKeyID INT IDENTITY(1,1) NOT NULL PRIMARY KEY 
    ,Col1 INT NULL 
    ,Col2 INT NULL 
    ,OriginalPrimaryKeyId INT NULL 
    ,CreateDate DATETIME DEFAULT(GETDATE()) 
    ,UpdateDate DATETIME DEFAULT(GETDATE()) 
    ,IsLatestVersion BIT NOT NULL DEFAULT(1) 
) 
GO 

CREATE TRIGGER dbo.TrigForInsertEnforceVersionColTblName ON dbo.TblName 
FOR INSERT 
AS 
BEGIN 
BEGIN TRY 
    IF (SELECT COUNT(*) FROM TblName WHERE IsLatestVersion <> 1 AND OriginalPrimaryKeyId IS NULL) > 0 
    BEGIN 
     ;THROW 51000, 'Attempted to insert a record identified as Previous Version without referencing another record', 1 
    END 

END TRY 
BEGIN CATCH 
     IF @@TRANCOUNT > 0 ROLLBACK TRANSACTION 
    --this will mean the loss of some Primary Keys but it is better than an 
    --INSTEAD of INSERT because you wont have to handle the insert code 

    ;THROW 
END CATCH 

END 
GO 

CREATE TRIGGER dbo.TriggerName ON dbo.TblName 
INSTEAD OF UPDATE, DELETE 

AS 
BEGIN 

BEGIN TRY 

    IF EXISTS (
     SELECT * 
     FROM 
      TblName t 
      INNER JOIN inserted i 
      ON t.PrimaryKeyID = i.PrimaryKeyID 
      AND (t.OriginalPrimaryKeyId <> i.OriginalPrimaryKeyId 
      OR t.IsLatestVersion <> i.IsLatestVersion) 
    ) 
    BEGIN 
     ;THROW 51000, 'OriginalPrimaryKeyId Column or IsLatestVersion Column was attempted to be updated', 1 
    END 

    --don't have to test count can just run the update statement 
    IF ((SELECT COUNT(*) FROM inserted) > 0) 
    BEGIN 
     --It's an UPDATE Operations so insert new row but maintain original primary key 
     --so you know what the new row is a version of 
     INSERT INTO dbo.TblName (Col1, Col2, OriginalPrimaryKeyId) 
     SELECT 
      i.Col1 
      ,i.Col2 
      ,OriginalPrimaryKeyId = CASE 
       WHEN t.OriginalPrimaryKeyId IS NULL THEN t.PrimaryKeyID 
       ELSE t.OriginalPrimaryKeyId 
      END 
     FROM 
      inserted i 
      INNER JOIN TblName t 
      ON i.PrimaryKeyID = t.PrimaryKeyID 
    END 

    UPDATE t 
    SET IsLatestVersion = 0 
     ,UpdateDate = GETDATE() 
    FROM 
     TblName t 
     INNER JOIN deleted d 
     ON t.PrimaryKeyID = d.PrimaryKeyID 

END TRY 
BEGIN CATCH 
     IF @@TRANCOUNT > 0 ROLLBACK TRANSACTION 

    ;THROW 
END CATCH 

END 

Permissions обсуждение:

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

на всей роли, которая была бы лучше, если вы поставите пользователей в роли

GRANT INSERT ON SchemaName.TableName TO RoleName 
DENY UPDATE ON SchemaName.TableName TO RoleName 
DENY DELETE ON SchemaName.TableName TO RoleName 

ИЛИ для конкретных пользователей те же команды просто изменить RoleName по имени пользователя

DENY UPDATE ON SchemaName.TableName TO UserName 

Это даст возможность вставлять, но отменять возможность обновления или удаления записи.

Вы также можете запретить выполнение, изменять, и кучу еще здесь документации от Microsoft: https://msdn.microsoft.com/en-us/library/ms173724.aspx

Использование триггера вместо разрешений безопасности намного грязнее, и если кто-то с достаточным количеством разрешений хочет внести изменения, они по-прежнему может он просто замедлит их, но не сильно. Поэтому, если вы беспокоитесь об этой способности, убедитесь, что у вас хорошие резервные копии.

+0

Я бы не слишком беспокоился о внесении изменений администратором db. моя озабоченность связана с тем, что код будет иметь ошибку, или кто-то напишет новый код и реализует что-то, что изменит запись и вызовет хаос. триггер кажется идеальным, но есть ли способ получить доступ к значениям. например, я хочу, чтобы 1 бит столбца был отключен, когда была сделана новая ревизия. это ведет к записи старой версии, но отключает ее –

+0

@DanHastings, что было бы причиной для резервного копирования транзакций каждые x минут. Версии можно выполнять на уровне БД, но это будет иметь влияние на производительность, а также создаст сложность для разработчиков, которые знают, как писать код на основе правил медленного изменения размеров. Если желательно, чтобы управление версиями, обработка на уровне приложения может быть более уместной в какой-то момент, DBA и разработчик должны доверять друг другу :) Я покажу вам способ сделать это с помощью sql – Matt

Смежные вопросы