我有一个关于交易的非常简单的问题。 (在sql server 2000中,但我想它适用于一般的db。事务)。
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
答案 0 :(得分:4)
但我的问题是:我了解到事务是原子的,因此允许在事务内部处于不一致状态。
这不是“原子”的意思。原子意味着“不可分割”,对于数据库而言,这仅仅意味着交易是全有或全无的事情。 Transactional 完整性要求事务完全提交或完全回滚。
这些都与Foreign-Keys无关,外键是确保参照完整性的手段之一,这是另一回事(虽然相关)。
至于你想要做什么,我知道在SQL Server 2005中你可以暂时禁用FK,这也可能在2000年。但是,这通常不被认为是最佳做法。相反,BP要么
1)NOT删除父键值,但改为更新行,同时保留父键值OR,
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无效。
答案 1 :(得分:2)
最干净的解决方案是延迟外键约束。这将推迟检查约束直到COMMIT
时间,允许在事务期间临时违反约束。不幸的是,这个功能显然在SQL Server中不可用。在支持延迟约束的系统上,类似下面的内容将起作用:
alter table tblForeignKey
modify constraint YourFKNameHere
deferrable
initially deferred;
某些系统不允许您更改约束的延迟,在这种情况下,您必须重新创建约束(可能还有表)。
SET CONSTRAINT[S]
语句可用于切换约束的延迟,例如在交易开始时:
set constraint YourFKNameHere deferred;
根据我的经验,ACID属性虽然明显不同,但往往可以协同工作。例如,在您的问题中,您正在尝试进行暂时无效的更新。其他用户在您提交之前不会看到任何更改(隔离,原子性)(持久性),并且您的事务的任何部分都不会产生任何影响(原子性),除非您的事务以数据库处于一致状态(一致性)结束。
答案 2 :(得分:1)
Consistency in 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
答案 3 :(得分:0)
现在我有一些改变它的逻辑 主键的表格,删除 并重新插入一把钥匙。
声音而不是DELETE / INSERT对,你应该只更新有问题的行? 要么是这样,要么首先必须删除tblForeignKey中的密钥,然后重新创建。