Avatar billede jesperhaun Nybegynder
09. oktober 2002 - 10:37 Der er 34 kommentarer og
1 løsning

Left join i MySQL

Jeg vil gerne have hjælp til en left-join. Denne query viser de poster, der har count>0 (altså hvor join passer og der er records på begge sider). Jeg vil også gerne have de records, der giver 0 på den anden side. Det vil sige, hvor s.sitename har 0 d.urlid.

select s.sitename, count(d.urlid)
from start_urls s, urls_done d
where s.urlid=d.profid and
d.time>'20021009'
group by s.sitename
Avatar billede erikjacobsen Ekspert
09. oktober 2002 - 10:41 #1
select s.sitename, count(d.urlid)
from start_urls s left join urls_done d
on s.urlid=d.profid where
d.time>'20021009'
group by s.sitename

tror jeg nok - prøv lige
Avatar billede jesperhaun Nybegynder
09. oktober 2002 - 10:45 #2
Ser ikke ud til at virke. Databasen går helt i knæ på forespørgslen. Der kommer time-out i stedet for svar...
Avatar billede erikjacobsen Ekspert
09. oktober 2002 - 10:47 #3
Hvor store er tabellerne - og hvor er der indexer på?
Avatar billede jesperhaun Nybegynder
09. oktober 2002 - 10:52 #4
urls_done er kæmpestor. Flere hundrede tusinde records. Kun index på urlid.

urls_done rummer 327 records. Index på urlid.
Avatar billede jesperhaun Nybegynder
09. oktober 2002 - 10:55 #5
ups sidste linje skulle have sagt start_urls rummer 327...
Avatar billede erikjacobsen Ekspert
09. oktober 2002 - 10:59 #6
Du skal nok også have index på "profid" i urls_done.
Ellers bliver din join for langsom - gætter jeg på :)
Avatar billede erikjacobsen Ekspert
09. oktober 2002 - 11:00 #7
Hovsa - måske du mener det her i stedet for

from urls_done d left join start_urls s

så du får de ca 400 tupler ud ??
Avatar billede erikjacobsen Ekspert
09. oktober 2002 - 11:04 #8
Nu bytter jeg om på dine navne .... start_urls er den lille.
Ikke sandt.

Har du mulighed for at teste med færre tupler for at se om
princippet virker.
Avatar billede jesperhaun Nybegynder
09. oktober 2002 - 11:08 #9
start_urls er lille. Men forespørgslen returerer få svar, for datoen (d.time) gælder jo kun én dag. Resultatet bliver 327 records, der rummer et navn og et tal.
Avatar billede jesperhaun Nybegynder
09. oktober 2002 - 11:10 #10
Den SQL-streng, jeg skrev i toppen, returnerer de 198 records fra start_urls, der har records fra i dag af i urls_done (og viser totallen). Det, som jeg er ude efter, er de resterende 129 records fra start_urls, der har 0 records fra i dag i urls_done.
Avatar billede erikjacobsen Ekspert
09. oktober 2002 - 11:13 #11
Og det tror jeg også den giver. Men det tager sikkert for lang tid
uden index på begge felter i den join.
Avatar billede jesperhaun Nybegynder
09. oktober 2002 - 11:15 #12
Jeg kan prøve igen. Skal jeg prøve din oprindelige SQL - eller den fra 11:00:57, hvor du har byttet rundt?
Avatar billede erikjacobsen Ekspert
09. oktober 2002 - 11:25 #13
10:41:10 skulle være god nok. Jeg har ikke lige tid til at
lave en test på små tabeller
Avatar billede jfl Nybegynder
09. oktober 2002 - 11:43 #14
Er dette svaret:
If there is no matching record for the right table in the ON or USING part in a LEFT JOIN, a row with all columns set to NULL is used for the right table. You can use this fact to find records in a table that have no counterpart in another table:
mysql> SELECT table1.* FROM table1
    ->        LEFT JOIN table2 ON table1.id=table2.id
    ->        WHERE table2.id IS NULL;

This example finds all rows in table1 with an id value that is not present in table2 (that is, all rows in table1 with no corresponding row in table2). This assumes that table2.id is declared NOT NULL, of course.
http://www.mysql.com/doc/en/JOIN.html
Avatar billede jfl Nybegynder
09. oktober 2002 - 11:49 #15
SELECT start_urls.sitename FROM start_urls LEFT JOIN urls_done ON start_urls.urlid = urls_done.profid WHERE urls_done.profid IS NULL AND urls_done.time > '20021009' GROUP BY start_urls.sitename;
Avatar billede jesperhaun Nybegynder
09. oktober 2002 - 12:07 #16
select s.sitename
from start_urls s left join urls_done d
on s.urlid=d.profid where
d.profid IS NULL and
d.time>'20021009'
group by s.sitename

giver 0 resultater (har prøvet med og uden sidste linje med group by)
Avatar billede jfl Nybegynder
09. oktober 2002 - 12:37 #17
Hvad giver denne?
SELECT start_urls.sitename FROM start_urls LEFT JOIN urls_done ON start_urls.urlid = urls_done.profid WHERE urls_done.profid IS NULL;
Avatar billede jfl Nybegynder
09. oktober 2002 - 12:42 #18
Alternativt kan du gøre det via 2 selects, som hvis du har store tabeller vil være hurtigere.
1. select henter de id'er som skulle være gennemført.
2. select henter de id'er som ikke blev gennemført.
Avatar billede jfl Nybegynder
09. oktober 2002 - 12:44 #19
brug WHERE urlid NOT IN (1,2,3,4)
Avatar billede limemedia Nybegynder
09. oktober 2002 - 14:36 #20
Har du evt. lidt data vi må lege med ?

Udfra kode jeg har lanceret i mindre fora, burde følgende virke
SELECT s.sitename, count(d.urlid)
FROM start_urls AS s LEFT JOIN urls_done AS d
WHERE s.urlid = d.profid && d.time > '20021009'
GROUP BY s.urlid

men uden at kunne teste på reelt indhold er det selvfølgeligt svært
Avatar billede jesperhaun Nybegynder
09. oktober 2002 - 16:45 #21
Jeg får SQL fejl "near where...." og jeg har prøvet at erstatte && med AND. Samme fejl. Kigger lige på det der med data.
Avatar billede jesperhaun Nybegynder
09. oktober 2002 - 16:50 #22
JFL 12:37:30 .. besynderligt. Den SQL giver 8 records. Jeg kan ikke forstå hvorfor. Nå men jeg skulle have 129.
Avatar billede jfl Nybegynder
09. oktober 2002 - 16:51 #23
du kan evt. prøve ljweb's uden AS'erne. Jeg er ikke sikker på at AS kan bruges sammen med JOIN.
Avatar billede limemedia Nybegynder
09. oktober 2002 - 17:29 #24
hvilken mysql version kører du med ? jeg bruger en let omskrevet version på en 3.23.51a på FreeBSD platform. Der virker AS navngivningen fint - også && fremfor AND, gammel vane jeg har
Avatar billede jesperhaun Nybegynder
13. oktober 2002 - 11:12 #25
AS virker fint nok.

Flere garvede MySQL-brugere har hjulpet mig med dette, og de siger nu, at der IKKE er noget galt med SQL-syntaksen. Problemet må ligge i MySQL. Det er en LEFT JOIN lige efter lærebogen, den virker bare ikke.

Jeg har prøvet at indeksere felterne i urls_done, og det giver ingen forskel.
Avatar billede erikjacobsen Ekspert
13. oktober 2002 - 11:51 #26
Kan du vise os tabel definitionerne?
Avatar billede jesperhaun Nybegynder
13. oktober 2002 - 14:18 #27
CREATE TABLE urls_done (
  URLID int(15) NOT NULL auto_increment,
  URLSTATUS int(1) NOT NULL default '0',
  TRIES int(1) NOT NULL default '1',
  PROFID int(12) NOT NULL default '0',
  URL mediumtext NOT NULL,
  time timestamp(14) NOT NULL,
  PRIMARY KEY  (URLID),
  KEY url_idx (URL(254))
) TYPE=MyISAM;

CREATE TABLE start_urls (
  urlid int(10) NOT NULL auto_increment,
  url text NOT NULL,
  URLSTATUS tinyint(1) NOT NULL default '0',
  sitename varchar(200) NOT NULL default '',
  catid int(10) NOT NULL default '1',
  retrstatus tinyint(1) NOT NULL default '0',
  tries tinyint(1) NOT NULL default '0',
  PRIMARY KEY  (urlid),
  KEY urlid_idx (urlid),
  KEY urlstat_idx (URLSTATUS)
) TYPE=MyISAM;
Avatar billede jesperhaun Nybegynder
13. oktober 2002 - 14:29 #28
Jeg tror, at problemet er, at urls_done.time kan ikke være større end '20021009' samtidig med, at urls_done.profid er NULL.

Derfor virker JFL's svar fra 12:37:30. Det SQL viser de sitenames, der aldrig har haft records i urls_done. Sætter man dato-begrænsning på, giver SQL-forespørgslen ikke mening.
Avatar billede erikjacobsen Ekspert
13. oktober 2002 - 15:03 #29
Du skal have index på profid, da den indgår i din join. Jo, det med
dato og null er i sig selv ikke et problem - nu skal vi lige have den til
at give et resultat først.
Avatar billede jesperhaun Nybegynder
13. oktober 2002 - 16:12 #30
Der har været index på profid de sidste 3 dage. Vi har lige slettet det igen, da det ikke hjalp.
Avatar billede erikjacobsen Ekspert
13. oktober 2002 - 16:27 #31
Hmm - det ville være dejligt at kunne prøve selv
på dine originale data ... men det er vel udelukket ? :)
Avatar billede jesperhaun Nybegynder
13. oktober 2002 - 16:30 #32
Ja.. Det er desværre et system i produktion, og de ovenstående forespørgsler får reelt serveren til at gå i knæ med 100 % cpu udnyttelse. Derfor er jeg ikke glad for eksperimenter. Men jeg prøver selv at skabe disse tabeller i et testmiljø og lægge en håndfuld rækker ind.
Avatar billede jesperhaun Nybegynder
13. oktober 2002 - 16:40 #33
select s.sitename
from start_urls s left join urls_done d
on s.urlid=d.profid where
d.profid IS NULL

Virker fint. Den returnerer de rækker, der aldrig har haft tilhørende rækker i urls_done. Derfor virker JFL's svar fra 12:37:30. Men det løser naturligvis ikke problemet, for jeg er kun interesseret i data fra sidste døgn.

Men hvis jeg sætter dato-delen ind, så fejler forespørgslen. Den returnerer hverken rækker eller en fejl. Det vil sige "AND d.time>'20021009'". Det har ingen betydning, om time kommer før profid.
Avatar billede jesperhaun Nybegynder
20. oktober 2002 - 11:00 #34
Problemet er, som jeg skrev tidligere, at urls_done.time kan ikke være større end '20021009' samtidig med, at urls_done.profid er NULL. Derfor kan problemet slet ikke løses med en LEFT JOIN. Man skal bruge SUBSELECT, og det understøtter MySQL ikke endnu.
Avatar billede limemedia Nybegynder
20. oktober 2002 - 11:26 #35
Nu er det jo en del nemmere at sige end gennemføre, men med så mange rækker begynder tankerne at svirre hen mod større db-systemer - måske man skulle kigge lidt på PostgreSQL der også er opensource.

Ad subselects er der i manualen denne
http://www.mysql.com/doc/en/ANSI_diff_Sub-selects.html

Da opgaven lyder mere administrativ end noget der kører i drift, vil en hack-løsning eventuelt kunne løses med
http://www.mysql.com/doc/en/INSERT_SELECT.html
i forbindelse med en temporær tabel, men det er noget hack-værk
Avatar billede Ny bruger Nybegynder

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.

Loading billede Opret Preview
Kategori
Computerworld tilbyder specialiserede kurser i database-management

Log ind eller opret profil

Hov!

For at kunne deltage på Computerworld Eksperten skal du være logget ind.

Det er heldigvis nemt at oprette en bruger: Det tager to minutter og du kan vælge at bruge enten e-mail, Facebook eller Google som login.

Du kan også logge ind via nedenstående tjenester