[MySQL] SQL syntax-error of phpMyAdmin-bug

Pagina: 1
Acties:

  • funkwurm
  • Registratie: December 2005
  • Laatst online: 22-02-2021
De oplettende Tweaker is het al opgevallen dat ik nu voor de 3e keer een query-probleem post over hetzelfde forum wat ik aan het bouwen ben, alhoewel de tussenpauzen erg groot zijn. Het is je dan ook vergeven als dit niet onmiddellijk een belletje doet rinkelen }:O

Dit keer zegt een schermprint van Opera met phpMyAdmin bijna genoeg (schrik niet, resolutie van 1680x1050): http://members.home.nl/gm.damen/phpMyAdminError.png

Het lijkt er dus ook alsof phpMyAdmin achter de schermen per ongeluk de onvolledige query "SHOW KEYS FROM" naar MySQL stuurt, want zo ziet het er niet uit als ik zelf een typo heb gemaakt.

Ook zie je in de print-screen de betreffende query, maar ik zal voor het copy en paste gemak hem hier nog eens posten (de PHP-variabelen spreken voor zich, in phpMyAdmin vul ik voor beiden 1 in):

SQL:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
SELECT t.boven, t.slot, p.topic_id, p.titel, l.naam,
  (
    SELECT t.id NOT IN
    (
      SELECT topic_id FROM
      (
        SELECT topic_id, MAX(posted) ma FROM posts
        GROUP BY topic_id
      ) p, gezien g
      WHERE p.topic_id=g.wat_id AND g.wat=1 AND p.ma<=g.tijd AND g.lid_id='.$iLidId.'
    )
  ) new,
  (
    SELECT FROM_UNIXTIME(MAX(p.posted), "%Y%m")+0
    FROM posts p WHERE p.topic_id=t.id
  ) orderme FROM topics t, posts p, leden l
  WHERE t.sub_id='.$iSubId.' AND p.topic_id=t.id AND p.poster_id=l.id
  GROUP BY t.id ORDER BY t.boven, t.slot, orderme, t.id, p.id


Moet ik mijn code aanpassen of een bug rapporteren op phpmyadmin.net?

  • michelzwarts
  • Registratie: Juni 2005
  • Laatst online: 19-10-2024
Als je hem nu gewoon uitvoerd vanuit PHP, dan weet je meteen of het aan PHPMyAdmin ligt.

Windows Veteran turned Apple Addict


Verwijderd

Kun je wel order by een veld doen dat je benoemd hebt? Dus orderme??

Probeer idd deze query gewoon eens in PHP uit te voeren...

  • Mr. Bondt
  • Registratie: Februari 2005
  • Laatst online: 16:46
funkwurm schreef op dinsdag 26 december 2006 @ 01:50:
Het lijkt er dus ook alsof phpMyAdmin achter de schermen per ongeluk de onvolledige query "SHOW KEYS FROM" naar MySQL stuurt, want zo ziet het er niet uit als ik zelf een typo heb gemaakt.
Daar heb ik ook regelmatig last van. Wat bij mij altijd helpt is eerst een tabel openen (een van die links links) en dan je query uitvoeren.

Verwijderd

funkwurm schreef op dinsdag 26 december 2006 @ 01:50:
De oplettende Tweaker is het al opgevallen dat ik nu voor de 3e keer een query-probleem post over hetzelfde forum wat ik aan het bouwen ben, alhoewel de tussenpauzen erg groot zijn. Het is je dan ook vergeven als dit niet onmiddellijk een belletje doet rinkelen }:O
Tuurlijk joh! We zitten hier allemaal te wachten wanneer funkwurm weer 's wat post. :P
"Heeft funkwurm al weer van zich laten horen?" "Nee, laatste tijd niet" "Bummer :'( " :+

Maar in je query vind ik 'SELECT t.id NOT IN' wel heel erg vreemd. Bij mijn weten is dat geen correcte SQL. Misschien dat MySQL er wel chocola van kan (proberen te) maken, maar dan kan je phpMyAdmin niet kwalijk nemen dat 'ie niet overweg kan met dit koeterwaals... ;)

  • Flying_Thunder
  • Registratie: December 2001
  • Niet online
Verwijderd schreef op dinsdag 26 december 2006 @ 14:19:
[...]
Maar in je query vind ik 'SELECT t.id NOT IN' wel heel erg vreemd. Bij mijn weten is dat geen correcte SQL. Misschien dat MySQL er wel chocola van kan (proberen te) maken, maar dan kan je phpMyAdmin niet kwalijk nemen dat 'ie niet overweg kan met dit koeterwaals... ;)
MySQL kan daar wel mee uit de voeten (zoek op IN). En als MySQL het kan, mag je het phpMyAdmin imho wel kwalijk nemen aangezien 't een admin tool is speciaal voor MySQL :)

  • funkwurm
  • Registratie: December 2005
  • Laatst online: 22-02-2021
Nou AfterLife, ik begrijp dat jij als mijn grootste fan op een alternatief voor mijn SELECT t.id NOT IN-constructie gaat broeden? *O*

Ik wil mijn forum een beetje volledig laten bijhouden wanneer een topic gelezen is of niet. Dus niet alles gelezen at logout a la phpBB, en niet alles gelezen een uur na login a la react.

De tabel gezien houdt dit bij (maar Alterlife, dit heb jij al lang in mijn vorige topic gelezen) door voor elke gebruiker de timestamp waarop hij/zij een topic bekeek te bewaren (ook andere site-onderdelen, vandaar wat_id en wat=1. Natuurlijk heeft een topic geen timestamp, maar haal ik die uit de onderliggende posts (MAX(posted)). Om dus te weten of een topic als "nieuw" aangegeven moet worden kijk ik of het id voorkomt in de verzameling id's van topics waar de gezien-timestamp groter is dan de grootste posted-timestamp. Op die manier neem ik ook topics mee die nog nooit bekeken zijn en dus geen entry in gezien hebben whatsoever. Veldje new geeft dus eigenlijk een boolean terug die dan wel meteen weer een 1 of 0 wordt.

Dan heb ik ook niet graag dat bij elke nieuwe post de volgorde van topics verandert, en daarom sorteer ik op orderme (als dat in gewone SQL niet kan, vind ik dat serieus stom :+ ) waardoor topics per maand van laatste post worden gesorteerd.

Tot zover "funkwurm schept op over zijn DB-design"

Guys, mijn welgemeende excuses dat ik geen test php'tje in elkaar had geflanst om dit te testen, wat Mr. Bondt zegt geldt voor mij ook, als ik vanuit een tabel de query run werkt het zonder problemen.

Ik ben geholpen, het ligt aan phpMyCrappyAdmin, als een zeker iemand puntjes tot verbetering van de query heeft, be my guest.

  • OnTracK
  • Registratie: Oktober 2002
  • Laatst online: 18:05
Nou, met al die subselects vind ik het niet vreemd dat phpMyAdmin op z'n bek gaat. Iedere andere taal had je gewoon keihard een foutmelding terug gegeven, waarschijnlijk over foute group-by. Als ik je database goed begrijp zou dit hetzelfde moeten doen:

SQL:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
SELECT
    t.boven,
    t.slot,
    t.id AS topic_id,
    q.titel,
    l.naam,
    MAX(p.id) AS last_post_id,
    q.ma<=g.tijd AS new
FROM
    topic t
    INNER JOIN post p ON p.topic_id=t.id
    INNER JOIN post q ON q.id=last_post_id
    INNER JOIN leden l ON l.id=q.poster_id
    LEFT JOIN gezien g ON g.wat_id=t.id AND g.wat=1 AND g.lid_id=".$iLidId."
WHERE
    t.sub_id=".$iSubId."
GROUP BY
    t.id
ORDER BY
    t.boven,
    t.slot,
    YEAR(q.posted) DESC,
    MONTH(q.posted) DESC,
    t.id,
    last_post_id


Zo kun je prima sorteren mbv YEAR en MONTH. En als je ervoor zorgt dat je slechts een JOIN doet op één post binnen een topic (de laatste welteverstaan) kun je alles doen zonder subselects. Dit word gedaan door de tweede keer te JOIN'en met posts.

Hoewel deze ongetest is, dus ik weet niet of het helemaal goed werkt, is het iig een goed idee om niet zo met subselects te werken. Je databasemodel is nauwelijks te begrijpen uit je query...

[ Voor 9% gewijzigd door OnTracK op 27-12-2006 12:46 ]

Not everybody wins, and certainly not everybody wins all the time.
But once you get into your boat, push off and tie into your shoes.
Then you have indeed won far more than those who have never tried.


Verwijderd

funkwurm schreef op dinsdag 26 december 2006 @ 18:42:
Nou AfterLife, ik begrijp dat jij als mijn grootste fan op een alternatief voor mijn SELECT t.id NOT IN-constructie gaat broeden? *O*
Get real. Ik wil best meedenken over je data access, maar wanneer je 4 subselects (waarvan 1 sub-subselect) denkt nodig te hebben, zou ik 's heel erg gaan nadenken over je datamodel. OnTracK geeft al aan hoe 't ook zou kunnen zonder de SQL taal te verkrachten, dus kijk daar 's naar.

edit:
Al smokkelt OnTracK ook nogal: elke kolom die niet in de GROUP BY staat hoort een aggregate te zijn. Voor zover ik weet is MySQL de enige database die dit soort syntax accepteert en dan een 'lucky guess' doet in het resultaat wat 'ie terugstuurt.

Overigens is 't vaak handiger om je queries overzichtelijk te houden en de "bend over backwards" subselects etc. in code (PHP, C#, Java, Delphi, whatever) af te handelen. Wel zo onderhoudbaar, en je hebt dan meer grip op de indexen die je database dan denkt te moeten gebruiken. Bij jouw query zal dat "God zegen de greep" zijn, met een bijbehorende performance... ;)

[ Voor 12% gewijzigd door Verwijderd op 27-12-2006 00:48 ]


  • OnTracK
  • Registratie: Oktober 2002
  • Laatst online: 18:05
Verwijderd schreef op dinsdag 26 december 2006 @ 21:49:
[...]
Get real. Ik wil best meedenken over je data access, maar wanneer je 4 subselects (waarvan 1 sub-subselect) denkt nodig te hebben, zou ik 's heel erg gaan nadenken over je datamodel. OnTracK geeft al aan hoe 't ook zou kunnen zonder de SQL taal te verkrachten, dus kijk daar 's naar.

edit:
Al smokkelt OnTracK ook nogal: elke kolom die niet in de GROUP BY staat hoort een aggregate te zijn. Voor zover ik weet is MySQL de enige database die dit soort syntax accepteert en dan een 'lucky guess' doet in het resultaat wat 'ie terugstuurt.

Overigens is 't vaak handiger om je queries overzichtelijk te houden en de "bend over backwards" subselects etc. in code (PHP, C#, Java, Delphi, whatever) af te handelen. Wel zo onderhoudbaar, en je hebt dan meer grip op de indexen die je database dan denkt te moeten gebruiken. Bij jouw query zal dat "God zegen de greep" zijn, met een bijbehorende performance... ;)
Ik smokkel een beetje, al weet ik zelf niet echt hoeveel. Misschien mag dit in andere talen ook wel. Ik doe namelijk een MAX(p.id) in mijn SELECT, en doe dan een opnieuw een JOIN op deze. Daardoor zal MySQL niets hoeven te gokken. Omdat q.id een UNIQUE-constraint heeft (tenminste, dat mag ik hopen), en uit MAX(p.id) ook altijd maar eentje komt.

[ Voor 4% gewijzigd door OnTracK op 27-12-2006 12:42 ]

Not everybody wins, and certainly not everybody wins all the time.
But once you get into your boat, push off and tie into your shoes.
Then you have indeed won far more than those who have never tried.


Verwijderd

5 van de 7 kolommen in je SELECT zijn geen onderdeel van de GROUP BY clause en ook niet geaggregeerd. Dat is iets meer dan een beetje smokkelen...
Wanneer je zeker weet dat MySQL niets hoeft te gokken, vertel 'm dat dan ook. Door bv. max(l.naam) te gebruiken i.p.v. l.naam (ook al weet je dat l.naam uniek zal zijn binnen de GROUP BY). Je houdt dan correcte SQL syntax over, en het debugt een stuk beter omdat je geen rekening hoeft te houden met 't feit dat MySQL soms de ogen dicht doet en maar een willekeurige waarde uit de ballenbak graait wanneer de query geen unieke resultaten teruggeeft.

Bij MSSQL, Oracle, DB2, Interbase, PostgreSQL, etc. word je dan genadeloos afgestraft. "multiple rows in a singleton select", etc.

  • OnTracK
  • Registratie: Oktober 2002
  • Laatst online: 18:05
Verwijderd schreef op woensdag 27 december 2006 @ 13:17:
5 van de 7 kolommen in je SELECT zijn geen onderdeel van de GROUP BY clause en ook niet geaggregeerd. Dat is iets meer dan een beetje smokkelen...
Wanneer je zeker weet dat MySQL niets hoeft te gokken, vertel 'm dat dan ook. Door bv. max(l.naam) te gebruiken i.p.v. l.naam (ook al weet je dat l.naam uniek zal zijn binnen de GROUP BY). Je houdt dan correcte SQL syntax over, en het debugt een stuk beter omdat je geen rekening hoeft te houden met 't feit dat MySQL soms de ogen dicht doet en maar een willekeurige waarde uit de ballenbak graait wanneer de query geen unieke resultaten teruggeeft.

Bij MSSQL, Oracle, DB2, Interbase, PostgreSQL, etc. word je dan genadeloos afgestraft. "multiple rows in a singleton select", etc.
Dat weet ik wel, ik wil niet MAX(l.naam) hebben, ik wil de l.naam hebben die bij q.id hoort, ookal is dat dezelfde. Maar je hebt wel gelijk, alleen maakt dit soort dingen je query niet makkelijker te "lezen". Ik weet in dit geval zeker dat MySQL nooit zal hoeven te gokken, zelfs niet als de gegevens in de database niet kloppen. Daarvoor heb je een correct datamodel :)

Als je de GROUP BY weg zou halen, zou uit alle kolommen die volgens jou geen aggregrerende functies zijn hetzelfde komen, en dat is nou juist de eigenschap ervan. Maargoed, ik kom erachter dat ik niet mag joinen op een max van iets...

Wat natuurlijk beter was geweest is om alleen MAX(p.id) terug te geven, en dan met een nieuwe query de gegevens hiervan terug te halen, want "een topic" en "de poster van de laatste post van dat topic" hebben natuurlijk niet zoveel met elkaar te maken dat je die in één query zou moeten ophalen.

[ Voor 29% gewijzigd door OnTracK op 27-12-2006 14:13 ]

Not everybody wins, and certainly not everybody wins all the time.
But once you get into your boat, push off and tie into your shoes.
Then you have indeed won far more than those who have never tried.


Verwijderd

Je kunt een hoop beroerde queries op een correct datamodel loslaten.
En wanneer je die moet debuggen is 't wel handig wanneer die queries op z'n minst syntactisch correct zijn en geen gebruik maken van de 'grabbelton' mogelijkheden van MySQL...

Verwijderd

In de 1e regel een komma teveel na l.naam? Of zeg ik nu wat heel doms? :X

Verwijderd

Verwijderd schreef op woensdag 27 december 2006 @ 15:33:
In de 1e regel een komma teveel na l.naam? Of zeg ik nu wat heel doms? :X
Klopt wel...

  • Gwaihir
  • Registratie: December 2002
  • Niet online
michelzwarts schreef op dinsdag 26 december 2006 @ 10:57:
Als je hem nu gewoon uitvoerd vanuit PHP, dan weet je meteen of het aan PHPMyAdmin ligt.
Of vanuit MySQL's Query Browser :)

  • funkwurm
  • Registratie: December 2005
  • Laatst online: 22-02-2021
Afterlife, ik ben zelf mod op een ander webdev forum en wij verstrekken ook geen kant-en-klare oplossingen.

Maar... vraag ik dan teveel van SQL? Mijn gestripte datamodel ziet er ongeveer zo uit:

Afbeeldingslocatie: http://members.home.nl/gm.damen/forumdb.png

Voor wat ik heb meegekregen van normaliseren op school is dit een heel correct plaatje (relaties zijn afgedwongen). Geen data - als timestaps, onderwerp en TS'er - redundant in topics en posts.
Het query-idee van OnTrack uitgewerkt ziet er dan ongeveer zo uit (eveneens gestript van dingen als sticky en gesloten topics):
SQL:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
SELECT
  t.id AS topic_id,
  ep.titel,
  lp.posted,
  l.naam,
  MIN(p.id) AS eerste_post_id,
  MAX(p.posted) AS laatst_posted,
  lp.posted<=g.tijd AS nieuw
FROM
  topics t
  INNER JOIN posts p    ON p.topic_id = t.id
  INNER JOIN posts ep   ON      ep.id = p.eerste_post_id
  INNER JOIN posts lp   ON  lp.posted = p.laatste_posted
  INNER JOIN leden l    ON       l.id = ep.poster_id
   LEFT JOIN t_gezien g ON     g.t_id = t.id AND g.lid_id = 1
WHERE
  t.sub_id=1
GROUP BY
  t.id
ORDER BY
  YEAR(lp.posted) DESC,
  MONTH(lp.posted) DESC,
  t.id DESC

Een aantal dingen:
  • t_gezien vervangt gezien van mijn vorige query en gaat alleen over topics, niet meer andere (hier sowieso niet ter zake doende) site-onderdelen. Op die manier kan ik ook die relatie afdwingen)
  • Dit uit OnTracks query:
    SQL:
    1
    
    lp.posted<=g.tijd AS nieuw

    Jeukt me, omdat een soortgelijke query van een vorige forum-ontwerp (dit is een herbouw) alleen topics als nieuw merkte als er een waarde in t_gezien bestond die dan kleiner was. Maar als er nog geen waarde is is het topic ook nieuw, namelijk nooit bekeken. Dit was de reden van één van de sub selects, maar goed kan nu toch nog niet testen.
  • Je ziet dat ik de tabel q vervang met ep (eerste post) en lp (laatst posted), dit omdat het ongelezen zijn op basis van de laatste, maar onderwerp en TS'er op basis van de eerste moet gebeuren.
  • Ik gebruik bij lp MAX(p.posted) ipv MAX(p.id) omdat posts naderhand gewijzigd kunnen worden (gebeurt veel op ons forum) en een gewijzigde post het topic ongelezen kan maken, waar de post met de hoogste id misschien al wel gelezen was.
OnTrack was er zelf ook al achter gekomen dat je inderdaad niet mag JOINen op MIN() of MAX(). Nou zou ik zelf niet echt weten hoe ik hier iets aan doe, dat komt omdat nested selects voor mij logischer zijn bij het denk proces dat ik maak in het bedenken van een query. Ik zou tot zoiets geneigd zijn (schrap je):
SQL:
1
...JOIN posts ep ON ep.id = (SELECT MIN(p.id) FROM...

Maar volgens mij was ik gaan joinen om dit soort onmogelijke sub's te ontwijken...

En ik zie ook niet hoe ik dit code-wise moet oplossen, ik kan natuurlijk
SQL:
1
2
3
SELECT t.id, MIN(p.id), MAX(p.posted)
FROM topics t, posts p
WHERE p.topic_id=t.id GROUP BY t.id

en dan bij elk topic de bovenstaande INNER JOIN-query draaien met MIN() en MAX() door php ingevuld, maar dat zie ik ook nog geen wenselijke performance opleveren. Maar wat moet ik dan wel doen, m'n data-model overboord gooien?

Verder wil ik Afterlife vragen z'n "mysql considered harmfull"-betoog niet in dit topic te propageren ;)
Good practice tips zijn altijd erg welkom, in de talen die ik wel goed beheers ben ik daar ook een groot voorstander van, dus je hoeft m'n mentaliteit op dat gebied niet aan te pakken :D

Anders was ik niet aan een mooie JOIN-oplossing begonnen maar had het bij het verwijten van phpMyAdmin gelaten O-)
Pagina: 1