mysql - SQL - 比较 1 亿张表上的文本(组合)

标签 mysql sql oracle

我有个问题。
我有一张表,里面有大约 80-1 亿条记录。在该表中,我有一个字段,该字段存储了 3 到 16 个不同的“组合”(varchar)。组合是一个 4 位数字、一个冒号和一个 char(A-E), 。例如:'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 天了,没有任何可能的解决方案,最接近的是不同类型的插入/哈希,但我不知道它是如何工作的。

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

    最佳答案

    it checks every row and see if they have the same "group".
    恕我直言,如果 是你的数据结构的一个基本元素,你的数据库结构是有缺陷的:它应该让每个组在它自己的单元格中进行规范化。您描述的结构清楚地表明您在字段中存储了一个复合值。

    我会把 table 撕成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
    

    这将返回与当前序列相似的所有 header ID。

    编辑
    再考虑一下,您甚至可以将小组分成两部分,但据我所知,您总是要处理完整的小组,因此似乎没有必要。

    编辑2 也许如果您想进一步加快该过程,我建议您将使用双射的序列转换为数字数据。例如,将前 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)
    

    数字值由数据库更有效地处理,因此这应该会带来更好的性能 - 当然是使用新结构。

    关于mysql - SQL - 比较 1 亿张表上的文本(组合),我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/14258710/

    相关文章:

    mysql - SQL 加入预先加入的地方

    sql - 是否可以在 where 条件下使用列元组?

    oracle - 在oracle 11g中将数据从临时表加载到主表

    mysql - TYPO3:删除没有 pid 的页面记录

    MySQL SELECT 将多个值显示为列

    MySQL 数据库到 Excel(1 个表到 1 个工作表)

    php - JOIN 2 具有不同列的表

    c# - 在 SQL 数据库中存储加密的信用卡号

    sql - 从数据库中选择几个时期的平均值

    java - 如何使 JOOQ codegen 与基于 Oracle 版本的重定义配合使用?