从MySQL中的列表字符串中获取单个项目

时间:2013-07-05 09:30:28

标签: mysql list stored-procedures

给定以下表示可能列表的字符串,如何获取指定索引n

的项目
  • 1,2,3,4,5
  • word1 word2 word3
  • pipe|delimited|list

此功能的可能原因是

  • 从GROUP_CONCAT输出中提取特定元素
  • 从SET列输出中提取特定元素(当转换为字符串时)
  • 从包含逗号分隔列表的规范化程度较低的表中提取特定元素
  • 在迭代过程中使用循环遍历列表并对
  • 中的每个元素执行操作

3 个答案:

答案 0 :(得分:1)

这没有原生功能。您可以使用两个SUBSTRING_INDEX函数。您需要检查该特定索引项是否存在:

SET @string:='1,2,3,4,5';
SET @delimiter:=',';
SET @n:=6;

SELECT
  CASE WHEN
    CHAR_LENGTH(@string)-CHAR_LENGTH(REPLACE(@string, @delimiter, ''))>=
    @n*CHAR_LENGTH(@delimiter)-1
  THEN
    SUBSTRING_INDEX(SUBSTRING_INDEX(@string, @delimiter, @n), @delimiter, -1)
  END;
  • SUBSTRING_INDEX(@string, @delimiter, @n)@string出现@n之前返回字符串@delimiter的子字符串。
  • SUBSTRING_INDEX( ... , @delimiter, -1)返回最终分隔符右侧的所有内容
  • 您需要检查分隔符@n是否存在。我们可以使用分隔符减去字符串的长度,并使用REPLACE(@string, @delimiter, '')删除分隔符的字符串 - 并查看它是否大于@n*CHAR_LENGTH(@delimiter)-1

答案 1 :(得分:1)

Pure SQL的做法: -

SELECT SUBSTRING_INDEX(SUBSTRING_INDEX(somefield, '|', 3), '|', -1)
FROM sometable a

如果你想要返回NULL(或其他一些固定值),如果没有第三个元素(例如): -

SELECT IF((LENGTH(somefield) - LENGTH(REPLACE(somefield, '|', '')) + 1) >= 10, SUBSTRING_INDEX(SUBSTRING_INDEX(somefield, '|', 10), '|', -1), NULL)
FROM sometable a

答案 2 :(得分:0)

更新完全忘记了SUBSTRING_INDEX -1的事情(由@fthiella和@Kickstart指出),所以更新了以下示例

通过创建可以在线使用的存储函数来解决。存储的函数能够接受输入字符串,任何单字符分隔符以及要提取的所需项目的索引。

存储的函数定义如下

CREATE DEFINER = `user`@`%` 
FUNCTION `getListElement`(
    inString VARCHAR(255) , 
    inDelimiter CHAR(1) , 
    inIndexToReturn TINYINT UNSIGNED
) RETURNS varchar(255) READS SQL DATA DETERMINISTIC SQL SECURITY INVOKER
BEGIN

    -- Takes in as argument a string, and then breaks out the desired string
    DECLARE resultString VARCHAR(255) DEFAULT inString;
    DECLARE numberOfListElements TINYINT UNSIGNED DEFAULT 0;
    DECLARE errorMessage VARCHAR(255) DEFAULT 'Requested index was < 1 which was invalid';

    -- First of all, additional processing only needed for element 2 upwards
    IF inIndexToReturn = 1 THEN

        RETURN SUBSTRING_INDEX( resultString , inDelimiter , inIndexToReturn);

    ELSEIF inIndexToReturn > 1 THEN

        -- Count the number of elements
        -- This will count the missing delimiters based off the replace.  A list of 4 will be missing 3 characters.
        SET numberOfListElements = ( CHAR_LENGTH( resultString ) + 1 ) - CHAR_LENGTH( REPLACE( resultString , inDelimiter , '' ) );

        IF numberOfListElements >= inIndexToReturn THEN

            -- Make sure to only return the last of the elements returend by the first SUBSTRING_INDEX
            RETURN SUBSTRING_INDEX( SUBSTRING_INDEX( inString , inDelimiter , inIndexToReturn ) , inDelimiter , -1 );

        END IF;

        SET errorMessage = CONCAT('List index ',inIndexToReturn,' was requested from a list with ',numberOfListElements,' elements');

    END IF;

    SIGNAL SQLSTATE '45000' SET MESSAGE_TEXT = errorMessage;

END

在上面的示例中,以下内容可用于提取特定的列表元素

  • SELECT getListElement( '1,2,3,4,5' , ',' , 2 )返回2
  • SELECT getListElement( REPLACE( 'word1 word2 word3' ,' ', ',' ) , ',' , 3 )返回word3(请参阅下面的说明,了解REPLACE上的原因)
  • SELECT getListElement( 'pipe|delimited|list' , '|' , 1 )返回pipe

也可以在迭代器中使用它来遍历列表中的元素。首先,你需要计算列表中的项目(参见How to count items in comma separated list MySQL),但是一旦你有了这个项目,只需要在存储过程的这个片段中迭代它们

-- Reinitialise variables
SET @list = '1,2,3,4,5';
SET @delimiter = ',';
SET @listLength = (CHAR_LENGTH( @list ) + 1 ) - CHAR_LENGTH( REPLACE( @list , @delimiter , '' ) );
SET @currentElement = 1;

listLoop: REPEAT

    -- Select the list element and do something with it
    SELECT getListElement( @list , @delimiter , @currentElement );

    -- Increment the current element
    SET @currentElement = @currentElement + 1;

UNTIL @currentElement > @listLength
END REPEAT listLoop;

重要以空格分隔的列表似乎会导致此过程出现问题,因此在将字符串解析为函数之前,我建议您使用简单的REPLACE将空格替换为另一个合适的单字符分隔符(即,|,具体取决于字符串中的内容)