mysql对多个字符串集合取交集以及判断一个字符串集在包含另一个字符串集的自定义方法

最近在做开发一块业务,碰到了一些棘手的问题,需要排序,但是系统中却没有成熟的模型设计,只能用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

这两个方法实现起来逻辑很简单,但是归因于本人的存储过程语法不是特别熟练,在写的过程遇到很多坑,让自己浪费了很多时间。所以我在这将这两个方法发布在论坛上,为了更多的小伙伴后面遇到这样的需求可以直接拿来用,而不会重复造轮子,把更多的时间用在更重要的事情上。

在下一篇博客中,我会介绍一下如何调试存储过程,感兴趣的小伙伴可以参考一下。

期望看到各位的评论和指导!

你可能感兴趣的:(数据库)