我尝试创建一个以m
和n
为输入的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
答案 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 KEY
是UNIQUE KEY
是INDEX
;消除冗余索引。INT(4)
- (4)
没有任何意义; INT
总是32位(4字节),范围很大。请参阅SMALLINT UNSIGNED
(2个字节,0..64K范围)。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)
注意:
@@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)
注意: