Tietokantani rakenne:
T H E I N T E R S E C T kayttajat ystavapyynnot postaukset -id -lahettajan_id -lahettajan_id -nimi -vastaanottajan_id -postaus -sahkoposti -status -lahettajan_nimi -salasana
Kayttajat id nimi sahkoposti salasana 1 Erkki erkki@outlook.com 12345 2 Kaveri kaveri@outlook.com 123456 3 Trolli spammi@spammi.com 123123 ystavapyynnot lahettajan_id vastaanottajan_id status 1 2 OK 2 1 OK postaukset lahettajan_id postaus A I K A 1 Tulostui oikein 10.10.2012 19:50:00 2 VÄÄRIN TULOSTUI 11.10.2012 20:00:00 1 Tulostui oikein 9.10.2012 19:50:00 3 Tämä EI saa 10.10.2012 00:00:00 tulostua
PHP-KOODI
// YHDISTÄ TIETOKANTAAN mysql_connect("localhost", "root", "XXXXX")or die("Yhdistäminen tietokantaan epäonnistui: Voisitko ilmoittaa siitä meille?"); mysql_select_db("TheIntersect")or die("Ei voida valita tietokantaa: Voisitko ilmoittaa siitä meille?"); $omaid = $_SESSION['id']; $hae = mysql_query("SELECT * FROM ystavapyynnot WHERE status='ok' AND lahettajan_id='$omaid'")OR DIE(mysql_error()); while($row = mysql_fetch_array($hae)) { $kaverit1 = $row['vastaanottajan_id']; //Haetaan postaukset $haepostaukset = mysql_query("SELECT * FROM postaukset WHERE lahettaja='$kaverit1'") OR DIE(mysql_error()); while($row = mysql_fetch_array($haepostaukset)) { $postaus = $row['viesti']; $lahettaja = $row['lahettaja']; $lahettajan_nimi = $row['nimi']; $postauksen_id = $row['id']; ?> <!--POSTAUS JA NIIDEN SISÄLTÖ --> <div class="lahettajan_nimi"> <a class="lahettajan_nimi" href="profile.php?id=<?php print $lahettaja; ?>"><?php print htmlspecialchars($lahettajan_nimi);?></a> </div> <div class="postauksen_sisalto"><?php print $postaus; ?> </div> <?php // WHILET LOPPUU } } ?>
Tulostus:
POSTAUKSET Erkki 10.10.2012 19:50 Tämä tulostuu oikein Erkki 9.10.2012 19:50 Tämäkin tulostuu oikein Kaveri 11.10.2012 20:00 TÄMÄ TULOSTUU VÄÄRÄÄN PAIKKAAN, TÄMÄN PITÄISI OLLA YLIMPÄNÄ KOSKA TÄMÄ ON LÄHETETTY VIIMEKSI
Olisko neuvoa miten saisin postaukset oikeaan järjestykseen?
EDIT:
Eli koodin tarkoitus on aluksi katsoa ketkä ovat henkilön Erkki kavereita.
Sen jälkeen koodi hakee postaukset tietokannasta ja näyttää Erkin kavereiden postaukset.
Muiden kuin kaverien postauksia ei saa näyttää.
Kannattaisi mieluummin käyttää Pdota.
Sillä koodista tulee tietoturvallisempaa ja omasta mielestäni myös selkeämpää.
Esimerkiksi:
Miksi olet tehnyt noita omia päivämäärä- ja aikakenttiä? kyllä kannan oma tekniikka osaa järjestäjää rivit oikein, kunhan et tee noita omia juttuja.
Eli ota aikapaiva, aikatunnit ja aikasekunnit pois ja tilalle aika jonka tyyppi on datetime tai timestamp. Sitten vain order by aika desc
Koodissa nyt käytössä PDO.
<?php try { $yhteys = new PDO("mysql:host=localhost;dbname=theintersect", "root", "XXXX"); } catch (PDOException $e) { die("VIRHE: " . $e->getMessage()); } // YHTEYS 1 $yhteys->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION); $yhteys->exec("SET NAMES latin1"); $kysely = $yhteys->prepare("SELECT * FROM postaukset, ystavapyynnot"); $kysely->execute(); while ($row = $kysely->fetch()) { ?> <a href="profile.php?id=<?php print $row["lahettaja"]; ?>"> <?php print htmlspecialchars($row["nimi"]).$row["aika"];?> </a> <div class="postauksen_sisalto"> <?php print $row["viesti"]; ?> </div> <br /> <? } ?>
Tulostus:
Erkki 10.10.2012 19:50 Tämä tulostuu oikein Erkki 10.10.2012 19:50 Tämä tulostuu oikein Erkki 9.10.2012 19:50 Tämäkin tulostuu oikein Erkki 9.10.2012 19:50 Tämäkin tulostuu oikein Kaveri 11.10.2012 20:00 TÄMÄ TULOSTUU VÄÄRÄÄN PAIKKAAN, TÄMÄN PITÄISI OLLA YLIMPÄNÄ KOSKA TÄMÄ ON LÄHETETTY VIIMEKSI Kaveri 11.10.2012 20:00 TÄMÄ TULOSTUU VÄÄRÄÄN PAIKKAAN, TÄMÄN PITÄISI OLLA YLIMPÄNÄ KOSKA TÄMÄ ON LÄHETETTY VIIMEKSI
Kaikki postaukset tulostuvat kahteen kertaan?
Onko ORDER BY aika DESC käytössä? Pitäisi tulla ainakin oikein päin viestit. Tuohon tuplaantumiseen en oikein osaa sanoa mitään, sama on tullut itselläkin vastaan, mutta en muista miten korjasin ongelman.
Tuplaantumisen syy on, että kyselyssäsi on mukana kaksi taulua, joista toisessa on kaksi riviä, vaikka kyselyssä pitäisi olla vain yksi taulu tai sopiva liitosehto tai UNION. Väärän järjestyksen syy on, että et pyydä tuloksia missään tietyssä järjestyksessä. Järjestäminen ei myöskään onnistu, jos tiedot ovat tietokannassa väärässä muodossa; päivämäärän ja ajan oikea muoto on DATETIME tai TIMESTAMP.
Koodin pitäisi tulostaa henkilön "Erkki" kavereiden postaukset aikajärjestyksessä.
$kysely = $yhteys->prepare("SELECT * FROM postaukset, ystavapyynnot");
Jos on tällainen kysely, voiko siihen lisätä ORDER BY:ta?
Itsellä ainakin tuli virheilmoituksia jos lisäsin ORDER BY:n
http://www.w3schools.com/sql/sql_orderby.asp
Esimerkki: Jos jaksat/ehdit lukea rauhassa läpi.
latenleffahylly kirjoitti:
http://www.w3schools.com/sql/sql_orderby.asp
Esimerkki: Jos jaksat/ehdit lukea rauhassa läpi.
Olen käynyt nuo W3Schoolsin oppaat läpi aikaisemminkin.
Sain ORDER BY:n nyt toimimaan, mutta tulostaa edelleen kaikki postaukset 2 kertaa
Kaveri 11.10.2012 20:00 VIESTI TULOSTUU KAHTEEN KERTAAN? Kaveri 11.10.2012 20:00 VIESTI TULOSTUU KAHTEEN KERTAAN? Erkki 10.10.2012 19:50 MIKSI? Erkki 10.10.2012 19:50 MIKSI? Erkki 9.10.2012 19:50 MITEN SAAN KORJATTUA Erkki 9.10.2012 19:50 MITEN SAAN KORJATTUA
http://www.w3schools.com/sql/sql_join.asp
http://www.lpt.fi/it/opetus/tietokannat/
http://appro.mit.jyu.fi/doc/tiedonhallinta/sql/
Nyt sain toimimaan lisäämällä if-lauseen ennen tulostusta:
if($row["lahettajan_id"] == $_SESSION["id"] && $row["vastaanottajan_id"] == $row["lahettaja"]){ //TULOSTUS }
Jonttu kirjoitti:
Sain ORDER BY:n nyt toimimaan, mutta tulostaa edelleen kaikki postaukset 2 kertaa
Kerroin jo syyn. Oletko lukutaidoton vai etkö vain viitsi lukea vastauksia, vai oletko jotenkin todennut väitteeni vääräksi?
Metabolix kirjoitti:
Jonttu kirjoitti:
Sain ORDER BY:n nyt toimimaan, mutta tulostaa edelleen kaikki postaukset 2 kertaa
Kerroin jo syyn. Oletko lukutaidoton vai etkö vain viitsi lukea vastauksia, vai oletko jotenkin todennut väitteeni vääräksi?
Anteeksi tyhmyyteni (En ollut huomannut ylempää kommenttiasi)
Mutta nyt sain pelittämään koko systeemin, postaukset tulevat niinkuin pitääkin ja oikeassa järjestyksessä:
<?php try { $yhteys = new PDO("mysql:host=localhost;dbname=theintersect", "root", "XXXXX"); } catch (PDOException $e) { die("VIRHE: " . $e->getMessage()); } // YHTEYS PDO $yhteys->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION); $yhteys->exec("SET NAMES latin1"); $kysely = $yhteys->prepare("SELECT * FROM ystavapyynnot, postaukset ORDER BY aika DESC"); $kysely->execute(); while ($row = $kysely->fetch()) { if($row["lahettajan_id"] == $_SESSION["id"] && $row["vastaanottajan_id"] == $row["lahettaja"]){ ?> <a href="profile.php?id=<?php print $row["lahettaja"]; ?>"> <?php print htmlspecialchars($row["nimi"]).$row["aika"];?> </a> <div class="postauksen_sisalto"> <?php print $row["viesti"]; ?> </div> <br /> <? } ?>
Tulostus:
Kaveri 11.10.2012 20:00 Kaikki tulostuvat nyt oikein oikeassa järjestyksessä ja rivit eivät enää tuplaannu. :) Erkki 10.10.2012 19:50 Tämä tulostuu oikein Erkki 9.10.2012 19:50 Tämäkin tulostuu oikein
Sun koodisi on kyllä ihan pielessä. Haet kaiken datan postaukset- ja ystavapyynnot-tauluista? Tietokantarakenteestasi ei myöskään käy selville, että miten taulut ystavapyynnot ja postaukset liittyvät toisiinsa. Valaisisitko vähän?
Jos tarkoitus on näyttää käyttäjälle järjestelmässä lähetettyjä ystäväpyyntöjä, niin olisiko järkevämpää säilöä samaan tauluun lähettän id, vastaanottajan id ja viestin sisältö? Silloin data voitaisiin hakea tälläisellä kyselyllä
SELECT lahettaja_id, aika, viesti FROM ystavapyynnot WHERE vastaanottaja_id = 123 ORDER BY aika DESC
Huomaathan, että kun aika-sarakkeesi ei ole timestamp- eikä datetime-muodossa, niin ORDER BY järjestää ne väärin, jos se on data on vaikka muodossa dd.mm.yyyy HH:mm.
Jos pistäisit ajat tietokannassa esim. datetime-muodossa (sarakkeen tyypiksi datetime, lisäyshetkellä NOW() antaa oikean arvon sarakkeelle), niin järjestyskin pysyisi oikeana. Myös jälkikäsittely helpottuu (mitä jos haluat päivämäärät toiseen muotoon?).
Macro kirjoitti:
Sun koodisi on kyllä ihan pielessä. Haet kaiken datan postaukset- ja ystavapyynnot-tauluista? Tietokantarakenteestasi ei myöskään käy selville, että miten taulut ystavapyynnot ja postaukset liittyvät toisiinsa. Valaisisitko vähän?
Eli koodi hakee henkilön A kaverit "ystavapyynnot"-taulusta ja viestit "postaukset"-taulusta.
Koodi tarkistaa onko postauksen lähettäjä kaveri, jos on koodi tulostaa postauksen. Jos ei ole kaveri koodi ei tulosta mitään.
Muiden kuin kaverien postaukset eivät näy A:lle
Pitäiskö sillon mahdollisesti käyttäjiä estää lähettämästä viestejä, ettei tietokanta täyttyisi spämmiviesteistä?
Eikö kavereille olisi loogisempaa luoda oma taulu kaverit
?
Jonttu kirjoitti:
Eli koodi hakee henkilön A kaverit "ystavapyynnot"-taulusta ja viestit "postaukset"-taulusta.
Koodi tarkistaa onko postauksen lähettäjä kaveri, jos on koodi tulostaa postauksen. Jos ei ole kaveri koodi ei tulosta mitään.
Muiden kuin kaverien postaukset eivät näy A:lle
KYSELY hakee "ystavapyynnot"-taulusta ja "postaukset"-taulusta yhteensä:
ystavapyynnot( kaikki rivit ) * postaukset ( kaikki rivit ). Onkohan jotain pielessä?
Koodissa yrität sitten korjata virheellistä kyselyä. Ei näin.
Muuta taulujen rakennetta Macro:n esittämällä tavalla tai lue antamani linkit.
Edit ennen lähetystä: Jos käyttäjä voi lähettää yleisiä postauksia kaikille kavereille, ei Macron rakenne oikein toimi.
Opettele nyt vaan se liitosten käyttö.
Tässähän ilmeisesti "korjaukseksi" keksityn if-lauseen sisältö kuuluisi SQL-kyselyn WHERE-osaan.
$kysely = $yhteys->prepare("SELECT postaukset.lahettajan_nimi,postaukset.aika from ystavapyynnot,postaukset WHERE ( ystavapyynnot.lahettajan_id = postaukset.lahettajan_id OR ystavapyynnot.vastaanottajan_id = postaukset.lahettajan_id ) AND ystavapyynnot.status = 'OK' AND postaukset.lahettajan_id != ? AND ( ystavapyynnot.lahettajan_id = ? OR ystavapyynnot.vastaanottajan_id = ? ) ORDER BY aika DESC"); $user = (int) $_SESSION["id"]; $kysely->bindParam(1, $user, PDO::PARAM_INT ); $kysely->bindParam(2, $user, PDO::PARAM_INT ); $kysely->bindParam(3, $user, PDO::PARAM_INT ); $kysely->execute();
Aika ruma, mutta ehkä sinne päin.
Se on ruma, koska et kirjoita siistiä koodia. Toisekseen se on ruma myös, koska tietokanta-arkkitehtuuri ei ole optimaalinen.
$sql = ' SELECT b.lahettajan_nimi, b.aika FROM kaverit a INNER JOIN postaukset b ON a.kaveri_id = b.lahettajan_id WHERE a.kayttaja_id = ? ORDER BY b.aika DESC '; $user = $_SESSION['user_id']; $smt = $pdo->prepare($sql); $smt->execute(array($user));
Jokseenkin parempi, tosin oikeaoppisesta sisentämisestä voi aina kiistellä.
Teet siis uuden taulun ("kaverit"), johon lisäät hyväksyttyjen kaveripyyntöjen pohjalta syntyneet kaverisuhteet. Jokaisesta kaverisuhteesta tulee kaksi riviä, mutta nähdäkseni se on parempi niin, kuin ruveta arpomaan joinissa, että minkä sarakkeen suhteen rivit yhdistetään.
En itse käyttäisi bindParam()-kutsua kuin silloin, kun arvon tyypillä on oikeasti jotain väliä (esimerkiksi LIMIT-ehdossa). Turhaa toistoa ja sotkua.
P.S. Yritin joskus googlettaa SQL-kyselyiden sisentämisen "best practises" -ohjeita, mutta hyvin usein ihmiset perustelivat omaa valintaansa koodin lukemisen helppoudella, mutta samalla tekivät kyselyiden kirjoittamisesta vaikeampaa, mikä tosin riippunee myös editorin ominaisuuksista. Oma sisennystyylini sallii hyödyntää editorin automaattista sisennystä samalla kuitenkin pitäen kyselyt siisteinä ja luettavina.
Aihe on jo aika vanha, joten et voi enää vastata siihen.