|
| INSERT eller UPDATE (MySQL) Fra : Niels Andersen |
Dato : 21-02-03 16:38 |
|
I en tabel vil jeg gerne lave en optælling. Altså noget med "UPDATE tabel
SET count=count+1 WHERE id='7845gh3g7834'". Problemet er, at jeg ikke har
en komplet liste over id'er. Dvs. hvis id'en ikke findes i tabellen i
forvejen, skal den oprettes: "INSERT INTO tabel SET id='7845gh3g7834',
count=1"
Som udgangspunkt ved jeg ikke hvilke ID'er der er i tabellen. Den bedste
løsning jeg kender er at fyre INSERT eller UPDATE af. Giver det fejl (eller
det ikke ændrer nogen rækker i tabellen), så skal den anden fyres af.
Findes der en bedre løsning? Kan den bruges i MySQL?
--
Mvh.
Niels Andersen
| |
Mads Lie Jensen (21-02-2003)
| Kommentar Fra : Mads Lie Jensen |
Dato : 21-02-03 19:49 |
|
On Fri, 21 Feb 2003 16:37:39 +0100, Niels Andersen
<niels-usenet@myplace.dk> wrote:
>I en tabel vil jeg gerne lave en optælling. Altså noget med "UPDATE tabel
>SET count=count+1 WHERE id='7845gh3g7834'". Problemet er, at jeg ikke har
>en komplet liste over id'er. Dvs. hvis id'en ikke findes i tabellen i
>forvejen, skal den oprettes: "INSERT INTO tabel SET id='7845gh3g7834',
>count=1"
>
>Findes der en bedre løsning? Kan den bruges i MySQL?
INSERT ... ON DUPLICATE KEY UPDATE ...
http://www.mysql.com/doc/en/INSERT.html
--
Mads Lie Jensen - mads@gartneriet.dk - ICQ #25478403
http://www.gartneriet.dk
Lær af andres fejl
- du lever alligevel ikke længe nok til selv at prøve dem alle.
| |
Mads Lie Jensen (21-02-2003)
| Kommentar Fra : Mads Lie Jensen |
Dato : 21-02-03 19:50 |
|
On Fri, 21 Feb 2003 19:48:37 +0100, Mads Lie Jensen <mads@gartneriet.dk>
wrote:
>INSERT ... ON DUPLICATE KEY UPDATE ...
>
> http://www.mysql.com/doc/en/INSERT.html
Hmm.. jeg skulle nok lige have nærlæst det link - det virker først fra
mySQL 4.1.0
Beklager.
--
Mads Lie Jensen - mads@gartneriet.dk - ICQ #25478403
http://www.gartneriet.dk
Lær af andres fejl
- du lever alligevel ikke længe nok til selv at prøve dem alle.
| |
Niels Andersen (21-02-2003)
| Kommentar Fra : Niels Andersen |
Dato : 21-02-03 21:09 |
|
Mads Lie Jensen wrote in <96tc5v0n3l2jmqlpvf971ji22btu3rfvqr@4ax.com>:
>>INSERT ... ON DUPLICATE KEY UPDATE ...
>> http://www.mysql.com/doc/en/INSERT.html
> Hmm.. jeg skulle nok lige have nærlæst det link - det virker først fra
> mySQL 4.1.0
Nårja, den havde jeg glemt igen. Den glæder jeg mig til at kunne benytte
engang i fremtiden.
Bare for at være nysgerrig: Er det en SQL-ting, eller er det kun en
MySQL-ting? Altså er det noget man kan forvente at kunne benytte på andre
dbmanagers?
--
Mvh.
Niels Andersen
| |
Jimmy (21-02-2003)
| Kommentar Fra : Jimmy |
Dato : 21-02-03 21:50 |
|
"Niels Andersen" <niels-usenet@myplace.dk> wrote in message
news:2810074.9M8rKgThd6@home.myplace.dk...
> Mads Lie Jensen wrote in <96tc5v0n3l2jmqlpvf971ji22btu3rfvqr@4ax.com>:
> >>INSERT ... ON DUPLICATE KEY UPDATE ...
> >> http://www.mysql.com/doc/en/INSERT.html
> > Hmm.. jeg skulle nok lige have nærlæst det link - det virker først fra
> > mySQL 4.1.0
>
> Nårja, den havde jeg glemt igen. Den glæder jeg mig til at kunne benytte
> engang i fremtiden.
>
> Bare for at være nysgerrig: Er det en SQL-ting, eller er det kun en
> MySQL-ting? Altså er det noget man kan forvente at kunne benytte på andre
> dbmanagers?
Det er en MySQL tilføjelse til ANSI SQL.
Du skal ikke regne med at kunne bruge syntaksen andetsteds, men mon ikke
MSSQl har en lignende egenskab.
Jeg ville løse problemet ved at lave en SELECT først og derefter enten
INSERTe eller UPDATE'e.
Jeg ville *bestemt* ikke forsøge at lave en INSERT og afvente en fejl!
Ofte kan man dog redisegne databasen eller konceptet og derved undgå, at
skulle igennem ovenstående.
Hvad skal du præcis opnå?
mvh
Jimmy
| |
Niels Andersen (22-02-2003)
| Kommentar Fra : Niels Andersen |
Dato : 22-02-03 10:48 |
|
Jimmy wrote in <wjw5a.5688$VK.4691@news.get2net.dk>:
>> >>INSERT ... ON DUPLICATE KEY UPDATE ...
> Det er en MySQL tilføjelse til ANSI SQL.
OK, tak. :)
> Jeg ville løse problemet ved at lave en SELECT først og derefter enten
> INSERTe eller UPDATE'e.
Så vil du *altid* lave to forespørgsler. Ved at køre INSERT eller UPDATE
først, vil du i nogle tilfælle kunne nøjes med én forespørgsel.
Desuden er jeg lidt bange for, at en række kan blive oprettet mellem en
SELECT (som viser at rækken ikke findes) og INSERT.
Hmm... Den kunne vel løses ved at køre UPDATE hvis INSERT fejler.
> Jeg ville *bestemt* ikke forsøge at lave en INSERT og afvente en fejl!
En INSERT der fejler er altså langt værre end altid at køre SELECT først?
Det er primært performance jeg tænker på, men godt design er jo altid en god
ting. :)
> Ofte kan man dog redisegne databasen eller konceptet og derved undgå, at
> skulle igennem ovenstående.
> Hvad skal du præcis opnå?
Tabellen det drejer sig om, har følgende felter:
brugerid int
dato date
string varchar
count int
De første 3 felter er nøglen.
"string" kan være hvad som helst. Det er bare noget som giver mening, for
den enkelte bruger. Den bruges til at "gruppere" med.
Det er ikke hensigtsmæssigt at skulle oprette grupperne manuelt, en bruger
kan have hundredevis af grupper (og få flere nye hver dag), hvoraf de
fleste måske ikke engang bliver brugt hver dag.
Så jeg skal altså tælle hvor mange gange en "event" forekommer pr. bruger,
pr. dag, pr. gruppe.
Alternativet jeg har overvejet er, at droppe "count", og i stedet lave en
række for hver event. (Evt. lave "dato" om til et timestamp)
Tabellen som den ser ud nu er bygget op over det data jeg vil have ud. Jeg
skal nemlig kun bruge en optælling på hvor mange events der er sket om
dagen, og i hvilke "grupper".
Det jeg bla. er bekymret for, er store mængder overflødig data. Man kunne
måske lave to tabeller (én med en række for hver event, og en anden som
beskrevet ovenfor), og så jævnligt (fx. hver nat) flytte data fra den ene
tabel til den anden. Ville det mon være smart?
--
Mvh.
Niels Andersen
| |
Jimmy (22-02-2003)
| Kommentar Fra : Jimmy |
Dato : 22-02-03 18:56 |
|
"Niels Andersen" <niels-usenet@myplace.dk> wrote in message
news:3676012.MO57GBVRPH@home.myplace.dk...
> Jimmy wrote in <wjw5a.5688$VK.4691@news.get2net.dk>:
> >> >>INSERT ... ON DUPLICATE KEY UPDATE ...
> > Det er en MySQL tilføjelse til ANSI SQL.
>
> OK, tak. :)
>
> > Jeg ville løse problemet ved at lave en SELECT først og derefter enten
> > INSERTe eller UPDATE'e.
>
> Så vil du *altid* lave to forespørgsler.
Ja - Dit setup gør jo, at det er nødvendigt.
Jeg ville aldrig ende i en sådan situation, da jeg ville ændre mit setup
først.
> Ved at køre INSERT eller UPDATE
> først, vil du i nogle tilfælle kunne nøjes med én forespørgsel.
Jep.
> Desuden er jeg lidt bange for, at en række kan blive oprettet mellem en
> SELECT (som viser at rækken ikke findes) og INSERT.
> Hmm... Den kunne vel løses ved at køre UPDATE hvis INSERT fejler.
Rækken kan da oprettes ligemeget hvilket af ovenstående scenarie du vælger.
Du kan løse det ved at lave feltet UNIQUE.
> > Jeg ville *bestemt* ikke forsøge at lave en INSERT og afvente en fejl!
>
> En INSERT der fejler er altså langt værre end altid at køre SELECT først?
> Det er primært performance jeg tænker på, men godt design er jo altid en
god
> ting. :)
Det er 100% sikkert elendig kodning at INSERTe og afvente en fejl.
Det er mere intuitivt korrekt at undersøge om rækken er der og derefter
handle.
Samtidig øges læsbarheden af dit script enormt i forhold til din ide.
Se eksemplerne i ASP:
Dit:
---------
On Error Resume Next
DB.Execute ("INSERT")
If Err.Number >0 Then DB.Execute ("UPDATE")
On Error Goto 0
---------
Mit:
---------
If DB.Execute ("SELECT").EOF Then
DB.Execute ("INSERT")
Else
DB.Execute ("UPDATE")
End If
---------------------------
Din er kortere, men på ingen måde elegant.
Jens' eksempel kan jeg ikke forstå.
Uanset om rækken findes eller ej vil en UPDATE ikke ændre antallet af
rækker, og det vil derfor ikke umiddelbart være muligt at se, om en række er
blevet opdateret eller UPDATE ignoreret.
Jens?
> Tabellen det drejer sig om, har følgende felter:
>
[KLIP]
Jeg må indrømme, at jeg ikke helt forstod din beskrivelse.
Hvis jeg har forstået den korrekt tror jeg, at dit databasedesign er forkert
og at du skal sørge for at gøre det relationelt.
F.eks. oprette grupper i deres egen tabel og have en ekstra tabel, som
relatere en brugers ID med de grupper han tilhører.
Mvh
Jimmy
| |
Niels Andersen (22-02-2003)
| Kommentar Fra : Niels Andersen |
Dato : 22-02-03 19:45 |
|
Jimmy wrote in <pSO5a.61$cj3.34@news.get2net.dk>:
> Det er 100% sikkert elendig kodning at INSERTe og afvente en fejl.
Jeg synes heller ikke det er pænt, derfor spørger jeg her. :)
> Jens' eksempel kan jeg ikke forstå.
> Uanset om rækken findes eller ej vil en UPDATE ikke ændre antallet af
> rækker, og det vil derfor ikke umiddelbart være muligt at se, om en række
> er blevet opdateret eller UPDATE ignoreret.
Jeg ved ikke med andre, men i MySQL kan man få at vide hvor mange rækker der
blev påvirket af en UPDATE.
>> Tabellen det drejer sig om, har følgende felter:
> Jeg må indrømme, at jeg ikke helt forstod din beskrivelse.
> Hvis jeg har forstået den korrekt tror jeg, at dit databasedesign er
> forkert og at du skal sørge for at gøre det relationelt.
> F.eks. oprette grupper i deres egen tabel og have en ekstra tabel, som
> relatere en brugers ID med de grupper han tilhører.
Grupperingen synes jeg ikke er fast nok. Det er ikke egentlige grupper, der
vil bare være meget sammenfald.
Forestil dig et system, hvor der kommer en masse tekster ind hver dag.
Hvilke, og hvad der står i dem, er ligegyldigt. Det interessante er hvilke
ord der bliver brugt, og især hvilke der bliver brugt mest.
Så kunne man oprette en ny række for hvert ord der kommer ind. Det vil
fungere, men der vil være mange ens rækker.
At oprette en helt ny række hver gang, og så lave noget i stil med "SELECT
ord, COUNT(ord) FROM tabel GROUP BY ord" synes jeg ikke virker praktisk, i
forhold til noget i stil med "SELECT ord, count FROM tabel".
Alle tællere "nulstilles" hver midnat. Det er altså meningen at man skal
kunne følge udviklingeren. Tanken var derfor at lave nøgle på både "ord" og
"dato".
Indtil videre hælder jeg vist mest mod denne ide, som jeg også beskrev i mit
sidste indlæg:
For hver event (hvert ord) oprettes der en ny række. Der bliver gemt et
timestamp i rækken.
Til midnat (eller senere...) laves der en optælling på det sidste døgns data
(ud fra timestamp), som så gemmes i en anden tabel. Data fra det sidste
døgn kan så slettes fra den første tabel.
Når der så skal laves statistik er det blot at kigge i tabel 2.
Det jeg vil undgå er at spilde diskplads og CPU-kraft på tusindvis af ens
rækker, når al den info jeg skal bruge kan være i én.
--
Mvh.
Niels Andersen
| |
Jimmy (22-02-2003)
| Kommentar Fra : Jimmy |
Dato : 22-02-03 20:06 |
|
"Niels Andersen" <niels-usenet@myplace.dk> wrote in message
news:3702061.bIQRKrZDyM@home.myplace.dk...
> Jimmy wrote in <pSO5a.61$cj3.34@news.get2net.dk>:
> > Det er 100% sikkert elendig kodning at INSERTe og afvente en fejl.
>
> Jeg synes heller ikke det er pænt, derfor spørger jeg her. :)
>
> > Jens' eksempel kan jeg ikke forstå.
> > Uanset om rækken findes eller ej vil en UPDATE ikke ændre antallet af
> > rækker, og det vil derfor ikke umiddelbart være muligt at se, om en
række
> > er blevet opdateret eller UPDATE ignoreret.
>
> Jeg ved ikke med andre, men i MySQL kan man få at vide hvor mange rækker
der
> blev påvirket af en UPDATE.
Ja, men så er du jo ude i minimum to forespørgsler og max tre.
UPDATE
SELECT
INSERT
Jeg synes fortsat mit eksempel er langt det mest elegante, dels pga
simpliciteten og det, at det ikke per definition kaster fejl af sig.
> Det jeg vil undgå er at spilde diskplads og CPU-kraft på tusindvis af ens
> rækker, når al den info jeg skal bruge kan være i én.
Jeg siger ikke din løsning ikke kan lade sig gøre og at den ingen fordele
har.
Dog stiller jeg mig tvivlende overfor, hvorvidt diskplads og CPU er noget
der er relevant i dit tilfælde.
Jeg siger blot at din løsning ikke er køn og ikke overholder reglerne for en
relationel database.
Det er en AD HOC-løsning og vil ramme dig senere, når du ønsker at
videreudbygge dit setup.
Mvh
Jimmy
| |
Niels Andersen (22-02-2003)
| Kommentar Fra : Niels Andersen |
Dato : 22-02-03 20:25 |
|
Jimmy wrote in <LTP5a.79$iD3.25@news.get2net.dk>:
>> Jeg ved ikke med andre, men i MySQL kan man få at vide hvor mange rækker
>> der blev påvirket af en UPDATE.
> Ja, men så er du jo ude i minimum to forespørgsler og max tre.
> UPDATE
> SELECT
> INSERT
Nej. :)
Først UPDATE. Så ved jeg hvor mange rækker der blev påvirket. Hvis
nødvendigt kan jeg så derefter køre INSERT.
> Jeg siger blot at din løsning ikke er køn og ikke overholder reglerne for
> en relationel database.
Hvad så med min alternative løsning?
Altså hvor jeg har to tabeller. Først opsamles det hele på din måde i én
tabel, og en gang i mellem bliver det flyttet til en anden tabel, hvor det
lagres på en mere praktisk måde.
(Og da dato er ét af nøglefelterne vil der ikke blive problemer med at der
bliver tilføjet data i den ene tabel, efter de relevante rækker er flyttet
til den anden.)
> Det er en AD HOC-løsning og vil ramme dig senere, når du ønsker at
> videreudbygge dit setup.
På den anden side vil din løsning ramme mig når projektet bliver brugt.
Før eller siden vil diskpladsen blive et problem. Altså ikke fordi nye
harddiske er så dyre, men der skal tages backup, laves statistik mv. Derfor
skal det lagres effektivt.
--
Mvh.
Niels Andersen
| |
Jimmy (22-02-2003)
| Kommentar Fra : Jimmy |
Dato : 22-02-03 20:33 |
|
"Niels Andersen" <niels-usenet@myplace.dk> wrote in message
news:6668214.EJyWuyUkKO@home.myplace.dk...
> Jimmy wrote in <LTP5a.79$iD3.25@news.get2net.dk>:
> >> Jeg ved ikke med andre, men i MySQL kan man få at vide hvor mange
rækker
> >> der blev påvirket af en UPDATE.
> > Ja, men så er du jo ude i minimum to forespørgsler og max tre.
> > UPDATE
> > SELECT
> > INSERT
>
> Nej. :)
>
> Først UPDATE. Så ved jeg hvor mange rækker der blev påvirket. Hvis
> nødvendigt kan jeg så derefter køre INSERT.
Hvordan får du denne information?
Når jeg anvender UPDATE i ASP returneres ROWS_AFFECTED ikke til mig.
> > Jeg siger blot at din løsning ikke er køn og ikke overholder reglerne
for
> > en relationel database.
>
> Hvad så med min alternative løsning?
> Altså hvor jeg har to tabeller. Først opsamles det hele på din måde i én
> tabel, og en gang i mellem bliver det flyttet til en anden tabel, hvor det
> lagres på en mere praktisk måde.
Jeg synes det lyder som en masse arbejde for at opnå ingenting.
> > Det er en AD HOC-løsning og vil ramme dig senere, når du ønsker at
> > videreudbygge dit setup.
>
> På den anden side vil din løsning ramme mig når projektet bliver brugt.
Langt fra enig.
Du vælger at anvende en relationel database men ikke at anvende den til det,
som den er designet til.
> Før eller siden vil diskpladsen blive et problem. Altså ikke fordi nye
> harddiske er så dyre, men der skal tages backup, laves statistik mv.
Derfor
> skal det lagres effektivt.
Det er effektivt på min måde, men efter min mening ikke på din.
Jeg tror ikke vi bliver enige.
Held og lykke,
Jimmy
| |
Niels Andersen (22-02-2003)
| Kommentar Fra : Niels Andersen |
Dato : 22-02-03 21:22 |
|
Jimmy wrote in <nhQ5a.83$qX3.77@news.get2net.dk>:
>> Først UPDATE. Så ved jeg hvor mange rækker der blev påvirket. Hvis
>> nødvendigt kan jeg så derefter køre INSERT.
> Hvordan får du denne information?
For eksempel med "ren" PHP:
<?php
$con = mysql_connect($host, $username, $password);
mysql_select_db($db, $con);
$sql = "UPDATE tabel SET count=count+1 WHERE id=34634";
$res = mysql_query($sql, $con);
$count = mysql_affected_rows($con);
echo $count.' rækker blev påvirket.';
?>
(Og ja, jeg ved godt at jeg ikke tjekker for fejl. Det er ikke det, det
drejer sig om her. *s*)
Eller med PEAR::DB:
<?php
$db = DB::connect('mysql://'.$username.':'.$password.'@'.$host.'/'.$db);
$sql = "UPDATE tabel SET count=count+1 WHERE id=34634";
$db->query($sql);
$count = $db->affectedRows();
echo $count.' rækker blev påvirket.';
?>
>> Hvad så med min alternative løsning?
>> Altså hvor jeg har to tabeller. Først opsamles det hele på din måde i én
>> tabel, og en gang i mellem bliver det flyttet til en anden tabel, hvor
>> det lagres på en mere praktisk måde.
> Jeg synes det lyder som en masse arbejde for at opnå ingenting.
Ingenting?
En ny række for hvert "hit" synes jeg ikke virker effektiv. Det lyder som
mig som at få et kontoudtog, med en streg for hver krone der er på kontoen,
i stedet for blot et tal.
Det eneste jeg er interesseret i er hvor mange der er af hver, synes du så
ikke det ville være smart at have tallet, i stedet for en række til hver?
Altså i stedet for
A
B
C
A
A
B
A
C
synes jeg det er smartere med:
A 4
B 2
C 2
Synes du slet ikke der bliver opnået noget?
Forestil dig nogle tusinde rækker om dagen i nogle år. Ville det så ikke
være smart at gemme optællingerne i stedet for hver eneste række?
> Du vælger at anvende en relationel database men ikke at anvende den til
> det, som den er designet til.
Jeg har ikke lige andre databaser der passer bedre til formålet. :)
>> Før eller siden vil diskpladsen blive et problem. Altså ikke fordi nye
>> harddiske er så dyre, men der skal tages backup, laves statistik mv.
>> Derfor skal det lagres effektivt.
> Det er effektivt på min måde,
Ja, opsamlingen. Men hvad så med opbevaring og generering af statistik?
Det er vel et valg jeg må tage. :)
> men efter min mening ikke på din.
> Jeg tror ikke vi bliver enige.
Vi behøver skam ikke blive enige. Men der kunne måske komme noget godt ud af
at vi forstod hinanden. :)
Jeg tror ikke helt jeg forstår hvorfor du foretrækker den løsning du
beskriver.
Er det udelukkende fordi det er pænest design under data-opsamling, eller
tror du også det vil være mest effektivt når man ser på systemet som
helhed?
--
Mvh.
Niels Andersen
| |
Jimmy (22-02-2003)
| Kommentar Fra : Jimmy |
Dato : 22-02-03 21:36 |
|
"Niels Andersen" <niels-usenet@myplace.dk> wrote in message
news:8241948.YhCLxbg3lH@home.myplace.dk...
> Altså i stedet for
>
> A
> B
> C
> A
> A
> B
> A
> C
>
> synes jeg det er smartere med:
>
> A 4
> B 2
> C 2
>
> Synes du slet ikke der bliver opnået noget?
Jo - ovenstående kunne jeg også finde på at anvende.
Jeg hæfter mig ved noget, som du skrev tidligere.
At du havde en kolonne "string", som du opdaterede med de grupper en bruger
var medlem af.
Dette er ikke effektivt, hvorfor jeg foreslog den relationelle metode.
Jeg kan sagtens have misforstået dig.
> > Du vælger at anvende en relationel database men ikke at anvende den til
> > det, som den er designet til.
>
> Jeg har ikke lige andre databaser der passer bedre til formålet. :)
Pointen er, at relationelle databaser skal anvendes relationelt og ikke AD
HOC med mindre man rent faktisk *ved* hvad man laver og hvilke ulemper det
har.
> Er det udelukkende fordi det er pænest design under data-opsamling, eller
> tror du også det vil være mest effektivt når man ser på systemet som
> helhed?
Efter min mening er din løsning som beskrevet ovenfor med A B C mener jeg er
fin og effektiv.
Jeg har ikke tidligere taget stilling til den del, men blot snakket om din
"string", som jeg også angav jeg nok ikke havde forstået 100%.
Jeg druknede i de meget lange beskrivelser
Hvis du beskriver projektet med så absolut få ord som muligt tror jeg, at du
vil kunne få bedre råd af f.eks. Jens eller Morten, som afgjort ved mere om
effektiv databaseprogrammering end jeg gør.
Mvh
Jimmy
| |
Niels Andersen (22-02-2003)
| Kommentar Fra : Niels Andersen |
Dato : 22-02-03 21:58 |
|
Jimmy wrote in <LbR5a.93$J04.45@news.get2net.dk>:
>> Synes du slet ikke der bliver opnået noget?
> Jo - ovenstående kunne jeg også finde på at anvende.
OK. :)
> Jeg hæfter mig ved noget, som du skrev tidligere.
> At du havde en kolonne "string", som du opdaterede med de grupper en
> bruger var medlem af.
Ah, det var der kæden hoppede af. :)
Brugerne er ikke organiserede i grupper. Ved hvert "hit" kan brugeren angive
en 100% valgfri streng (og dermed uforudsigelig). Det interessante er så
hvor mange gange hver streng bliver brugt hver dag, da hver streng normalt
vil blive angivet mange gange om dagen.
> Efter min mening er din løsning som beskrevet ovenfor med A B C mener jeg
> er fin og effektiv.
Godt, så er vi enige så langt. :)
> Hvis du beskriver projektet med så absolut få ord som muligt tror jeg, at
> du vil kunne få bedre råd af f.eks. Jens eller Morten, som afgjort ved
> mere om effektiv databaseprogrammering end jeg gør.
Jeg tror jeg har fundet en bedre måde at forklare det på nu:
<ny forklaring>
Der kommer nogle tusinde navne hver dag. Jeg vil lave statistik over hvor
populære enkelte navne er fra dag til dag.
Dvs. jeg i den sidste ende gerne vil have en tabel i stil med:
navn varchar
dag date
antal int
Det er umuligt på forhånd at have en liste over alle mulighederne.
Hver gang der kommer et navn, skal der tælles én op ved dette navn. Findes
rækken ikke, skal den oprettes. Hvordan gør man det mest effektivt?
</ny forklaring>
--
Mvh.
Niels Andersen
| |
Jimmy (22-02-2003)
| Kommentar Fra : Jimmy |
Dato : 22-02-03 22:07 |
|
"Niels Andersen" <niels-usenet@myplace.dk> wrote in message
news:1197449.mb8T4NpeBd@home.myplace.dk...
>
> <ny forklaring>
>
> Der kommer nogle tusinde navne hver dag. Jeg vil lave statistik over hvor
> populære enkelte navne er fra dag til dag.
>
> Dvs. jeg i den sidste ende gerne vil have en tabel i stil med:
>
> navn varchar
> dag date
> antal int
>
> Det er umuligt på forhånd at have en liste over alle mulighederne.
> Hver gang der kommer et navn, skal der tælles én op ved dette navn. Findes
> rækken ikke, skal den oprettes. Hvordan gør man det mest effektivt?
>
> </ny forklaring>
Fin forklaring.
Jeg ville fortsat anvende:
---------
If DB.Execute ("SELECT").EOF Then
DB.Execute ("INSERT")
Else
DB.Execute ("UPDATE")
End If
---------------------------
Det er sandt, at den altid vil generere to forespørgsler, men jeg mener
løsbarheden af koden er øget ved denne løsning.
Samtidig er den intuitiv enkel og kaster ikke fejl af sig.
Hvis nogen kan finde en måde vha. ANSI SQL og et fortolket sporg, der er
mere effektiv vil jeg da meget gerne høre om denne, da jeg uden at blinke
ville anvende ovenstående i den givne situation.
Jeg mener ikke man kan programmere sig ud af det problem du er havnet i, som
jeg tidligere foreslog.
Mvh
Jimmy
| |
Niels Andersen (22-02-2003)
| Kommentar Fra : Niels Andersen |
Dato : 22-02-03 23:17 |
|
Jimmy wrote in <2FR5a.98$Sx4.38@news.get2net.dk>:
> Jeg ville fortsat anvende:
> If DB.Execute ("SELECT").EOF Then
> DB.Execute ("INSERT")
> Else
> DB.Execute ("UPDATE")
> End If
OK, så vil jeg gøre det på den måde. :)
--
Mvh.
Niels Andersen
| |
Jimmy (23-02-2003)
| Kommentar Fra : Jimmy |
Dato : 23-02-03 03:16 |
|
"Jimmy" <nyhedsgruppe@get3_erstat_3_med_2_net.dk> wrote in message
news:2FR5a.98$Sx4.38@news.get2net.dk...
>
> Jeg ville fortsat anvende:
>
> ---------
> If DB.Execute ("SELECT").EOF Then
> DB.Execute ("INSERT")
> Else
> DB.Execute ("UPDATE")
> End If
> ---------------------------
Nå, nu måtte jeg jo teste, hvad der var hurtigst.
3000 iterationer
To felter
- Felt int
- Noegle CHAR(1) UNIQUE
Felt populeres med 1 og Noegle med x
Min løsning tager 12 sekunder.
Mortens løsning med en INSERT, check for fejl, og derefter UPDATE tager 6.
Ved 1000 iterationer tog min 6 og Mortens 3.
Jeg har ikke kunnet teste Jens' løsning med Update, hvis 0 rækker -> INSERT,
da mysql_affected_rows() tilsyneladende ikke fungerer med den nuværede ODBC
driver fra MySQL.
Man må kunne konkludere, at min løsning ikke er den, der performer bedst.
Dog vil jeg fortsat hævde, at den er mere elegant set i forhold til
læsbarhed og intuition.
Det må bero på det antal daglige forespørgsler samt temperament at vurdere,
hvilken løsning der skal anvendes.
Mvh
Jimmy
| |
Ulrich (28-02-2003)
| Kommentar Fra : Ulrich |
Dato : 28-02-03 22:08 |
|
Blot en tanke, med en lidt anden indgangsvinkel.
Måske er det slet ikke muligt.
1. Delete poster der er fra igår
2. Lav en insert hvergang med f.eks.
Hans
Bent
Hans
Hans
Anders
Bent
Altså ingen nøgle på feltet "navn"
3. Lav en sum med en group per navn
Hvis tabellen ikke bliver større end 2000 per dag
tror jeg ikke det giver performance problemer.
Venlig hilsen
Ulrich
| |
Morten Guldager (22-02-2003)
| Kommentar Fra : Morten Guldager |
Dato : 22-02-03 19:33 |
|
Sat, 22 Feb 2003 at 09:48 GMT Niels Andersen wrote
> Jimmy wrote in <wjw5a.5688$VK.4691@news.get2net.dk>:
>
>> Jeg ville løse problemet ved at lave en SELECT først og derefter enten
>> INSERTe eller UPDATE'e.
>
> Så vil du *altid* lave to forespørgsler. Ved at køre INSERT eller UPDATE
> først, vil du i nogle tilfælle kunne nøjes med én forespørgsel.
>
> Desuden er jeg lidt bange for, at en række kan blive oprettet mellem en
> SELECT (som viser at rækken ikke findes) og INSERT.
> Hmm... Den kunne vel løses ved at køre UPDATE hvis INSERT fejler.
Jo, men er du så ikke ude i 3 SQL'er?
Hvis du ikke vil låse noget ser jeg ikke andre løsninger end:
INSERT INTO klonk values ('hans', 1)
UPDATE klonk SET n = n + 1 where noegle = 'hans'
Den første insert fejler ofte, men hvis du har et index på "noegle" er
det billigt sluppet.
Du kan i dit kode være lige glad med om den fejler, det er først kritisk hvis
din update enten fejler eller opdaterer 0 rækker.
Metoden udnytter at hver enkelt SQL statement udføres atomisk. Vores indledende
insert er bare til for at den efterfølgende update ikke skal fejle.
Man kan diskutere om det er skidt stil at basere sit kode/design på at visse
ting gerne må fejle. Jeg synes ikke det er et problem. Men måske en lille
kommentar om at det altså _skal_ være sådan ville pynte.
/Morten
| |
Jens Gyldenkærne Cla~ (22-02-2003)
| Kommentar Fra : Jens Gyldenkærne Cla~ |
Dato : 22-02-03 12:29 |
|
Niels Andersen skrev:
>> Jeg ville løse problemet ved at lave en SELECT først og
>> derefter enten INSERTe eller UPDATE'e.
>
> Så vil du *altid* lave to forespørgsler. Ved at køre INSERT
> eller UPDATE først, vil du i nogle tilfælle kunne nøjes med én
> forespørgsel.
Ja. Jeg ved ikke om fejlen (der opstår hvis man vil indsætte en
eksisterende nøgle) er tungere at behandle end en tom update - men
jeg ville nok umiddelbart foretrække at køre update først (og så
tjekke antallet af opdaterede poster).
Hvis der ikke er forskel på hvor lang tid de to forespørgsler tager
ville jeg lægge rækkefølgen sådan så den mest almindelige
forespørgsel køres først. Det vil sige hvis der er 20.000
nøgleværdier og kun ca. 1000 i databasen (og de vokser langsomt)
vil det være smart at starte med INSERT og vice versa.
--
Jens Gyldenkærne Clausen
MF (medlem af FIDUSO - www.fiduso.dk)
I ovenstående tekst benyttes nyt komma.
| |
Jens Gyldenkærne Cla~ (23-02-2003)
| Kommentar Fra : Jens Gyldenkærne Cla~ |
Dato : 23-02-03 02:04 |
|
Jimmy skrev:
> If DB.Execute ("SELECT").EOF Then
Så vidt jeg husker er det noget bedre at benytte COUNT hvis man er
ude på at tjekke for eksistens (EXISTS er endnu bedre, men det er
vist ikke understøttet af MySQL).
Det vil sige
"SELECT COUNT(*) As antal FROM tabel WHERE navn = 'hans'"
- og så bliver kontroltjekket naturligvis:
If rs("antal") = 0 Then
' Insert
Else
' Update
End If
> Det er sandt, at den altid vil generere to forespørgsler, men
> jeg mener løsbarheden af koden er øget ved denne løsning.
> Samtidig er den intuitiv enkel og kaster ikke fejl af sig.
Hvis man opdaterer først og derefter laver insert hvis det berørte
antal poster er nul har man sparet en forespørgsel i en del
tilfælde - og man er ikke ude i at håndtere fejl (jeg er egentlig
ikke sikker på om det at håndtere en insertfejl vil give problemer,
men at bruge recordsaffected gør i al fald ikke).
> Hvis nogen kan finde en måde vha. ANSI SQL og et fortolket
> sporg, der er mere effektiv vil jeg da meget gerne høre om
> denne, da jeg uden at blinke ville anvende ovenstående i den
> givne situation.
I asp ville jeg skrive:
' Kør opdater-forespørgsel, gem antal poster i RecordsAffected
Conn.Execute UpdateSQL, RecordsAffected
If RecordsAffected = 0 Then
' Kør indsæt-forespørgsel
Conn.Execute InsertSQL
End If
Jeg ved ikke om egenskaben RecordsAffected virker med alle
databaser under asp - men det virker i de databaser jeg har brugt
asp med (Access, MSSQL). Ud fra Nils' eksempler (i php) vil jeg
gætte på at det også virker med mysql.
--
Jens Gyldenkærne Clausen
MF (medlem af FIDUSO - www.fiduso.dk)
I ovenstående tekst benyttes nyt komma.
| |
Jens Gyldenkærne Cla~ (23-02-2003)
| Kommentar Fra : Jens Gyldenkærne Cla~ |
Dato : 23-02-03 02:07 |
|
Morten Guldager skrev:
> Jo, men er du så ikke ude i 3 SQL'er?
Det er dog altid kun to af dem der køres.
> INSERT INTO klonk values ('hans', 1)
> UPDATE klonk SET n = n + 1 where noegle = 'hans'
Burde du ikke indsætte 0 som værdi i første linje? Så vidt jeg kan
se bliver værdier nu talt dobbelt første gang.
Jeg vil stadig foretrække at nøjes med én forespørgsel i de
tilfælde hvor det er muligt (eksempler givet andetsteds i tråden).
--
Jens Gyldenkærne Clausen
MF (medlem af FIDUSO - www.fiduso.dk)
I ovenstående tekst benyttes nyt komma.
| |
Morten Guldager (23-02-2003)
| Kommentar Fra : Morten Guldager |
Dato : 23-02-03 09:20 |
|
Sun, 23 Feb 2003 at 01:07 GMT Jens Gyldenkærne Clausen wrote
> Morten Guldager skrev:
>
>> INSERT INTO klonk values ('hans', 1)
>> UPDATE klonk SET n = n + 1 where noegle = 'hans'
>
> Burde du ikke indsætte 0 som værdi i første linje? Så vidt jeg kan
> se bliver værdier nu talt dobbelt første gang.
Jo, naturligvis.
> Jeg vil stadig foretrække at nøjes med én forespørgsel i de
> tilfælde hvor det er muligt (eksempler givet andetsteds i tråden).
Jo, så skal du låse din tabel. Ellers er der risiko for at to
processer laver deres insert samtidigt.
Jimmys metode:
process-A process-B
---------------------
SELECT
SELECT
INSERT(1)
eller
UPDATE(+1)
INSERT(1)
eller
UPDATE(+1)
Herefter er tælleren kun 1 hvis rækken ikke fandtes på forhånd.
Din metode:
process-A process-B
---------------------
UPDATE(+1)
UPDATE(+1)
hvis fejl
INSERT(1)
hvis fejl
INSERT(1)
Herefter er tælleren kun 1 hvis rækken ikke fandtes på forhånd.
Med min metode (efter din rettelse):
process-A process-B
---------------------
insert(0)
insert(0)
update(+1)
update(+1)
Og tælleren er nu 2
/Morten
| |
Jens Gyldenkærne Cla~ (23-02-2003)
| Kommentar Fra : Jens Gyldenkærne Cla~ |
Dato : 23-02-03 02:13 |
|
Jimmy skrev:
> Jens' eksempel kan jeg ikke forstå.
> Uanset om rækken findes eller ej vil en UPDATE ikke ændre
> antallet af rækker, og det vil derfor ikke umiddelbart være
> muligt at se, om en række er blevet opdateret eller UPDATE
> ignoreret.
Antallet af rækker er underordnet. I ADO kan man med parameteren
RecordsAffected til execute-metoden på en Connection eller en
Command få returneret det antal poster der er berørt af en
handlingsforespørgsel. Ved at tjekke værdien her kan man afgøre om
en ekstra forespørgsel er nødvendig (uden at sende noget til
databasen igen).
Eksempel:
Conn.Execute "UPDATE foo SET bar = 3 WHERE foobar = 'hans'", antal
If antal = 0 Then
' Ingen poster opdateret
Else
' Poster opdateret
End If
--
Jens Gyldenkærne Clausen
MF (medlem af FIDUSO - www.fiduso.dk)
I ovenstående tekst benyttes nyt komma.
| |
Jens Gyldenkærne Cla~ (23-02-2003)
| Kommentar Fra : Jens Gyldenkærne Cla~ |
Dato : 23-02-03 12:52 |
|
Morten Guldager skrev:
> Jo, så skal du låse din tabel. Ellers er der risiko for at to
> processer laver deres insert samtidigt.
Det kan der være noget om. Jeg ville (i MSSQL) lave det som en
lagret procedure - her er der ikke nogen risiko for at en anden
proces kan bryde ind mellem to forespørgsler. Men den mulighed
ligger jo ikke i mysql.
Ellers kan man sætte en lock - men om det performancetab man kan
risikere her er større end din løsning med insert(0) kan jeg ikke
afgøre.
--
Jens Gyldenkærne Clausen
MF (medlem af FIDUSO - www.fiduso.dk)
I ovenstående tekst benyttes nyt komma.
| |
Morten Guldager (23-02-2003)
| Kommentar Fra : Morten Guldager |
Dato : 23-02-03 13:12 |
|
Sun, 23 Feb 2003 at 11:52 GMT Jens Gyldenkærne Clausen wrote
> Morten Guldager skrev:
>
>> Jo, så skal du låse din tabel. Ellers er der risiko for at to
>> processer laver deres insert samtidigt.
>
> Det kan der være noget om. Jeg ville (i MSSQL) lave det som en
> lagret procedure - her er der ikke nogen risiko for at en anden
> proces kan bryde ind mellem to forespørgsler. Men den mulighed
> ligger jo ikke i mysql.
>
> Ellers kan man sætte en lock - men om det performancetab man kan
> risikere her er større end din løsning med insert(0) kan jeg ikke
> afgøre.
Nej, heller ikke jeg.
Måske Jimmy lige laver en test, hwa?
Men sikkert er det at hvis du begynder at bruge låse så opstår der
en markant flaskehals. Som måske først bliver synlig hvis du får mange
samtidige tråde mod databasen.
Ligeledes skal du være opmærksom på at du skal låse _samtlige_ tabeller
du har tænkt dig at arbejde med indtil du slipper låsen igen. Også selv
om du kun skal læse statiske data fra underordnede tabeller.
Jeg vil ikke anbefale at låse med mindre der ikke er andre mulighedder.
/Morten
| |
Peter Brodersen (22-02-2003)
| Kommentar Fra : Peter Brodersen |
Dato : 22-02-03 01:51 |
|
On Fri, 21 Feb 2003 16:37:39 +0100, Niels Andersen
<niels-usenet@myplace.dk> wrote:
>Som udgangspunkt ved jeg ikke hvilke ID'er der er i tabellen. Den bedste
>løsning jeg kender er at fyre INSERT eller UPDATE af. Giver det fejl (eller
>det ikke ændrer nogen rækker i tabellen), så skal den anden fyres af.
>
>Findes der en bedre løsning? Kan den bruges i MySQL?
Fx REPLACE?
http://www.mysql.com/documentation/mysql/bychapter/manual_Reference.html#REPLACE
--
- Peter Brodersen
| |
Jimmy (22-02-2003)
| Kommentar Fra : Jimmy |
Dato : 22-02-03 03:38 |
|
"Peter Brodersen" <usenet@ter.dk> wrote in message
news:b36hgj$fcc$1@dknews.tiscali.dk...
> On Fri, 21 Feb 2003 16:37:39 +0100, Niels Andersen
> <niels-usenet@myplace.dk> wrote:
>
> >Som udgangspunkt ved jeg ikke hvilke ID'er der er i tabellen. Den bedste
> >løsning jeg kender er at fyre INSERT eller UPDATE af. Giver det fejl
(eller
> >det ikke ændrer nogen rækker i tabellen), så skal den anden fyres af.
> >
> >Findes der en bedre løsning? Kan den bruges i MySQL?
>
> Fx REPLACE?
>
>
http://www.mysql.com/documentation/mysql/bychapter/manual_Reference.html#REP
LACE
Hmm der kunne jeg ikke finde noget, men:
http://www.mysql.com/doc/en/REPLACE.html
var fin.
Jeg havde i øvrigt overset den, men det er værd at notere, at den første
DELETEr en post og derefter laver en ny INSERT med et dertilhørende nyt
AUTO_INCREMENT.
Hvis du refererer til dens primære nøgle forsvinder den altså ved brug af
REPLACE.
Mvh
Jimmy
| |
|
|