【发布时间】:2019-07-18 02:31:38
【问题描述】:
我需要区分两个父母之间的子对象集合。每个大约有 30,000 个对象,并且有大约十几个不同的属性。 Ruby 的 Set 类提供了一种快速的方法来从另一个集合中减去一个集合,并获得差值。我一直在用 JSON 数据做这个,整个过程只用了几秒钟。
现在我使用 ActiveRecord 来获取数据集。当然,一旦孩子从数据库中解组出来,它们就会包括属性:id、:created_at 和:updated_at。不幸的是,这会自动破坏 diff 中的比较,因为这些字段总是不同的,并导致比较失败。
在这组属性中,我真的只关心:label 和:data。也就是我想比较两个集合之间标签相同的对象,看看它们的数据是否不同。
我可以在我的班级中添加一个自定义等价运算符:
def ==(other)
self.label == other.label && self.data == other.data
end
这适用于单个对象的比较。如果(仅)它们的标签和数据匹配,则它们被认为是相等的。但是,为了确定等效性,此操作似乎并未使用此覆盖:
@diff = (@left.to_set - @right.to_set)
我希望 Set 会使用对象的类的重写 == 运算符,但情况似乎并非如此。我的差异只是一侧或另一侧,取决于差异的顺序。有没有办法做到这一点? (我也已经尝试过覆盖.eql?。)
由于评论太长,这里是这个想法的 SQL 实现。
WITH
t1 AS (SELECT * FROM tunings WHERE calibration_id = 7960),
t2 AS (SELECT * FROM tunings WHERE calibration_id = 7965)
SELECT t1.label, t1."data", t2."data" FROM t1 FULL OUTER JOIN t2 ON t1.label = t2.label
WHERE t1."data" != t2."data" OR t1."data" IS NULL OR t2."data" IS NULL
我什至还没有提出的另一个速度问题是,当我在视图中显示差异时,我必须从相应的集合中查找“正确”值,而这又需要 10 秒。这一切都是一步完成的。
由于 CTE,我猜我无法将其放入 ActiveRecord 语义中,我只需要传递带有种子值的原始 SQL,但我希望被证明是错误的。
另外,我在学术上仍然对原始问题感兴趣。
【问题讨论】:
-
数据库中的数据是如何存储的? DBM 具有各种出色的功能来执行此类操作,并且它们的运行速度比检索数据并在代码中执行要快得多。看起来它的存储方式并没有使 DBM 可以轻松地做到这一点。我建议寻找一种更好的方式来存储数据。
-
好吧,废话。我花了一整天的时间来研究这个,我什至没有想到这一点。在通常的 Rails 实践中,父母的所有孩子都在一个表中,由 parent_id 字段标识。我刚刚尝试了 FULL OUTER JOIN,并且 1)这与“设置”方法之间存在差异,因此需要更多的工作,2)但只需要 1.5 秒,以及 3)我将不得不弄清楚如何让 AR 做这个查询。一个积极的方面是,通过这样做,我意识到我还没有在我的 label 或 parent_id 字段上创建索引,并且这样做大大加快了这两种方法的速度。
-
David,提醒您在回复评论时应包含目标用户名 (@theTinMan),以便 SO 将评论通知他们。在这里 TinMan 会收到通知,但这只是因为这是您之前的唯一评论。
-
@tadman,虽然这行得通,但我可以很容易地想象在繁忙的生产服务器中的情况,由于来回移动数据,即使转储 JSON 并将其作为字符串进行比较也可能代价高昂。如果 DBM 将数据视为字符串并进行比较,那么在代码需要知道究竟发生了什么变化之前,它会快得多,然后它会再次减慢速度,并且比存储更多数据的成本还要高细粒度地。我与之合作的最后一个团队也在做同样的事情,存储序列化的对象,然后来回移动它们以进行比较。我解释了为什么不,但他们不在乎。
-
只是解释 Ruby 在内部做了什么。
标签: ruby-on-rails ruby set