1

我有个问题。我有一张表,里面有大约 80-1 亿条记录。在该表中,我有一个字段,该字段存储了 3 到 16 个不同的“组合”(varchar)。组合是一个 4 位数字、一个冒号和一个 char(AE),. 例如: '0001:A/0002:A/0005:C/9999:E'。在这种情况下,有 4 种不同的组合(最多可以有 16 种)。该字段在表的每一行中,绝不为空。

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

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

如您所见,这些行中的每一行都与其他行相似(在某种程度上)。这里需要做的是当您'0001:A/0002:A/0003:C'通过程序(或 SQL 中的参数)发送时,它会检查每一行并查看它们是否具有相同的“组”。现在这里的问题是它必须双向进行,并且必须“快速”完成,并且 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 ) 有效地转换为“varchar”,或者通过一些哈希码 + 知道它们中的哪一个是熟悉的。

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

SELECT * FROM TABLE WHERE ROW = "a346adsad"

参数将通过程序发送到哪里。这个脚本需要非常快地执行,不到 1 分钟,因为表中可能有新的插入,您需要检查。

这样做的重点是查看 SQL 中是否已经存在任何类似的组合,并阻止任何对于插入“相似”的新组合。

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

预先感谢您提供任何可能的帮助,或者如果这是可能的!

4

4 回答 4

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 可以更有效地处理数值,因此这应该会带来更好的性能 - 当然是使用新结构。

于 2013-01-10T13:15:53.257 回答
2

所以基本上你想在一分钟内对80-1 亿行执行复杂的字符串操作!哈哈哈,不错!

哦,等等,你是认真的。

您不能希望即时进行这些搜索。阅读 Joel Spolsky 关于回归基础的文章以了解原因。

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

然后,只需对搜索字符串进行标记并选择将搜索标记连接到新表即可。不确定它的性能如何:这取决于您拥有多少不同的令牌。

于 2013-01-10T13:41:45.587 回答
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 是键的适当子集?

于 2013-01-10T13:42:59.027 回答
0

我想我可以推荐建立特殊的“索引”。它会很大,但您将获得超快速的结果。

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

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

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

由于符号是按单词排序的,因此我们只有 2^N-1 种组合(4 个符号对应 15 个)。对于 16 个符号的单词,有 2^16 - 1 = 65535 种组合。

因此,我们制作了一个额外的索引组织表,如下所示

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

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

于 2013-01-11T09:34:58.977 回答