Pomoc - Szukaj - Użytkownicy - Kalendarz
Pełna wersja: Złączenia CROSS JOIN + Smarty [php][smarty]
Forum PHP.pl > Forum > PHP
rodipl
Witam.

Mam pewien problem ze złączeniem typu CROSS JOIN w mysqli, a do tego wynik jest przesyłany do Smarty.

Oto moje zapytanie (oczywiście działa i dobrze się ma)
Kod
SELECT * FROM news CROSS JOIN admin CROSS JOIN kat_news WHERE news.id_login = admin.id ORDER BY news.data AND news.id_kat = kat_news.id AND slider = '0' DESC LIMIT 4


Następnie dane trafiają do SMARTY poprzez: $smarty->assign("news", $news);

W pliku .tpl mam
Kod
{foreach item=news from=$news}
<div class="news">
<a class="news" href="nowosc,{$news.id},{$news.tytul|lower|nl2br|strip_tags|replace:' ':'_'}">
<span class="ngnews">
{$news.tytul|nl2br|strip_tags}
</span>
</a>
</div>
{/foreach}


To także działa - wyniki wyświetlają się poprawnie oprócz małego niuansu. W linku nowosc,IDNEWSA,TYTUL_NEWSA -> zamiast IDNEWSa wyświetla się ID kategorii newsa ze złączenia. Próbowałem modyfikować zapytanie lecz nieskutcznie. Może ktoś ma jakiś lepszy sposób?

Proszę o konsultację. biggrin.gif

Życzę miłego wieczoru.
Pozdrawiam,
Robert.
thek
Cross join ma to do siebie, że łączy wszystko ze wszystkim, co w przypadku większych tabel jest niestety zajeżdżaniem bazy. Radziłbym Ci je przerobić, bo i tak zauważ, że limitujesz te dane a na dodatek wyświetlasz tylko kilka rzeczy, więc przerzucanie wszystkich używając gwiazdki w select to dodatkowe obciążanie łącza nadmiarem przesyłanej informacji. Zresztą patrząc na kod jakiekolwiek złączanie z kategorią jest bezsensowne, gdyż nie używasz danych z tej tabeli. Smarty uzywa tylko id i tytuł, a to sugeruje skrócenie zapytania do:
  1. SELECT id, tytul FROM news WHERE author_id = admin_id ORDER BY id DESC LIMIT 4
gdzie admin_id to numer id admina gdy masz tylko jednego w bazie. Jeśli jednak masz kilku adminów w serwisie to modyfikujesz nieznacznie całość do:
  1. SELECT id, tytul FROM news WHERE author_id IN (SELECT id FROM admin) ORDER BY id DESC LIMIT 4

Nie potrzeba nawet sortowania po dacie newsa, bo automatycznie newsy z najwyższymi id są jednocześnie najświeższymi. A id jest zazwyczaj indeksem w przeciwieństwie do daty, co sprawi, ze zapytanie jest jeszcze szybciej wykonywane.
Tak przynajmniej można Twój kod poprawić winksmiley.jpg Bo na co robić tyle niepotrzebnych joinów generujących bezsensownie tyle wierszy, skoro nie musi być żadnego? No chyba, że dołączasz kategorię jeszcze z innych powodów, ale przeanalizuj swój kod, niezbędne zmienne i sam zobaczysz jak wiele nadmiarowych rzeczy robisz niepotrzebnie. Bo przecież choćby w Twoim wypadku przesyłasz teżtreść wszystkich rekordów włącznie z treścią newsa, którego nie wyświetlasz nigdzie. A przesłanie tego między Apachem i MySQL też swój czas zajmie. Nie przesyłaj więc niepotrzebnie danych, których nie używasz.

Ale jeśli już jakoś musisz użyć całości jak jest to poczytaj o czymś takim jak ALIAS winksmiley.jpg
rodipl
To było uproszczone... Przedstawiłem tylko to co nie działa tak jak powinno + kilka linijek więcej, aby było wiadomo o co chodzi. Wyświetlam wszystko co jest w bazie danych, a nie chciało mi się po prostu tego wypisywać dlatego użyłem gwiazdkę. Wypisuję np. link obrazka, zajawkę, treść, autora, datę itd.

moja baza wygląda tak:

news
id -> klucz podstawowy + auto inctrement
id_kat
id_login
....

kat_news
id -> klucz podstawowy + auto inctrement
nazwa

admin

id -> klucz podstawowy + auto inctrement
login
...

Dlaczego złączenia są bez sensu? Nie przepisuje danych do tabeli "news" tylko działa to na zasadzie relacji id_kat łączy się z id od kat_news dzięki, któremu wyświetlam nazwa. Tak samo z adminem tylko, że wyświetlam login osoby, która dodała nowość.

Niestety jak na razie czekam na dalsze wskazówki. smile.gif
thek
Dla mnie za dużo kombinowania z łączeniami typu CROSS JOIN robisz, gdzie są one tylko spowalniaczem całości skryptu i w takiej sytuacji nie stosuje się ich. Przy takiej strukturze tabel robisz LEFT JOIN po kolumnach łączących i wyciągasz odpowiednie dane. Nazwa tabeli admin była dla mnie myląca i sugerowała, że tylko admin może pisać i dlatego ująłem id_admina jako warunek ograniczający. W takim wypadku zapytanie powinno być:
  1. SELECT n.*, k.nazwa AS kategoria, a.login FROM news n LEFT JOIN kategoria k ON n.id_kat = k.id LEFT JOIN admin a ON n.id_login = a.id ORDER BY n.id DESC LIMIT 4

Takie zapytanie ograniczy już "na dzień dobry" liczbę tworzonych rekordów do liczby newsów, bo cross join tworzy w bazie tymczasowo (newsy * userzy * kategorie) rekordów, czyli, przykładowo 50 newsów * 40 userów * 5 kategorii, to 10000 rekordów tymczasowych(!) i jedzie po bazie przez to równo. LEFT JOINy zaś do newsów wyszukają z bazy konkretną kategorię i usera przypisanego. Problemem może być przypisanie newsa do kilku kategorii, ale w takim wypadku baza (i zapytanie) nieco inaczej wygląda, ponieważ usuwa się z tabeli news id_kategorii i tworzy tabelę złączeniową, w której jako kolumny są id_newsa i id_kategorii.
Błąd wyskakiwał ci ponieważ przy jakimkolwiek joinie kilka kolumn o tej samej nazwie zawsze robi bajzel przy próbie wyciągania danych z rekordu. Stąd najlepiej wybrać tylko niezbędne Ci dane, a dla dubli na wszelki wypadek ustawić aliasy. To fragment:
k.nazwa AS kategoria
co sprawi, że nazwa kategorii będzie widoczna jako kategoria. W Twoim wypadku problem był z kolumnami id, które były w każdej z tabel i jeśli byś chciał się do nich odnieść to musiałbyś po prostu znać ich dokładne nazwy po zastosowaniu join, ale lepiej w takich wypadkach właśnie na te duble zastosować aliasy. Ja użyłem wszystkich danych z tabeli news, a z pozostałych wyciągnąłem tylko te niezbędne mi do działania skryptu, czyli login usera i nazwę kategorii. Ty akurat możesz chcieć coś więcej z tych dwóch ostatnich.
rodipl
Nie głupie. smile.gif

Chociaż równie dobrze można by było zmienić w tabeli id na idcostam i byłby już spokój. ^^

Dzięki za pomoc.
Można zamknąć.

Edit:
Mam jeszcze jedno pytanie (więc nie zamykać tongue.gif) -> czy jeżeli zrobię w 1 zapytaniu np. 6 aliasów (dzięki, którym zmianie nazwy w locie) to czy to w jakiś sposób bardzo obciąży mi samo zapytanie?
thek
Można zmienić id w nazwie tabeli, ale później grzeb sobie po plikach gdzie już tej nazwy kolumny użyłeś i zmieniaj winksmiley.jpg Sam alias to tylko zmiana nazwy kolumny, co nie jest obciążające dla bazy. W końcu nazywasz kolumnę tylko raz i potem zapominasz o tym. Ciekawostka dla Ciebie może być fakt, że aliasu możesz używać w dalszej części zapytania jakby był nazwą normalną, czyli równoznaczny z nazwa(alias)_tabeli.pole_tabeli .
Aha i zrobiłem mały błąd w ostatnim poście. Ma być ORDER BY n.id, a nie samo id, by uniknąć problemów. Zresztą zaraz post wyedytuję winksmiley.jpg

EDIT: Jeszcze jedno... left join zawsze wyświetli wyniki, nawet jeśli w dołączanej tabeli nie znajdzie niczego po połączeniu. Na przykład news był napisany przez usera, którego usunąłeś z bazy lub z usuniętej kategorii i tym samym takie id w drugiej tabeli już nie istnieją. Wtedy pola w brakujących kolumnach uzupełni wartościami NULL. W takim przypadku po odebraniu danych sprawdź owe pola i jeśli napotkasz wartości puste przypisz podczas wyświetlania wyniku na stronie "Użytkownik usunięty" lub "Kategoria została usunięta".

W przypadku Smarty możesz to zrobić albo poprzez sprawdzenie danych zaraz po odebraniu z bazy lub dopiero w template, piszac dodatkowy warunek IF smile.gif
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.