SQL - 比较1亿个表格上的文本(组合)

时间:2013-01-10 13:10:27

标签: mysql sql oracle

我有问题。 我有一张桌子,里面有大约8000万到1亿的记录。在该表中,我有一个字段,存储了3到16个不同的“组合”(varchar)。组合是一个4位数字,冒号和字符(A-E),.例如: '0001:A/0002:A/0005:C/9999:E'。在这种情况下,有4种不同的组合(它们可以达到16种)。该字段位于表的每一行中,从不为null。

现在问题是:我必须通过表格,找到每一行,看看它们是否相似。 示例行:

0001:A/0002:A/0003:C/0005:A/0684:A/0699:A/0701:A/0707:A/0709:A/0710:D/0711:C/0712:A/0713:A
0001:A/0002:A/0003:C
0001:A/0002:A/0003:A/0006:C
0701:A/0709:A/0711:C/0712:A/0713:A

如您所见,这些行中的每一行都与其他行类似(以某种方式)。这里需要做的是当您通过程序(或SQL中的参数)发送'0001:A/0002:A/0003:C'时,它会检查每一行并查看它们是否具有相同的“组”。现在这里的问题是它必须双向进行并且必须“快速”完成,并且SQL需要以某种方式比较它们。

因此,当您发送'0001:A/0002:A/0003:C/0005:A/0684:A/0699:A/0701:A/0707:A/0709:A/0710:D/0711:C/0712:A/0713:A'时,必须找到所有包含3-16个相同组合的字段并返回行。这个3-16可以通过参数指定,但问题是您需要找到所有可能的组合,因为您可以发送'0002:A:/0711:C/0713:A',如您所见,您可以发送0002:A作为第一个参数。

但是您无法编制索引,因为组合可以在字符串中的任何位置,并且您可以发送不“附加”的不同组合(中间可能有不同的组合)。

因此,发送'0001:A/0002:A/0003:C/0005:A/0684:A/0699:A/0701:A/0707:A/0709:A/0710:D/0711:C/0712:A/0713:A'必须返回具有相同3-16字段的所有字段 并且它必须双向,如果你发送“0001:A / 0002:A / 0003:C”它必须找到上面的行+相似的行(所有包含所有参数)。

我尝试过的一些事情/选项:

  • 对所有发送组合执行LIKE不实用+太慢
  • 提供字段全索引搜索不是一种选择(不知道原因为何)
  • 可能有用的少数事情之一就是为字段制作一些“哈希”类型的编码,通过程序计算它,并搜索所有相同的“哈希”(不知道你会怎么做,鉴于哈希会为类似的文本生成不同的组合,也许会为那个
  • 编写一些哈希
  • 创建一个新字段,计算/写入(可以在插入时完成)所有可能的组合,并通过SQL /程序检查它们是否具有相同的组合%,但我不知道如何存储10080种组合(在16)的情况有效地,或通过一些哈希代码+知道然后他们熟悉哪些。

还有另一个问题,这个表几乎全天候使用,做组合检查它们是否在SQL中是相同的太慢因为表太大,可以通过程序或其他东西来完成,但是我没有任何关于你如何将它存储在一个你会知道它们是相同的新行中的线索。您可能会计算组合,通过某些哈希代码或每个行插入的内容存储它们,通过程序计算“哈希”,并检查表格如下:

SELECT * FROM TABLE WHERE ROW = "a346adsad"

参数将通过程序发送。 这个脚本需要在1分钟内快速执行,因为表中可能会有新的插入,您需要检查。

这一点的重点在于查看SQL中是否存在任何类似的组合,并阻止任何与插入类似的新组合。

我一直在处理这个问题3天没有任何可能的解决方案,最接近的是不同类型的插入/散列,但我不知道它是如何工作的。

提前感谢您提供任何可能的帮助,或者甚至可以提供帮助!

4 个答案:

答案 0 :(得分:2)

it checks every row and see if they have the same "group".

恕我直言,如果是数据结构的基本元素,那么您的数据库结构就存在缺陷:它应该让每个组都在自己的单元格中进行规范化。您描述的结构清楚地表明您在字段中存储了复合值。

我把桌子撕成3:

  • 一个用于组序列的“标题”信息
  • 一组自己
  • 两者之间的连接表

这些方面的东西:

CREATE TABLE GRP_SEQUENCE_HEADER (
    ID BIGINT PRIMARY KEY,
    DESCRIPTION TEXT
  );


CREATE TABLE GRP (
    ID BIGINT PRIMARY KEY,
    GROUP_TXT CHAR(6)
  );

CREATE TABLE GRP_GRP_SEQUENCE_HEADER (
    GROUP_ID BIGINT, 
    GROUP_SEQUENCE_HEADER_ID BIGINT,
    GROUP_SEQUENCE_HEADER_ORDER INT, /* For storing the order in the sequence */
    PRIMARY KEY(GROUP_ID, GROUP_SEQUENCE_HEADER_ID)
  );

(当然,添加外键,最重要的是必要的索引)

然后,您只需将输入拆分为组,并在正确索引的表上执行简单查询。

此外,您可能也会因为不存储重复项而节省磁盘空间......

查找“类似”序列ID的示例查询:

SELECT ggsh.GROUP_SEQUENCE_HEADER_ID,COUNT(1)
FROM GRP_GRP_SEQUENCE_HEADER ggsh  
JOIN GRP g ON ggsh.GROUP_ID=g.GROUP_ID
WHERE g.GROUP_TXT IN (<groups to check for from the sequence>)
GROUP BY gsh.ID
HAVING COUNT(1) BETWEEN 3 AND 16 --lower and upper boundaries

这将返回当前序列类似的所有标头ID。

修改 再多思考一下,你甚至可以将小组分成两部分,但正如我似乎明白的那样,你总是有完整的小组来处理,所以似乎没有必要。

EDIT2 也许如果你想加快这个过程,我建议将使用双射的序列翻译成数字数据。例如,将前4个数字评估为整数,将其向左移4位(乘以16,但更快),并在最后一位添加字符的十六进制值。

示例:

0001/A --> 1 as integer, A is 10, so 1*16+10 =26
...
0002/B --> 2 as integer, B is 11, so 2*16+11 =43
...
0343/D --> 343 as integer, D is 13, so 343*16+13 =5501
...
9999/E --> 9999 as integer, E is 14, so 9999*16+14 =159998 (max value, if I understood correctly)

DB会更有效地处理数值,因此这会带来更好的性能 - 当然还有新的结构。

答案 1 :(得分:2)

所以基本上你想在不到一分钟的时间内在 80-100万行上执行复杂的字符串操作!哈,哈,好的!

等等,你是认真的。

你不能希望在飞行中进行这些搜索。阅读Joel Spolsky关于让Back to Basics了解原因的文章。

你需要做的是将那些8000万到1亿个字符串放入他们自己的表中,分解成那些离散的标记,即'0001:A/0002:A/0003:C'被分成三个记录(可能是两列 - 你是一个对于某些数字和字母组成部分之间的关​​系有点模糊。这些记录可以编入索引。

然后,只需将搜索字符串标记化并选择将搜索标记加入新表即可。不确定它的表现如何:这取决于你拥有多少个不同的令牌。

答案 2 :(得分:0)

正如人们评论过的那样,您可以从规范化数据中受益匪浅,但是你不能欺骗并使用密钥创建临时表并在“/”上爆炸你的专栏,所以你来自

KEY | "0001:A/0002:A/0003:A/0006:C"
KEY1| "0001:A/0002:A/0003:A"

KEY | 0001:A
KEY | 0002:A
KEY | 0003:A
KEY | 0006:C
KEY1| 0001:A
KEY1| 0002:A
KEY1| 0003:A

这将允许您开发类似以下(未测试)的查询:

SELECT
    t1.key
    , t2.key
    , COUNT(t1.*)
FROM
    temp_table t1
    , temp_table t2
    , ( SELECT t3.key, COUNT(*) AS cnt FROM temp_table t3 GROUP BY t3.key) t4
WHERE
    t1.combination IN ( 
        SELECT 
            t5.combination 
        FROM 
            temp_table t5 
        WHERE 
            t5.key = t2.key)
    AND t1.key <> t2.key
HAVING
    COUNT(t1.*) = t4.cnt

那么返回两个键,其中key1是键的正确子集?

答案 3 :(得分:0)

我想我可以建议建立特殊的“索引”。 它会很大,但你会获得超级结果。

让我们将此任务视为搜索一组符号。 有设计条件。 符号由模式“NNNN:X”制成,其中NNNN是数字[0001-9999],X是字母[A-E]。 所以我们在字母表中有5 * 9999 = 49995个符号。 这个字母表的最大单词长度是16.

我们可以为其符号组合的每个单词集构建。 例如,单词“abcd”将具有下一个组合:

abcd
abc
ab
a
abd
acd
ac
ad
bcd
bc
b
bd
cd
с
d

当符号按单词排序时,我们只有2 ^ N-1个组合(15个符号为4个符号)。 对于16个符号的字,有2 ^ 16 - 1 = 65535个组合。

因此我们制作了一个像这样的附加索引组织表

create table spec_ndx(combination varchar2(100), original_value varchar2(100))

在开销价格方面性能非常出色 - 在最坏的情况下,原始表格中的每条记录都会有65535“索引”记录。
因此,对于1亿张桌子,我们将获得6万亿桌子。 但是如果我们有短期值,“特殊指数”的大小会急剧减少。