计算字符串中唯一字符的数量

Posted

技术标签:

【中文标题】计算字符串中唯一字符的数量【英文标题】:Count number of unique characters in a string 【发布时间】:2015-04-30 12:02:58 【问题描述】:

我正在寻找一个 sql 语句来。

例如

3333333333 -> returns 1
1113333333 -> returns 2
1112222444 -> returns 3

我用 REGEX 和 mysql-string-functions 做了一些测试,但没有找到解决方案。

【问题讨论】:

你真的认为这是 MySQL 的工作吗? 也许这篇文章可以帮助您解决问题。 [1]***.com/questions/12344795/… 您需要编写代码块来执行此操作,给我一些时间我会执行此操作。 【参考方案1】:

这是为了好玩吧?

SQL 都是关于处理行集的,所以如果我们可以将“单词”转换为作为行的字符集,那么我们可以使用“组”函数来做有用的事情。

使用“关系数据库引擎”进行简单的字符操作感觉不对。不过,是否可以仅使用 SQL 来回答您的问题?是的...

现在,我总是有一个表,其中有一个整数列,其中包含大约 500 行,升序为 1 .. 500。它被称为“整数”。这是一个非常小的表,使用了很多,所以它被缓存在内存中。它旨在替换查询中的from 'select 1 ... union ... 文本。

它对于生成任何可以基于整数计算的连续行(表)很有用,方法是在cross join(也可以是任何inner join)中使用它。我用它来生成一年的日子,解析逗号分隔的字符串等。

现在,sql mid 函数可用于返回给定位置的字符。通过使用“整数”表,我可以“轻松”将“单词”转换为每个字符一行的字符表。然后使用“组”功能...

SET @word='Hello World';

SELECT charAtIdx, COUNT(charAtIdx)
FROM (SELECT charIdx.id,
    MID(@word, charIdx.id, 1) AS charAtIdx 
    FROM integerseries AS charIdx
    WHERE charIdx.id <= LENGTH(@word)
    ORDER BY charIdx.id ASC
    ) wordLetters
GROUP BY
   wordLetters.charAtIdx
ORDER BY charAtIdx ASC  

输出:

charAtIdx  count(charAtIdx)  
---------  ------------------
                            1
d                           1
e                           1
H                           1
l                           3
o                           2
r                           1
W                           1

注意:输出中的行数是字符串中不同字符的数量。因此,如果计算输出行的数量,那么“不同字母”的数量将是已知的。

此观察结果用于最终查询。

最终查询:

这里有趣的一点是将“整数”“交叉连接”限制(1 .. length(word))移动到实际的“连接”中,而不是在where 子句中进行。这为优化器提供了有关如何限制在执行join 时生成的数据的线索。

SELECT 
   wordLetterCounts.wordId,
   wordLetterCounts.word,   
   COUNT(wordLetterCounts.wordId) AS letterCount
FROM 
     (SELECT words.id AS wordId,
             words.word AS word,
             iseq.id AS charPos,
             MID(words.word, iseq.id, 1) AS charAtPos,
             COUNT(MID(words.word, iseq.id, 1)) AS charAtPosCount
     FROM
          words
          JOIN integerseries AS iseq
               ON iseq.id BETWEEN 1 AND words.wordlen 
      GROUP BY
            words.id,
            MID(words.word, iseq.id, 1)
      ) AS wordLetterCounts
GROUP BY
   wordLetterCounts.wordId  

输出:

wordId  word                  letterCount  
------  --------------------  -------------
     1  3333333333                        1
     2  1113333333                        2
     3  1112222444                        3
     4  Hello World                       8
     5  funny - not so much?             13

词表和数据:

CREATE TABLE `words` (
  `id` int(11) NOT NULL AUTO_INCREMENT,
  `word` varchar(128) COLLATE utf8mb4_unicode_ci NOT NULL,
  `wordlen` int(11) NOT NULL,
  PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=6 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;

/*Data for the table `words` */

insert  into `words`(`id`,`word`,`wordlen`) values (1,'3333333333',10);
insert  into `words`(`id`,`word`,`wordlen`) values (2,'1113333333',10);
insert  into `words`(`id`,`word`,`wordlen`) values (3,'1112222444',10);
insert  into `words`(`id`,`word`,`wordlen`) values (4,'Hello World',11);
insert  into `words`(`id`,`word`,`wordlen`) values (5,'funny - not so much?',20);

Integerseries 表:此示例的范围为 1 .. 30。

CREATE TABLE `integerseries` (
  `id` int(11) unsigned NOT NULL,
  PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=500 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci

【讨论】:

【参考方案2】:

没有直接或简单的方法可以做到这一点。您可能需要编写一个存储函数来完成这项工作,并查看数据中您可能期望的所有字符。这是一个仅针对 digits 的示例,可以将其扩展为存储函数中的所有字符

mysql> select * from test ;
+------------+
| val        |
+------------+
| 11111111   |
| 111222222  |
| 1113333222 |
+------------+


select 
val, 
sum(case when locate('1',val) > 0 then 1 else 0 end ) 
+ sum( case when locate('2',val) > 0 then 1 else 0 end)
+ sum(case when locate('3',val) > 0 then 1 else 0 end)
+sum(case when locate('4',val) > 0 then 1 else 0 end ) as occurence
from test group by val


+------------+-----------+
| val        | occurence |
+------------+-----------+
| 11111111   |         1 |
| 111222222  |         2 |
| 1113333222 |         3 |
+------------+-----------+

或者,如果您有足够的时间,请创建一个包含您能想到的所有字符的查找表。并在 2 行中进行查询

mysql> select * from test ;
+------------+
| val        |
+------------+
| 11111111   |
| 111222222  |
| 1113333222 |
+------------+
3 rows in set (0.00 sec)

mysql> select * from look_up ;
+------+------+
| id   | val  |
+------+------+
|    1 | 1    |
|    2 | 2    |
|    3 | 3    |
|    4 | 4    |
+------+------+
4 rows in set (0.00 sec)

select 
t1.val, 
sum(case when locate(t2.val,t1.val) > 0 then 1 else 0 end ) as occ 
from test t1,(select * from look_up)t2 
group by t1.val ;

+------------+------+
| val        | occ  |
+------------+------+
| 11111111   |    1 |
| 111222222  |    2 |
| 1113333222 |    3 |
+------------+------+

【讨论】:

硬编码查询太多!如果字符串有[A-Z][a-z][@#$%^]等呢? @Vikrant 那么您认为在 mysql 中完成工作的最简单方法是什么,作为答案发布。 这不是解决方案。我们需要编写代码块来获得这个结果。您的解决方案无效,需要硬编码。 @Anvesh 正如我在回答中提到的那样,mysql 中没有这样的功能可以做这些工作,但是如果你仍然想在 mysql 中做这些工作,这是一种方法。如果您知道更好的方法,请添加。请注意,更好的方法是不使用 php 或其他一些我可以举出 100 个示例的语言,它应该在 mysql 中。 整个前提是愚蠢的。我什至不知道你们为什么在这里批评 Abhik 为 OP 给出的精确输入参数提供了解决方案。 MySQL 不是这项工作的工具,甚至没有关于它的讨论。【参考方案3】:

您可以做的一件事是拥有一个包含所有角色的表格,例如:

mysql> select * from chars;
+----+------+
| id | c    |
+----+------+
|  1 | 1    |
|  2 | 2    |
|  3 | 3    |
|  4 | 4    |
+----+------+

如果您的单词表如下所示:

mysql> select * from words;
+----+-----------+
| id | word      |
+----+-----------+
|  1 | 111222333 |
|  2 | 11111111  |
|  3 | 2222111   |
|  4 | 5555555   |
+----+-----------+

然后,您可以在单词中包含字符的条件下加入这些表,并获取计数,如下所示:

mysql> select word, count(c) from words w inner join chars c on locate(c.c, word) group by word;
+-----------+----------+
| word      | count(c) |
+-----------+----------+
| 11111111  |        1 |
| 111222333 |        3 |
| 2222111   |        2 |
+-----------+----------+

【讨论】:

这是一种方法。您的存储过程已损坏,所以不要再试图贬低其他有效的答案。【参考方案4】:

我认为这不是Mysql的工作, 但是如果你足够努力,你可以做任何事情;)

我不喜欢这个答案,但它有效,而且如果你只有数字也不会太难看

SELECT 
    CASE WHEN yourcolumn LIKE '%1%' THEN 1 ELSE 0 END + 
    CASE WHEN yourcolumn LIKE '%2%' THEN 1 ELSE 0 END +
    CASE WHEN yourcolumn LIKE '%3%' THEN 1 ELSE 0 END + 
    CASE WHEN yourcolumn LIKE '%4%' THEN 1 ELSE 0 END +
    CASE WHEN yourcolumn LIKE '%5%' THEN 1 ELSE 0 END +
    CASE WHEN yourcolumn LIKE '%6%' THEN 1 ELSE 0 END +
    CASE WHEN yourcolumn LIKE '%7%' THEN 1 ELSE 0 END +
    CASE WHEN yourcolumn LIKE '%8%' THEN 1 ELSE 0 END +
    CASE WHEN yourcolumn LIKE '%9%' THEN 1 ELSE 0 END +
    CASE WHEN yourcolumn LIKE '%0%' THEN 1 ELSE 0 END
FROM yourtable

【讨论】:

【参考方案5】:
DROP FUNCTION IF EXISTS test.count_chrs;
CREATE DEFINER=`test`@`localhost` FUNCTION `count_chrs`(s CHAR(100)) RETURNS CHAR(4)
  BEGIN 
    DECLARE string_length int(4);
    DECLARE unique_string CHAR(100) DEFAULT "";
    DECLARE count_unique int(4) DEFAULT 0;
    DECLARE current_char int(4) DEFAULT 1;
    SET string_length = CHAR_LENGTH(s);

    WHILE current_char <= string_length DO
      IF (!LOCATE(SUBSTR(s, current_char, 1), unique_string)) THEN
        SET count_unique = count_unique + 1;
        SET unique_string = CONCAT(unique_string, SUBSTR(s, current_char, 1));
      END IF;

      SET current_char = current_char + 1;
    END WHILE;

    RETURN count_unique; 
  END;

我是 MySQL 函数声明的新手,但这可能会让你走上正确的道路。

【讨论】:

我懒得测试它,但这看起来是正确的。我唯一要说的是函数返回一个整数,而不是像你在定义中声明的字符【参考方案6】:

有几个级别的子查询可能会推迟一些,并且需要针对具有较长字符串的列进行扩展,但是通过使用 UNPIVOT 将其翻转过来非常简单。

declare @Data table (RowID nvarchar(1), StringData nvarchar(10))
insert into @Data values (N'1', N'3333333333'),(N'2', N'1113333333'),(N'3', N'1112222444')

select  t1.StringData, cast(t2.CharCount as nvarchar) as 'Unique Characters in String'
from    @Data t1
        inner join (
            select  RowID,count(*) as 'CharCount'
            from    (
                    select  distinct RowID, [char]
                    from    (
                        select  RowID,
                            substring(StringData,1,1) as '1',
                            substring(StringData,2,1) as '2',
                            substring(StringData,3,1) as '3',
                            substring(StringData,4,1) as '4',
                            substring(StringData,5,1) as '5',
                            substring(StringData,6,1) as '6',
                            substring(StringData,7,1) as '7',
                            substring(StringData,8,1) as '8',
                            substring(StringData,9,1) as '9',
                            substring(StringData,10,1) as '10'
                        from    @Data
                        ) Unpivd
                    unpivot ( [char] for chars in ([1],[2],[3],[4],[5],[6],[7],[8],[9],[10])) unpiv
                    where [char] <> ''
                ) CharCounter
            group by RowID
            ) t2
            on t2.RowID = t1.RowID

这会返回:

StringData  Unique Characters in String
3333333333  1
1113333333  2
1112222444  3

【讨论】:

发现这是一个 MySQL 问题,而不是我的示例中的 TSQL。

以上是关于计算字符串中唯一字符的数量的主要内容,如果未能解决你的问题,请参考以下文章

如何计算 ArrayList 中的唯一值?

python文本联系--计算字符串中各个字符的数量

OpenOffice Calc:计算单元格范围内的唯一字符串

如何将字符串空格分隔的键,唯一字的值对转换为字典

Python - 计算字符串中的整数个数?

如何快速计算字符串中连续单个字符的最大数量?