Sommaire
AprĂšs avoir lu la rĂ©cente dĂ©pĂȘche de BenoĂźt, jâai voulu rĂ©viser mon SQL⊠(non, en fait seulement faire « mumuse » car je bouffe dĂ©jĂ assez de SQL ces temps ci.) Ceci est mon retour dâexpĂ©rience.
prise en mains
Commençons simplement par le premier cas.
On arrive sur lâonglet « Case Brief » (soit « RĂ©sumĂ© du Cas » en gros), que jâai vite passĂ© pour cliquer sur lâonglet « SQL Workspace » (soit « Espace de travail » mais) on comprend vite que câest la fenĂȘtre pour faire les requĂȘtes.
inventaire des objets
Chiche, beaucoup vont pouvoir plonger tĂȘte baissĂ©e dans du « requĂȘtage » sauvage. đŽââ ïž Mais quels objets se trouvent dans ce schĂ©ma ? Mes souvenirs (et quelques notes) me disent :
- Oracle :
SELECT owner, table_name FROM dba_tables;
âŠsi on a de supers droits (les vues commençant pardba_
sont rĂ©servĂ©es Ă lâusage des DBA), ou alorsSELECT owner, table_name FROM all_tables;
qui devrait lister toutes les tables auxquelles lâon peut accĂ©der⊠Minute papillon, il y a aussiSELECT table_name FROM user_tables;
qui ne liste que les tables qui nous appartiennent (un peu comme si on avait fait plutĂŽt unSELECT table_name FROM all_tables WHERE owner = (SELECT user FROM DUAL);
directement.) Je me souviens aussi deSELECT * FROM tab;
mais je ne sais pas si câest officiel ou si le « hack » est toujours supportĂ©.
Pour les vues, il faut remplacer_tables
par_views
et donc faire lâunion pour les deux. Jâaime bien trier partable_name
. - MySQL et MariaDBÂ :
SHOW TABLES
simplement pour les deux, et il faut utiliserSHOW FULL TABLES
pour avoir lâinformation de qui est qui. On peut filtrer sur le nom des tables et vue avecLIKE
mais on ne peut pas trier la rĂ©sultat renvoyĂ©. - SQL Server (en tout cas pour le souvenir que jâai des versions 2000 et
précédentes) :
SELECT * FROM sysobjects WHERE xtype = 'U'
- SQLite : depuis le client en ligne de commande, lâincantation
.tables
(qui peut ĂȘtre abrĂ©gĂ©.table
ou mĂȘme.ta
) permet de lister tables et vues en filtrant éventuellement sur le nom mais sans pouvoir trier le résultat.
Depuis fin novembre 2021 (version 3.37 si je me souviens bien), on peut aussi lancer le sortPRAGMA table_list;
depuis nâimporte quelle session (autrement que via la CLI officielle donc) et obtenir une table sur laquelle on nâa pas plus de contrĂŽle mais qui offre plus dâinformations (la colonne « type » par exemple indique si câest une table ou une vue). Ce « pragma » ajoute des informations en plus (ncol
pour le nombre de colonnes,wr
pour indiquer si la table est créé avecWITHOUT ROWID
â1â ou pas â0â,strict
pour indiquer si table est crĂ©Ă© avec lâoptionSTRICT
ou pas,schema
) comparĂ© Ă la requĂȘteSELECT name, type FROM sqlite_schema;
qui permet filtrage et tri⊠Historiquement (jâespĂšre quand mĂȘme que vous avez fait les mises Ă jour de sĂ©curitĂ©) la tablesqlite_schema
sâappelaitsqlite_master
et ce nom est toujours reconnu (alias) pour des raisons de compatibilitĂ© (je crois pour une partie de lâAPI) - PostgreSQL : depuis le client en ligne de commandes (
psql
normalement), il faut lancer lâincantation\dt
(pour les tables) ou\dv
(pour les vues), éventuellement suffixée de+
(pour afficher deux fois plus de colonnes dont celle de la description). On peut filter sur le nom mais on ne peut pas trier le résultat renvoyé.
Depuis nâimporte quelle session (pas juste le client CLI), on peut aussi simplement lancer la requĂȘteSELECT tablename, schemaname, tableowner FROM pg_catalog.pg_tables;
avec toute la capacité de filtrage et de tris.
Oracle, IBM DB2, Firebird, Derby, ont aussi un catalogue similaire. - etc. Oui, câest un peu la plaie Ă ce niveau mais il y a eu une
standardisation (si je ne dis pas de bĂȘtise câest arrivĂ© avec SQL-92) et
on devrait pouvoir faire de nos jours cette requĂȘte commune :
SELECT table_name, table_schema, table_type FROM information_schema.tables;
Cette vue peut comporter dâautres informations selon les SGBD, liste aussi bien les tables que les vues, est triable et filtrable.
âŠce que je teste en vain (Ă ce stade je commence Ă connaĂźtre par cĆur le « Error in query no such table: ») đ„Č
Câest mieux de ne pas se prĂ©cipiter et prendre le temps de faire le tour du propriĂ©taire. AprĂšs avoir perdu un bon quart dâheure, je clique sur lâonglet « Schema » (soit schĂ©ma) qui me donne la liste que je cherchais.
inventaire des champs
Bien, on a les tables prĂ©sentes et donc une base de dĂ©part. Sans liste des colonnes et graphe des relations ça va cependant ĂȘtre coton. Je me lance aussi :
Comme on peut sây attendre (puisque la base virtuelle des mĂ©ta-donnĂ©es, on
lâa vu, nâest pas reconnue âmais je garde espoir que câest peut-ĂȘtre la
table prĂ©cĂ©demment demandĂ©e qui nâest pas prĂ©sente et que dâautres seront
lĂ ), le standard
SELECT column_name, data_type,
character_maximum_length AS max_length,
numeric_scale AS num_length,
numeric_precision AS num_precis
FROM information_schema.columns
WHERE table_name = 'suspects';
âŠne passe pas. đ„Č Et comme ce nâest pas du My/Maria non plus (puisque, jâai eu lâerreur « Error in query near "show": syntax error » plus tĂŽtâŠ) ceci ne fonctionne pas non plus :
-
SHOW COLUMNS IN suspects
ou -
SHOW FIELDS FROM suspects
ou -
EXPLAIN suspects
moins connu.
Enfin jâai testĂ© le standard de fait introduit par Oracle : DESCRIBE suspects;
ou son abréviation DESC suspects;
Cela se solde par « Error in query near "describe": syntax error »
Avant de me rĂ©signer Ă tout « dumper » (ça va piquer sâil y a des milliers ou des millions de rĂ©sultats), je tente un dernier coup de poker
- Oracle :
SELECT * FROM suspects WHERE ROWNUM <= 1;
â « Error in query no such column: ROWNUM » (off courseâŠ) - avec PostgreSQLâ„9.0 et probablement dâautres SGBD, on peut explorer la
piste du fenĂȘtrage pour mimer lâoracle :
SELECT ROW_NUMBER() OVER () AS i, s.* FROM suspects s WHERE i <= 1;
â « Error in query misuse of aliased window function i » (toute façon leOVER ()
âsansORDER BY
iciâ me plait pas trop)
SELECT * FROM suspects WHERE ROW_NUMBER() <= 1;
â « Error in query misuse of window function row_number() » (idem avecRANK()
) - Microsoft (Access, SQL Server), SAP (ASE, IQ), Teradata, etc.:
SELECT * FROM suspects TOP 1;
â « Error in query near "1": syntax error » (cela laisse entendre que letop
est accepté�)
âŠou plutĂŽt (je confonds souvent)SELECT TOP 1 * FROM suspects;
â « Error in query near "1": syntax error » (en tout cas pas mieux iciâŠ) - Ingres/Informix/Firebird :
SELECT FIRST 1 * FROM suspects;
â « Error in query near "1": syntax error » - Pg/My/Maria/Db2/SQLite/HSQLDB/H2/etc. :
SELECT * FROM suspects LIMIT 1;
â ça marche et jâai un joli tableau qui va bien đ Par contre, contrairement Ă une description en bonne et due forme, on peut juste dĂ©duire si le champ non null est numĂ©rique ou textuel⊠- ANSI SQL:2008 prĂ©conise plutĂŽt :
SELECT * FROM suspects FETCH FIRST 1 ROWS ONLY;
(supportĂ© par : Oracleâ„12c, DB2â„11, PostgreSQLâ„8.4, partiellement par T-SQLâ„2008, HSQLDBâ„2.0, etc.) â « Error in query near "FIRST": syntax error » (dommage, mais on en dĂ©duit que le SGBD sous-jacent nâa pas ce niveau de norme/standard.)
Câest seulement aprĂšs avoir rĂ©solu le cas que jâai remarquĂ© dans lâonglet « Schema » que la structure (nom et type des colonnes, ainsi que si câest une clĂ© primaire/Ă©trangĂšre) sâaffiche en cliquant sur le nom de la table⊠puis se masque si on refait un clic sur le nom.
Mon Ă©ternel souci avec les cliquodromes : il faut cliquer partout et dĂ©couvrir au petit bonheur la chance (surtout que lâon part trop souvent du principe que câest facile et quâil nây a donc pas besoin de manuelâŠ)
le reste
Fort de mes dĂ©boires, je clique sur les autres onglets pour voir un peu. Jâai Ă©tĂ© dans un vague indescriptible en arrivant sur « Notes » puis jâai eu lâeureka en lisant la note en bas : Il sâagit dâun bloc-notes perso et ça utilise le stockage persistant.
Je regarde le suivant, « Submit » qui dit dâentrer la rĂ©ponse trouvĂ©e. Bien, il faudra donc recopier la rĂ©ponse finale.
Du coup, je remarque quâon ouvre avec le « Case Brief » qui prĂ©sente le dossier et quâon termine avec le « Submit » qui permet de le clĂŽturer. Dans ma logique, jâaurais mis le « Schema » avant le « Workspace » ; mais ce nâest que mon avis.
Et toi, journal, as-tu vu des choses intĂ©ressantes que jâai loupĂ©es ?
1er cas
Pour commencer, je poursuis mon exploration avec quelques requĂȘtes simples :
SELECT COUNT(*) FROM suspects; -- 400
SELECT COUNT(*) FROM interviews; -- 200
SELECT COUNT(*) FROM crime_scene; -- 100
SELECT COUNT(DISTINCT name) FROM suspects; -- 218
SELECT COUNT(DISTINCT attire) FROM suspects; -- 13
SELECT COUNT(DISTINCT scare) FROM suspects; -- 19
SELECT COUNT(DISTINCT suspect_id) FROM interviews; -- 200
SELECT COUNT(DISTINCT type) FROM crime_scene; -- 3
SELECT COUNT(DISTINCT location) FROM crime_scene; -- 77
SELECT COUNT(DISTINCT date) FROM crime_scene; -- 99
Je mâempresse de noter tout cela dans mon calepin ainsi :
400 suspects (id): 218 names, 19 scars, 13 attires
200 interviews (suspect_id):
100 crime_scene (id): 3 types, 77 locations, 99 dates
Câest que jâaime me faire une petite idĂ©e que de juste balancer toutes les lignes sur ma sortie.
Ă partir dâici, ça va un peu « divugalcher ».
3/13/400
Lâexploration en nombre me permet dâexaminer les (treize) tenues et les (dix-neuf) marques/cicatrices des suspects.
SELECT DISTINCT attire FROM suspects LIMIT 6;
SELECT DISTINCT scar FROM suspects LIMIT 6;
Et là , je relis les objectifs indiqués dans le « Case Brief »
- Identify the suspect whose profile matches the witness description.
Puis la description de lâaffaire juste au dessus
A witness reported that a man in a trench coat was seen fleeing the scene.
Lâexamen de la table mâindique que ce sera trĂšs simple en fait.
SELECT id, name FROM suspects WHERE attire LIKE 'trench coat';
Lequel de ces mousquetaires a fait le coup ? Balafre à gauche ou à droite ?
1/77/100
Câest encourageant et je retourne voir la liste des objectifs
- Retrieve the correct crime scene details to gather the key clue.
âŠet le rĂ©sumĂ© de lâaffaire au dessus
Set in the gritty 1980s, a valuable briefcase has disappeared from the Blue Note Lounge.
Lâexploration en nombre me permet dâexaminer les (trois) types de dĂ©lits, ainsi que quelques lieux (septante sept) lieux et (nonante neuf) dates.
SELECT DISTINCT type FROM crime_scene;
SELECT DISTINCT date FROM crime_scene LIMIT 6;
SELECT DISTINCT location FROM crime_scene LIMIT 6;
Petite digression avant de poursuivre : les noms date
et type
sont des mots Ă Ă©viter (ou Ă utiliser comme prĂ©fixe ou suffixe), mais bon pas des mots rĂ©servĂ©s sinon la table nâaurait pas pu se crĂ©er simplement. Ici, les requĂȘtes sont passĂ©es sans souci, sinon il aurait fallu protĂ©ger ces noms en Ă©crivant (toutes ces formes sont reconnues, suis perplexeâŠ)
-
"date"
et"type"
(câest la forme du standard ANSI SQL et dans MySQL/MariaDB quandSQL_MODE=ANSI_QUOTES
est actif) -
`date`
et`type`
(câest la variante spĂ©cifique de MySQL/MariaDB probablement reconnue ailleursâŠ) -
[date]
et[type]
(câest la forme T-SQL reconnue aussi par MariaDB avecSQL_MODE=MSSQL
âŠ)
Revenons Ă notre vol. Lâaperçu des donnĂ©es indique que lâon peut Ă©crire simplement :
SELECT *
FROM crime_scene
WHERE type LIKE 'theft' AND location LIKE '%Lounge%';
-- WHERE type location LIKE '%Blue%';
La question demeure : lequel de ces loustics a fait le coup ?
1/2/200
Retour sur le dossier pour consulter lâobjectif suivant
- Verify the suspect using their interview transcript.
On va donc juste lire les interrogatoires de nos trois gredins.
SELECT * FROM interviews WHERE suspect_id IN (3,183,237);
Il y a un qui nâa pas Ă©tĂ© interrogĂ© (en fait pas quâun seul puisquâil y moins de retranscriptions dâinterrogatoires que de suspects) et un autre pour lequel la transcription est indĂ©terminĂ©e/inconnue/inexistante (reste Ă savoir le sens exact de ce NULL
.) Quand à notre coupable, il est passé aux aveux.
Il nây en avait que trois Ă examiner donc il mâĂ©tait facile de recopier les clĂ©s prĂ©cĂ©demment rĂ©cupĂ©rĂ©es, sinon il aurait suffit de lui demander de rĂ©cupĂ©rer la liste en lui passant la sous-requĂȘte
SELECT * FROM interviews
WHERE suspect_id IN (
SELECT id FROM suspects
WHERE attire = 'trench coat'
);
âŠou mieux encore, rĂ©cupĂ©rer directement le(s) nom(s)⊠soit en imbriquant la prĂ©cĂ©dente requĂȘte dans une autre, soit en joignant les deux tables :
SELECT s.name, i.transcript
FROM interviews i
INNER JOIN suspects s ON i.suspect_id = s.id
WHERE s.attire = 'trench coat' ;
Les autres variations sont laissĂ©es Ă titre dâexercice. đ
cri tic
Lâart est difficile et la critique facile, mais je vais faire la remarque que cette base de donnĂ©es doit ĂȘtre peu utile dans la vraie vie oĂč on nâa pas juste des transcriptions associĂ©es Ă un nom. Il faut que les interrogatoires soient :
- DatĂ©es bien que pas utile ici. Ceci est utile classer les entretiens lorsquâune personne est interrogĂ©e plusieurs fois. Câest Ă©galement utile pour retrouver la chronologie des entrevues dans certaines affaires.
- Une transcription doit certes ĂȘtre associĂ©e Ă une personne (je pars du principe quâil nây a pas de truc en groupe) mais aussi Ă une affaire (certaines personnes se retrouvent dans plusieurs affaires, sinon jâaurais mis cette rĂ©fĂ©rence au niveau de la table des suspects et tĂ©moins.)
- Et probablement dâautres (on nâa par exemple pas Ă©voquĂ© les enquĂȘteurs et enquĂȘtrices sinon il faudrait indiquer la paire qui a interrogĂ© ici.)
Jâai passĂ© un bon moment sur ce premier cas qui prend bien cinq minutes (si lâon exclu ma dĂ©couverte de lâinterface.) Cela mâa donnĂ© envie de faire le secondâŠ
2nd cas
Comme jâai pris le pli, je vais regarder le schĂ©ma (la liste des tables donc) : on retrouve les mĂȘmes, sauf que les suspects ont maintenant un bandana et un accessoire, et il y a en plus une table de tĂ©moignages ou piĂšces Ă convictionsâŠ
Je vais aussi suivre les Ă©tapes indiquĂ©es en objectifs. Câest parti.
affaire
On nous indique :
- Retrieve the crime scene report for the record theft using the known date and location.
Sachant que :
In the neon glow of 1980s Los Angeles, the West Hollywood Records store was rocked by a daring theft. A prized vinyl record, worth over $10,000, vanished during a busy evening, leaving the store owner desperate for answers.
On va explorer la premiĂšre table comme de coutumeâŠ
SELECT COUNT(*) FROM crime_scene; -- 100
SELECT COUNT(DISTINCT type) FROM crime_scene; -- 3
SELECT COUNT(DISTINCT date) FROM crime_scene; -- 99
SELECT COUNT(DISTINCT location) FROM crime_scene; -- 77
SELECT DISTINCT type FROM crime_scene; -- theft, murder, bribery
SELECT DISTINCT date FROM crime_scene LIMIT 2; -- 20120909, 19920415
SELECT DISTINCT location FROM crime_scene LIMIT 4;
-- 'City Street', 'Downtown Bank', 'Corner Café', 'Metro Station'
âŠet en dĂ©duire une tactique de recherche
SELECT COUNT(*) FROM crime_scene
WHERE date LIKE '198%' AND type = 'theft'; -- 2
SELECT id, date, location, description FROM crime_scene
WHERE date LIKE '198%' AND type = 'theft';
SELECT COUNT(*) FROM crime_scene
WHERE location LIKE 'West%'; -- 1
On note lâidentifiant (champ id
) et/ou la requĂȘte affinĂ©e Ă rĂ©utiliser par la suite.
indices
On nous indique :
- Retrieve witness records linked to that crime scene to obtain their clues.
Avec le rĂ©sultat prĂ©cĂ©dent, on va simplement rĂ©cupĂ©rer les informationsâŠ
SELECT clue FROM witnesses
WHERE crime_scene_id = (SELECT id
FROM crime_scene WHERE location LIKE 'West%');
La chose peut se faire via une sous-requĂȘte (ci-avant) ou une jointe (ci-aprĂšs)
SELECT w.clue
FROM witnesses w
INNER JOIN crime_scene c ON w.crime_scene_id = c.id
WHERE c.location LIKE 'West%' ;
La premiĂšre indique la couleur du bandana.
La deuxiĂšme indique lâaccessoire vestimentaire remarquable.
Je note les deux lignes dans le calepin : je pense pas pouvoir les rĂ©cupĂ©rer simplement par une sous-requĂȘte.
suspects
On nous indique :
3.Use the clues from the witnesses to find the suspect in the suspects table.
Jâexplore aussi cette troisiĂšme table pour dĂ©cider de comment organiser la recherche.
SELECT COUNT(*) FROM suspects; -- 100
SELECT COUNT(DISTINCT bandana_color) FROM suspects; -- 11
SELECT COUNT(DISTINCT accessory) FROM suspects; -- 41
SELECT DISTINCT bandana_color FROM suspects LIMIT 3; -- green, purple, yellow
SELECT DISTINCT accessory FROM suspects LIMIT 2; -- 'silver chain', 'gold necklace'
OK, je sais donc comment extraire les lignes recherchée en utilisant les informations précédemment recueillies.
SELECT id, name FROM suspects
WHERE bandana_color = 'red' AND accessory = 'gold watch';
VoilĂ donc le(s trois du) podium, dont on prend note si on ne pense pas pouvoir combiner la requĂȘte plus tard.
coupable
On nous indique :
- Retrieve the suspect's interview transcript to confirm the confession.
Comme prĂ©cĂ©demment, il suffit de coller les identifiants rĂ©cupĂ©rĂ©s plus tĂŽtâŠ
Ou dâutiliser une requĂȘte imbriquĂ©e dans le filtrage⊠et rĂ©cupĂ©rer le nom correspond notĂ© plus tĂŽt.
SELECT suspect_id, transcript FROM interviews
WHERE suspect_id IN (SELECT id FROM suspects
WHERE bandana_color = 'red' AND accessory = 'gold watch');
Ou de faire une jointure pour lire directement le nomâŠ
SELECT s.name, i.transcript FROM interviews i
INNER JOIN suspects s ON i.suspect_id = s.id
WHERE bandana_color = 'red' AND accessory = 'gold watch';
Dans tous les cas, la confession donne le clĂ©. Encore une affaire rondement menĂ©e dĂ©tective Escuelle. đ”ïžââïž
re marque
Bon, cette fois ci la table des tĂ©moignages, contrairement Ă celle des interrogatoires, a bien Ă©tĂ© liĂ©e Ă celle des affaires. Par contre, inversement, on nâa pas les noms des tĂ©moins. Et pareillement, il manque la date du recueil et le policier ou la policiĂšre qui pris le tĂ©moignage.
Pendant quâon y est, il aurait aussi, dans la vraie vie, une table des victimes dâune affaire (donc reliĂ©e Ă la table des dossiersâŠ) Personne nâa dit cependant quâil sâagissait de modĂ©liser la vie rĂ©elle, et le jeu ici fourni juste le nĂ©cessaire pour rĂ©soudre les cas soumis.
3Ăšme cas
Je ne lâai pas signalĂ©, mais je me suis crĂ©Ă© un compte pour recevoir les points. Je ne sais pas si câest liĂ© (aprĂšs tout, sâil y a du stockage local ça pourrait le faire aussi) mais jâai les deux cas intermĂ©diaires suivants qui sont dĂ©bloquĂ©s.
crime scene
Tiens, dans le « Case Brief » il nây a plus quâun seul objectif (bien que le titre soit au pluriel)
- Find the murderer. ( Start by finding the crime scene and go from there )
En fait les deux premiers cas nous ont pris par la main et maintenant nous allons rouler sans les petites roues supplĂ©mentaires sur notre vĂ©lo. Bien, ouvrons le dossier : la table, dans lâonglet « Schema », a toujours les mĂȘmes colonnes⊠Donc en partant de cet extrait de la prĂ©sentation dans lâonglet de dĂ©briefing
[âŠ] near the docks of Coral Bay Marina in the early hours of August 14, 1986.
Je tente directement
SELECT * FROM crime_scene WHERE date = '19860814'; -- 3 lignes
SELECT * FROM crime_scene WHERE location LIKE 'Coral%'; -- 5 lignes
SELECT * FROM crime_scene WHERE date = 19860814 AND location LIKE 'Coral%';
SELECT * FROM crime_scene WHERE location = 'Coral Bay Marina';
Je prends note de la seconde phrase puis mâoriente sur la table des personnes.
persons of interest
On va juste traduire les notes prises plus tĂŽt en requĂȘtes pour trouver les suspectsâŠ
--- one who lives on 300ish "Ocean Drive"
SELECT * FROM person WHERE address LIKE '%Ocean%Drive%'; -- 5 lignes
SELECT * FROM person WHERE address LIKE '3__ Ocean Drive'; -- 1 ligne
-- another whose first name ends with "ul" and his last name ends with "ez"
SELECT * FROM person WHERE name LIKE '%ul %ez' -- 1 ligne
On suppose que ces personnes ont Ă©tĂ© entendues et on va donc rechercher les comptes rendus de ces entretiensâŠ
SELECT * FROM interviews WHERE person_id IN (
SELECT id FROM person WHERE
(address LIKE '3__ Ocean Drive')
OR
(name LIKE '%ul %ez')
);
Il aurait fallu passer par une jointure si lâon voulait afficher aussi les noms au/en lieu/plus des « id » mais je nâen ai pas le besoin pour le moment.
Les deux tĂ©moignages nous orientent vers des hĂŽtels. On va donc interroger les fiches quotidiennes obligatoires de lâhĂŽtellerie cette fois « pour la manifestation de la vĂ©ritĂ© judiciaire » et non Ă fins marketing⊠Par chance pour nous, tout est numĂ©risĂ© et tout le monde dans la chaĂźne a travaillĂ© proprement⊠đ
-- I saw someone check into a hotel on August 13.
SELECT COUNT(*) FROM hotel_checkins WHERE check_in_date = 19860813; -- 87
-- I heard someone checked into a hotel with "Sunset" in the name.
SELECT COUNT(*) FROM hotel_checkins WHERE hotel_name LIKE '%Sunset%'; -- 50
-- both
SELECT COUNT(*) FROM hotel_checkins
WHERE (check_in_date = 19860813) AND (hotel_name LIKE '%Sunset%') ; -- 50
-- The guy looked nervous.
SELECT COUNT(*) FROM surveillance_records
WHERE person_id IN (
SELECT person_id FROM hotel_checkins
WHERE (check_in_date = 19860813) AND (hotel_name LIKE '%Sunset%')
) AND (suspicious_activity IS NOT NULL); -- 30
Jâai affichĂ© les trente et y a pas grand chose de folichonâŠ
the final
âŠmais on peut retenir par exemple
6 6 34 Spotted entering late at night
7 7 89 Seen arguing with an unknown person
8 8 2 Left suddenly at 3 AM
15 15 44 Asked for directions to beach
De lĂ , on peut se focaliser sur ces alibis ou confessions (lĂ , câest un peu cousu du fil blanc mais bon)
SELECT COUNT(*) FROM confessions; -- 100
SELECT * FROM confessions WHERE person_id IN (6,7,8,15); -- 4
Tiens, lâintroduction nous a averti que
This case might require the use of JOINs, wildcard searches, and logical deduction.
Alors, pour la route, voici quelques idées de jointures.
SELECT COUNT(*) FROM surveillance_records s
INNER JOIN hotel_checkins h ON h.person_id = s.person_id
AND h.id = s.hotel_checkin_id
WHERE
(h.check_in_date = 19860813)
AND
(h.hotel_name LIKE '%Sunset%')
AND
(s.suspicious_activity IS NOT NULL); -- 0
SELECT COUNT(*) FROM surveillance_records s
INNER JOIN hotel_checkins h ON h.person_id = s.person_id
WHERE
(h.check_in_date = 19860813)
AND
(h.hotel_name LIKE '%Sunset%')
AND
(s.suspicious_activity IS NOT NULL); -- 30
SELECT COUNT(*) FROM surveillance_records s
INNER JOIN hotel_checkins h ON h.person_id = s.person_id
INNER JOIN interviews i ON i.person_id = s.person_id
WHERE
(h.check_in_date = 19860813)
AND
(h.hotel_name LIKE '%Sunset%')
AND
(s.suspicious_activity IS NOT NULL); -- 50
SELECT COUNT(*) FROM surveillance_records s
INNER JOIN hotel_checkins h ON h.person_id = s.person_id
INNER JOIN interviews i ON i.person_id = s.person_id
WHERE
(h.check_in_date = 19860813)
AND
(h.hotel_name LIKE '%Sunset%')
AND
(s.suspicious_activity IS NOT NULL)
AND
(i.transcript IS NOT NULL); -- 27
SELECT COUNT(*) FROM surveillance_records s
INNER JOIN hotel_checkins h ON h.person_id = s.person_id
INNER JOIN confessions i ON c.person_id = s.person_id
WHERE
(h.check_in_date = 19860813)
AND
(h.hotel_name LIKE '%Sunset%')
AND
(s.suspicious_activity IS NOT NULL)
AND
(c.confession IS NOT NULL); -- 30
Sur ce, nous remercions detective Esucelle. đ”đ»ââïž
6Ăšme cas
Ici aussi, on a assez rodage pour ne pas ĂȘtre guidĂ©s ; on a juste pour instruction :
- Find who stole the diamond.
VoilĂ le dĂ©cor plantĂ©, câest parti.
find case
La phrase dâintroduction est lacunaire, mais câest la seule piste de dĂ©part
SELECT COUNT(DISTINCT location) FROM crime_scene; -- 99
SELECT DISTINCT location FROM crime_scene LIMIT 10;
SELECT COUNT(*) FROM crime_scene WHERE location LIKE '%Miami%'; -- 19
SELECT COUNT(*) FROM crime_scene WHERE location LIKE '%Fontaineb%'; -- 1
SELECT COUNT(*) FROM crime_scene WHERE location LIKE '%Hotel%'; -- 1
SELECT * FROM crime_scene WHERE location LIKE '%Fontaineb%'; -- Fontainebleau Hotel
On aurait pu passer aussi par la description : aprĂšs coup, je peux constater quâil y a le nom du fameux diamant.
find clues
La description indique quâil nây a que deux tĂ©moignages valables et donne des pistes pour les atteindre
SELECT COUNT(DISTINCT occupation) FROM guest; -- 93
SELECT DISTINCT occupation FROM guest LIMIT 10;
-- One of them is a really famous actor.
SELECT COUNT(*) FROM guest WHERE occupation LIKE 'actor'; -- 5
SELECT id, name, invitation_code FROM guest WHERE occupation LIKE 'actor';
-- The other one is a woman who works as a consultant for a big company
-- and her first name ends with "an".
SELECT COUNT(*) FROM guest WHERE occupation LIKE 'consultant'; -- 8
SELECT id, name, invitation_code FROM guest
WHERE occupation LIKE 'consultant' AND name LIKE '%an %';
En effet, lâacteur est bien connu des dinosaures dont je fais partie.
Bien, suivons le fil dâAriane et voyons ce que nous ont confiĂ© ces deux tĂ©moins.
SELECT * FROM witness_statements WHERE guest_id IN (
SELECT id FROM guest WHERE
(occupation='Actor' AND name LIKE 'c% e%') OR
(occupation='Consultant' AND name LIKE '%an %')
);
Y a plus quâĂ passer les personnes prĂ©sentes en revueâŠ
find suspects
Câest marrant quâil y ait un registre des attirails, mais cela va ĂȘtre bien utile ici.
SELECT note FROM attire_registry LIMIT 10;
SELECT g.*, a.note
FROM guest g
INNER JOIN attire_registry a ON g.id = a.guest_id
WHERE
-- I saw someone holding an invitation ending with "-R".
g.invitation_code LIKE '%-R'
AND
-- He was wearing a navy suit and a white tie.
a.note LIKE 'navy suit, white tie'
;
Les registres de plaisance maritime vont ĂȘtre mis Ă profit aussi.
SELECT dock_number FROM marina_rentals LIMIT 3;
SELECT g.*, m.boat_name, m.rental_date
FROM guest g
INNER JOIN marina_rentals m ON g.id = m.renter_guest_id
WHERE
-- I overheard someone say, "Meet me at the marina, dock 3."
m.dock_number = 3
;
Ici, il y a du monde⊠(remplacer g.*, m.boat_name, m.rental_date
par COUNT(*)
pour afficher 20) âŠmais deux dates quand on y prĂȘte attention (du coup, on peut remplacer le COUNT(*)
par COUNT(*), m.rental_date
, et ajouter un GROUP BY m.rental_date
Ă la finâŠ) donc seulement la moitiĂ© utile quand on se restreint au jour des faitsâŠ
find culprit
Pour finir, il faut lire les aveux de notre liste rĂ©duiteâŠ
SELECT COUNT(*) FROM guest; -- 200
SELECT COUNT(*) FROM final_interviews; -- 199
SELECT g.*, i.confession
FROM guest g
INNER JOIN final_interviews i ON g.id = i.guest_id
WHERE
(g.invitation_code LIKE '%-R'
AND
g.id = (SELECT guest_id FROM attire_registry
WHERE note = 'navy suit, white tie')
) OR
guest_id IN (
SELECT renter_guest_id FROM marina_rentals
WHERE dock_number = 3
AND
rental_date = (SELECT date FROM crime_scene
WHERE location = 'Fontainebleau Hotel')
);
La requĂȘte est un peu âŠ(mettre le mot qui vient Ă lâesprit)⊠mais illustre lâidĂ©e dâassemblage de puzzle⊠đ Ce nâest pas optimum quand on a beaucoup de donnĂ©es (des milliers de lignesâŠ) mais ça câest laissĂ© Ă titre dâexercice au lectorat.
En fait dans les jointures de la sous-section précédente, on pouvait interpoler (ou alors ne sélectionner que g.*
pour les deux et appliquer INTERSECT
âŠ), la table des aveux servant alors juste Ă valider. đ
En tout cas, bon boulot dĂ©tective Escuelle ! đ”đŸ
Ă suivre
Je fatigue un peu et par consĂ©quence vais arrĂȘter ma rĂ©daction ici. Je pourrai faire une seconde partie sâil y a de la demande. Mais le plus important pour moi est de partager vos trouvailles, astuces, conseils, remarques, etc.
Pour le site, je trouve que câest bien fait (et je donne un B+ comme notation) et espĂšre que de nouveaux cas vont sâajouter rapidement. Une des amĂ©liorations que je demanderai serait dâamĂ©liorer la coloration syntaxique (attention quâil ne sâagit pas de rajouter plus de couleurs mais de ne pas avoir les bizarreries comme avec le champ crime_scene.location
.) Et toi, quelles améliorations verrais tu ?
# Cool deux Ă©nigmes de plus, j'y retourne
PostĂ©Â par BenoĂźt Sibaud (site web personnel) . ĂvaluĂ©Â Ă Â 5 (+2/-0). DerniĂšre modification le 13 avril 2025 Ă 21:25.
(la premiĂšre fois, j'ai essayĂ© plein de commandes pour avoir les tables et le schĂ©ma aussi⊠avant de voir qu'ils sont donnĂ©s⊠et aprĂšs on dira aux enfants de lire les Ă©noncĂ©s avant de commencerâŠ)
On a la liste des tables présentes, et si on clique sur une table on a son schéma :
Exemple:
(donc 'id' est la clé primaire, et guest_id une clé étrangÚre vers le champ 'id' de la table 'guest')
Sinon l'exploration des tables avec LIMIT 1 suffit aussi Ă comprendre ce qu'elles contiennent.
[^] # Non, quatre de plus, dont deux indiqués ici.
PostĂ©Â par Gil Cot â (site web personnel, Mastodon) . ĂvaluĂ©Â Ă Â 2 (+0/-0).
Oui, je lâai vu aussi :) « Câest seulement aprĂšs avoir rĂ©solu le cas que jâai remarquĂ© dans lâonglet â Schema â que la structure (nom et type des colonnes, ainsi que si câest une clĂ© primaire/Ă©trangĂšre) sâaffiche en cliquant sur le nom de la table⊠puis se masque si on refait un clic sur le nom. »
(ça va plus loin que lire lâĂ©noncĂ©, il faut savoir le cliquerâŠ)
âIt is seldom that liberty of any kind is lost all at once.â â David Hume
[^] # Re: Non, quatre de plus, dont deux indiqués ici.
PostĂ©Â par BenoĂźt Sibaud (site web personnel) . ĂvaluĂ©Â Ă Â 5 (+2/-0).
J'ai fait les deux qui me manquaient hier.
Et tu demandais ce que je changerais : perso j'avais tiqué sur le wifi en 1986 dans les hÎtels, mais c'est purement sur la partie narrative.
Un rappel de commandes / une historisation pourrait ĂȘtre sympa.
[^] # Re: Non, quatre de plus, dont deux indiqués ici.
PostĂ©Â par Gil Cot â (site web personnel, Mastodon) . ĂvaluĂ©Â Ă Â 2 (+0/-0).
Ah oui, les rĂ©ponses parfois lunaires dans les entrevues et les faits consignĂ©s comme marquants qui prĂȘtent Ă sourire (remarque, qui demande des glaçons en pleine nuit âpour son whisky coca ?â ou qui a besoin de supplĂ©ment oreiller ?) Dans le flot je nâavais pas tiquĂ© sur cet anachronisme :D
Bien vu pour l'historique : on retape souvent les mĂȘmes lignes pour juste changer un motâŠ
âIt is seldom that liberty of any kind is lost all at once.â â David Hume
[^] # Re: Non, quatre de plus, dont deux indiqués ici.
PostĂ©Â par BAud (site web personnel) . ĂvaluĂ©Â Ă Â 3 (+1/-0).
celleux qui trompent leur moitié ?
# XP: 1900
PostĂ©Â par steph1978 . ĂvaluĂ©Â Ă Â 2 (+0/-0).
J'ai beaucoup aimé, j'attends la suite avec impatience.
J'ai pas résisté pour une des énigmes qui propose une table "confession". J'ai passé ça à un LLM: "dis moi qui a avoué le crime", il l'a trouvé direct.
Pour le reste, beaucoup de jointures et un peu de scrutation oculaire.
[^] # cheater arrĂȘte de cheater :D
PostĂ©Â par Gil Cot â (site web personnel, Mastodon) . ĂvaluĂ©Â Ă Â 2 (+0/-0).
IntĂ©ressantâŠ
Tu lui as recopié le résultat du
SELECT * FROM confession
ou tu lâas branchĂ© sur la page ?Et les autres moules, avez-vous trouvĂ© des moyens de tricher ? Pour quels Ă©nigmes et comment ?
âIt is seldom that liberty of any kind is lost all at once.â â David Hume
[^] # Re: cheater arrĂȘte de cheater :D
PostĂ©Â par steph1978 . ĂvaluĂ©Â Ă Â 2 (+0/-0).
Copier colle le résultat du sélect, oui
Envoyer un commentaire
Suivre le flux des commentaires
Note : les commentaires appartiennent Ă celles et ceux qui les ont postĂ©s. Nous nâen sommes pas responsables.