最近在做开发一块业务,碰到了一些棘手的问题,需要排序,但是系统中却没有成熟的模型设计,只能用SQL去拉各个表的数据进行组合,这样子看来也能进行的下去,但是遇到了最后的排序问题,让我着实有点头疼。
问题一:多个字符串集合取交集
简单的说就是把查出来的部分固定的字段数据中的交集数据拿出来,然后根据交集数据的个数进行排序举个例子就是这样:
一个字段的数据是这样:
123,234,456,789
b字段的数据是这样:
123,234,456,695
那想要得到的结果是两个交集:
123,234,456
这样用mysql的内置的方法肯定是不能直接得出这样的结果的,只能借助方法进行实现,所以我写了这样的方法:
DELIMITER $$
USE `sogaa_online`$$
DROP FUNCTION IF EXISTS `find_interstr_in_set`$$
CREATE DEFINER=`root`@`%` FUNCTION `find_interstr_in_set`(str VARCHAR(5000)) RETURNS VARCHAR(500) CHARSET utf8
BEGIN
DECLARE first_one_str VARCHAR(100);
DECLARE result BOOLEAN DEFAULT TRUE;
DECLARE Done INT DEFAULT 0;
DECLARE first_str_one_index INT(3);
DECLARE inner_done INT DEFAULT 0;
DECLARE str_first VARCHAR(500);
DECLARE str_first_temp VARCHAR(500);
DECLARE str_set_one VARCHAR(500);
DECLARE index_one INT(3);
DECLARE index_one_first INT(3);
DECLARE str_set CURSOR FOR SELECT SUBSTRING_INDEX(SUBSTRING_INDEX(str,';',m.help_topic_id+1),';',-1) FROM mysql.`help_topic` m WHERE m.help_topic_id < (LENGTH(str) - LENGTH(REPLACE(str,';',''))+1);
DECLARE first_str_set CURSOR FOR
SELECT SUBSTRING_INDEX(SUBSTRING_INDEX(temp.str_first,',',m.help_topic_id+1),',',-1) FROM mysql.`help_topic` m
LEFT JOIN (
SELECT SUBSTRING_INDEX(SUBSTRING_INDEX(str,';',m.help_topic_id+1),';',-1)AS str_first FROM mysql.`help_topic` m WHERE m.help_topic_id = 0
) temp ON 1=1
WHERE m.help_topic_id < (LENGTH(temp.str_first) - LENGTH(REPLACE(temp.str_first,',',''))+1);
DECLARE CONTINUE HANDLER FOR NOT FOUND SET Done =1;
SELECT SUBSTRING_INDEX(SUBSTRING_INDEX(str,';',m.help_topic_id+1),';',-1)INTO str_first FROM mysql.`help_topic` m WHERE m.help_topic_id = 0;
SELECT SUBSTRING_INDEX(SUBSTRING_INDEX(str,';',m.help_topic_id+1),';',-1)INTO str_first_temp FROM mysql.`help_topic` m WHERE m.help_topic_id = 0;
IF str IS NULL THEN RETURN NULL;END IF;
IF INSTR(str,';') <= 0 THEN RETURN str;END IF;
/*循环第一个子数据元的集合*/
OPEN first_str_set;
flag_loop:LOOP -- 进入外层循环
FETCH first_str_set INTO first_one_str;-- 取到循环体元素
IF Done =1 THEN LEAVE flag_loop ; END IF ;
IF INSTR(str_first_temp,first_one_str) <= 0 THEN LEAVE flag_loop;END IF;
-- 在内循环中循环大的数据元集合
OPEN str_set;
inner_loop:LOOP -- 内循环开始
FETCH str_set INTO str_set_one;
IF Done = 1 THEN LEAVE inner_loop;END IF; -- 该语句一定放在fetch后面
IF str_set_one = str_first THEN ITERATE inner_loop;END IF;-- 跳过跟第一个相同的大字符串
SET index_one = (SELECT INSTR(str_set_one,first_one_str) FROM DUAL); -- 取出str_first中子数据源在str_set_one的索引
SET index_one_first = (SELECT INSTR(str_first_temp,first_one_str) FROM DUAL); -- 取出first_one_str中子数据源在str_first_temp的索引用来判断切割位置
IF index_one > 0 THEN ITERATE inner_loop;END IF;
IF index_one <= 0 THEN SET str_first_temp = (SELECT REPLACE(str_first_temp,
CASE WHEN index_one_first = 1 THEN CONCAT(first_one_str,',')
ELSE CONCAT(',',first_one_str) END
,'') FROM DUAL);
END IF;-- 该字符串源在str_set_one中没有,就从str_first_temp剔除
END LOOP inner_loop;-- 内循环结束
SET Done = 0;
IF INSTR(str,';') <= 0 THEN RETURN str;END IF;
CLOSE str_set;
END LOOP flag_loop; -- 循环结束
CLOSE first_str_set;
RETURN str_first_temp;
END$$
DELIMITER ;
是这样使用的:
SELECT find_interstr_in_set('123,124,345;123,124;222,123;365,123') FROM DUAL;
>> 123
这里面把多个字符串集用 ';' 号进行连接,得出交集。
问题二:判断一个字符串集在包含另一个字符串集
这个意思就是说
一个集合是这样:
123,124,345
b集合是这样:
123,124,345,456
那么判断集合一个是否包含于b
方法很简单是这样写的:
DELIMITER $$
USE `sogaa_online`$$
DROP FUNCTION IF EXISTS `find_str_in_set`$$
CREATE DEFINER=`root`@`%` FUNCTION `find_str_in_set`(str1 VARCHAR(500),str2 VARCHAR(5000)) RETURNS TINYINT(1)
BEGIN
DECLARE one_str VARCHAR(100);
DECLARE result BOOLEAN DEFAULT TRUE;
DECLARE Done INT DEFAULT 0;
DECLARE first_strs CURSOR FOR SELECT SUBSTRING_INDEX(SUBSTRING_INDEX(str1,',',m.help_topic_id+1),',',-1) AS one_str
FROM mysql.`help_topic` m WHERE m.help_topic_id < (LENGTH(str1) - LENGTH(REPLACE(str1,',',''))+1);
DECLARE CONTINUE HANDLER FOR NOT FOUND SET Done =1;
IF(str1 = '' OR str1 IS NULL ) THEN RETURN TRUE; END IF;
IF(str2 IS NULL ) THEN RETURN FALSE; END IF ;
IF(str1 != '' AND str1 IS NOT NULL AND str2 != '' AND str2 IS NOT NULL)THEN
OPEN first_strs;
flag_loop:LOOP
FETCH first_strs INTO one_str;
IF Done =1 THEN LEAVE flag_loop ; END IF ;
IF NOT FIND_IN_SET(one_str,str2) THEN SET result = FALSE;END IF;
END LOOP flag_loop; /*循环结束*/
CLOSE first_strs;
END IF;
RETURN result;
END$$
DELIMITER ;
使用是这样的:
SELECT find_str_in_set('123,124,345','123,124,345,365') FROM DUAL;
>> 1
这两个方法实现起来逻辑很简单,但是归因于本人的存储过程语法不是特别熟练,在写的过程遇到很多坑,让自己浪费了很多时间。所以我在这将这两个方法发布在论坛上,为了更多的小伙伴后面遇到这样的需求可以直接拿来用,而不会重复造轮子,把更多的时间用在更重要的事情上。
在下一篇博客中,我会介绍一下如何调试存储过程,感兴趣的小伙伴可以参考一下。
期望看到各位的评论和指导!