Avatar billede ultraskytte Nybegynder
02. oktober 2012 - 09:34 Der er 9 kommentarer og
1 løsning

Sql problem vedr. join af to tabeller

Kære forum

Jeg er igang med at programmere nogle hjemmelavede fix til mit phpbb-forum, men jeg er stød tpå et database-problem, jeg har svært ved at løse. Håber, der er nogle kvikke/erfarne hoveder, der kan komme med et fornuftigt bud på, hvordan jeg kan løse problemet.

Min database indeholder følgende to tabeller (skåret ind til benet!):

mysql> explain database topics;

-----------+-----------------------+------+-----+---------+-------+
| Field    | Type                  | Null | Key | Default | Extra |
+-----------+-----------------------+------+-----+---------+-------+
| topic_id  | mediumint(8) unsigned | NO  | PRI | 0      |      |
+-----------+-----------------------+------+-----+---------+-------+

mysql> explain database topics_track;

+-----------+-----------------------+------+-----+---------+-------+
| Field    | Type                  | Null | Key | Default | Extra |
+-----------+-----------------------+------+-----+---------+-------+
| user_id  | mediumint(8) unsigned | NO  | PRI | 0      |      |
| topic_id  | mediumint(8) unsigned | NO  | PRI | 0      |      |
| mark_time | int(11) unsigned      | NO  |    | 0      |      |
+-----------+-----------------------+------+-----+---------+-------+

Table topics indeholder alle topics på forummet, og topics_track indeholder alle læste topics for brugerne, hvor mark_time angiver tidspunktet for læsningen. Har en bruger ikke læst en topic, figurer han dog ikke i tabellen.

Jeg ønsker at vise en liste, der indeholder både læste og ulæste topics for en specifik bruger med en status læst/ulæst tilknyttet hver topic.

Hvis jeg benytter følgende sql (fx for bruger med id 53):

SELECT T.topic_id, TT.mark_time FROM topics as T LEFT JOIN topics_track as TT ON TT.topic_id=T.topic_id WHERE TT.user_id=53 order by T.topic_id;

så vil jeg ikke få de topics med, som brugeren ikke har læst endnu, da disse ikke figurer i topics_track. Hvis jeg på en eller anden måde kunne få dem med, men hvor mark_time fx havde værdien NULL på de topics, der ikke var læste, så ville jeg have noget brugbart. Har dog lidt svært ved at lure, hvordan det lige kan lade sig gøre.

Håber, der er nogen, der kan komme med et fornuftigt bud!

mvh.

Thomas
02. oktober 2012 - 11:40 #1
Jeg vil foreslå en UNION query, hvor du først søger efter de topic_ids læst af bruger 53, og derefter søger efter de topics der ikke i topic_tracs har en bruger_id 53.  Såsom (ikke testet):

SELECT T.topic_id, TT.mark_time, 'laest'
FROM topics T
JOIN topics_track TT ON TT.topic_id = T.topic_id
WHERE TT.user_id = 53
UNION
SELECT topic_id, '', 'ulaest'
FROM topics
WHERE topic_id NOT IN(SELECT topic_id FROM topic_track WHERE user_id = 53)
ORDER BY topic_id
Avatar billede ultraskytte Nybegynder
02. oktober 2012 - 12:33 #2
Tak for buddet, Christian. Har selv overvejet UNION. Det skal dog nævnes, at listen bliver genereret globalt under forummet, så sql'en skulle gerne være nogenlunde hurtig, hvis databasen skal kunne holde til den. Brug af både UNION og IN er temmelig hårde kald for databasen (specielt sidstnævne). Jeg var mere på udkig efter en god JOIN løsning, hvor man kunne få alle topics med i table topics også selvom der ikke var en tilsvarende værdi i table topics_track - og i disse tilfælde få en NULL-værdi el.lign. ud som mark_time. Er slet ikke klar over, om problemet kan løses. I så fald skal jeg gerne give point for dit svar :-)
02. oktober 2012 - 20:41 #3
Om det kan løses uden UNION ved jeg ikke (jeg ventede med dette svar for at se, om andre i mellemtiden havde en løsning).  Jeg eksperimenterede med IF, men fandt ikke noget der virkede.  Jeg opretter svar, som du bad om.
Avatar billede arne_v Ekspert
03. oktober 2012 - 03:54 #4
proev:

SELECT t.topic_id, tt.mark_time
FROM topics as t LEFT JOIN topics_track as tt ON tt.topic_id=t.topic_id
WHERE (tt.user_id=53 OR tt.user_id IS NULL) order by T.topic_id;
Avatar billede ultraskytte Nybegynder
03. oktober 2012 - 09:03 #5
Tak for buddet, Arne. Sql'en virker dog ikke, men crasher databasen. Table topics_track er selvfølgelig meget stor, og ved at benytte "user_id IS NULL"
medtages alle resultater på samtlige user_id, der endnu ikke forekommer i topics_track - samt resultater på user_id 53. Det er en meget stor mængde data!
Avatar billede ultraskytte Nybegynder
07. oktober 2012 - 09:37 #6
Er du vitterlig ikke nogen, der kan løse denne sql udfordring? Det er jo en relativ generel problematik indenfor anvendelse af sql JOINs:

Der skal uddrages alle poster fra tabel A med en tilhørende værdi V fra tabel B, hvor de to tabeller har en id relation. Hos de poster fra A uden sammenfald med B skal returneres NULL i stedet for V.

Det må da være en typisk problemstilling rigtig mange før mig er stødt på?

Ingen bud?
Avatar billede arne_v Ekspert
07. oktober 2012 - 15:59 #7
Det er et kendt problem med en kendt loesning:

mysql> CREATE TABLE topics_track (
    ->    user_id INTEGER NOT NULL,
    ->    topic_id INTEGER NOT NULL,
    ->    mark_time VARCHAR(50),
    ->    PRIMARY KEY (user_id,topic_id)
    -> );
Query OK, 0 rows affected (0.02 sec)

mysql>
mysql> INSERT INTO topics VALUES(1);
Query OK, 1 row affected (0.00 sec)

mysql> INSERT INTO topics VALUES(2);
Query OK, 1 row affected (0.00 sec)

mysql> INSERT INTO topics VALUES(3);
Query OK, 1 row affected (0.00 sec)

mysql>
mysql> INSERT INTO topics_track VALUES(52, 1, 'Mandag');
Query OK, 1 row affected (0.02 sec)

mysql> INSERT INTO topics_track VALUES(52, 2, 'Tirsdag');
Query OK, 1 row affected (0.01 sec)

mysql> INSERT INTO topics_track VALUES(53, 1, 'Onsdag');
Query OK, 1 row affected (0.00 sec)

mysql> INSERT INTO topics_track VALUES(53, 2, 'Torsdag');
Query OK, 1 row affected (0.00 sec)

mysql>
mysql> SELECT * FROM topics;
+----------+
| topic_id |
+----------+
|        1 |
|        2 |
|        3 |
+----------+
3 rows in set (0.00 sec)

mysql> SELECT * FROM topics_track;
+---------+----------+-----------+
| user_id | topic_id | mark_time |
+---------+----------+-----------+
|      52 |        1 | Mandag    |
|      52 |        2 | Tirsdag  |
|      53 |        1 | Onsdag    |
|      53 |        2 | Torsdag  |
+---------+----------+-----------+
4 rows in set (0.00 sec)

mysql>
mysql> SELECT t.topic_id, tt.mark_time
    -> FROM topics t JOIN topics_track tt ON t.topic_id=tt.topic_id
    -> ORDER BY t.topic_id;
+----------+-----------+
| topic_id | mark_time |
+----------+-----------+
|        1 | Mandag    |
|        1 | Onsdag    |
|        2 | Torsdag  |
|        2 | Tirsdag  |
+----------+-----------+
4 rows in set (0.00 sec)

mysql>
mysql> SELECT t.topic_id, tt.mark_time
    -> FROM topics t JOIN topics_track tt ON t.topic_id=tt.topic_id
    -> WHERE tt.user_id=53
    -> ORDER BY t.topic_id;
+----------+-----------+
| topic_id | mark_time |
+----------+-----------+
|        1 | Onsdag    |
|        2 | Torsdag  |
+----------+-----------+
2 rows in set (0.01 sec)

mysql>
mysql> SELECT t.topic_id, tt.mark_time
    -> FROM topics t LEFT JOIN topics_track tt ON t.topic_id=tt.topic_id
    -> ORDER BY t.topic_id;
+----------+-----------+
| topic_id | mark_time |
+----------+-----------+
|        1 | Mandag    |
|        1 | Onsdag    |
|        2 | Torsdag  |
|        2 | Tirsdag  |
|        3 | NULL      |
+----------+-----------+
5 rows in set (0.00 sec)

mysql>
mysql> SELECT t.topic_id, tt.mark_time
    -> FROM topics t LEFT JOIN topics_track tt ON t.topic_id=tt.topic_id
    -> WHERE tt.user_id=53 ORDER BY t.topic_id;
+----------+-----------+
| topic_id | mark_time |
+----------+-----------+
|        1 | Onsdag    |
|        2 | Torsdag  |
+----------+-----------+
2 rows in set (0.00 sec)

mysql>
mysql> SELECT t.topic_id, tt.mark_time
    -> FROM topics t LEFT JOIN topics_track tt ON t.topic_id=tt.topic_id
    -> WHERE (tt.user_id=53 OR tt.user_id IS NULL)
    -> ORDER BY t.topic_id;
+----------+-----------+
| topic_id | mark_time |
+----------+-----------+
|        1 | Onsdag    |
|        2 | Torsdag  |
|        3 | NULL      |
+----------+-----------+
3 rows in set (0.00 sec)

mysql>
mysql> DROP TABLE topics;
Query OK, 0 rows affected (0.00 sec)

mysql> DROP TABLE topics_track;
Query OK, 0 rows affected (0.00 sec)
Avatar billede arne_v Ekspert
07. oktober 2012 - 16:02 #8
Hvis du vil have flere raekker retur end din database og/eller app kan klare, saa har du et problem. Men det problem kan vi ikke loese.
Avatar billede ultraskytte Nybegynder
11. oktober 2012 - 19:07 #9
Hej Arne

Jeg tror ikke, vi kommer videre. Men jeg synes, du er den, der kommer tættest på et svar, så hvis du vil åbne op for en pointgivning for sidste svar, så godkender jeg det.

mvh.

Thomas
Avatar billede arne_v Ekspert
11. oktober 2012 - 19:35 #10
ok
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