recette #11
Livre Guinness des records

Février 2011


Précédent

Table des matières

Suivant


Local Council

County

Region

ATRANI

SALERNO

CAMPANIA

BARDONECCHIA

TORINO

PIEMONTE

BRIGA ALTA

CUNEO

PIEMONTE

CASAVATORE

NAPOLI

CAMPANIA

LAMPEDUSA E LINOSA

AGRIGENTO

SICILIA

LU

ALESSANDRIA

PIEMONTE

MORTERONE

LECCO

LOMBARDIA

NE

GENOVA

LIGURIA

OTRANTO

LECCE

PUGLIA

PINO SULLA SPONDA DEL LAGO MAGGIOR

VARESE

LOMBARDIA

PREDOI

BOLZANO

TRENTINO-ALTO ADIGE

RE

VERBANO-CUSIO-OSSOLA

PIEMONTE

RO

FERRARA

EMILIA-ROMAGNA

ROMA

ROMA

LAZIO

SAN VALENTINO IN ABRUZZO CITERIORE

PESCARA

ABRUZZO

VO

PADOVA

VENETO


La liste de local councils ci dessus est une sorte de live Guinness des Records.
Pour une raison ou pour une autre, chacun possède une caractéristique exceptionnelle.
Peut-être êtes-vous étonnés, car (excepté Rome) aucune d'elles ne fait partie des lieux les plus renommées d'Italie.
Voici l'explication:

Mettre au point une telle table, aussi inutile soit-elle, est assez facile.
Voici la requête SQL correspondante.

SELECT lc.lc_name AS LocalCouncil,
  c.county_name AS County,
  r.region_name AS Region,
  lc.population AS Population,
  ST_Area(lc.geometry) / 1000000.0 AS "Area sqKm",
  lc.population / (ST_Area(lc.geometry) / 1000000.0)
    AS "PopDensity [peoples/sqKm]",
  Length(lc.lc_name) AS NameLength,
  MbrMaxY(lc.geometry) AS North,
  MbrMinY(lc.geometry) AS South,
  MbrMinX(lc.geometry) AS West,
  MbrMaxX(lc.geometry) AS East
FROM local_councils AS lc
JOIN counties AS c ON (c.county_id = lc.county_id)
JOIN regions AS r ON (r.region_id = c.region_id)
WHERE lc.lc_id IN (
    SELECT lc_id
    FROM local_councils
    WHERE population IN (
      SELECT Max(population)
      FROM local_councils
    UNION
      SELECT Min(population)
      FROM local_councils)
  UNION
    SELECT lc_id
    FROM local_councils
    WHERE ST_Area(geometry) IN (
      SELECT Max(ST_area(geometry))
      FROM local_councils
    UNION
      SELECT Min(ST_Area(geometry))
      FROM local_councils)
  UNION
    SELECT lc_id
    FROM local_councils
    WHERE population / (ST_Area(geometry) / 1000000.0) IN (
      SELECT Max(population / (ST_Area(geometry) / 1000000.0))
      FROM local_councils
    UNION
      SELECT MIN(population / (ST_Area(geometry) / 1000000.0))
      FROM local_councils)
  UNION
    SELECT lc_id
    FROM local_councils
    WHERE Length(lc_name) IN (
      SELECT Max(Length(lc_name))
      FROM local_councils
    UNION
      SELECT Min(Length(lc_name))
      FROM local_councils)
  UNION
    SELECT lc_id
    FROM local_councils
    WHERE MbrMaxY(geometry) IN (
      SELECT Max(MbrMaxY(geometry))
      FROM local_councils)
  UNION
    SELECT lc_id
    FROM local_councils
    WHERE MbrMinY(geometry) IN (
      SELECT Min(MbrMinY(geometry))
      FROM local_councils)
  UNION
    SELECT lc_id
    FROM local_councils
    WHERE MbrMaxX(geometry) IN (
      SELECT Max(MbrMaxX(geometry))
      FROM local_councils)
  UNION
    SELECT lc_id
    FROM local_councils
    WHERE MbrMinX(geometry) IN (
      SELECT Min(MbrMinX(geometry))
      FROM local_councils));


Et oui, cette requête est assez complexe.
Mais vous êtes dorénavant dans la section Haute cuisine.
Donc je suppose que vous êtes à la recherche de requêtes goûteuses et épicées : les voilà !

Après tout, cette requête n'est pas aussi complexe qu'elle en a l'aire, sa structure est même plutôt simple.
analysons la étape par étape:

SELECT lc.lc_name AS LocalCouncil,
  c.county_name AS County,
  r.region_name AS Region,
  lc.population AS Population,
  ST_Area(lc.geometry) / 1000000.0 AS "Area sqKm",
  lc.population / (ST_Area(lc.geometry) / 1000000.0)
    AS "PopDensity [peoples/sqKm]",
  Length(lc.lc_name) AS NameLength,
  MbrMaxY(lc.geometry) AS North,
  MbrMinY(lc.geometry) AS South,
  MbrMinX(lc.geometry) AS West,
  MbrMaxX(lc.geometry) AS East
FROM local_councils AS lc
JOIN counties AS c ON (c.county_id = lc.county_id)
JOIN regions AS r ON (r.region_id = c.region_id)
WHERE lc.lc_id IN (... some list of values ...);


Vous pouvez maintenant reconnaître l'architecture de cette requête et comprendre ce qu'elle fait.
Rien de trop complexe ni de difficile:

Vous savez déjà tout ça, et je suppose que la suite vous intéresse davantage.
La partie la plus intéressante est située dans la clause WHERE ... IN (...) 

...
SELECT Max(population)
FROM local_councils
...
SELECT Min(population)
FROM local_councils
...

Ce code SQL est vraiment simple: chaque requête détermine une valeur Min / Max.

...
  SELECT Max(population)
  FROM local_councils
UNION
  SELECT Min(population)
  FROM local_councils
...


C'est la première fois que nous introduisons la clause UNION:

...
SELECT lc_id
FROM local_councils
WHERE population IN (
    SELECT Max(population)
    FROM local_councils
  UNION
    SELECT Min(population)
    FROM local_councils)
...


SQL implémente un mécanisme fantastique: la sous-requete.
Vous pouvez définir une sous-requête, qui sera exécutée en premier, dont les résultats seront insérés dans la requête principale.
Maintenant, le code SQL ne paraît plus si mystérieux:

...
  SELECT lc_id
  FROM local_councils
  WHERE population IN (
      SELECT Max(population)
      FROM local_councils
    UNION
      SELECT Min(population)
      FROM local_councils)
UNION
  SELECT lc_id
  FROM local_councils
  WHERE ST_Area(geometry) IN
      SELECT Max(ST_area(geometry))
      FROM local_councils
    UNION
      SELECT Min(ST_Area(geometry))
      FROM local_councils)
...


Rien ne nous empêche de faire plus d'une UNION: cette requète est valide.
Ainsi, le code SQL ci-dessus s'interprète de cette façon:



SELECT lc.lc_name AS LocalCouncil,
  c.county_name AS County,
  r.region_name AS Region,
  lc.population AS Population,
  ST_Area(lc.geometry) / 1000000.0 AS "Area sqKm",
  lc.population / (ST_Area(lc.geometry) / 1000000.0)
    AS "PopDensity [peoples/sqKm]",
  Length(lc.lc_name) AS NameLength,
  MbrMaxY(lc.geometry) AS North,
  MbrMinY(lc.geometry) AS South,
  MbrMinX(lc.geometry) AS West,
  MbrMaxX(lc.geometry) AS East
FROM local_councils AS lc
JOIN counties AS c ON (c.county_id = lc.county_id)
JOIN regions AS r ON (r.region_id = c.region_id)
WHERE lc.lc_id IN (
--
-- a list of lc.lc_id values will be returned
-- by this complex sub-query
--
    SELECT lc_id
    FROM local_councils
    WHERE population IN (
--
-- this further sub-query will return
-- Min/Max POPULATION
--
      SELECT Max(population)
      FROM local_councils
    UNION
      SELECT Min(population)
      FROM local_councils)
  UNION -- merging into first-level sub-query
    SELECT lc_id
    FROM local_councils
    WHERE ST_Area(geometry) IN (
--
-- this further sub-query will return
-- Min/Max ST_AREA()
--
      SELECT Max(ST_area(geometry))
      FROM local_councils
    UNION
      SELECT Min(ST_Area(geometry))
      FROM local_councils)
  UNION -- merging into first-level sub-query
    SELECT lc_id
    FROM local_councils
    WHERE population / (ST_Area(geometry) / 1000000.0) IN (
--
-- this further sub-query will return
-- Min/Max POP-DENSITY
--
      SELECT Max(population / (ST_Area(geometry) / 1000000.0))
      FROM local_councils
    UNION
      SELECT MIN(population / (ST_Area(geometry) / 1000000.0))
      FROM local_councils)
  UNION -- merging into first-level sub-query
    SELECT lc_id
    FROM local_councils
    WHERE Length(lc_name) IN (
--
-- this further sub-query will return
-- Min/Max NAME-LENGTH
--
      SELECT Max(Length(lc_name))
      FROM local_councils
    UNION
      SELECT Min(Length(lc_name))
      FROM local_councils)
  UNION -- merging into first-level sub-query
    SELECT lc_id
    FROM local_councils
    WHERE MbrMaxY(geometry) IN (
--
-- this further sub-query will return
-- Max NORTH
--
      SELECT Max(MbrMaxY(geometry))
      FROM local_councils)
  UNION -- merging into first-level sub-query
    SELECT lc_id
    FROM local_councils
    WHERE MbrMinY(geometry) IN (
--
-- this further sub-query will return
-- Max SOUTH
--
      SELECT Min(MbrMinY(geometry))
      FROM local_councils)
  UNION -- merging into first-level sub-query
    SELECT lc_id
    FROM local_councils
    WHERE MbrMaxX(geometry) IN (
--
-- this further sub-query will return
-- Max WEST
--
      SELECT Max(MbrMaxX(geometry))
      FROM local_councils)
  UNION -- merging into first-level sub-query
    SELECT lc_id
    FROM local_councils
    WHERE MbrMinX(geometry) IN (
--
-- this further sub-query will return
-- Max EAST
--
      SELECT Min(MbrMinX(geometry))
      FROM local_councils));

D'après la syntaxe SQL, l'utilisation consécutive de deux tirets (--) permet de marquer un commentaire.
i.e. tout le texte situé après -- ne sera pas interprété par le moteur SQL.
 Commenter ces requêtes permet de rendre plus accessibles des requêtes complexes.



Conclusion: SQL est un langage fantastique, basé sur une syntaxe simple et intuitive.
Chaque fois que vous serez face à une requête complexe, ne soyez pas intimidés. Essayez simplement de la découper en plusieurs parties et vous découvrirez que la complexité était moins grande que ce que vous imaginiez.

Astuce: debugger une requête SQL très complexe peut vite devenir fastidieux. Le meilleur moyen d'y parvenir est de la découper en requêtes simples, et de les tester individuellement.


Précédent

Table des matières

Suivant


Author: Alessandro Furieri a.furieri@lqt.it
Traduced From English by RIVIERE Romain

This work is licensed under the Attribution-ShareAlike 3.0 Unported (CC BY-SA 3.0) license.


Permission is granted to copy, distribute and/or modify this document under the terms of the
GNU Free Documentation License, Version 1.3 or any later version published by the Free Software Foundation;
with no Invariant Sections, no Front-Cover Texts, and no Back-Cover Texts.