MySQL快速检查是否存在哈希

时间:2017-03-14 14:45:22

标签: mysql sql performance combinations permutation

我尝试创建一个以mn为输入的MySQL函数,并根据查询结果生成m个ID的SELECT `Item`.`id` FROM `Item` LEFT JOIN `ItemKeyword` ON `Item`.`id` = `ItemKeyword`.`ItemID` WHERE (`Item`.`user_id` = '2') AND(`ItemKeyword`.`keywordID` = 7130) AND(`Item`.`type` = 1) ORDER BY RAND() LIMIT 100 个唯一组合

该功能每次调用都会返回一个组合,并且该组合必须与之前的所有组合不同。

在生成期间,它必须检查另一个表:如果组合已存在,则继续循环直到每个组合保持唯一。将组合返回为短划线分隔的ID,或者如果没有空间可以使用唯一组合返回false。

所以我得到100个这样的随机项目:

itemID

过去的组合被-存储为- s串联的md5。

所以我需要通过Combination连接此查询的结果并创建它的md5。然后将另一个查询发送到名为hash的第二个表中,并检查n列是否存在。并继续此循环,直到我得到{{1}}结果。

我无法弄清楚如何正确快速地实现这一目标。有什么建议吗?

更新

整个SQL转储在此处:https://gist.github.com/anonymous/e5eb3bf1a10f9d762cc20a8146acf866

2 个答案:

答案 0 :(得分:3)

如果您通过md5测试唯一性,则需要在获取md5之前对列表进行排序。这可以通过SELECT MD5('1-2'), MD5('2-1');

来证明

摆脱LEFT,似乎毫无用处。之后,优化工具可以从ItemKeyword而不是Item开始选择。 (在不知道数据分布的情况下,无法说明这是否有帮助。)

(如果您为每个表提供SHOW CREATE TABLE会很有帮助。如果他们缺席,我会假设您正在使用InnoDB,并且PRIMARY KEY(id)PRIMARY KEY(keywordID)。)

'复合'需要的索引:

Item: INDEX(user_id, type, id)
ItemKeyword: INDEX(ItemID, keywordID)

ItemKeyword闻起来像很多:很多映射表。 大多数这样的表可以从抛出id开始进行改进。请参阅7 tips on many:many

我在你的二次处理中有点迷失。

My tips on RAND可能有用,也可能没用。

架构批判

  • PRIMARY KEYUNIQUE KEYINDEX;消除冗余索引。
  • INT(4) - (4)没有任何意义; INT总是32位(4字节),范围很大。请参阅SMALLINT UNSIGNED(2个字节,0..64K范围)。
  • MD5应声明CHAR(32) CHARACTER SET ascii,而不是255,而不是utf8。 (latin1没问题。)
  • Combination(id + hash)似乎没用。相反,只需将表格KEY md5 (md5) USING BTREE,中的UNIQUE(md5)更改为Item
  • 您已使用SET NAMES utf8mb4;开始使用utf8mb4,但表格(及其列)仍为utf8。表情符号和中文需要utf8mb4;大多数其他文字没有。

解决这些问题后,可以解决原始问题 (以及进行一些清理)。如果现在,请添加一些进一步的说明。

<强>缩小的

1。获取m个唯一ID的排序列表。 (我需要&#34;排序&#34;下一步,因为你正在寻找&#34;组合&#34;,似乎&#34;排列&#34;不需要。)

SELECT GROUP_CONCAT(id) AS list
    FROM (
        SELECT id FROM tbl
            ORDER BY RAND()
            LIMIT $m
         ) AS x;

2. 检查唯一性。通过MD5(list)(从上面)并查看“使用过的”表格来执行此操作。 MD5&#39;第注意:除非你在一小撮ID中要求很多组合,否则不太可能重复(但并非不可能)。

3. 发送list。但是,它是由逗号分隔的一串ID。拆分最好在应用程序代码中完成,而不是MySQL函数。

4. 你会对这个清单做什么?这可能很重要,因为在步骤3中折叠步骤4可能很方便。

结论:我只会在SQL中执行步骤1和步骤2的一部分;我会建立一个&#39;功能&#39;在应用程序代码中完成剩下的工作。

答案 1 :(得分:1)

<强>排列组合

DROP FUNCTION IF EXISTS unique_perm;

DELIMITER //
CREATE FUNCTION unique_perm()
    RETURNS VARCHAR(255) CHARACTER SET ascii
    NOT DETERMINISTIC
    SQL SECURITY INVOKER
BEGIN
SET @n := 0;
iterat: LOOP
    SELECT SUBSTRING_INDEX(
             GROUP_CONCAT(province ORDER BY RAND() SEPARATOR '-'),
             '-', 3) INTO @list   -- Assuming you want M=3 items
        FROM world.provinces;
    SET @md5 := MD5(@list);
    INSERT IGNORE INTO md5s (md5) VALUES (@md5);  -- To prevent dups
    IF ROW_COUNT() > 0 THEN  -- Check for dup
        RETURN @list;       -- Got a unique permutation
    END IF;
    SET @n := @n + 1;
    IF @n > 20 THEN
        RETURN NULL;    -- Probably ran out of combinations
    END IF;
END LOOP iterat;
END;
//
DELIMITER ;

输出:

mysql> SELECT unique_perm(),  unique_perm(),  unique_perm()\G
*************************** 1. row ***************************
unique_perm(): New Brunswick-Nova Scotia-Quebec
unique_perm(): Alberta-Northwest Territories-New Brunswick
unique_perm(): Manitoba-Quebec-Prince Edward Island
1 row in set (0.01 sec)

注意:

  • 我硬编码M = 3;根据需要调整。 (它可以作为一个arg传递。)
  • 根据需要更改列和表名称。
  • 在@n上进行测试时,如果你的组合用完了,你就可以进入循环。 (但是,如果N非常大,那就是“不可能”,所以你可以删除测试。)
  • 如果M足够大,则需要增加@@group_concat_max_len。另外,RETURNS
  • CREATE TABLE md5s ( md5 CHAR(32) CHARACTER SET ascii PRIMARY KEY ) ENGINE=InnoDB是必需的。而且,在批次调用此函数之间,您需要TRUNCATE md5s
  • 这是一个有效的例子。
  • 缺陷:它提供了唯一的排列,而不是唯一的组合。如果这还不够,请继续阅读......

<强>组合

DROP FUNCTION IF EXISTS unique_comb;

DELIMITER //
CREATE FUNCTION unique_comb()
    RETURNS VARCHAR(255) CHARACTER SET ascii
    NOT DETERMINISTIC
    SQL SECURITY INVOKER
BEGIN
SET @n := 0;
iterat: LOOP
    SELECT GROUP_CONCAT(province ORDER BY province SEPARATOR '-') INTO @list
        FROM ( SELECT province FROM world.provinces
                  ORDER BY RAND() LIMIT 2 ) AS x;   -- Assuming you want M=2 items
    SET @md5 := MD5(@list);
    INSERT IGNORE INTO md5s (md5) VALUES (@md5);  -- To prevent dups
    IF ROW_COUNT() > 0 THEN  -- Check for dup
        RETURN @list;       -- Got a unique permutation
    END IF;
    SET @n := @n + 1;
    IF @n > 20 THEN
        RETURN NULL;    -- Probably ran out of combinations
    END IF;
END LOOP iterat;
END;
//
DELIMITER ;

输出:

mysql> SELECT unique_comb(),  unique_comb(),  unique_comb()\G
*************************** 1. row ***************************
unique_comb(): Quebec-Yukon
unique_comb(): Ontario-Yukon
unique_comb(): New Brunswick-Nova Scotia
1 row in set (0.01 sec)

注意:

  • 子查询会增加一些费用。
  • 请注意,现在(必然)订购每个输出字符串中的项目。