2015-09-24 10 views
14

У меня есть таблица под названием results с 5 колонками.Как найти наиболее популярные слова в MySQL?

Я хотел бы использовать столбец 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... 
+2

Ваш вопрос очень неоднозначный. Просьба предоставить образцы данных и желаемые результаты. –

+1

Возможный дубликат [MySQL match() против() - порядок по релевантности и столбцу?] (Http://stackoverflow.com/questions/6259647/mysql-match-against-order-by-relevance-and-column) Неточный дубликат, но он отвечает на ваш вопрос – AgeDeO

+0

@GordonLinoff обновлен – User

ответ

7

Вы можете извлечь слова с некоторыми строками. Предполагая, что у вас есть таблица чисел, и что слова разделены пробелами:

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, Скрипка (любезно предоставлена ​​@GrzegorzAdamKowalski) - here.

+0

Можете ли вы поместить это в скрипт SQL? – User

+2

Кажется, он работает неправильно. Проверьте это: http://sqlfiddle.com/#!9/b0749/2 –

+1

@GrzegorzAdamKowalski. , , Спасибо Спасибо. У меня было сравнение назад. Исправлено и тройное спасибо за SQL Fiddle. –

0

Обновление

Идея взята из https://stackoverflow.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 
+2

Вы можете упомянуть, что для этого нужен индекс FULLTEXT, поддерживаемый для MyISAM, и с 5.6 также InnoDB – Kaii

+0

Почему нет подстроки для разбиения строки в каждом пространстве? – User

+0

@User вам не нужно разделить 'матч ... против' даст вам целое число от нуля до единицы, которое выше для лучшего соответствия. –

2

Это даст вам одно слово (Просто, если я понимаю, что означает ваш 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(... 
+0

Какое значение имеет союз от 1 до 9? – User

+0

@user - это число 100. Так что в этом случае подзапрос 'n' будет возвращать 1-100. Взгляните на http://stackoverflow.com/questions/19073500/sql-split-comma-separated-row для подробного объяснения. – RubahMalam

+0

Какое имя таблицы в этом? – User

0

SQL i ы не очень хорошо подходит для решения этой задачи, в то время возможно есть ограничения (количество слов, например)

быстрый 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 
+0

Python - это то, что я использовал для сбора данных, но это может сработать. Я не очень часто использую PHP, так что вы можете изменить код для загрузки строк из БД? – User

0

Здесь работает SQL скрипку: 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 и без пробелов). Inner one извлекает все строки, начиная с любого символа пробела и заканчивая любым другим символом пробела, поэтому строки не являются словами (несмотря на то, что они назвали этот подзапрос 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 
3

Вы можете использовать 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 
Смежные вопросы