Pomoc - Szukaj - Użytkownicy - Kalendarz
Pełna wersja: wyszukiwanie - problem z zapytaniem
Forum PHP.pl > Forum > Bazy danych > MySQL
qchar
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:
  1. SELECT DISTINCT img_id FROM keywords WHERE word LIKE "PILKA%" OR word LIKE "NOZNA%"

OPCJA 2:
  1. 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
WHERE word LIKE '%PILKA%' OR word LIKE '%NOZNA%'
qchar
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
No to w takim razie:

WHERE word LIKE '%PILKA%' AND word LIKE '%NOZNA%'
qchar
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
Ale jak popatrzyzs na moje propozycje, to zauważysz że różnią się od twoich małymi, ale istotnymi detalami (dosłownie dwoma)
qchar
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
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
smile.gif 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. winksmiley.jpg Jeszcze raz dzięki.
Mchl
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
O to jest świetne! Wielkie dzięki!
Mchl
Dla formalności zapytam: indeksy w tej tabeli masz? Jakie?
qchar
Mam, mam. Tak to mniejwięcej wygląda
  1. CREATE TABLE IF NOT EXISTS `keywords` (
  2. `fl_kw_id` bigint(20) UNSIGNED NOT NULL AUTO_INCREMENT,
  3. `fl_kw_img_id` bigint(20) UNSIGNED NOT NULL,
  4. `fl_kw_word` varchar(50) COLLATE utf8_polish_ci NOT NULL,
  5. PRIMARY KEY (`fl_kw_id`),
  6. KEY `fl_kw_img_id` (`fl_kw_img_id`),
  7. KEY `fl_kw_word` (`fl_kw_word`)
  8. ) ENGINE=MyISAM DEFAULT CHARSET=utf8 COLLATE=utf8_polish_ci;
Mchl
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
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ł.
Mchl
Musi być, ale podejrzewam, że nie masz dwa razy tego samego słowa przypisanego do jednego obrazka.
qchar
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ąć.
Mchl
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
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...
Mchl
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
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.
Mchl
Sporo musisz mieć tych duplikatów chyba...
qchar
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" winksmiley.jpg 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:
  1. SELECT DISTINCT t1.fl_kw_img_id AS id
  2. FROM keywords AS t1
  3. CROSS JOIN keywords AS t2
  4. USING ( fl_kw_img_id )
  5. CROSS JOIN keywords AS t3
  6. USING ( fl_kw_img_id )
  7. WHERE t1.fl_kw_word LIKE 'STADION%'
  8. AND t2.fl_kw_word LIKE 'WARSZAWA%'
  9. 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:
  1. SELECT DISTINCT t1.fl_kw_img_id AS id
  2. FROM keywords AS t1
  3. CROSS JOIN keywords AS t2
  4. USING ( fl_kw_img_id )
  5. CROSS JOIN keywords AS t3
  6. USING ( fl_kw_img_id )
  7. WHERE t1.fl_kw_word LIKE 'STADION%'
  8. AND t2.fl_kw_word LIKE 'WARSZAWA%'
  9. AND t3.fl_kw_word NOT LIKE 'KONFERENCJA%'


WHY, OH WHY? winksmiley.jpg
trucksweb
a moze tak ?
  1.  
  2. SELECT DISTINCT t1.fl_kw_img_id AS id
  3. FROM keywords AS t1
  4. CROSS JOIN keywords AS t2
  5. USING ( fl_kw_img_id )
  6. CROSS JOIN keywords AS t3
  7. USING ( fl_kw_img_id )
  8. WHERE
  9. (t1.fl_kw_word LIKE 'STADION%'
  10. AND t2.fl_kw_word LIKE 'WARSZAWA%')
  11. AND (t3.fl_kw_word NOT LIKE 'KONFERENCJA%')
  12.  
qchar
Nie, niestety nawiasy nic nie pomagają. Ciągle dostaję wszystkie rekordy, znaczy również te zawierające słowo KONFERENCJA.
Mchl
Cytat(qchar @ 3.01.2010, 23:27:52 ) *
WHY, OH WHY? winksmiley.jpg


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
Co prawda chyba trafiło Ci się parę literówek, ale ostatecznie poprawiony przykład o którym mówimy wygląda tak:
  1. SELECT sq1.id
  2. FROM (
  3.  
  4. SELECT DISTINCT t1.fl_kw_img_id AS id
  5. FROM keywords AS t1
  6. CROSS JOIN keywords AS t2
  7. USING ( fl_kw_img_id )
  8. WHERE t1.fl_kw_word LIKE 'STADION%'
  9. AND t2.fl_kw_word LIKE 'WARSZAWA%'
  10. ) AS sq1
  11. LEFT JOIN (
  12.  
  13. SELECT fl_kw_img_id
  14. FROM keywords
  15. WHERE fl_kw_word = 'KONFERENCJA'
  16. ) AS sq2 ON (
  17. sq1.id = sq2.fl_kw_img_id
  18. )
  19. 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ę winksmiley.jpg

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.
Mchl
Najwięcej się nauczysz rozwiązując prawdziwe problemy (własne lub cudze - myślisz że ja tutaj siedzę dla zabawy? tongue.gif)

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
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 winksmiley.jpg
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.
Invision Power Board © 2001-2025 Invision Power Services, Inc.