Разрешено ли несовместимое состояние в транзакции? - PullRequest
3 голосов
/ 07 июня 2009

У меня очень простой вопрос о транзакциях. (в SQL Server 2000, но я думаю, что это относится к общим транзакциям БД).

tblPrimaryKey

PkId        
-----
1
2
3

tblForeignKey

Id   ForeignKey  
---- ----- 
1    1
2    2
3    3
4    1

У меня есть 2 таблицы, одна ссылается на другую (tblForeingKey.ForeignKey ссылается на tblPrimaryKey.PkID). Теперь у меня есть логика, которая изменяет табель первичного ключа, удаляя и вставляя ключ заново.

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

Так что я думаю, что-то вроде этого должно работать:

BEGIN TRAN eg

    DELETE tblPrimaryKey WHERE PkId = 3     
    INSERT INTO tblPrimaryKey  SELECT 3

COMMIT TRAN eg

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

ОБНОВЛЕНИЯ:

Последовательность Эта характеристика означает, что база данных должна быть согласованной до и после транзакции.

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

Не означает ли это, что в возможна несогласованность транзакций?

ОБНОВЛЕНИЕ:

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

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

CREATE PROC usp_SyncRecords
(
 @tableName1 as nvarchar(255),
 @tableName2 as nvarchar(255), 
 @joinClause as nvarchar(255),
 @whereClause as nvarchar(1000)
)
-- this proc updates all fields in table 1 that have corresponding names 
-- in table2 to the value of the field in table2.
AS 
BEGIN 
    DECLARE @sqlClause nvarchar(4000)
    DECLARE @curFieldName nvarchar(255)
    DECLARE @sqlColumnCursorClause nvarchar(1000)
    SET @sqlClause = 'UPDATE [' + @tableName1 + '] SET '

    -- get FieldNames for second table 
    SET @sqlColumnCursorClause = 
        'DECLARE cur CURSOR FAST_FORWARD FOR SELECT name FROM syscolumns ' + 
        'WHERE id=' + CAST(object_id(@tableName2) as nvarchar(50))

    EXEC sp_executeSql @sqlColumnCursorClause


    OPEN cur
        -- compose sqlClause using fieldnames
        FETCH NEXT FROM CUR INTO @curFieldName
        WHILE @@fetch_status <> -1 
        BEGIN 
            SET @sqlClause = @sqlClause + @curFieldName  + '=' +
                                                      @tableName2 +  '.' + @curFieldName  + ','
            FETCH NEXT FROM CUR INTO @curFieldName
        END

    CLOSE cur 
    DEALLOCATE cur 

    -- drop last comma 
    SET @sqlClause = LEFT(@sqlClause,LEN(@sqlClause) -1)

    -- adding from/join/where clauses 
    SET @sqlClause = @sqlClause + ' FROM [' + @tableName1 + '] INNER JOIN [' + @tableName2 + '] '
               + 'ON ' + @joinClause +  ' WHERE '  +  @whereClause

    EXEC sp_executeSQL @sqlClause

END

Ответы [ 4 ]

4 голосов
/ 07 июня 2009

Но мой вопрос заключается в следующем: я узнал, что транзакция является атомарной, поэтому внутри транзакции допускается несогласованное состояние.

Это не то, что означает «атомный». Атомарный означает «неделимый», а для баз данных это просто означает, что транзакция - это дело «все или ничего». Транзакционная целостность требует, чтобы транзакция была либо полностью зафиксирована, либо полностью откатана.

Ничто из этого не имеет ничего общего с Foreign-Keys, которые являются одним из средств обеспечения целостности Referential , что является другим (хотя и связанным).

Что касается того, что вы пытаетесь сделать, я знаю, что в SQL Server 2005 вы можете временно отключить FK, и это может произойти и в 2000 году. Однако, это обычно не считается лучшей практикой. Вместо этого BP либо

1) НЕ удаляет значение родительского ключа, но вместо этого обновляет строку, сохраняя значение родительского ключа, ИЛИ,

2) Если вы намереваетесь удалить (или изменить) родительский ключ навсегда, вам следует сначала удалить или переназначить дочерние записи.

Структурная несогласованность никогда не должна быть видна пользователям (если это так, то вы структурно повреждены).

Транзакционное несоответствие допускается только внутри транзакции. Он никогда не должен быть видим за пределами транзакции (за исключением того, что уровни изоляции ниже, чем Serializable, позволяют это в некоторой степени).

Ссылочное несоответствие не имеет ничего общего с этими двумя. Однако в большинстве случаев ссылочную целостность можно отключить с помощью параметра NOCHECK:

    -- Disable the constraint.
ALTER TABLE cnst_example NOCHECK CONSTRAINT FK_salary_caps;

--Do stuff that violates RI here:

-- Reenable the constraint.
ALTER TABLE cnst_example WITH CHECK CHECK CONSTRAINT FK_salary_caps;

Однако это НЕ предпочтительный способ. Предпочтительным способом является внесение изменений в правильном порядке (это прямо из BOL).

ПРИМЕЧАНИЕ 1. У меня нет доступа к SQL 2000, поэтому я не знаю, работает ли вышеизложенное. Работает в 2005 году.

ПРИМЕЧАНИЕ 2. «DEFERRABLE» - это параметр Oracle. Недопустимо для SQL Server.

2 голосов
/ 07 июня 2009

Самым чистым решением было бы отложить ограничение внешнего ключа. Это отложит проверку ограничения до COMMIT времени, что позволит временно нарушить его во время транзакции. К сожалению, эта функция, очевидно, недоступна в SQL Server. В системах, которые поддерживают отложенные ограничения, будет работать что-то вроде следующего:

alter table tblForeignKey
  modify constraint YourFKNameHere
    deferrable
    initially deferred;

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

Оператор SET CONSTRAINT[S] может использоваться для переключения отсрочки ограничения, например в начале транзакции:

set constraint YourFKNameHere deferred;

По моему опыту, свойства КИСЛОТ, хотя они явно различны, имеют тенденцию работать вместе Например, в вашей проблеме вы пытаетесь сделать обновление, которое временно недействительно. Другие пользователи не увидят ваши изменения (изоляция, атомарность) до тех пор, пока вы их не зафиксируете (долговечность), и никакая часть вашей транзакции не окажет никакого влияния (атомарность), если ваша транзакция не завершится с согласованным состоянием базы данных (согласованность).

1 голос
/ 07 июня 2009

Согласованность в ACID означает, что будут записаны только действительные данные. Не то чтобы в транзакции допускались несоответствия.

Для решения этой конкретной проблемы SQL, но предположим, что столбцы ForeignKey могут быть NULL.

DECLARE @FKTabIDs (FKTabID int)

BEGIN TRAN eg

    INSERT FKTabIDs (FKTabID) SELECT [Id] FROM tblForeignKey WHERE ForeignKey = 3

    --Assumes NULL but could use any valid value
    UPDATE tblForeignKey SET ForeignKey = NULL WHERE ForeignKey = 3

    DELETE tblPrimaryKey WHERE PkId = 3         
    INSERT tblPrimaryKey SELECT 3

    UPDATE tFK
    SET ForeignKey = 3
    FROM tblForeignKey tFK JOIN @FKTabIDs tv ON tFK.[Id] =  tv.FKTabID
    --... or use exists, in etc if you prefer

COMMIT TRAN eg
0 голосов
/ 08 июня 2009

Теперь у меня есть логика, которая изменяет табель первичного ключа, удалив и вставив ключ.

Похоже, вместо пары УДАЛИТЬ / ВСТАВИТЬ, вам лучше просто ОБНОВИТЬ строку? Либо это, либо вы должны сначала удалить ключ в вашем tblForeignKey и воссоздать его.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...