【问题标题】:Limited joining of a huge table to itself in Oracle在 Oracle 中有限地加入一个巨大的表
【发布时间】:2013-06-06 20:04:35
【问题描述】:

问题:我们想从我们的数据库中删除拼写错误的地址。但是我们有太多的事情要做。因此,我有一个函数 FN,如果两个地址看起来非常相似(表明可能拼写错误),它会返回 true。一个简单的检查是做类似...

select *
from
    address adr1
    join address adr2
    on FN(adr1, adr2)

但是,这基本上是进行交叉连接并比较行。由于我们的表有多大(> 100 万行),这是不可能做到的。但是,我可以将其限制为仅查看彼此附近的地址。例如,同一城市内的地址。所以,我试着做这样的地址计数......

select count(1)
from
    address adr1
    join address adr2
    on adr1.zip = adr2.zip
    and adr1.city = adr2.city
    --Don't want to compare to self
    and adr1.ID <> adr2.ID

问题是这需要很长时间才能运行(我已经等了,但它还没有完成)。我怀疑 oracle 有更好的方法来处理大量行的这些类型的事情,但我只是不知道。

那么,如果有办法限制要连接的内容(例如只查看相同的邮政编码),那么一个人应该如何将一个超大的表连接到自己呢?

附:数万亿条记录算大数据还是应该去掉标签?

Edit1:Zip 和 City 已编入索引。

Edit2:Zip 和 City 都有大量 200,000+ 的空值。这可能会影响索引在连接中的使用方式。

解释计划:

SELECT STATEMENT  ALL_ROWSCost: 35,301  Bytes: 42  Cardinality: 1           
    4 SORT AGGREGATE  Bytes: 42  Cardinality: 1         
        3 HASH JOIN  Cost: 35,301  Bytes: 2,195,769,492  Cardinality: 52,280,226    
            1 TABLE ACCESS FULL TABLE SCHEMA.ADDRESS Cost: 15,677  Bytes: 21,388,962  Cardinality: 1,018,522  
            2 TABLE ACCESS FULL TABLE SCHEMA.ADDRESS Cost: 15,677  Bytes: 21,388,962  Cardinality: 1,018,522  

Edit3:我尝试过以不同的方式计算行数。

select
    sum(cnt * (cnt - 1))
from
(
select
    count(1) as CNT
from schema.address adr1
group by adr1.zip, adr1.city
)

这在不到 10 秒的时间内返回了约 450 亿个不同的配对。我不确定我的函数是否可以每秒处理超过 100k 行,而这正是在 12 小时内运行所需要的。

【问题讨论】:

  • 你的FN有多复杂?
  • 您的第二个查询看起来没问题。 Oracle 有办法处理连接大量数据,例如使用哈希连接。 100 万行甚至不是真正的大量数据。如果您发布说明计划,有人可能会帮助您调整查询。
  • @Gerrat 我会说非常讨厌,因为它正在执行各种字符串操作并涉及上下文切换。话虽这么说,这对于尝试优化连接是否重要?
  • 我问的唯一原因是,如果它不是太复杂,那么可能有比首先使用函数和连接更好的方法。不过,这似乎是不可能的。
  • 我只是要提一下(在您最近的编辑之前),即使您可以解决加入问题,我敢打赌您的功能将花费太长时间。您提到 100000/秒...如果它正在执行上下文切换,它可能只能进行 1000 次比较/秒。我认为您可能想尝试另一种方法 - 也许导出您的数据,将其按 soundex 之类的东西分组,然后从那里开始。

标签: sql plsql bigdata


【解决方案1】:

1) 在字段ZIPCITY 上建立索引

2) 要获得重复项(这是您在第二种情况下所做的),请使用 GROUP BY:

SELECT ZIP,CITY, count(*) FROM ADDRESS HAVING COUNT(*)>1 GROUP BY ZIP,CITY

【讨论】:

    【解决方案2】:

    我有一些好消息,也有一些坏消息。

    好消息是您现有的查询可能有接近 50 亿行,而不是 450 亿行。

    坏消息是,这是因为它不会尝试匹配 200,000 条具有 null zip 或 null city 值的记录中的任何一条 - Oracle(以及我知道的所有其他 RDBMS)不会将 NULL 值连接到其他空值;有关示例,请参见 here。您可以使用 coalesce 作为连接条件的一部分来解决此问题,但我建议单独处理空城市/zip 记录。

    假设您的函数对称地处理地址(以便FN(addr1,addr2) 返回与FN(addr2,addr1) 相同的结果),您可以通过将现有查询中的adr1.ID &lt;&gt; adr2.ID 更改为adr1.ID &lt; adr2.ID 进一步将组合数量减半。如果您还没有合适的索引,我建议您在 zip、city 和 id 上添加一个(按此顺序)。

    【讨论】:

      【解决方案3】:

      另一种方法是使用邮政局 idcode 对每个地址进行编码(如果相关地址/国家/地区存在)。这意味着您无需将每个地址与其自身进行比较,而是首先将所有精力放在解析和解码地址上。我们使用这种方法,并将 id 存储在每一行中,这意味着我们以后可以非常准确快速地加入。

      如果您不能使用邮政 ID(我的意思是邮局分配的每个送货地址的唯一 ID),请考虑对每个地址进行地理编码,然后通过地理附近的地址加入。如果地址不是纯粹的邮政地址,地理编码也可能适用。

      我也对 FN() 对地址的作用非常感兴趣,您是否看到与您的问题无关的 http://www.mjt.me.uk/posts/falsehoods-programmers-believe-about-addresses/,但如果您是地址处理的新手,请好好阅读。

      【讨论】:

        猜你喜欢
        • 2018-06-01
        • 1970-01-01
        • 2020-12-17
        • 2011-07-02
        • 2016-06-27
        • 1970-01-01
        • 1970-01-01
        • 2013-05-14
        • 2015-08-07
        相关资源
        最近更新 更多