如何在 MySQL 中找到最流行的单词出现?

Posted

技术标签:

【中文标题】如何在 MySQL 中找到最流行的单词出现?【英文标题】:How to find most popular word occurrences in MySQL? 【发布时间】:2015-12-22 01:07:47 【问题描述】:

我有一个名为 results 的表,有 5 列。

我想使用title 列来查找说:WHERE title like '%for sale%' 的行,然后在该列中列出最流行的单词。一个是for,另一个是sale,但我想看看还有什么其他词与此相关。

样本数据:

title
cheap cars for sale
house for sale
cats and dogs for sale
iphones and androids for sale
cheap phones for sale
house furniture for sale

结果(单字):

for    6
sale    6
cheap    2
and    2
house    2
furniture 1
cars    1
etc...

【问题讨论】:

你的问题很模棱两可。请提供样本数据和期望的结果。 mysql match() against() - order by relevance and column? 的可能重复项不是完全重复,但它回答了您的问题 @GordonLinoff 更新 你有单词列表吗? @GordonLinoff 列出所有可能的单词?还是大样本数据集? 【参考方案1】:

您可以通过一些有趣的方式使用 ExtractValue。在此处查看 SQL 小提琴:http://sqlfiddle.com/#!9/0b0a0/45

我们只需要一张桌子:

CREATE TABLE text (`title` varchar(29));

INSERT INTO text (`title`)
VALUES
    ('cheap cars for sale'),
    ('house for sale'),
    ('cats and dogs for sale'),
    ('iphones and androids for sale'),
    ('cheap phones for sale'),
    ('house furniture for sale')
;

现在我们构建一系列选择,从转换为 XML 的文本中提取整个单词。每个选择从文本中提取第 N 个单词。

select words.word, count(*) as `count` from
(select ExtractValue(CONCAT('<w>', REPLACE(title, ' ', '</w><w>'), '</w>'), '//w[1]') as word from `text`
union all
select ExtractValue(CONCAT('<w>', REPLACE(title, ' ', '</w><w>'), '</w>'), '//w[2]') from `text`
union all
select ExtractValue(CONCAT('<w>', REPLACE(title, ' ', '</w><w>'), '</w>'), '//w[3]') from `text`
union all
select ExtractValue(CONCAT('<w>', REPLACE(title, ' ', '</w><w>'), '</w>'), '//w[4]') from `text`
union all
select ExtractValue(CONCAT('<w>', REPLACE(title, ' ', '</w><w>'), '</w>'), '//w[5]') from `text`) as words
where length(words.word) > 0
group by words.word
order by `count` desc, words.word asc

【讨论】:

【参考方案2】:

您可以通过一些字符串操作来提取单词。假设您有一个数字表并且单词由单个空格分隔:

select substring_index(substring_index(r.title, ' ', n.n), ' ', -1) as word,
       count(*)
from results r join
     numbers n
     on n.n <= length(title) - length(replace(title, ' ', '')) + 1
group by word;

如果您没有数字表,您可以使用子查询手动构建一个:

from results r join
     (select 1 as n union all select 2 union all select 3 union all . . .
     ) n
     . . .

SQL Fiddle(由@GrzegorzAdamKowalski 提供)是here。

【讨论】:

你能把它放在 SQL 小提琴里吗? @GrzegorzAdamKowalski 。 . .谢谢谢谢。我把比较倒过来了。修复并三重感谢 SQL Fiddle。【参考方案3】:

这里正在运行 SQL Fiddle:http://sqlfiddle.com/#!9/0b0a0/32

让我们从两张表开始——一张用于文本,一张用于数字:

CREATE TABLE text (`title` varchar(29));

INSERT INTO text
    (`title`)
VALUES
    ('cheap cars for sale'),
    ('house for sale'),
    ('cats and dogs for sale'),
    ('iphones and androids for sale'),
    ('cheap phones for sale'),
    ('house furniture for sale')
;

CREATE TABLE iterator (`index` int);

INSERT INTO iterator
    (`index`)
VALUES
    (1),(2),(3),(4),(5),(6),(7),(8),(9),(10),(11),(12),(13),(14),(15),
    (16),(17),(18),(19),(20),(21),(22),(23),(24),(25),(26),(27),(28),(29),(30)
;

第二个表 iterator 必须包含从 1 到 N 的数字,其中 N 大于或等于 text 中最长字符串的长度。

然后,运行这个查询:

select
  words.word, count(*) as `count`
from 
(select
  substring(concat(' ', t.title, ' '), i.index+1, j.index-i.index) as word
from
  text as t, iterator as i, iterator as j
where
    substring(concat(' ', t.title), i.index, 1) = ' '
and substring(concat(t.title, ' '), j.index, 1) = ' '
and i.index < j.index
) AS words
where
    length(words.word) > 0
and words.word not like '% %'
group by words.word
order by `count` desc, words.word asc

有两个选择。外层简单地对单个单词进行分组和计数(长度大于 0 且没有任何空格的单词)。内层提取从任何空格字符开始并以任何其他空格字符结尾的所有字符串,因此字符串不是单词(尽管将这个子查询命名为 words),因为它们可以包含除开头和结尾之外的其他空格。

结果:

word    count
for     6
sale    6
and     2
cheap   2
house   2
androids    1
cars    1
cats    1
dogs    1
furniture   1
iphones     1
phones  1

【讨论】:

【参考方案4】:

SQL 不太适合这项任务,虽然可能存在限制(例如字数)

执行相同任务的快速 php 脚本可能更容易长期使用(而且可能更快)

<?php
$rows = [
    "cheap cars for sale",
    "house for sale",
    "cats and dogs for sale",
    "iphones and androids for sale",
    "cheap phones for sale",
    "house furniture for sale",
];

//rows here should be replaced by the SQL result
$wordTotals = [];
foreach ($rows as $row) 
   $words = explode(" ", $row);
    foreach ($words as $word) 
        if (isset($wordTotals[$word])) 
            $wordTotals[$word]++; 
            continue;
        

        $wordTotals[$word] = 1;
    


arsort($wordTotals);

foreach($wordTotals as $word => $count) 
    echo $word . " " . $count . PHP_EOL;

输出

for 6
sale 6
and 2
cheap 2
house 2
phones 1
androids 1
furniture 1
cats 1
cars 1
dogs 1
iphones 1

【讨论】:

Python 是我一直用来收集数据的工具,但这可以工作。我不怎么用 PHP,所以你介意更改代码以从数据库中加载行吗?【参考方案5】:

这会给你一个单词(只要我明白你的single word 的意思。):

select concat(val,' ',cnt) as result from(
    select (substring_index(substring_index(t.title, ' ', n.n), ' ', -1)) val,count(*) as cnt
        from result t cross join(
         select a.n + b.n * 10 + 1 n
         from 
                (select 0 as n union all select 1 union all select 2 union all select 3 
                        union all select 4 union all select 5 union all select 6 
                        union all select 7 union all select 8 union all select 9) a,
                (select 0 as n union all select 1 union all select 2 union all select 3 
                        union all select 4 union all select 5 union all select 6 
                        union all select 7 union all select 8 union all select 9) b
                order by n 
        ) n
    where n.n <= 1 + (length(t.title) - length(replace(t.title, ' ', '')))
    group by val
    order by cnt desc
) as x

结果应该是这样的:

Result
--------
for 6
sale 6
house 2
and 2
cheap 2
phones 1
iphones 1
dogs 1
furniture 1
cars 1
androids 1
cats 1

但如果single word 你需要这样:

result
-----------
for 6 sale 6 house 2 and 2 cheap 2 phones 1 iphones 1 dogs 1 furniture 1 cars 1 androids 1 cats 1

只需将上面的查询修改为:

select group_concat(concat(val,' ',cnt) separator ' ') as result from( ...

【讨论】:

@user 它的计数为 100。因此在这种情况下,子查询 n 将返回 1-100。详细解释请看***.com/questions/19073500/…。 这里面的表名是什么? 也适用于 MariaDB 10.1。【参考方案6】:

更新

想法来自https://***.com/a/17942691/98491

这个查询在我的机器上运行(MySQL 5.7),但是 Sqlfiddle 报告了一个错误。 基本思想是,您应该在您的领域中创建一个数字从 1 到最大单词出现次数(如 4)的表格,或者像我一样,为简单起见使用 UNION 1 .. 4。

CREATE TABLE products (
  `id` int,
  `name` varchar(45)
);

INSERT INTO products
    (`id`, `name`)
VALUES
    (1, 'for sale'),
    (2, 'for me'),
    (3, 'for you'),
    (4, 'you and me')
;

SELECT name, COUNT(*) as count FROM
(
SELECT
  product.id,
  SUBSTRING_INDEX(SUBSTRING_INDEX(product.name, ' ', numbers.n), ' ', -1) name
FROM
  (
    SELECT 1 AS n
    UNION SELECT 2
    UNION SELECT 3
    UNION SELECT 4
  ) AS numbers
  INNER JOIN products product
  ON CHAR_LENGTH(product.name)
     -CHAR_LENGTH(REPLACE(product.name, ' ', ''))>=numbers.n-1
ORDER BY
  id, n
)
AS result
GROUP BY name
ORDER BY count DESC

结果将是

for | 3
you | 2
me  | 2
and | 1
sale| 1

【讨论】:

您可能会提到,为此需要一个 FULLTEXT 索引,MyISAM 支持,从 5.6 开始也支持 InnoDB 为什么没有substring在每个空格处分割字符串? @User you don't need split match ... against 会给你一个介于 0 和 1 之间的整数,这个整数越大越好。 结果应该返回单个单词,但事实并非如此。检查原始问题。 @User 是的,在您更新了您的问题后,我现在了解您想要实现的目标。我会更新我的答案。

以上是关于如何在 MySQL 中找到最流行的单词出现?的主要内容,如果未能解决你的问题,请参考以下文章

如何提取 MySQL 字符串中的第 n 个单词并计算单词出现次数?

如何将一个单词与所选单词列表进行比较,以找到最相关的单词? [关闭]

在单词中找到最短的重复周期?

如何使用 word2vec 找到最接近向量的单词

如何找到使用Python的数据上最常用的单词? [重复]

我如何找到并输出只出现一次的所有单词?