2015-09-24 8 views
14

Mam tabelę o nazwie results z 5 kolumnami.Jak znaleźć najbardziej popularne wystąpienia słów w MySQL?

Chciałbym użyć kolumny title, aby znaleźć wiersze, które mówią: WHERE title like '%for sale%', a następnie wymień najbardziej popularne słowa w tej kolumnie. Jeden byłby for, a drugi byłby sale, ale chcę zobaczyć, jakie inne słowa korelują z tym.

Dane

Próbka:

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 

Wyniki (pojedyncze słowa):

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

Twoje pytanie jest bardzo niejednoznaczne. Podaj przykładowe dane i pożądane wyniki. –

+1

możliwy duplikat [dopasowania MySQL() względem() - kolejność według trafności i kolumny?] (Http://stackoverflow.com/questions/6259647/mysql-match-against-order-by-relevance-and-column) Nie jest to dokładny duplikat, ale odpowiada na twoje pytanie – AgeDeO

+0

@GordonLinoff zaktualizowano – User

Odpowiedz

7

Można wyodrębnić słowa z jakimś ciąg manipulacji. Zakładając, że masz tablicę liczb i słów, które są oddzielone pojedynczymi odstępami:

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; 

Jeśli nie masz tablicę liczb, można skonstruować jedną ręcznie za pomocą podzapytania:

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

SQL Fiddle (dzięki uprzejmości @GrzegorzAdamKowalski) to here.

+0

Czy umieścisz to w skrzypcach SQL? – User

+2

Wygląda na to, że nie działa poprawnie. Sprawdź to: http://sqlfiddle.com/#!9/b0749/2 –

+1

@GrzegorzAdamKowalski. . . Dziękuję dziękuję. Porównałem się wstecz. Naprawiono i potrójne dzięki za SQL Fiddle. –

0

Aktualizuj

Pomysł pochodzi z https://stackoverflow.com/a/17942691/98491

Ta kwerenda działa na moim komputerze (MySQL 5.7) jednak Sqlfiddle zgłasza błąd. Podstawową ideą jest utworzenie tabeli z numerami od 1 do maksymalnego wystąpienia słowa (np. 4) w polu lub tak jak ja, użycie UNION 1 .. 4 dla uproszczenia.

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 

Wynik będzie

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

Można wspomnieć, że do tego potrzebna byłaby zmienna FULLTEXT, obsługiwana przez MyISAM, a od 5.6 także InnoDB – Kaii

+0

. Dlaczego nie ma "podłańcucha", który dzieliłby łańcuch w każdym miejscu? – User

+0

@Użytkownik, dla którego nie potrzebujesz podziału 'match ... against', daje liczbę całkowitą od zera do jednej, która jest wyższa dla lepszego dopasowania. –

2

To da ci pojedyncze słowa (Tylko jeśli rozumiem, co oznacza twój 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 

Wynik powinien wyglądać następująco:

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 

Ale jeśli single word trzeba tak:

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 

Wystarczy zmodyfikować kwerendę powyżej:

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

Jakie jest znaczenie związku 1 do 9? – User

+0

@user to liczba 100. Więc w tym przypadku podzapytanie 'n' zwróci 1-100. Zajrzyj na http://stackoverflow.com/questions/19073500/sql-split-comma-separated-row, aby uzyskać szczegółowe wyjaśnienie. – RubahMalam

+0

Co to jest nazwa tabeli? – User

0

SQL i nie s dobrze nadaje się do tego zadania, podczas gdy możliwe są ograniczenia (liczba słów na przykład)

szybkie skrypt PHP aby zrobić to samo zadanie może być łatwiejsze do długotrwałego stosowania (i prawdopodobnie szybciej też)

<?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; 
} 

Wyjście

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 jest tym, czego używałem do zbierania danych, ale to może zadziałać. Nie używam tak dużo PHP, więc czy zmieniasz kod, aby załadować wiersze z DB? – User

0

Tu pracuje SQL Fiddle: http://sqlfiddle.com/#!9/0b0a0/32

Zacznijmy od dwóch tabel - jeden dla tekstów i jeden dla liczb:

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) 
; 

Drugi stół iterator moszcz zawiera numery od 1 do N, gdzie N większa lub równa długości najdłuższego łańcucha w text.

Następnie uruchomić tę kwerendę:

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 

Istnieją dwa wybiera. Zewnętrzny po prostu grupuje i zlicza pojedyncze słowa (słowa o długości większej od 0 i bez spacji). Wewnętrzna wyodrębnia wszystkie ciągi zaczynając od dowolnego znaku spacji i kończąc na dowolnym innym znaku spacji, więc łańcuchy nie są słowami (pomimo nazwania tego podzapytania words), ponieważ mogą zawierać inne spacje niż początkowe i końcowe.

Wyniki:

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

Można używać ExtractValue w jakiś ciekawy sposób. Zobacz SQL skrzypce tutaj: http://sqlfiddle.com/#!9/0b0a0/45

Musimy tylko jedną tabelę:

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') 
; 

Teraz budujemy serii wybiera które wydobywają całe słowa z tekstu przeliczone na XML. Każda opcja wyodrębnia n-te słowo z tekstu.

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 
Powiązane problemy