22. december 2002 - 19:28Der er
36 kommentarer og 1 løsning
søgning i database
Hej
Jeg har en søge formular med navn, adresse, email osv. Det problem jeg er løbet ind i er at hvis man søger på kurt som navn og kurt@kurt.dk som email vil jeg have at der kun kommer den kurt frem med den email kurt@kurt.dk, det er jo øjensynligt fordi at jeg bruger "OR" i min sql sætning, men hvordan skal jeg finde ud af den eksakte sql sætning hvis f.eks at der kun er tastet noget i navn skal den kun søge på navn og ikke tage alt i database fordi at de andre felter er tomme.
I dette særtema ser vi på, hvordan cloud og AI bliver fundamentet for virksomhedernes digitale forretning, og hvordan de nye muligheder for automatisering og forretningsværdi kan udnyttes uden at miste overblik, sikkerhed og menneskelig kontrol.
<?php include "connect.php"; if (isset($submit)) { $result = mysql_query(" SELECT * FROM tlfbruger WHERE navn LIKE '%$navn%' OR bruger LIKE '%$bruger%' OR email LIKE '%$email%' OR fodselsdato LIKE '%$fdato%' "); while ($row = mysql_fetch_array($result)) { echo "$row[navn]<br>"; echo "$row[bruger]<br>"; echo "$row[email]<br>"; echo "$row[fodselsdato]<p>"; } } ?>
dsj>>> Det vil ingen forskel gøre i et sådant tilfælde! ... en if-sætning kræver intet og arbejder ligeså hurtigt, som hvis du lægger arbejdet over på databasen...
Det er princippet når man laver systemer, at man ALTID skal lægge så meget arbejde som muligt over til databasen. I det lange løb betyder det noget, og da php fortolkes HVER gang det køres er det ikke just hurtigt. Rigtigt nok en eller tre if-sætninger kan ikke mærkes, men kan man sit kram, gør man det rigtigt, hver gang.
Følgende laver en strømlinet sql-sætning, som vil være langt mere effektiv end at have nyttesløse betingelser på de felter, hvor der ikke er indtastet noget!
<?php include "connect.php"; if (isset($submit)) { $tmparray = array; if(!empty($navn) { $tmparray[] = "navn LIKE '%$navn%'"; } if(!empty($bruger) { $tmparray[] = "bruger LIKE '%$bruger%'"; } if(!empty($email) { $tmparray[] = "email LIKE '%$email%'"; } if(!empty($fdato) { $tmparray[] = "fodselsdato LIKE '%$fdato%'"; } $sql = "SELECT navn, bruger, email, fodselsdato FROM tlfbruger WHERE "; $sql .= implode(" AND ", $tmparray); //echo $sql; -se hvordan sql'en ser ud! $result = mysql_query($sql) or die(mysql_error()); while (list($navn, $bruger, $email, $fodselsdato) = mysql_fetch_row($result)) { echo "$navn<br>$bruger<br>$email<br>$fodselsdato<br>"; }
Sålænge det er små databaser uden mange poster vil man ikke kunne mærke nævneværdig forskel på dårlig og god sql-kode, men LIKE kommandoen er meget tung at arbejde med, specielt med jokertegn både foran og bagved søgeordet, da diverse index så ikke kan udnyttes! Så derfor bør man undgå unødvendige LIKE kommandoer!
Ok, hvis I ikke tror på at jeg har ret, synes jeg I skulle lave et lille eksperiment:
I har en tabel med 1000 personer (navn, adresse, email, postnr) og en tabel med alle bynavne (postnr, by)
hvor postnr i bynavne er fremmednøgle i persontabellen. Så laver man en sql-sætning der hiver alle kolonner ud for alle personer (navn, adresse, email, postnr, by) altså inkl. bynavnet.
Så laver I det hele i php, hvor først kolonnerne fra person-tabellen (navn, adresse, email, postnr) altså uden bynavnet. Derefter hentes for HVER person med en ny sql-sætning bynavnet der svarer til personens postnr. Det vil altså resultere i 1001 sql-sætninger.
I vil nok finde ud af at det sidste er mindst 10 gange så lang tid om at køre end det første eksempel.
Man vil ikke kunne erstatte en LIKE-sætning med noget kode der gør det bedre. Databasen vil altid gøre det hurtigere.
Hvis man altid tænker på at det ikke betyder andet end at par hundrede millisekunder til forskel, vil man jo øjensynligt ende ud med noget langsomt stads. Hvor ved du fra at der i moreless' tabeller kun er få entiteter, hvor ved du fra at det ikke vil vokse i fremtiden. Som systemudvikler vil jeg sige at man lige så godt kan gøre det rigtigt første gang ... især når det som her også er langt det nemmeste.
tipsen du har ikke ret med dine if-sætninger. Faktum er at det er langsommere og professionelle vil mene at det er langt "grimmere" når nu det kan gøres som en enkelt sql-sætning.
dsj: Vi er totalt uenige og jeg tror du misforstår det jeg skriver med små og store db'er! Jeg mener at din sql-sætning som involverer unødvendige like-betingelser er langsommere end min optimerede udgave - det vil man dog ikke kunne mærke, hvis man kører med små tabeller, men min udgave vil give mærkbart bedre resultater ved større tabeller!
Jeg ved så heller ikke hvorfor du snakker om mange sql-sætninger - det tyder på du ikke har forstået min kode! Jeg laver én sql-sætning, men medtager kun de nødvendige betingelser - det er dét if-betingelserne bruges til! Der er ikke tale om flere sql-sætninger, men derimod én optimeret sætning!
jeg kan godt se det fra begge sider, har man en fortrinsvis lille tabel er der ikke noget i vejen for at lave LIKE på alle tabellerne selvom at de ikke bliver brugt, men vil man lave koden så optimal som muligt og så hurtig som mulig må man naturligvis bruge if sætninger til at sortere hvilke tabeller der skal søges i.
moreless: Nemlig! LIKE er i forvejen meget langsom - specielt når der er % både i starten og slutningen af søgestrengen - så at have nogle betingelser som lyder AND feltnavn3 LIKE '%%' AND feltnavn4 LIKE '%%' er total spild af tid!
NB: Vær opmærksom på, at der kun arbejdes med en tabel, men på flere felter/kolonner!
NB: Jeg ved ikke om MySQL er klog nok til at ignorere en betingelse i stil med AND LIKE '%%' men derfor behøver jo ikke lave mere idiotisk kode end højst nødvendig ;-)
jeg ved ikke om det står der, men når jeg bruger denne kode
$result = mysql_query(" SELECT * FROM tlfbruger WHERE navn LIKE '%$navn%' AND bruger LIKE '%$bruger%' AND email LIKE '%$email%' AND fodselsdato LIKE '%$fdato%' ");
og jeg submitter en tom $navn variabel burde den jo finde alt eller intet i databasen men den ignorere det bare og går videre til næste søge kriterie
nå.. nej-nej - betingelsen er bare unødvendig - se her:
WHERE navn LIKE '%$navn%' <-- sorterer de rækker fra som ikke har $navn i navn AND bruger LIKE '%$bruger%' <-- sorterer yderligere de rækker fra som ikke har $bruger i bruger AND email LIKE '%$email%' <-- sorterer yderligere de rækker fra som ikke har $email i email AND fodselsdato LIKE '%$fdato%' <-- sorterer yderligere de rækker fra som ikke har $fdato i fodselsdato
Dvs. der er hele tiden tale om at vi mindsker antallet af poster i resultatsættet - dvs. en betingelse som ...AND LIKE '%%' vil pille alle resterende poster ud af det nuværende resultatsæt - dvs. den mindsker ikke antallet af poster yderligere, da alle poster oplagt opfylder betingelsen som dermed er nyttesløs! (desværre bruger den bare tid...)
Jeg skal lige hilse at sige, at den tid der bliver brugt er umenskeligt lille. Databaser er idag monster-hurtige til at lave søgninger... sjovt nok er de jo optimerede til det. MySQL har jeg set i test være næsten lige så hurtig som Oracle, langt over DB2 og MS SQL Server.
Den tid der vil gå med at gennemløbe nogle if-sætninger og sammenlægninger af String's, vil stadig veje langt mest. At lægge String's sammen er iøvrigt en af de tunge operationer at foretage; det har noget med hvad der sker i RAM at gøre...
Så den meget langsomme ram er måske ikke så tåbelig igen... - min metode ER hurtigere, men forskellen er også minimal - jeg havde selv regnet med at den var større!
Det er forøvrigt sjovt at du først taler for at optimere kode og derefter siger du at det da også kan være lige meget fordi databaserne alligevel er så "monster-hurtige"... - ikke konsistente udsagn!
Og jeg går selvfølgelig ikke udfra du bare tager ovenstående som gode varer, så her er mit testprogram:
//sætter 2 variable, så der er lidt at tage af: $ip = '.2'; $id = '12';
$connection = mysql_connect($hostname, $username, $password) or die ("Unable to connect to $hostname!"); mysql_select_db($database);
//tidsfunktion fra php.net: function getmicrotime(){ list($usec, $sec) = explode(" ",microtime()); return ((float)$usec + (float)$sec); }
//kør 20 gange for konsistens: for($i=0;$i<20;$i++) { //her starter dsj's metode: $time_start2 = getmicrotime(); $query2 = "SELECT * from stats WHERE id LIKE '%$id%' AND browser LIKE '%$var3%' AND ip LIKE '%$ip%' AND referer LIKE '%$var4%' AND received LIKE '%$var5%'"; $result2 = mysql_query($query2, $connection) or die ("Error in query: $query2." . mysql_error()); $time2 = getmicrotime() - $time_start2; mysql_free_result($result2);
//her starter tipsen's metode: $time_start1 = getmicrotime(); $tmparray = array(); if(!empty($ip)) { $tmparray[] = "ip LIKE '%$ip%'"; } if(!empty($id)) { $tmparray[] = "id LIKE '%$id%'"; } if(!empty($var2)) { $tmparray[] = "var2 LIKE '%$var3%'"; } if(!empty($var3)) { $tmparray[] = "var3 LIKE '%$var4%'"; } if(!empty($var4)) { $tmparray[] = "var4 LIKE '%$var5%'"; } $sql1 = "SELECT * from stats WHERE "; $sql1 .= implode(" AND ", $tmparray); $result1 = mysql_query($sql1, $connection) or die ("Error in query: $sql1." . mysql_error()); $time1 = getmicrotime() - $time_start1; mysql_free_result($result1);
Og forøvrigt så har MySQL også slået Oracle i forskellige tests, men resultaterne afhænger meget af hvordan testen bliver stillet op - det er nemt at farve resultaterne!
Så moralen må være at selv en professionel (som du vist betragter dig selv som) også kan tage fejl. Vi er helt enige om, at det er vigtigt at gøre det rigtigt første gang - vi var så bare ikke enige om, hvad der var egentlig var rigtigt - jeg giver lidt mere arbejde til php og mindre arbejde til mysql og det var hurtigere i dette tilfælde, men forskellen er så lille, at det i praksis kan være ligegyldigt.
NB: Sålænge de strenge man bikser rundt med ikke er større end tilfældet er her, tager det ikke synderligt lang tid, men ellers er vi enige om at det kan være en tung operation, pga. alle de kopier og flytninger der laves.
Det jeg mente var, at funktionen jo skal placeres et sted og da databaser er optimerede til formålet mente jeg at det ville være smartest at placere funktionen her.
Jeg har lavet en lignende test i Java, som viser noget lidt andet. Dog viser den at det faktisk kan være ligemeget, forskellen er forsvindende lille, i hvert fald med denne lille tabel... Hvis du meget gerne vil se koden må du sige til, ellers vil jeg ikke poste den, men den ligner din ret meget.
Tabellen har 3000 poster og hvert tal er et gennemsnit af 3000 forsøg.
dsj 16.865 ms 16.874 ms 16.869 ms tipsen 17.042 ms 16.905 ms 17.029 ms
Min konklusion må være, at jeg stadig vil gøre det på min måde, men kun grundet mindre kode :)
Jeg vil så samtidig lave noget validering af de inputs der kommer fra brugeren for at undgå "uheld" og så vil den ekstra kode ikke være bemærkelsesværdig længere - men jeg skal ikke udelukke, at jeg kunne finde på at bruge begge metoder i fremtiden, så længe der ikke er nævneværdig forskel i tidsforbruget.
Synes godt om
Ny brugerNybegynder
Din løsning...
Tilladte BB-code-tags: [b]fed[/b] [i]kursiv[/i] [u]understreget[/u] Web- og emailadresser omdannes automatisk til links. Der sættes "nofollow" på alle links.