qchar
30.12.2009, 17:44:30
Witam
Mam dużą bazę danych ze zdjęciami, i słowami kluczowymi. W uproszczeniu baza składa się z 2 tabel: zdjęcia z polami id_zdjecia i ścieżką do zdjęcia oraz tabelę słowa kluczowe, gdzie mam pola id, id_zdjecia, slowo. I teraz chciałbym jednym zapytaniem sql znaleźć zdjęcia które mają przypisane np 2 słowa kluczowe np: jeśli wpiszę "piłka nożna", chcę znaleźć tylko te identyfikatory zdjęć które mają słowo PIŁKA i NOŻNA. Wszelkie moje próby kończą się 2 wyjściami. Albo dostaję wynik w postaci identyfikatorów zdjęć zawierających słowo piłka lub słowo nożna, albo pusty zbiór danych. Oczywiście mam w bazie słów kluczowych takie wpisy, gdzie do jednego identyfikatora zdjęcia przypisane są oba te słowa. Robię to mniejwiecej tak:
OPCJA 1:
SELECT DISTINCT img_id FROM keywords WHERE word LIKE "PILKA%" OR word LIKE "NOZNA%"
OPCJA 2:
SELECT DISTINCT img_id FROM keywords WHERE word LIKE "PILKA%" AND word LIKE "NOZNA%"
Proszę o jakąś podpowiedź, bo stanąłem w miejscu, w którym nie spodziewałem się w ogóle problemu...
Mchl
30.12.2009, 19:11:45
WHERE word LIKE '%PILKA%' OR word LIKE '%NOZNA%'
qchar
30.12.2009, 20:19:34
Nie wiem, może nie byłem za bardzo precyzyjny, ale takie zapytanie zwraca mi tak jak napisałem wszystkie zdjęcia powiązane ze słowem PIŁKA oraz te ze słowem NOŻNA konkretnie rzecz ujmując ~ok 35000 rekordów. A ja chciałbym dostać TYLKO te zdjęcia które opisane są oboma słowami, których w bazie mam mniejwięcej 12000.
Mchl
30.12.2009, 20:44:31
No to w takim razie:
WHERE word LIKE '%PILKA%' AND word LIKE '%NOZNA%'
qchar
30.12.2009, 23:04:26
No dobra, ale jak popatrzysz na mój pierwszy post OBA rozwiązania które proponujesz są w tam i oba zapytania nie działają, przynajmniej nie tak jak bym tego oczekiwał. Dlatego właśnie piszę na forum...
Mchl
31.12.2009, 00:01:13
Ale jak popatrzyzs na moje propozycje, to zauważysz że różnią się od twoich małymi, ale istotnymi detalami (dosłownie dwoma)
qchar
31.12.2009, 01:50:39
Twoja propozycja nie działa, bo jest praktycznie identyczna jak moja. Co do różnic, widzę 2, moim zdaniem nie istotne dla istoty problemu: ja mam cudzysłowy, ty ', ja mam jeden znak procenta po wyrazie który szukam(bo tak własnie chce szukać - to co wpiszę+jakieś znaki, stąd WYRAZ%), Ty proponujesz % także przed wyrazem, mi zupełnie niepotrzebne(bo to oznaczało by szukania fraz jakiesZnakiWYRAZjakiesZnaki). Dodam dla uściślenia że w polu word zawsze znajduje się jedno słowo, więc prawdę mówiąc można by szukać bez procentów, lub w ogóle pisząc word = "SZUKANYWYRAZ". Ale żeby nie było - sprawdziłem mimo wszystko. Więc stwierdzam raz jeszcze - nie działa. W obrazku zrzut ekranu:

Jeśli czegoś więcej nie widzę, proszę o oświecenie, bo mija już drugi dzień jak z tym walczę a rozwiązania łopatologiczne jakie stosuje są niedopuszczalnie wolne.
Mchl
31.12.2009, 08:47:55
No to trzeba było zacząć od opisania struktury danych. Zasugerowałem się Twoimi zapytaniami, że trzymasz wszystkie słowa kluczowe w jednym polu. (W sumie moja wina, bo z Twojego pierwszego posta wynika co innego)
Ok.
Spróbuj tak:
Kod
SELECT fl_kw_img_id AS c, GROUP_CONCAT(fl_kw_word) AS w FROM keywords GROUP BY fl_kw_img_id HAVING w LIKE '%STADION%' AND w LIKE '%WARSZAWA%'
qchar
31.12.2009, 12:04:10

No właśnie taką mam strukturę narzuconą, i raczej muszę przy niej pozostać, choć sam pewnie bym to trochę inaczej zorganizował. Cieszę się, że w końcu widzę że można to zrobić jednym zapytaniem. Tylko pozostał mi problem szybkości. Bo pisząc w ten sposób jak proponujesz czekam na wynik 30s. Do tej pory, ponieważ, nie potrafiłem napisać tego jedną linijką (bo spodziewałem się że będzie szybciej) radziłem sobie w taki sposób, że pobierałem wszystkie id zdjęć zawierające jedno słowo, wszystkie id zawierające słowo, i dalej już za pomocą PHP w otrzymanych tablicach wybierałem te zawierające oba. I taki sposób trwa nie uwzględniając cache mysql-a ok 15 sekund. Ale mimo wszystko jestem wdzięczny bardzo bo myślę że mam punkt wyjścia jakiś do dalszych rozważań, szczególnie że niewiele mam tylko kilka takich słów których jest w bazie tak dużo że powodują problem. I w końcu jakieś życiowe zastosowanie funkcji GROUP_CONCAT o której też myślałem.

Jeszcze raz dzięki.
Mchl
31.12.2009, 12:32:53
Inny sposób, być może szybszy, może wyglądać tak:
Kod
SELECT t1.fl_kw_img_id AS c FROM keywords AS t1 CROSS JOIN keywords AS t2 USING (fl_kw_img_id) WHERE t1.fl_kw_word = 'STADION' AND t2.fl_kw_word = 'WARSZAWA'
Problem z tym jest taki, że dla dwóch, czy trzech słow to będzie jeszcze jako tako, ale potem coraz bardziej się ślimaczy (bo trzeba kolejne JOINy dorzucać)
qchar
31.12.2009, 13:02:06
O to jest świetne! Wielkie dzięki!
Mchl
31.12.2009, 13:13:30
Dla formalności zapytam: indeksy w tej tabeli masz? Jakie?
qchar
31.12.2009, 14:34:52
Mam, mam. Tak to mniejwięcej wygląda
CREATE TABLE IF NOT EXISTS `keywords` (
`fl_kw_id` bigint(20) UNSIGNED NOT NULL AUTO_INCREMENT,
`fl_kw_img_id` bigint(20) UNSIGNED NOT NULL,
`fl_kw_word` varchar(50) COLLATE utf8_polish_ci NOT NULL,
PRIMARY KEY (`fl_kw_id`),
KEY `fl_kw_img_id` (`fl_kw_img_id`),
KEY `fl_kw_word` (`fl_kw_word`)
) ENGINE=MyISAM DEFAULT CHARSET=utf8 COLLATE=utf8_polish_ci;
Mchl
31.12.2009, 17:45:16
Kod
ALTER TABLE keywords
ADD INDEX imgID_word(fl_kw_img_id,fl_kw_word)
(na dobrą sprawę, to mógłby być Twój PRIMARY)
Sprawdź EXPLAINem, ale wydaje mi się, że powinien dobrze pasować do tego zapytania.
qchar
3.01.2010, 00:13:20
A to nie jest tak, że polo PRIMARY KEY musi być unikalne? Bo u mnie w tej tabeli tak nie jest stąd podejrzewam oddzielne pole z indeksem. Ale nawet w takiej strukturze to ostatnie zapytanie nieźle dawało radę, mimo że wiele czasu na testy nie miałem przed sylwkiem, więc pewnie będe z niego intensywnie korzystał.
Musi być, ale podejrzewam, że nie masz dwa razy tego samego słowa przypisanego do jednego obrazka.
qchar
3.01.2010, 14:46:07
No właśnie niestety mam, bo ktoś tworząc tą całą bazę poszedł w wielu miejscach na skróty. Dlatego też pewnie w miarę czasu będę szukał sposobu żeby te duplikaty łatwo szybko i przyjemnie usunąć.
UWAGA: Nie sprawdzone. Nie biorę gwarancji. Nie testować na żywych danych.
To powinno załatwić sprawę duplikatów
Kod
DELETE
FROM keywords
WHERE fl_kw_id IN (
SELECT DISTINCT t1.fl_kw_id
FROM keywords AS t1
CROSS JOIN keywords AS t2
USING(fl_kw_img_id,fl_kw_word)
WHERE t1.fl_kw_id > t2.fl_kw_id
)
qchar
3.01.2010, 19:25:43
Wywała błąd:
#1093 - You can't specify target table 'keywords' for update in FROM clause
Z braku czasu nie googlowałem za dużo, ale nie wiem czy nie trafiliśmy na jakiś bug w mysql...
Nie nie... to ma sens...
Spróbuj tego:
Kod
DELETE
k
FROM
keywords AS k,
(
SELECT DISTINCT t1.fl_kw_id
FROM keywords AS t1
CROSS JOIN keywords AS t2
USING(fl_kw_img_id,fl_kw_word)
WHERE t1.fl_kw_id > t2.fl_kw_id
) AS sq
WHERE k.fl_kw_id = sq.fl_kw_id
qchar
3.01.2010, 22:02:08
Strasznie wolno to działa, więc rezultaty będę znał dopiero rano, jak zostawię to na noc, bo teraz potrzebuję dostępu do bazy, a komenda go blokuje.
Sporo musisz mieć tych duplikatów chyba...
qchar
3.01.2010, 23:27:52
Dokładnych danych nie znam, ale wygląda to mniej więcej tak że najczęstsze słowo kluczowe występuje w bazie ponad 30tys razy, a zdjęć jest tylko ok 26 tysięcy. Może to dać Ci jakieś pojecie ile tam jest "syfu"

Wszystkich słów razem jest ok 600tys i to już po odfiltrowaniu wielu rzeczy, bo zaczynałem z pułapu 750tys słów.
I jeszcze jedno pytanie, bo napewno zostaję przy konstrukcji z CROSS JOIN. Gdy chcę 3 słowa: WARSZAWA, STADION, KONFERENCJA to robię tak i działa:
SELECT DISTINCT t1.fl_kw_img_id AS id
FROM keywords AS t1
CROSS JOIN keywords AS t2
USING ( fl_kw_img_id )
CROSS JOIN keywords AS t3
USING ( fl_kw_img_id )
WHERE t1.fl_kw_word LIKE 'STADION%'
AND t2.fl_kw_word LIKE 'WARSZAWA%'
AND t3.fl_kw_word LIKE 'KONFERENCJA%'
Gdybym chciał wszystkie zdjęcia gdzie jest słowo kluczowe WARSZAWA, STADION ale nie ma słowa KONFERENCJA to spłodziłem coś takiego, ale to tez nie chce współpracować i nie uwzględnia 3 warunku:
SELECT DISTINCT t1.fl_kw_img_id AS id
FROM keywords AS t1
CROSS JOIN keywords AS t2
USING ( fl_kw_img_id )
CROSS JOIN keywords AS t3
USING ( fl_kw_img_id )
WHERE t1.fl_kw_word LIKE 'STADION%'
AND t2.fl_kw_word LIKE 'WARSZAWA%'
AND t3.fl_kw_word NOT LIKE 'KONFERENCJA%'
WHY, OH WHY?
trucksweb
4.01.2010, 12:05:12
a moze tak ?
SELECT DISTINCT t1.fl_kw_img_id AS id
FROM keywords AS t1
CROSS JOIN keywords AS t2
USING ( fl_kw_img_id )
CROSS JOIN keywords AS t3
USING ( fl_kw_img_id )
WHERE
(t1.fl_kw_word LIKE 'STADION%'
AND t2.fl_kw_word LIKE 'WARSZAWA%')
AND (t3.fl_kw_word NOT LIKE 'KONFERENCJA%')
qchar
4.01.2010, 17:07:43
Nie, niestety nawiasy nic nie pomagają. Ciągle dostaję wszystkie rekordy, znaczy również te zawierające słowo KONFERENCJA.
Cytat(qchar @ 3.01.2010, 23:27:52 )

WHY, OH WHY?

Nieubłagane prawa logiki zbiorów.
Musiałbyś zrobić coś takiego:
1. Uzyskać listę rekordów które mają słowa 'STADION' i 'WARSZAWA'
2. Z tej listy usunąć rekordy które mają słowo 'KONFERENCJA'
Krok 1. Potrafisz. Krok 2 właściwie też powinieneś. Kwestia odpowiedniego ułożenia podzapytania.
Kod
SELECT
sq1.ID
FROM
(podzapytanie1) AS sq1 --podzapytanie1 to zapytanie realizujące krok1 powyżej
LEFT JOIN (
SELECT
fl_kw_img_id
FROM
keywords
WHERE
fl_kw_word = 'KONFERENCJA'
) AS sq2
ON
(sq1.ID = sq2.fl_kw_word)
WHERE
fl_kw_img_id.sq2 IS NULL
qchar
5.01.2010, 00:44:37
Co prawda chyba trafiło Ci się parę literówek, ale ostatecznie poprawiony przykład o którym mówimy wygląda tak:
SELECT sq1.id
FROM (
SELECT DISTINCT t1.fl_kw_img_id AS id
FROM keywords AS t1
CROSS JOIN keywords AS t2
USING ( fl_kw_img_id )
WHERE t1.fl_kw_word LIKE 'STADION%'
AND t2.fl_kw_word LIKE 'WARSZAWA%'
) AS sq1
LEFT JOIN (
SELECT fl_kw_img_id
FROM keywords
WHERE fl_kw_word = 'KONFERENCJA'
) AS sq2 ON (
sq1.id = sq2.fl_kw_img_id
)
WHERE sq2.fl_kw_img_id IS NULL
I działa też zajefajnie. Dzięki wielkie. Teraz tylko jakoś sprytnie to generować z php ale z tym już dam radę
BTW, mógłbyś polecić jakieś źródła w necie, albo książki żeby się trochę podszkolić w sql, bo widzę że oprócz prostych selectów, i innych takich przydało by się lepiej zgłębić temat.
Najwięcej się nauczysz rozwiązując prawdziwe problemy (własne lub cudze - myślisz że ja tutaj siedzę dla zabawy?

)
Ale są też różne strony typu:
http://sql-ex.ru/ to co prawda MSSQL a nie MySQL, ale logika to logika niezależnie od języka.
qchar
5.01.2010, 01:37:16
Pewnie, że najlepiej na przykładach. Choć do tej pory wystarczała mi moja (mizerna) wiedza, bo i problemy jakie miałem przed sobą były trywialne. Ale ta stronka całkiem ciekawa. Musze znaleźć teraz tylko trochę czasu
To jest wersja lo-fi głównej zawartości. Aby zobaczyć pełną wersję z większą zawartością, obrazkami i formatowaniem proszę
kliknij tutaj.