关系数据库中的“差异”对象

时间:2009-05-11 09:54:20

标签: language-agnostic oop diff

我们的win32应用程序从MySQL关系数据库中的许多表中的数据中组装对象。在这样的对象中,多个修订版存储在数据库中。

当存储多个版本的东西时,迟早你会问自己一个问题,如果你可以看到两个版本之间的差异:)所以我的问题是:什么是“差异”两个好方法这样的数据库对象?

  • 您会在数据库级别进行比较吗? (听起来不是一个好主意:太低级别,对模式过于敏感)。
  • 你会比较对象吗?
    • 您是否会编写一个“手动”比较两个对象的属性和字段的函数?
    • 你如何存储差异?在一个单独的,通用的“TDiff”对象?
    • 有关如何在用户界面中显示此类内容的任何一般性建议?

非常欢迎有关您自己的经历的建议或故事;谢谢你!

关于用例的额外信息(20090515)

回复安东尼的评论:这个特定的应用程序用于安排由教师团队运营的培训课程。教师的日程安排存储在数据库的各个表中,并包含诸如“她必须在哪一天去哪里”,“谁是她的团队中的同事”等信息。此信息分布在多个表。

有一段时间,我们会“发布”日程安排,以便教师可以在网页上看到它。每个“出版物”都是一个修订版,我们希望能够向用户(以及后来的教师)展示两个出版物之间的变化 - 如果有的话。

希望使场景更加切实:)

最后一些评论

好吧,赏金已经结束,所以我接受了答案。如果以某种方式可以将我的代表中的额外100个分开,并将其提供给其他一些答案,我会毫不犹豫地这样做。你们所有人的帮助都很棒,我非常感激! ~Onno 20090519

12 个答案:

答案 0 :(得分:8)

只是一个想法,但您是否值得将两个对象版本转换为某种文本格式,然后使用现有的差异程序比较这些文本对象 - 例如diff?有很多不错的差异程序,可以提供很好的视觉表现等。

所以例如

对象1的文本版本:

first_name: Harry
last_name: Lime
address: Wien
version: 0.1

对象2的文本版本:

first_name: Harry
last_name: Lime
address: Vienna
version: 0.2

差异就像:

3,4c3,4
< address: Wien
< version: 0.1
---
> address: Vienna
> version: 0.2

答案 1 :(得分:4)

假设一个类有5个已知属性 - 日期,时间,主题,大纲,位置。当我查看我的日程安排时,我最感兴趣的是这些属性的最新版本(即当前/准确版本)。对我来说,知道什么(如果有的话)已经改变也是有用的。 (作为附注,如果日期,时间或地点发生变化,我也希望收到一封电子邮件/短信通知我,以防万一我没有检查更新时间表: - )

我建议在修改时间表时执行“差异”。因此,当创建类的第2版时,记录哪些值已更改,并将其存储在版本2对象的两个“更改日志”字段中(必须已有一个父表位于所有表的顶部 - 使用该表! )。一个更改日志字段是“人类可读文本”,例如“日期从5月1日星期一更改为5月2日星期二,时间从上午10:00更改为上午10:30”。第二个更改日志字段是已更改字段的分隔列表,例如“日期,时间”为此,在保存之前,您将循环访问用户提交的值,与当前数据库值进行比较,并连接2个字符串,一个人类可读,一个字段名称列表。然后,更新数据并将连接的字符串设置为“changelog”值。

显示计划时默认加载当前版本。遍历changelog字段列表中的字段,并注释显示以显示值已更改(a *或突出显示等)。然后,在单独的面板中显示人类可读的更改日志。

如果时间表被多次修改,您可能希望将版本1和版本之间的更改日志组合在一起。 2和2&amp; 3.在版本3中说只更改了课程大纲 - 如果这是显示日程安排时唯一的更改日志,则不会显示日期和时间的更改。

请注意,这种非规范化的方法对于分析来说不是很好 - 例如,确定哪个特定位置总是更改了类 - 但您可以使用E-A-V模型来扩展它以存储更改日志。

答案 2 :(得分:2)

如果您关心的是对数据库的更改,那么在数据库级别进行比较会很好。如果你试图在数据库本身之上设计一层通用功能,那就最有意义了。

如果您关心数据的更改,那么在对象级别进行比较会很好。例如,如果数据是程序的输入,并且您有兴趣查看输入中的更改以验证输出的更改是否正确。

您的用例似乎不属于这些用例。您似乎关心输出并希望从该角度看出差异。如果是这种情况,我会对输出报告(或其纯文本版本)而不是基础数据做差异。你可以用任何现成的diff工具做到这一点。为了让最终用户更轻松,您可以解析diff结果并将其呈现为HTML。这里有很多选项:与颜色编码并排显示变化,一个文档带有更改标记(例如删除的红色删除线和添加的绿色),可能只是突出显示已更改的区域并使用气球显示先前/当前需求值。

我考虑过进行数据库比较,但从未尝试过实现它。正如您所指出的,任何此类尝试都与架构密切相关。

我已完成对象级比较。一般算法是这样的:

  1. 对对象ID列表进行集合比较。这将创建三个结果分组:添加的对象,已删除的对象以及两个集合中的对象。
  2. 报告删除。
  3. 报告添加内容。
  4. 对于两组中的事物,进行逐个属性比较。
  5. 如果发现任何差异,请报告对象ID,不同的属性以及相应的值。如果适用,请突出显示已更改的属性值部分。
  6. 在我的例子中,比较算法是手写的,以匹配对象属性。这让我可以控制比较哪些属性以及如何进行比较。在某些情况下,通用比较器可能是可能的,但取决于具体情况,至少部分取决于实现语言。

答案 3 :(得分:1)

我多次研究过MysQL Diffing。不幸的是,没有任何真正好的解决方案。

我尝试过的一个工具是mysqldiff(www.mysqldiff.org)。 mysqldiff是一个用PHP编写的工具,它能够区分mysql模式。不幸的是,它在很多时候都没有做得很好。

MySQL Workbench,MySQL自己的SQL IDE提供了生成alter脚本的选项,我想它会通过内部执行某种diff操作来实现这一点。

Aqua Data Studio是另一种能够比较模式并输出两者差异的工具。虽然ADS差异非常好,但它不提供创建alter脚本的工具。

如果我自己编写,我想我会编写能够比较两个表结构的代码。这样的代码可以被调整为高度敏感(Ig,如果列顺序不同于版本到下一个,它是一个区别)或更中等敏感(例如,列顺序不是主要问题,数据类型和长度是重要的,因为索引和约束)。

存储,我不确定。我将研究一个版本控制系统如Mercurial如何存储其差异信息以进行修订,并使用它来详细说明适用于数据库的方法。

最后,对于视觉输出,我建议您查看Aqua Data Stduio比较功能(您可以使用试用版来测试此...)。它的差异输出非常好。

答案 4 :(得分:1)

我的应用程序dbscript比较存储过程中的分层数据(数据库模式),当然这必须将每个对象的每个字段/属性与其对应项进行比较。我想你不会绕过那一步(除非你有一个通用的对象描述模型)

至于问题的用户界面部分,请查看viewselect差异的屏幕截图。

答案 5 :(得分:1)

我会考虑对象的某种常见文本表示,并让文本与现有的差异工具(如WinMerge)进行比较。

我认为没有必要自己发明差异,因为我已经有很多很好的工具可供使用。

答案 6 :(得分:1)

在PostgreSQL的情况下,我使用了与模式的差异表:

history_columns (
    column_id smallint primary key,
    column_name text not null,
    table_name text not null,
    unique (table_name, column_name)
);
create temporary sequence column_id_seq;
insert into history_columns
select nextval('column_id_seq'), column_name, table_name
    from information_schema.columns
    where
        table_name in ('table1','table2','table3')
        and table_schema=current_schema() and table_catalog=current_database();

create table history (
    column_id smallint not null references history_columns,
    id int not null,
    change_time timestamp with time zone not null
        constraint change_time_full_second -- only one change allowed per second
            check (date_trunc('second',change_time)=change_time),
    primary key (column_id,id,change_time),
    value text
);

在桌子上我使用了这样的触发器:

create or replace function save_history() returns trigger as
$$
    if (tg_op = 'DELETE') then
        insert into historia values (
            find_column_id('id',tg_relname), OLD.id,
            date_trunc('second',current_timestamp),
            OLD.id );
        [for each column_name] {
            if (char_length(OLD.column_name)>0) then
                insert into history values (
                    find_column_id(column_name,tg_relname), OLD.id,
                    OLD.change_time, OLD.column_name
                )
        }
    elsif (tg_op = 'UPDATE') then
        [for each column_name] {
            if (OLD.column_name is distinct from NEW.column_name) then
                insert into history values (
                    find_column_id(column_name,tg_relname), OLD.id,
                    OLD.change_time, OLD.column_name
                );
            end if;
        }
    end if;
$$ language plpgsql volatile;

create trigger save_history_table1
    before update or delete on table1
    for each row execute procedure save_history();

答案 7 :(得分:1)

这不是你问的问题的答案,而是试图重新设想这个问题。您是否会考虑更改数据库和对象模型以存储聚合根和一系列增量?也就是说,建模和存储作为修订集合的RevisionSets;修订版是与值配对的实体属性。从某种意义上说,这是将修订结构内化到您的架构中,其他海报建议您通过“日志”固定您已经拥有的内容。

从增量中显示聚合是微不足道的,甚至更容易将增量显示为更改历史记录。您使用具有状态和本地内存的富客户端这一事实使这一点更加引人注目。您可以非常轻松地显示“自日期xxxx以来的所有更改”,而无需重新访问数据库。

基本想法归功于Greg Young和他在财务数据流方面的工作,但它很适用于您的问题。

答案 8 :(得分:1)

我正在嘲笑Harry Lime的建议:将属性输出为文本格式,然后对结果进行哈希处理。这样,您可以比较哈希值并轻松标记已更改的数据。通过这种方式,您可以直观地看到差异,但可以通过编程方式识别差异。如果您希望存储和检索增量,那么您将有一个很好的索引源。

答案 9 :(得分:1)

鉴于你想为此创建一个用户界面并需要指出差异所在,我觉得你可以自定义或创建一个通用对象比较器 - 后者取决于你正在使用的语言。

对于自定义方法,您需要创建一个类,该类需要比较两个类的实例。然后它返回差异;

 public class Person
 {
     public string name;
 }

 public class PersonComparer
 {
     public PersonComparer(Person old, Person new)
     {
        ....         
     }

     public bool NameIsDifferent() { return old.Name != new.Name; }
     public string NameDifferentText() { return NameIsDifferent() ? "Name changed from " + old.Name + " to " + new.Name : ""; }
 }

这样您就可以使用NameComparer对象来创建GUI。

gereric方法将大致相同,只是你概括了调用,并使用对象insepection(下面的getObjectProperty调用)来查找差异;

 public class ObjectComparer()
 {
    public ObjectComparer(object old, object new)
    {
        ...
    }

    public bool PropertyIsDifferent(string propertyName) { return getObjectProperty(old, propertyName) != getObjectProperty(new, propertyName) };

     public string PropertyDifferentText(string propertyName) { return PropertyIsDifferent(propertyName) ? propertyName + " " + changed from " + getObjectProperty(old, propertyName) + " to " + getObjectProperty(new, propertyName): ""; }
 }
}

我会选择第二个,因为它可以很容易地根据需要更改GUI。 GUI我会尝试“淡化”差异以使它们易于查看 - 但这取决于您希望如何显示差异。

获取要比较的对象将使用初始修订版和最新版本加载对象。

我的2美分...不像数据库那样技术性比较已经存在的东西。

答案 10 :(得分:1)

你看过Open Source DiffKit吗?

www.diffkit.org

我认为它符合您的要求。

答案 11 :(得分:0)

Oracle示例。

  • 使用dbms_metadata将订购的对象导出到文本
  • 将有序表格数据导出为CSV或查询格式
  • 制作大文字文件
  • DIFF