[SQL] Ingewikkeld probleem

Overzicht Reageren

Jona than

Jona than

12/06/2010 13:09:48
Quote
Momenteel ben ik bezig met een webshop waarin boeken verkocht gaan worden. Het systeem wordt met de hand gebouwd en is dus volledig op maat ontworpen om alles zo optimaal mogelijk te maken voor boeken.

De moeilijkheid zit hem nu in het veld met de auteur(s) van een boek. Het zit namelijk zo: een boek kan één of meerdere auteurs hebben. Indien er meerdere auteurs zijn, wordt er rekening gehouden met de volgorde (de eerstgenoemde auteur is vaak de 'belangrijkste' auteur). Tot slot kan het nog zijn dat er erg veel mensen mee hebben gewerkt die niet allemaal los genoemd worden, dan wordt achter het gewone rijte van (de) auteur(s) de tekst "en anderen" toegevoegd.

Om het wat minder abstract te maken, even een voorbeeldje van een aantal mogelijke opties:
Quote:
- Eric Cartman
- Eric Cartman, Stan Marsh en Kyle Broflovski
- Eric Cartman en anderen
- Eric Cartman, Stan Marsh en anderen

Voor de opslag van deze data heb ik voor de volgende opbouw gekozen:
Quote:
book
- id
- author-others*

book_author
- book_id
- author_id
- number**

author
- id
- first-name
- last-name

* Boolean voor de suffix "en anderen".
** Nummer van de auteur bij een bepaald boek, om de volgorde te bepalen. Iemand toevallig een idee voor een betere naam?

Het probleem zit hem nu in het ophalen van de data: hoe kan ik een lijst met boeken én bijbehorende auteurs (in een array) ophalen? Misschien lukt het met trucjes nog wel om de auteurs al door MySQL te formatteren tot de uiteindelijke string, maar dat is absoluut niet de bedoeling. Ik wil dat ik gewoon een array met de auteurs (id, first-name, last-name en number) krijg en die op welke manier dan ook kan verwerken. Een klein voorbeeldje van hoe ik deze data wil hebben vind je hier.

Mijn vraag is nu dus: hoe kan ik dit zo handig mogelijk doen? Ik vrees dat dit niet gaat lukken met één query, dus moet ik nu per boek nog een query uitvoeren om de auteurs op de halen? Of hebben jullie misschien goede ideeën hierover?
Gewijzigd op 12/06/2010 13:11:16 door Jona than
 
PHP hulp

PHP hulp

05/02/2012 02:34:01
Gesponsorde koppelingen:
 
Noppes Homeland

Noppes Homeland

12/06/2010 13:14:53
Quote
Wat voor query heb je zelf kunnen bedenken om het in 1 keer uit de database te kunnen halen.
 
- SanThe -

- SanThe -

12/06/2010 13:18:18
Quote
Jonathan DatBenIk op 12/06/2010 13:09:48:
Een klein voorbeeldje van hoe ik deze data wil hebben vind je hier.


503 Service Unavailable

Edit:
Hij doet het al weer ....
Gewijzigd op 12/06/2010 13:20:44 door - SanThe -
 
Jona than

Jona than

12/06/2010 13:21:53
Quote
@Noppes: Het probleem is dus dat ik zelf nog geen query hiervoor heb of kan bedenken. Het lukt natuurlijk best om dit met twee queries op te lossen ("SELECT * FROM book" en "SELECT author.*, book_author.number FROM author, book_author WHERE author.id = book_author.author_id AND book_author.book_id = %d ORDER BY book_author.number"), maar de bedoeling is toch om het efficiënter te doen.
 
Noppes Homeland

Noppes Homeland

12/06/2010 13:29:32
Quote
Wel het kan best efficienter zijn om het op deze manier te doen, maar uiteraard kan je de tabel book ook nog bij de 2de query erbij joinen.

Alleen wordt ik dan weer totaal gek van het gebruik van de * in de select-clause, benoem de velden waar je wat mee gaat doen!
 
Jona than

Jona than

12/06/2010 13:33:05
Quote
@Noppes: hoe bedoel je, de tabel book JOIN'en bij de query om de auteurs op te halen? Mijn hele bedoeling is om alles (zowel boeken als auteurs) in één querie te doen. Die * is natuurlijk alleen zo in dit voorbeeldje, in de praktijk benoem ik natuurlijk netjes de namen van de kolommen ;-)
 
Noppes Homeland

Noppes Homeland

12/06/2010 13:43:35
Quote
Wat is dan je probleem, want het principe van joinen schijn je te begrijpen
 
Jona than

Jona than

12/06/2010 13:51:49
Quote
@Noppes: Ik wil met één query een lijst met boeken ophalen, met daarin per boek een array met alle auteurs. En ik heb totaal geen idee hoe ik dat kan doen. Zie dus nog even mijn voorbeeld, dat is wat ik dus met één query wil verkrijgen.
 
Jelmer rrrr

Jelmer rrrr

12/06/2010 13:54:49
Quote
SQL levert altijd een twee-dimensional vlak aan data op. Als het niet in Excel past, kan je het niet ophalen met SQL. Zo simpel is het, en het zuigt. Had je maar geen SQL database moeten gebruiken.

De enige oplossing is om je "array" tot string te maken binnen de database, en dan wanneer je hem eruit hebt gehaald hem weer uit elkaar te trekken. GROUP_CONCAT kan hierbij helpen. Maar er is hoe dan ook een bewerkingsstap achteraf nodig.
 
Noppes Homeland

Noppes Homeland

12/06/2010 13:57:04
Quote
Je verwijst naar iets wat niet bestaat.

Aanname is dan dat je een hele mooie multidimensionale array wilt opzetten. Maar zolang jij niet toont - met bijvoorbeeld een stukje relevante php code - waar dan jouw probleem ligt, wordt het lastig om je de juiste richting in te trappen.


Note: een GROUPBY_CONCAT heb je niet nodig
Gewijzigd op 12/06/2010 14:02:03 door Noppes Homeland
 
Jona than

Jona than

12/06/2010 14:01:46
Quote
@Jelmer: Oké, als echt multidimensionaal niet kan is die GROUP_CONCAT een mooi compromis. Ik zal eens gaan proberen of dat gaat lukken. In ieder geval bedankt, dit was waart ik naar zocht!

@Noppes: Dat is (was) het probleem dus: ik had geen idee of dit bestond of mogelijk was, en zo ja, hoe precies. het probleem waar jij om vraagt was dus: ik wist niet hoe ik dit kon doen en had ook geen idee in welke richting ik had moeten zoeken. Waarschijnlijk gaat het wel lukken met het antwoord van Jelmer, maar in ieder geval bedankt voor het meedenken!
 
Arjan -

Arjan -

12/06/2010 14:18:28
Quote
Er van uitgaande dat elk boek minimaal één auteur heeft kan je ook het volgende doen:

Code (php)
PHP script in nieuw venster Selecteer het PHP script
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
SELECT
    book.id AS book_id,
    book.title AS book_title,
    author.first-name AS author_firstname,
    author.last-name AS author_lastname
FROM
    book_author
INNER JOIN
    author
ON
    author.id = book_author.author_id
INNER JOIN
    book
ON
    book.id = book_author.book_id
ORDER BY
    book_author.book_id ASC


Code (php)
PHP script in nieuw venster Selecteer het PHP script
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
26
<?php
$bookArray
= new array();

$counter = 0;
$currentBook = 0;

foreach($fetch AS $key => $value) {
    
    if($currentBook !== $value['book_id']) {
        $counter++;
        
        $bookArray[$counter]['id'] = $value['book_id'];    
        $bookArray[$counter]['title'] = $value['book_title'];
        
    }

        
    $bookArray[$counter]['authors'][] = $value['author_firstname'].' '.$value['author_lastname'];
    
    $currentBook = $value['book_id'];
    
}


echo '<pre>';
print_r($bookArray);
echo '</pre>';
?>


Hiermee krijg je een soortgelijk array als in jouw voorbeeld. (niet getest, maar zoiets zal moeten werken).
Gewijzigd op 12/06/2010 14:27:56 door Arjan -
 
Jona than

Jona than

12/06/2010 15:32:44
Quote
@Arjen: Dat is inderdaad ook nog een optie en nu begin ik te twijfelen wat beter is (Jelmers GROUP_CONCAT of jouw PHP-oplossing). Beide oplossingen hebben extra gedoe in zowel de query als erna in PHP staan.

Het verschil lijkt mij dan: Jelmers optie is sneller, je hebt namelijk veel minder rijen die verstuurd moeten worden en daarna verwerkt worden. Nadeel is wel dat het een beetje onbetrouwbaar is omdat je meerdere waarden achter elkaar plakt, dus weet je nooit of alles goed gaat. Jouw optie heeft die onbetrouwbaarheid niet, maar lijkt me wel weer een stuk langzamer...

Wat denken jullie dat de beste optie is?
 
Arjan -

Arjan -

12/06/2010 16:00:46
Quote
Als jij wil dat je volledige controle hebt over je weergave van de auteurs kan je beter voor mijn optie kiezen, omdat je dan alle gegevens van de auteurs (zoals id en naam) apart kunt opvragen en weergeven.

Maar je moet het vanzelfsprekend zelf bepalen. En zoveel langzamer is mijn methode niet hoor. Een foreach loop heeft php zo doorlopen.

En persoonlijk ben ik van mening dat je ook té veel op de performance kan letten terwijl het verschil in tijd vaak minimaal is en de bezoeker het niet merkt.

Een group_concat geeft een string terug. Wanneer je hier iets mee wilt doen dan zal je al snel uitkomen op het exploden van die string.....
Gewijzigd op 12/06/2010 16:03:15 door Arjan -
 
Jelmer rrrr

Jelmer rrrr

12/06/2010 16:25:34
Quote
Als je GROUP_CONCAT en CONCAT combineert, kan je waarschijnlijk wel meerdere kolommen eruit halen. Of gewoon meerdere GROUP_CONCAT kolommen, voor ieder veld een, en dan later die weer bij elkaar zoeken.

Jonathan, wat weet je nu al? Hoeveel boeken haal je gemiddeld op, en hoeveel auteurs heeft ieder boek gemiddeld? Ik vraag me af of die GROUP_CONCAT functies wel in de query cache blijven hangen. Zo niet, dan is de query met veel joins waarschijnlijk gemiddeld sneller. Maar als die idioot veel rows oplevert, dan is daar ook wel wat tegen te zeggen.

Beste methode: Allebei de queries maken, en testen. #opendeur
Gewijzigd op 12/06/2010 16:26:04 door Jelmer rrrr
 
Jona than

Jona than

12/06/2010 19:52:43
Quote
Ik heb gekozen voor de manier van Jelmer waarbij alles ge-CONCAT wordt. Om de verwerking in PHP wat eenvoudiger te maken en dus niet met de hand allemaal explode-trucjes te moeten doen, heb ik gezorgd dat de ge-CONCAT-te kolommen in JSON-formaat zijn en dus zonder veel moeite om te zetten zijn naar een array.

In verband met de grootte en overzichtelijkheid van de uiteindelijke code moet je even doorklikken en staat het niet hier. Graag ontvang ik suggesties als die er zijn ;-)

Dan nog over de snelheid. Ik heb nu als test zes boeken in de database gezet, één met twee auteurs en de rest met één auteur (zo'n verhouding is al vrij hoog, in de echte shop verwacht ik dat de verhouding lager ligt). Als ik nu een gewone SELECT doe (zonder de auteurs erbij) duurt dat gemiddeld 24ms, als ik de VIEW gebruik en dus ook de auteurs erbij neem duurt het gemiddeld 28ms. Het duurt dus zo'n 4ms langer, omgerekend zo'n 17% langer. De methode met een 'simpele' JOIN waarbij PHP de auteurs samenvoegt duurt 26ms, waardoor het nettoverschil uitkomt op 2ms of 8%. Voor mij dus zeer acceptabel.

Bedankt voor alle hulp!
Gewijzigd op 12/06/2010 20:04:28 door Jona than
 



Overzicht Reageren

Get Adobe Flash player