Softwareontwikkeling FAQ: SQLMet dank aan EfBe, ACM, curry684 en whoami. Laatste wijziging: zondag 05 oktober 2008.Inhoudsopgave
Links
BoekenOntwerpen van databases
SQL
Hoe werken joins?Met dank aan ACM en drm.Wanneer een LEFT (OUTER) join?Een left join gebruik je wanneer er een relatie bestaat tussen 2 entiteiten die 1:m is, waarbij m ? 0. Een voorbeeld hier van is de relatie "categorie" - "product". Een categorie hoeft geen producten te bevatten om een categorie te zijn. Er kunnen dus records in een categoriën tabel voorkomen waar geen producten naar verwijzen. Als je dan een LEFT JOIN doet, worden ook de categorien meegenomen waar geen producten naar verwijzen. In de velden uit de producten tabel die je dan in de SELECT clause hebt staan, komt NULL te staan.SQL:
Wanneer een INNER JOIN?Een inner join gebruik je wanneer je enkel die resultaten wilt, waarbij de relatie (vastgelegd in de JOIN clause) voorkomt. Om uit te gaan van bovenstaand voorbeeld: een lege categorie weergeven is niet interessant. Je kunt dat oplossen door een LEFT JOIN te gebruiken en een COUNT() los te laten op de primaire sleutel van de producten tabel, maar je kunt ook gewoon een INNER JOIN gebruiken:SQL:
Overigens is een andere manier om tot een INNER JOIN resultaat te komen de volgende manier: SQL:
Deze wijze van inner joinen staat ook wel bekend als de EQUI join, waar "equi" natuurlijk staat voor "gelijk". Wanneer een RIGHT (OUTER) JOIN?Een RIGHT join is feitelijk het tegenovergestelde van een LEFT JOIN. Je gebruikt deze wanneer bij een relatie n:1, waarbij n ? 0. Dus je zou bovenstaande query kunnen herschrijven tot een RIGHT join query door de 2 tabellen om te wisselen.![]() SQL:
Wanneer een FULL OUTER JOIN?Waarschijnlijk heb je nu zelf al bedacht wat een FULL OUTER JOIN doet.![]() Samenvatting tot nu toeOm het even duidelijk te maken de volgende plaatjes.Wanneer een CROSS JOIN?De cross join is een join die twee tabellen kruislings aan elkaar koppelt, dus voor elk record in tabel wordt een record van tabel2 bijgevoegd. Dit wordt ook wel een carthetisch product genoemd. Het voordeel van de cross join is dat je daarmee expliciet twee tabellen een carthetisch product kan laten vormen en verder alle andere tabellen op normale wijze kan joinen.Cross joins zou je kunnen gebruiken wanneer je bijvoorbeeld een tabel met vormen en een tabel met kleuren hebt en hier alle mogelijke combinaties van terug wilt krijgen. Wanneer een NATURAL JOIN?Het NATURAL-keyword zorgt ervoor dat de database automatisch die kolommen bij elkaar past waadoor de "meest natuurlijke" overeenkomst genomen wordt. Dat zal bijvoorbeeld de primary key van de ene tabel zijn die matched op een foreign key in de andere tabel of simpelweg een lijst van alle kolommen met eenzelfde naam.Je gebruikt de natural join als je wilt dat de database voor je uitzoekt welke kolommen de relatie het beste weergeven. Wat is database-indexering?Met dank aan MrX en EfBe.Indexes kunnen je systeem zowel sneller als langzamer maken. Een goeie strategie opzetten voor indexeren kan je behoorlijk wat performance schelen. Een index kost performance in de volgende gevallen:
Een performance voordeel van indexen is te behalen op de volgende wijzen:
Ga nadenken over welke kolommen je in één van de bovengenoemde manieren gebruikt en hoe vaak je dat doet, hoe veel gegevens er in de tabel staan, etc. Op basis daarvan neem je beslissingen op welke kolommen een index moet staan. Goeie vuistregels zijn:
Wat zijn Clustered Indexen?Met dank aan EfBe.Dit gedeelte en volgende gedeelte "Overwegingen" gelden voor SqlServer 2000, maar is in grote lijnen ook toepasbaar op andere (gelijkwaardige) RDBMS'en. Een clustered index bepaalt de fysieke volgorde van gegevens in een tabel. Een clustered index is vergelijkbaar met een telefoonboek, waarin gesorteerd wordt op een achternaam. Omdat een clustered index de fysieke gegevens-opslag in een tabel bepaalt, kan een tabel slechts één clustured index hebben. Echter, een index kan wel meerdere kolommen beslaan (samengestelde index), net als dat in een telefoonboek ook eerst op achternaam, en daarna op voorletters gesorteerd wordt. Een clustured index is vooral efficient op kolommen waarin vaak gezocht wordt op waarden of series van waarden. Zodra een record is gevonden waarin een waarde staat waarop gezocht wordt, betekent dat dat de volgende records ook een waarde hebben die daar dicht bij ligt. Bijvoorbeeld: wanneer een applicatie vaak een query uitvoert die records tussen 2 datums opvraagt, kan de clustered index heel snel de eerste record met de begin-datum vaststellen en alle volgende records totdat de eind-datum is bereikt. Zodoende wordt de performance van een dergelijke query sterk verbeterd. Ook wanneer vaak op een bepaalde kolom of kolommen wordt gesorteerd kan het een grote verbetering opleveren als juist op die kolommen een clustered index wordt gezet. Wanneer een bepaald veld in de tabel uniek is, kan een clustered index een goede performance-boost geven. De snelste manier om bijvoorbeeld een bepaalde werknemer op te zoeken is door op de kolom werknemer_id een PRIMARY KEY constraint te zetten, of dat veld een clustered index te geven. Opmerking: PRIMARY KEY constraints maken automatisch een clustered index op de tabel aan, wanneer er niet al een (andere) clustered index op de tabel bestaat en geen non-clustered index is gespecificeerd op het moment van zetten van de PRIMARY KEY. Een andere manier is om een clustered index te zetten op achternaam en voornaam, omdat werknemers vaak gegroepeerd en gesorteerd worden op deze velden, in plaats van op werknemer_id. OverwegingenHet is belangrijk om een clustered index op zo min mogelijk kolommen te zetten. Als een grote clustered index is gedefinieerd, worden non-clustered indexen ook direct stukken groter, omdat de non-clustered index-entries ook de geclusterde sleutel bevatten.Voordat je clustered indexen aanmaakt is het handig te weten hoe je gegevens opgevraagd zullen worden. In de volgende gevallen kan een clustered index overwogen worden:
Wat is dan een non-clustered index?Non-clustered indexen zijn indexen die in aparte indexeringstabellen opgeslagen worden.Hoe kom ik van dubbele resultaten in mijn query af?Door gebruik te maken van SELECT DISTINCT kun je in de meeste gevallen voorkomen dat een DBMS dubbele resultaten teruggeeft. Let wel op dat DISTINCT filtert op die velden die uniek zijn uit je SELECT-clause. Als er dus non-unieke velden voorkomen in de SELECT list krijg je nog niet de resultaten die je wilt hebben. Het is dan vaak ook beter om gebruik te maken van GROUP BY en aggregated functions als COUNT, MAX, SUM, etcetera. Zie voor meer informatie de betreffende documentatie van je DBMS.Over het gebruik van Parametrized QueriesMet dank aan whoami.Dit stuk is niet van toepassing op de combinatie PHP/MySQL. Over hoe je SQL injection e.d. kan voorkomen lees je meer in de PHP FAQ over escapen in MySQL en beveiliging van websites. In dit voorbeeld wordt gebruik gemaakt van C# en MS SQL Server. Sla de documentatie van jouw gebruikte platform en DBMS er op na of parametrized queries ondersteund worden. Database applicaties maken gebruik van SQL om gegevens in een databank te stoppen en gegevens eruit te halen. Al te vaak zie je maar dat mensen hun SQL statements door middel van string concatenation opbouwen, bv: Fout C#:
Dit is echter een slechte manier om je queries op te bouwen. Allereerst is het moeilijk leesbaar, moeilijk onderhoudbaar, maar het weegt vooral op de performance van je query en het zorgt ook voor een veiligheidsrisico. Waarom weegt dit op de performance?Door de manier waarop je je query hebt opgebouwd, kan hij niet gecached worden door het DBMS als hij later nog eens moet worden uitgevoerd. Nu kan je zeggen: Ok, ik voer die query zowiezo slechts 1x uit. Wat is dat dan ivm die veiligheid?![]() Stel dat je in txtNaam een naam ingeeft waar een quote in voorkomt. Dan moet jij als programmeur allerhande truuks gaan toepassen om die quote goed in de DB te krijgen. Aangezien we ons niet graag met geneuzel bezig houden, en dat bovendien nogal eens foutgevoelig kan zijn, gaan we liever een andere methode gebruiken namelijk, parametrized queries. ![]() Wat is dat dan?Bij deze methode hoef je je als programmeur helemaal geen zorgen meer te maken over die quotes.In plaats van je query met behulp van string concatenation samen te stellen, ga je hem als volgt schrijven: C#:
Zoals je kunt zien gaan we nu nog geen waarden in de query gaan stoppen, maar maken we gebruik van parameters. Die kan je als 'placeholders' beschouwen waar we later de waarden gaan invullen. Het voordeel van deze aanpak is dat het DBMS in staat is om de query te cachen. Het optimale execution plan wordt dus bewaard, en zal hergebruikt worden als die query nog eens uitgevoerd wordt. (Dit is vooral van belang bij SELECT queries). Wat moet je nu doen om die parameters te gaan opvullen?De DbCommand class heeft een property 'Parameters'. Je zult daarmee dus aan de slag moeten:C#:
Zoals je ziet gaan we de 2 parameters 'kenbaar' maken bij het DbCommand object. We geven de naam van de parameter mee, en het datatype, daarna geven we de waarde die moet gebruikt worden ook mee. Indien er quotes oid in de naam staan, dan zal het DBMS er zelf voor zorgen dat die naam probleemloos in de DB ingevoerd wordt. Verder kan je ook nog bijkomende zaken over die parameter specifieren, zoals: is het een input of een outputparameter, etc... Om daar meer informatie over te verkrijgen kan je eens kijken naar de verschillende overloads van de Add() method, of naar de SqlParameter of OleDbParameter class (en de betreffende constructors). Als je gebruik maakt van stored procedures, dan kan je natuurlijk dezelfde werkwijze ivm parameters gebruiken. Opgepast: Als je gebruik maakt van de OleDbClient, moet je je parameters in dezelfde volgorde 'adden' zoals je ze in je SQL statement gedefinieerd hebt. Dit komt omdat OleDb geen rekening gaat gaan houden met de parameternamen. Hoe werkt dat GROUP BY nu eigenlijk?Met dank aan curry684.De GROUP BY clausule is een krachtig onderdeel van SQL, en tegelijk een van de minst begrepen helaas. In dit stukje zal ik proberen helder en duidelijk uit te leggen wat het nu eigenlijk doet, en wat het niet doet. Bij de voorbeelden gaan we even uit van de volgende tabel met data (alle data is integers):
Wat doet GROUP BY?Middels een GROUP BY-clausule in je SELECT-query kun je de database vragen om identieke resultaten te groeperen, oftewel 'samen te vatten'. Om het bovenstaande voorbeeld even te pakken met de eenvoudigste GROUP BY query:SQL:
Dit geeft 2 rows, namelijk:
wat alleszins duidelijk en logisch is. Het wordt moeilijker als je meerdere kolommen wil hebben. Stel je de volgende query eens voor: SQL:
SQL Server, Oracle en andere fatsoenlijke databases slikken deze query niet. Om SQL2000 vrij te citeren: Waarom zegt ie dat? Kijk eens mee naar de tabel waar we deze query op doen. We vragen hem te groeperen op identieke waarden in kolom A. Echter, als we dat doen, welke waarde van B moeten we dan selecteren bij waarde 1 in A? Daar kan je zowel 1 als 2 bij verwachten in kolom B, en omdat dat niet gespecificeerd is staat de database het terecht niet toe. MySQL is een hele brakke database, die deze laatste constructie wel toestaat. En volgens de handleiding is het 'by design' dat je vervolgens random waardes in kolom B aantreft. Don't do it. Aggregate functiesUit de Van Dale:Om dit probleem op te lossen hebben we 'Aggregate functies'. Waar het in bovenstaand voorbeeld onduidelijk was hoe B samengevat moest worden om bij de unieke A's te passen, kunnen we de DBMS natuurlijk vertellen welke waarde hij dan moet gebruiken. Zie bijvoorbeeld deze query: SQL:
Hier komt het volgende resultaat uit:
Door de max-functie los te laten op kolom B, hebben we de database duidelijk gemaakt hoe hij moet groeperen. Dus in dit geval groupeert hij op A, en vervolgens laat ie de 'aggregerende functie' los op de verzamelde waardes van B die bij die ene A terecht horen. Andere voorbeelden van aggregate functies zijn min(), avg() en count(). Een ander voorbeeld: SQL:
Oftewel we grijpen de A's bij elkaar, en selecteren daarbinnen de hoogste B's en het aantal C's dat we hebben 'weggegroepeerd'. Hoeveel kolommen kan ik aggregeren en groeperen?Zoveel je wilt.![]() SQL:
levert op:
Wat is hier nu gebeurd? Allereerst hebben we gegroepeerd op A's, en daarna op de B's. Van de resulterende set hebben we vervolgens het aantal C's genomen dat 'weggeaggregeerd' is, en dat zijn er inderdaad 2 per groep. Klopt exact dus. ![]() |
[Voor 222% gewijzigd door NMe op 21-12-2019 18:17]
'E's fighting in there!' he stuttered, grabbing the captain's arm.
'All by himself?' said the captain.
'No, with everyone!' shouted Nobby, hopping from one foot to the other.