2010-01-20 2 views
5

У меня есть поле «InsertTime» в таблице в базе данных SQL Server 2005, которая по умолчанию считается «getDate()», когда запись впервые вставляется в базу данных. Я хочу, чтобы этот столбец не обновлялся снова.SQL Server 2005 - Установка столбца как только для чтения

Может ли этот столбец быть установленным только для чтения или есть лучший способ сделать это, не написав все sql для разработчиков?

+0

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

ответ

1

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

+0

И заставьте разработчика написать sproc. –

4

Вы можете реализовать поле «только для чтения», создав триггер UPDATE, который проверяет наличие обновлений для этого столбца и затем отбрасывает их обратно.

IF EXISTS (SELECT name FROM sys.objects 
     WHERE name = 'ReadOnlyInsertTime_tr' AND type = 'TR') 
    DROP TRIGGER dbo.ReadOnlyInsertTime_tr; 
GO 

CREATE TRIGGER ReadOnlyInsertTime_tr 
ON dbo.MyTable 
AFTER UPDATE 
AS 
IF (UPDATE(InsertTime)) 
BEGIN 
ROLLBACK 
-- Raise an informative error 
-- RAISERROR (50009, 16, 10) 
END; 
GO 
+0

+1 Мне нравится это решение; я бы добавил, что если вы пишете 'InsertTime' в триггере' AFTER INSERT', вам нужно отключить этот триггер для продолжительности этого триггера. – briantyler

1

Вместо триггера можно также выполнить эту работу.

CREATE TRIGGER [dbo].[MyTableUpdateTrigger] 
ON [dbo].[MyTable] 
INSTEAD OF UPDATE 
AS 
BEGIN 
    SET NOCOUNT ON 

    UPDATE MyTable 
    SET Column1 = Inserted.Column1 
      ,Column2 = Inserted.Column2 
      -- Don't set this for the "InsertTime" field. 
    FROM Inserted 
    INNER JOIN MyTable 
    ON Inserted.TheKey = MyTable.TheKey 
END 
+0

Итак, как вы делаете ошибку, если пользователь пытается обновить запрещенный столбец? – Ian

2

Другой подход - воссоздать исходное значение InsertDate. Когда строка обновляется, исходная строка перемещается в (логическую) «удаленную» таблицу. Итак ...

ALTER TRIGGER trgDocuments 
ON dbo.Documents 
FOR INSERT, UPDATE 
AS 

-- For new records set the created date and the updated date to Now 
UPDATE Documents SET DateCreated = GetDate(), DateUpdated = GetDate() Where DocumentId in (SELECT DocumentId From inserted) 

-- For updated records set the created date to the original created date and the updated date to Now 
UPDATE Documents SET DateCreated = (SELECT DateCreated FROM deleted d WHERE DocumentId = d.DocumentId) Where DocumentId in (SELECT DocumentId From deleted) 
UPDATE Documents SET DateUpdated = GetDate() Where DocumentId in (SELECT DocumentId From deleted) 
0

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

Я хотел бы иметь столбец RecordCreated и LastModified, который управлялся базой данных, а не пользовательским агентом. В этом случае RecordCreated будет datetime, когда сначала была вставлена ​​строка, а LastModified - в последний раз, когда строка была обновлена. Я также хотел, чтобы пользователь не мог изменять эти столбцы напрямую. Вот что я сделал:

Добавлены две колонки в моей таблице как обнуляемого столбцов SMALLDATETIME:

RecordCreated SMALLDATETIME, 
LastModified SMALLDATETIME 

Создано ПОСЛЕ триггер для таблицы:

CREATE TRIGGER dbo.WidgetProductionTrigger ON dbo.WidgetProduction AFTER INSERT, UPDATE AS BEGIN 

Спусковой механизм сначала проверяет, если пользователь попытался вставить/изменить значения моих столбцов. Я столкнулся с проблемой, с функцией UPDATE() всегда возвращает истину, так что требовалось немного больше логики:

DECLARE @RecordCreated SMALLDATETIME; 
SELECT @RecordCreated = RecordCreated FROM INSERTED; 
IF UPDATE(RecordCreated) AND NOT @RecordCreated IS NULL BEGIN 
    RAISERROR('RecordCreated is not user maintainable.', 16, 1); 
    ROLLBACK TRANSACTION; 
    RETURN; 
END; 
DECLARE @LastModified SMALLDATETIME; 
SELECT @LastModified = LastModified FROM INSERTED; 
IF UPDATE(LastModified) AND NOT @LastModified IS NULL BEGIN 
    RAISERROR('LastModified is not user maintainable.', 16, 1); 
    ROLLBACK TRANSACTION; 
    RETURN; 
END; 

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

IF EXISTS (SELECT * FROM DELETED) BEGIN 
    UPDATE dbo.WidgetProduction SET 
     LastModified = CAST(GETDATE() AS SMALLDATETIME) 
    WHERE ProductionRecordID IN (SELECT ProductionRecordID FROM INSERTED); 
END ELSE BEGIN 
    UPDATE dbo.WidgetProduction SET 
     RecordCreated = CAST(GETDATE() AS SMALLDATETIME), 
     LastModified = CAST(GETDATE() AS SMALLDATETIME) 
    WHERE ProductionRecordID IN (SELECT ProductionRecordID FROM INSERTED); 
END; 

Это решение работает так, как ожидалось.

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