我有一个名为Books的表,其中包含一些列。
ColumnNames: BookId, BookName, BookDesc, xxx
我想跟踪某些列的更改。 我不需要保留旧价值和新价值的历史。我只想跟踪该值是否已更改。
实现这一目标的最佳方法是什么?
1)创建Books表格为:
ColumnNames: BookId, BookName, BookName_Changed_Flag, BookDesc, BookDesc_Changed_Flag,
xxx, xxx_Changed_Flag?
2)创建一个单独的表Books_Change_Log,与Books表完全相同,但只有轨道更改列为:
ColumnNames: BookId, BookName_Changed_Flag, BookDesc_Changed_Flag, xxx_Changed_Flag?
请告知。
- 更新 -
每张表中有超过20列。每列代表UI中的某个元素。如果列值从其原始记录中更改,我需要显示以不同样式表示列值的UI元素。其余的元素应该看起来很正常。
答案 0 :(得分:2)
如何在TSQL中使用位域(用于更新和读取)
在开始时将位域默认设置为0(意味着没有变化),您应该使用int类型最多32位数据,bigint最多64位数据。
要在位字段中设置位,请使用update语句中的|
(位OR运算符),例如
UPDATE table
SET field1 = 'new value', bitfield = bitfield | 1
UPDATE table
SET field2 = 'new value', bitfield = bitfield | 2
对于每个字段的等,使用2到N-1的幂为|
要读取位字段,请使用&
(位AND运算符)并查看它是否为真,例如
SELECT field1, field2,
CASE WHEN (bitfield & 1) = 1 THEN 'field1 mod' ELSE 'field1 same' END,
CASE WHEN (bitfield & 2) = 2 THEN 'field2 mod' ELSE 'field2 same' END
FROM table
注意我可能不会使用文本,因为这将由应用程序使用,类似这样的工作
SELECT field1, field2,
CASE WHEN (bitfield & 1) = 1 THEN 1 ELSE 0 END AS [field1flag],
CASE WHEN (bitfield & 2) = 2 THEN 1 ELSE 0 END AS [field2flag]
FROM table
或者您可以使用上面的!= 0来简化,就像我在下面的测试中所做的那样
Have to actually test to not have errors, click for the test script
原始答案:
如果表中的列少于16列,则可以将“flags”存储为整数,然后使用bit flag方法指示已更改的列。只是忽略或不打扰标记你不关心的那些。
因此,如果flagfield BOOLEAN AND 2 ^ N为真,则表示第N个字段已更改。
或者最大N = 2
的示例0 - 没有任何改变(所有位0)
1 - 字段1已更改(第1位)
2 - 字段2已更改(第二位1)
3 - 字段1 + 2已更改(第一位和第二位1)
请参阅此链接以获得更好的定义:http://en.wikipedia.org/wiki/Bit_field
答案 1 :(得分:1)
你应该有一个存储BookId和更改日期的日志表(你不需要那些其他列 - 如你所说,你不需要旧的和新的值,你可以随时获得Books表中的名称,描述等的当前值,没有理由将其存储两次)。除非你只对最后一次改变感兴趣。您可以使用books表上的简单更新触发器填充日志表。例如,使用您提供的新信息:
CREATE TABLE dbo.BookLog
(
BookID INT PRIMARY KEY,
NameHasChanged BIT NOT NULL DEFAULT 0,
DescriptionHasChanged BIT NOT NULL DEFAULT 0
--, ... 18 more columns
);
CREATE TRIGGER dbo.CreateBook
ON dbo.Books FOR INSERT
AS
BEGIN
SET NOCOUNT ON;
INSERT dbo.BookLog(BookID) SELECT BookID FROM inserted;
END
GO
CREATE TRIGGER dbo.ModifyBook
ON dbo.Books FOR UPDATE
AS
BEGIN
SET NOCOUNT ON;
UPDATE t SET
t.NameChanged = CASE WHEN i.name <> d.name
THEN 1 ELSE t.NameChanged END,
t.DescriptionChanged = CASE WHEN i.description <> d.description
THEN 1 ELSE t.DescriptionChanged END,
--, 18 more of these assuming all can be compared with simple <> ...
FROM dbo.BookLog AS t
INNER JOIN inserted AS i ON i.BookID = t.BookID
INNER JOIN deleted AS d ON d.BookID = i.BookID;
END
GO
答案 2 :(得分:1)
我知道你说你不需要它,但有时它更容易使用现成的东西来完成所有事情,例如:http://autoaudit.codeplex.com/
这只会在您的表中添加几列,并且几乎不会像您提出的任何模式那样具有侵入性,并且该工具也会生成跟踪更改所需的触发器。
答案 3 :(得分:1)
我可以向您保证,在您提供此解决方案后,下一个请求之一就是“告诉我之前的情况”。请继续,并有一个历史表。这将解决您当前的问题和您未来的问题。它是非平凡系统的标准设计。
答案 4 :(得分:0)
在表格中放置两个日期时间列,“created_at”和“updated_at”。默认为current_timestamp。如果要更改行中的数据,则只设置updated_at的值。您可以使用表上的触发器来强制执行此操作,该触发器检查是否有任何列值发生更改,然后更新“updated_at”(如果是)。
如果您想检查行是否有变化,只需检查updated_at&gt; created_at。