查找涉及STI和父子关系的“重复”记录的解决方案

我有一个名为Buyable的基于STI的模型,有两个模型Basket和Item。 这里可购买的关注属性是:

  • shop_week_id
  • LOCATION_ID
  • PARENT_ID

篮子和物品之间有亲子关系。 对于篮子,parent_id总是为零,但是通过引用唯一的篮子ID,项目可以属于篮子。 因此,篮子有很多物品,而物品属于一个篮子。

我需要一个篮子模型的方法:

如果表中有任何其他具有相同数量和类型的项目的篮子,则返回true。 当项目共享相同的shop_week_id和location_id时,它们被认为是相同的类型。

例如:

给出一个篮子(uid = 7)有2个项目:

项目#1

  • id = 3
  • shop_week_id = 13
  • location_id = 103
  • parent_id = 7

项目#2

  • id = 4
  • shop_week_id = 13
  • location_id = 204
  • parent_id = 7

如果表中有任何其他篮子包含正好2个项目,则返回true,其中一个项目具有shop_week_id = 13且location_id = 103,另一个项目具有shop_week_id = 13和location_id = 204.否则返回false。

你会怎么解决这个问题? 这是不言而喻的,但我正在寻找一个非常有效的解决方案。

为了澄清我的查询,以及对“可购买”表的表列的某种模糊描述,“Parent_ID”是有问题的篮子。 “Shop_Week_ID”是要比较的篮子的考虑因素…不要比较第1周到第2周到第3周的篮子。#ID列似乎是表格中的顺序ID,但不是实际ID要比较的项目… Location_ID似乎是常见的“项目”。 在该场景中,假设购物车,Location_ID = 103 =“Computer”,Location_ID = 204 =“Television”(仅用于我对数据的解释)。 如果这是不正确的,可能需要进行微调,除了原始海报显示一个说明列表…十几个数据条目以显示正确的相关性。

所以,现在,我的查询..我正在做一个STRAIGHT_JOIN所以它按我列出的顺序加入。

别名“MainBasket”的第一个查询专门用于查询有问题的篮子中有多少项目,因此不需要为每个可能的篮子匹配重新加入/查询。 没有“ON”子句,因为这将是单个记录,因此没有笛卡尔影响,因为我希望此COUNT(*)值应用于最终结果中的每条记录。

NEXT查询是找到一个DISTINCT OTHER篮子,在与所讨论的父母相同的一周内至少有一个“Location_ID”(项目)…这可能导致其他篮子具有1个,相同或更多的条目而不是篮子。 但是如果有100个篮子,但是只有18个篮子至少有1个与原始篮子中的1个项目相匹配的条目,那么你只需要大大减少最终比较的篮子数量(SameWeekSimilar别名结果)。

最后是再次加入可购买的桌子,但基于SameWeekSimilar的加入,但仅限于每个“其他”篮子有一个紧密匹配…没有具体项目,只是在篮子里。 用于获取SameWeekSimilar的查询已经在相同的一周内进行了预先限定,并且至少有一个匹配的项目来自原始篮子,但具体排除了原始篮子,因此它不能与自身进行比较。

通过基于SameWeekSimilar.NextBasket在外层做一个组,我们可以得到该篮子的实际项目数。 由于简单的笛卡尔连接到MainBasket,我们只需获取原始计数。

最后,HAVING子句。 由于这是在“COUNT(*)”之后应用的,因此我们知道“其他”篮子中有多少项目,以及“主要”篮子中有多少项目。 因此,HAVING子句仅包括计数相同的子句。

如果你想测试以确保我所描述的内容,请对你的表运行,但不要包含HAVING子句。 你会看到哪些都是可能的…然后重新添加HAVING子句,看看哪些匹配相同的数…

 select STRAIGHT_JOIN SameWeekSimilar.NextBasket, count(*) NextBasketCount, MainBasket.OrigCount from ( select count(*) OrigCount from Buyable B1 where B1.Parent_ID = 7 ) MainBasket JOIN ( select DISTINCT B2.Parent_ID as NextBasket from Buyable B1 JOIN Buyable B2 ON B1.Parent_ID != B2.Parent_ID AND B1.Shop_Week_ID = B2.Shop_Week_ID AND B1.Location_ID = B2.Location_ID where B1.Parent_ID = 7 ) SameWeekSimilar Join Buyable B1 on SameWeekSimilar.NextBasket = B1.Parent_ID group by SameWeekSimilar.NextBasket having MainBasket.OrigCount = NextBasketCount 

以下SQL似乎可以解决这个问题

 big_query = " SELECT EXISTS ( SELECT 1 FROM buyables b1 JOIN buyables b2 ON b1.shop_week_id = b2.shop_week_id AND b1.location_id = b2.location_id WHERE b1.parent_id != %1$d AND b2.parent_id = %1$d AND b1.type = 'Item' AND b2.type = 'Item' GROUP BY b1.parent_id HAVING COUNT(*) = ( SELECT COUNT(*) FROM buyables WHERE parent_id = %1$d AND type = 'Item' ) ) " 

使用ActiveRecord,您可以使用select_value获得此结果:

 class Basket < Buyable def has_duplicate !!connection.select_value( big_query % id ) end end 

然而,我对表现不太确定

如果你想尽可能提高效率,你应该考虑创建一个将篮子内容编码为单个字符串或blob的哈希,添加一个包含哈希的新列(每次篮子内容更改时都需要更新)通过应用程序或使用触发器),并比较哈希值以确定可能的相等性。 然后,您可能需要按顺序执行进一步的比较(如上所述)

你应该怎么用哈希? 如果您知道篮子的大小有限,并且有问题的ID是有界整数,那么您应该能够散列到足以测试相等性的字符串。 例如,您可以对每个shop_week和location进行base64编码,使用不在base64中的分隔符(如“|”)连接,然后与其他篮子项连接。 在新的哈希键上构建索引,并且比较将很快。