commit
60559728d3
@ -0,0 +1,3 @@
|
||||
secret.json
|
||||
node_modules/
|
||||
build/
|
@ -0,0 +1,12 @@
|
||||
# ds52-tp (and at the same time ds53-tp)
|
||||
|
||||
My answers to the DS52 and DS53 practical sessions (resp. "Database Optimization" and "Business Intelligence Big Data").
|
||||
|
||||
I chose to avoid using Oracle SQL as much as possible, because I believe that it is a piece of software whose entire purpose is to lock people into the Oracle ecosystem.
|
||||
|
||||
## Running the code
|
||||
|
||||
You will need to run a local version of the databases; [you can find docker images for those here](https://git.shadamethyst.xyz/amethyst/ds52-db).
|
||||
You can then copy `secret.json.example` to `secret.json`, and fill in the credentials accordingly.
|
||||
|
||||
*TODO: clean this up to automatically use docker*
|
@ -0,0 +1,23 @@
|
||||
-- Query 1: "2 last columns with the sum() analytic function" (?)
|
||||
-- From the screenshots, this query should be a copy of the query 1 of the group-by part,
|
||||
-- with two new columns that add a running sum, one global and one partitionned by year.
|
||||
|
||||
SELECT
|
||||
calendar_year_lookup.year,
|
||||
outlet_lookup.shop_name,
|
||||
SUM(shop_facts.amount_sold) AS total_amount_sold,
|
||||
SUM(shop_facts.quantity_sold) AS total_quantity_sold,
|
||||
SUM(SUM(shop_facts.amount_sold))
|
||||
OVER (ORDER BY calendar_year_lookup.year, outlet_lookup.shop_name)
|
||||
AS total_amount_sold_global_running_sum,
|
||||
SUM(SUM(shop_facts.amount_sold))
|
||||
OVER (
|
||||
PARTITION BY calendar_year_lookup.year
|
||||
ORDER BY calendar_year_lookup.year, outlet_lookup.shop_name
|
||||
)
|
||||
AS total_amount_sold_yearly_running_sum
|
||||
FROM outlet_lookup
|
||||
INNER JOIN shop_facts ON shop_facts.shop_code = outlet_lookup.shop_code
|
||||
INNER JOIN calendar_year_lookup ON shop_facts.week_key = calendar_year_lookup.week_key
|
||||
GROUP BY outlet_lookup.shop_code, calendar_year_lookup.year
|
||||
ORDER BY calendar_year_lookup.year, outlet_lookup.shop_name;
|
@ -0,0 +1,20 @@
|
||||
-- Query 2: "rank() by amount sold and quantity sold"
|
||||
-- From the screenshots, the query should be the same as query 1 of the group-by part,
|
||||
-- without grouping by year, and with two new columns that rank each shop:
|
||||
-- - once by turnover (??, assuming that turnover is equal to amount_sold)
|
||||
-- - once by quantity
|
||||
|
||||
SELECT
|
||||
outlet_lookup.shop_name,
|
||||
SUM(shop_facts.amount_sold) AS total_amount_sold,
|
||||
SUM(shop_facts.quantity_sold) AS total_quantity_sold,
|
||||
RANK()
|
||||
OVER (ORDER BY total_amount_sold DESC)
|
||||
AS amount_rank,
|
||||
RANK()
|
||||
OVER (ORDER BY total_quantity_sold DESC)
|
||||
AS quantity_rank
|
||||
FROM outlet_lookup
|
||||
INNER JOIN shop_facts ON shop_facts.shop_code = outlet_lookup.shop_code
|
||||
GROUP BY outlet_lookup.shop_code
|
||||
ORDER BY amount_rank;
|
@ -0,0 +1,26 @@
|
||||
-- Query 3: "" (sic)
|
||||
-- From the screenshots, the query should be the same as query 2,
|
||||
-- but with years introduced
|
||||
|
||||
SELECT
|
||||
calendar_year_lookup.year,
|
||||
outlet_lookup.shop_name,
|
||||
SUM(shop_facts.amount_sold) AS total_amount_sold,
|
||||
SUM(shop_facts.quantity_sold) AS total_quantity_sold,
|
||||
RANK()
|
||||
OVER (
|
||||
PARTITION BY calendar_year_lookup.year
|
||||
ORDER BY total_amount_sold DESC
|
||||
)
|
||||
AS amount_rank,
|
||||
RANK()
|
||||
OVER (
|
||||
PARTITION BY calendar_year_lookup.year
|
||||
ORDER BY total_quantity_sold DESC
|
||||
)
|
||||
AS quantity_rank
|
||||
FROM outlet_lookup
|
||||
INNER JOIN shop_facts ON shop_facts.shop_code = outlet_lookup.shop_code
|
||||
INNER JOIN calendar_year_lookup ON shop_facts.week_key = calendar_year_lookup.week_key
|
||||
GROUP BY calendar_year_lookup.year, outlet_lookup.shop_code
|
||||
ORDER BY calendar_year_lookup.year, amount_rank;
|
@ -0,0 +1,40 @@
|
||||
-- Query 4: amount sold for shop_code 351
|
||||
|
||||
(
|
||||
SELECT
|
||||
calendar_year_lookup.year,
|
||||
calendar_year_lookup.month,
|
||||
SUM(shop_facts.amount_sold) AS "amount sold",
|
||||
SUM(SUM(shop_facts.amount_sold))
|
||||
OVER(ORDER BY calendar_year_lookup.week_key)
|
||||
AS "running amount sold",
|
||||
SUM(SUM(shop_facts.amount_sold))
|
||||
OVER(
|
||||
PARTITION BY calendar_year_lookup.year
|
||||
ORDER BY calendar_year_lookup.week_key
|
||||
)
|
||||
AS "running year amount sold"
|
||||
FROM shop_facts
|
||||
INNER JOIN calendar_year_lookup ON shop_facts.week_key = calendar_year_lookup.week_key
|
||||
WHERE shop_facts.shop_code = 351
|
||||
GROUP BY calendar_year_lookup.year, calendar_year_lookup.month
|
||||
ORDER BY calendar_year_lookup.year, calendar_year_lookup.month
|
||||
) UNION (
|
||||
SELECT
|
||||
NULL AS "year",
|
||||
NULL AS "month",
|
||||
SUM(shop_facts.amount_sold) AS "amount sold",
|
||||
-- Because we are computing the last row by hand (grouping sets being unavailable in MySQL),
|
||||
-- the last row's running amount is defined manually here. I have chosen to set it to the total sum
|
||||
SUM(shop_facts.amount_sold) AS "running amount sold",
|
||||
SUM(shop_facts.amount_sold) AS "running year amount sold"
|
||||
FROM shop_facts
|
||||
WHERE shop_facts.shop_code = 351
|
||||
);
|
||||
|
||||
/* == TEST ==
|
||||
assert(result[12]["running amount sold"] === 807190.1);
|
||||
assert(result[12]["running year amount sold"] === 85616.4);
|
||||
assert(result.length === 37);
|
||||
assert(result[36]["amount sold"] === 3258640.5);
|
||||
*/
|
@ -0,0 +1,2 @@
|
||||
/* TODO: windowing */
|
||||
SELECT * FROM shop_facts;
|
@ -0,0 +1,12 @@
|
||||
-- Query 1: Total amount sold and total quantity for each shop name by year.
|
||||
|
||||
SELECT
|
||||
outlet_lookup.shop_name,
|
||||
calendar_year_lookup.year,
|
||||
SUM(shop_facts.amount_sold) AS total_amount_sold,
|
||||
SUM(shop_facts.quantity_sold) AS total_quantity_sold
|
||||
FROM outlet_lookup
|
||||
INNER JOIN shop_facts ON shop_facts.shop_code = outlet_lookup.shop_code
|
||||
INNER JOIN calendar_year_lookup ON shop_facts.week_key = calendar_year_lookup.week_key
|
||||
GROUP BY outlet_lookup.shop_code, calendar_year_lookup.year
|
||||
ORDER BY calendar_year_lookup.year, outlet_lookup.shop_name;
|
@ -0,0 +1,13 @@
|
||||
-- Query 2: Filtered version of query 1, to only show "e-Fashion San Francisco" and with descending years
|
||||
|
||||
SELECT
|
||||
outlet_lookup.shop_name,
|
||||
calendar_year_lookup.year,
|
||||
SUM(shop_facts.amount_sold) AS total_amount_sold,
|
||||
SUM(shop_facts.quantity_sold) AS total_quantity_sold
|
||||
FROM outlet_lookup
|
||||
INNER JOIN shop_facts ON shop_facts.shop_code = outlet_lookup.shop_code
|
||||
INNER JOIN calendar_year_lookup ON shop_facts.week_key = calendar_year_lookup.week_key
|
||||
WHERE outlet_lookup.shop_name = "e-Fashion San Francisco"
|
||||
GROUP BY calendar_year_lookup.year
|
||||
ORDER BY calendar_year_lookup.year DESC;
|
@ -0,0 +1,12 @@
|
||||
-- Query 3: Total amount sold and total quantity for each shop name in 2001.
|
||||
|
||||
SELECT
|
||||
outlet_lookup.shop_name,
|
||||
SUM(shop_facts.amount_sold) AS total_amount_sold,
|
||||
SUM(shop_facts.quantity_sold) AS total_quantity_sold
|
||||
FROM outlet_lookup
|
||||
INNER JOIN shop_facts ON shop_facts.shop_code = outlet_lookup.shop_code
|
||||
INNER JOIN calendar_year_lookup ON shop_facts.week_key = calendar_year_lookup.week_key
|
||||
WHERE calendar_year_lookup.year = 2001
|
||||
GROUP BY outlet_lookup.shop_code, calendar_year_lookup.year
|
||||
ORDER BY total_amount_sold DESC;
|
@ -0,0 +1,13 @@
|
||||
-- Query 4: Filtered version of query 3, to only show shops with at least 1M in sales
|
||||
|
||||
SELECT
|
||||
outlet_lookup.shop_name,
|
||||
SUM(shop_facts.amount_sold) AS total_amount_sold,
|
||||
SUM(shop_facts.quantity_sold) AS total_quantity_sold
|
||||
FROM outlet_lookup
|
||||
INNER JOIN shop_facts ON shop_facts.shop_code = outlet_lookup.shop_code
|
||||
INNER JOIN calendar_year_lookup ON shop_facts.week_key = calendar_year_lookup.week_key
|
||||
WHERE calendar_year_lookup.year = 2001
|
||||
GROUP BY outlet_lookup.shop_code, calendar_year_lookup.year
|
||||
HAVING total_amount_sold >= 1000000
|
||||
ORDER BY total_amount_sold DESC;
|
@ -0,0 +1,16 @@
|
||||
-- Query 5-2: same as query 5, but with the "grouping" columns
|
||||
|
||||
SELECT
|
||||
calendar_year_lookup.year,
|
||||
outlet_lookup.shop_name,
|
||||
(calendar_year_lookup.year IS NULL) as niveau_annee,
|
||||
SUM(shop_facts.amount_sold) AS total_amount_sold,
|
||||
SUM(shop_facts.quantity_sold) AS total_quantity_sold,
|
||||
(outlet_lookup.shop_name IS NULL) as niveau_magasin,
|
||||
-- Note: GROUPING_ID is only available on Oracle SQL and MSSQL, so we recreate it here
|
||||
(outlet_lookup.shop_name IS NULL) + (calendar_year_lookup.year IS NULL) * 2 as niveau_global
|
||||
FROM outlet_lookup
|
||||
INNER JOIN shop_facts ON shop_facts.shop_code = outlet_lookup.shop_code
|
||||
INNER JOIN calendar_year_lookup ON shop_facts.week_key = calendar_year_lookup.week_key
|
||||
-- Note: shop_name must be used here instead of shop_code for NULL to be handled properly.
|
||||
GROUP BY calendar_year_lookup.year ASC, outlet_lookup.shop_name ASC WITH ROLLUP;
|
@ -0,0 +1,12 @@
|
||||
-- Query 5: same as query 1, but with the "rollup" option.
|
||||
|
||||
SELECT
|
||||
calendar_year_lookup.year,
|
||||
outlet_lookup.shop_name,
|
||||
SUM(shop_facts.amount_sold) AS total_amount_sold,
|
||||
SUM(shop_facts.quantity_sold) AS total_quantity_sold
|
||||
FROM outlet_lookup
|
||||
INNER JOIN shop_facts ON shop_facts.shop_code = outlet_lookup.shop_code
|
||||
INNER JOIN calendar_year_lookup ON shop_facts.week_key = calendar_year_lookup.week_key
|
||||
-- Note: shop_name must be used here instead of shop_code for NULL to be handled properly.
|
||||
GROUP BY calendar_year_lookup.year ASC, outlet_lookup.shop_name ASC WITH ROLLUP;
|
@ -0,0 +1,14 @@
|
||||
-- Query 6-2: same as query 6, but with a "grouping_id" column.
|
||||
|
||||
SELECT
|
||||
calendar_year_lookup.year,
|
||||
outlet_lookup.state,
|
||||
outlet_lookup.shop_name,
|
||||
(calendar_year_lookup.year IS NULL) * 4 + (outlet_lookup.state IS NULL) * 2 + (outlet_lookup.shop_name IS NULL) AS niveau_global,
|
||||
SUM(shop_facts.amount_sold) AS total_amount_sold,
|
||||
SUM(shop_facts.quantity_sold) AS total_quantity_sold
|
||||
FROM outlet_lookup
|
||||
INNER JOIN shop_facts ON shop_facts.shop_code = outlet_lookup.shop_code
|
||||
INNER JOIN calendar_year_lookup ON shop_facts.week_key = calendar_year_lookup.week_key
|
||||
-- Note: "Grouping sets" is only available on Oracle SQL, so it is manually recreated here
|
||||
GROUP BY calendar_year_lookup.year ASC, outlet_lookup.state, outlet_lookup.shop_name ASC WITH ROLLUP;
|
@ -0,0 +1,13 @@
|
||||
-- Query 6: same as query 5, but with a "grouping sets" option.
|
||||
|
||||
SELECT
|
||||
calendar_year_lookup.year,
|
||||
outlet_lookup.state,
|
||||
outlet_lookup.shop_name,
|
||||
SUM(shop_facts.amount_sold) AS total_amount_sold,
|
||||
SUM(shop_facts.quantity_sold) AS total_quantity_sold
|
||||
FROM outlet_lookup
|
||||
INNER JOIN shop_facts ON shop_facts.shop_code = outlet_lookup.shop_code
|
||||
INNER JOIN calendar_year_lookup ON shop_facts.week_key = calendar_year_lookup.week_key
|
||||
-- Note: "Grouping sets" is only available on Oracle SQL, so it is manually recreated here
|
||||
GROUP BY calendar_year_lookup.year ASC, outlet_lookup.state, outlet_lookup.shop_name ASC WITH ROLLUP;
|
File diff suppressed because it is too large
Load Diff
@ -0,0 +1,19 @@
|
||||
{
|
||||
"name": "ds52-tp",
|
||||
"version": "1.0.0",
|
||||
"description": "",
|
||||
"main": "src/index.js",
|
||||
"scripts": {
|
||||
"test": "echo \"Error: no test specified\" && exit 1"
|
||||
},
|
||||
"type": "module",
|
||||
"author": "",
|
||||
"license": "ISC",
|
||||
"dependencies": {
|
||||
"chalk": "^5.2.0",
|
||||
"csv-string": "^4.1.1",
|
||||
"deep-equal": "^2.2.0",
|
||||
"mkdirp": "^2.1.5",
|
||||
"mysql": "^2.18.1"
|
||||
}
|
||||
}
|
@ -0,0 +1,5 @@
|
||||
{
|
||||
"host": "localhost",
|
||||
"user": "your_user",
|
||||
"password": "your_password"
|
||||
}
|
@ -0,0 +1,43 @@
|
||||
import fs from "node:fs/promises";
|
||||
import path from "node:path";
|
||||
import mkdirp from "mkdirp";
|
||||
|
||||
const TRANSFORMS = [
|
||||
(str) => str.replace(/,\n( +)/g, (_, spaces) => `\n${spaces},`),
|
||||
(str) => str.replace(/(?<=\s)AS\s+([a-z].*?),?(?=\n)/gi, (_, label) => "AS `" + label + "`"),
|
||||
(str) => str.replace(/(?<=\s)AS\s+["'`](.*?)["'`]/gi, (_, label) => "AS `" + label + "`"),
|
||||
(str) => str.replace(
|
||||
/^( *)FROM ([a-z0-9]+(?: [a-z0-9]+)?;?)$/gmi,
|
||||
(_, spaces, label) => `${spaces}FROM\n${spaces} ${label}`
|
||||
)
|
||||
];
|
||||
|
||||
async function generate(folder) {
|
||||
let res = "";
|
||||
|
||||
for (const file of sortFiles(await fs.readdir(folder))) {
|
||||
const rawQuery = await fs.readFile(path.join(folder, file), "utf8");
|
||||
let sql = rawQuery.split(/\/\*|\*\//g).filter((_, i) => i % 2 === 0);
|
||||
|
||||
for (const transform of TRANSFORMS) {
|
||||
sql = transform(sql);
|
||||
}
|
||||
|
||||
res += "\n\n" + sql.trim();
|
||||
}
|
||||
|
||||
return res.trim();
|
||||
}
|
||||
|
||||
await mkdirp("./build");
|
||||
|
||||
await fs.writeFile("./build/tp1.sql", await generate("./tp1"));
|
||||
|
||||
function sortFiles(files) {
|
||||
const collator = new Intl.Collator(undefined, {
|
||||
numeric: true,
|
||||
sensitivity: "base"
|
||||
});
|
||||
|
||||
return files.sort(collator.compare);
|
||||
}
|
@ -0,0 +1,169 @@
|
||||
import mysql from "mysql";
|
||||
import fs from "node:fs/promises";
|
||||
import path from "node:path";
|
||||
import assert from "node:assert";
|
||||
import { parse } from "csv-string";
|
||||
import deepEqual from "deep-equal";
|
||||
import chalk from "chalk";
|
||||
|
||||
const secret = JSON.parse(await fs.readFile('./secret.json', 'utf8'));
|
||||
|
||||
await runTests({
|
||||
database: 'cafe',
|
||||
}, './tp1');
|
||||
|
||||
await runTests({
|
||||
database: 'course',
|
||||
}, './tp2/');
|
||||
|
||||
await runTests({
|
||||
database: 'emode',
|
||||
}, './ds53-tp1/group-by');
|
||||
|
||||
await runTests({
|
||||
database: 'emode',
|
||||
}, './ds53-tp1/analytic');
|
||||
|
||||
async function runTests(mysqlSettings, folder) {
|
||||
const connection = mysql.createConnection({
|
||||
...secret,
|
||||
...mysqlSettings,
|
||||
});
|
||||
|
||||
connection.connect();
|
||||
|
||||
for (const file of sortFiles(await fs.readdir(folder))) {
|
||||
const rawQuery = await fs.readFile(path.join(folder, file), "utf8");
|
||||
|
||||
let sql = rawQuery.split(/\/\*|\*\//g).filter((_, i) => i % 2 === 0).join("\n");
|
||||
let comments = rawQuery.split(/\/\*|\*\//g).filter((_, i) => i % 2 === 1);
|
||||
|
||||
let res = await query(sql.trim());
|
||||
if (
|
||||
process.argv.includes(file)
|
||||
|| process.argv.includes(file.replace(".sql", ""))
|
||||
|| path.join(folder, file).replace(".sql", "").endsWith(process.argv[process.argv.length - 1])
|
||||
) {
|
||||
console.table(res);
|
||||
}
|
||||
|
||||
const result = runTest(res, comments, file);
|
||||
if (result === undefined) {
|
||||
console.log(chalk.yellow("NO TEST ") + file);
|
||||
} else if (result) {
|
||||
console.log(chalk.green("SUCCESS ") + file);
|
||||
} else {
|
||||
console.log(chalk.red("FAIL ") + file);
|
||||
}
|
||||
}
|
||||
|
||||
connection.end();
|
||||
|
||||
function query(query) {
|
||||
return new Promise((resolve, reject) => {
|
||||
connection.query(query, (error, results, fields) => {
|
||||
if (error) return reject(error);
|
||||
resolve(results);
|
||||
});
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
function checkEqual(actual, expected, name) {
|
||||
// Build diff of missing and superfluous items in actual
|
||||
const missing = new Set();
|
||||
const superfluous = new Set();
|
||||
|
||||
for (const row of actual) {
|
||||
if (!expected.find((value) => deepEqual(value, row))) {
|
||||
superfluous.add(row);
|
||||
}
|
||||
}
|
||||
|
||||
for (const row of expected) {
|
||||
if (!actual.find((value) => deepEqual(value, row))) {
|
||||
missing.add(row);
|
||||
}
|
||||
}
|
||||
|
||||
if (missing.size > 0 || superfluous.size > 0) {
|
||||
console.error("Mismatch: there are rows missing or superfluous!");
|
||||
console.error("Expected:", expected);
|
||||
console.error("Actual:", actual);
|
||||
console.error("Missing rows in actual:");
|
||||
console.error([...missing.values()].map(row => `- ${JSON.stringify(row)}`).join("\n"));
|
||||
console.error("Superfluous rows in actual");
|
||||
console.error([...superfluous.values()].map(row => `- ${JSON.stringify(row)}`).join("\n"));
|
||||
return false;
|
||||
}
|
||||
|
||||
// Then, check for order
|
||||
const misordered = new Set();
|
||||
for (let n = 0; n < expected.length; n++) {
|
||||
if (!deepEqual(actual[n], expected[n])) {
|
||||
misordered.add(n);
|
||||
}
|
||||
}
|
||||
|
||||
if (misordered.size > 0) {
|
||||
console.error("Mismatch: rows do not have the same order!");
|
||||
console.error("Expected:", expected);
|
||||
console.error("Actual:");
|
||||
for (let n = 0; n < actual.length; n++) {
|
||||
if (misordered.has(n)) {
|
||||
console.error(`- ${chalk.red(JSON.stringify(actual[n]))}`);
|
||||
} else {
|
||||
console.error(` ${JSON.stringify(actual[n])}`);
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
function lowercaseFirstLine(str) {
|
||||
const [firstLine, ...rest] = str.split("\n");
|
||||
|
||||
return firstLine.toLowerCase() + "\n" + rest.join("\n");
|
||||
}
|
||||
|
||||
function sortFiles(files) {
|
||||
const collator = new Intl.Collator(undefined, {
|
||||
numeric: true,
|
||||
sensitivity: "base"
|
||||
});
|
||||
|
||||
return files.sort(collator.compare);
|
||||
}
|
||||
|
||||
function runTest(queryResult, comments, file) {
|
||||
let checked = false;
|
||||
for (let comment of comments) {
|
||||
let [, title, ...rest] = comment.split("==");
|
||||
if (!rest.length || !title) continue;
|
||||
title = title.trim();
|
||||
rest = rest.join("==");
|
||||
if (title === "RESULT") {
|
||||
const csv = parse(lowercaseFirstLine(rest.trim()), { output: "objects" });
|
||||
|
||||
const res = queryResult.map((x) => Object.entries(x).reduce((acc, [key, value]) => {
|
||||
acc[key] = String(value);
|
||||
return acc;
|
||||
}, {}));
|
||||
|
||||
if (!checkEqual(res, csv, file)) return false;
|
||||
checked = true;
|
||||
} else if (title === "TEST") {
|
||||
const result = queryResult;
|
||||
eval(rest);
|
||||
checked = true;
|
||||
}
|
||||
}
|
||||
|
||||
if (!checked) {
|
||||
return undefined;
|
||||
} else {
|
||||
return true;
|
||||
}
|
||||
}
|
@ -0,0 +1,10 @@
|
||||
-- Query 1: Liste des serveurs
|
||||
SELECT nomserveur, villeserveur FROM serveur;
|
||||
/* == RESULT ==
|
||||
"NOMSERVEUR","VILLESERVEUR"
|
||||
"Durant Pierre", "BELFORT"
|
||||
"Duchemin Paul", "DELLE"
|
||||
"Martin Cathy", "BAVILLIERS"
|
||||
"Pillot Alain", "BELFORT"
|
||||
"Séré Alain", "DIJON"
|
||||
*/
|
@ -0,0 +1,17 @@
|
||||
-- Query 10: Liste des 3 consommations de type bière avec le prix le plus élevé
|
||||
|
||||
SELECT
|
||||
cns.numcons as 'numéro',
|
||||
cns.libcons as 'libellé',
|
||||
cns.prixcons as 'prix'
|
||||
FROM consommation cns
|
||||
WHERE cns.libcons LIKE "Bière%"
|
||||
ORDER BY cns.prixcons DESC, cns.libcons ASC
|
||||
FETCH FIRST 3 ROWS ONLY;
|
||||
|
||||
/* == RESULT ==
|
||||
"Numéro","Libellé","Prix"
|
||||
110,"Bière 50 Cl",4.5
|
||||
200,"Bière Blonde",4.5
|
||||
108,"Bière 33 Cl",3
|
||||
*/
|
@ -0,0 +1,19 @@
|
||||
-- Query 11
|
||||
|
||||
SELECT
|
||||
facture.numfacture,
|
||||
facture.numtable,
|
||||
tables.nomtable
|
||||
FROM facture
|
||||
INNER JOIN serveur ON serveur.numserveur = facture.numserveur
|
||||
INNER JOIN tables ON tables.numtable = facture.numtable
|
||||
WHERE
|
||||
facture.datefacture = "20060222"
|
||||
AND serveur.nomserveur = "Martin Cathy"
|
||||
ORDER BY facture.numtable;
|
||||
|
||||
/* == RESULT ==
|
||||
"numfacture","numtable","nomtable"
|
||||
1206,3,"Fenetre1"
|
||||
1204,4,"Fenetre2"
|
||||
*/
|
@ -0,0 +1,24 @@
|
||||
-- Query 12
|
||||
|
||||
SELECT
|
||||
consommation.numcons,
|
||||
consommation.libcons,
|
||||
consommation.prixcons,
|
||||
comprend.qte AS quantite,
|
||||
comprend.qte * consommation.prixcons AS "prixcons*quantite",
|
||||
serveur.nomserveur
|
||||
FROM
|
||||
comprend
|
||||
INNER JOIN facture ON facture.numfacture = comprend.numfacture
|
||||
INNER JOIN serveur ON serveur.numserveur = facture.numserveur
|
||||
INNER JOIN consommation ON consommation.numcons = comprend.numcons
|
||||
WHERE facture.numfacture = 1203;
|
||||
|
||||
/* == RESULT ==
|
||||
"numcons","libcons","prixcons","quantite","prixcons*quantite","nomserveur"
|
||||
101,"Café double",2,1,2,"Durant Pierre"
|
||||
102,"Café crème",1.6,1,1.6,"Durant Pierre"
|
||||
108,"Bière 33 Cl",3,1,3,"Durant Pierre"
|
||||
121,"Fruit pressé",3,1,3,"Durant Pierre"
|
||||
130,"Coca Cola",2,1,2,"Durant Pierre"
|
||||
*/
|
@ -0,0 +1,16 @@
|
||||
-- Query 13
|
||||
|
||||
SELECT
|
||||
srv2.numserveur,
|
||||
srv2.nomserveur,
|
||||
srv2.villeserveur
|
||||
FROM serveur srv1
|
||||
INNER JOIN serveur srv2 ON srv1.villeserveur = srv2.villeserveur
|
||||
WHERE
|
||||
srv1.nomserveur = "Durant Pierre"
|
||||
AND srv1.numserveur != srv2.numserveur;
|
||||
|
||||
/* == RESULT ==
|
||||
"numserveur","nomserveur","villeserveur"
|
||||
53,"Pillot Alain","BELFORT"
|
||||
*/
|
@ -0,0 +1,10 @@
|
||||
-- Query 14
|
||||
|
||||
SELECT
|
||||
COUNT(tables.numtable) AS "nb tables"
|
||||
FROM tables;
|
||||
|
||||
/* == RESULT ==
|
||||
"nb tables"
|
||||
6
|
||||
*/
|
@ -0,0 +1,11 @@
|
||||
-- Query 15
|
||||
|
||||
SELECT
|
||||
COUNT(serveur.numserveur) AS "nb serveur"
|
||||
FROM serveur
|
||||
WHERE serveur.villeserveur = "BELFORT";
|
||||
|
||||
/* == RESULT ==
|
||||
"nb serveur"
|
||||
2
|
||||
*/
|
@ -0,0 +1,13 @@
|
||||
-- Query 16
|
||||
|
||||
SELECT
|
||||
COUNT(facture.numfacture) AS "nb facture"
|
||||
FROM
|
||||
facture
|
||||
INNER JOIN serveur ON serveur.numserveur = facture.numserveur
|
||||
WHERE serveur.nomserveur = "Martin Cathy";
|
||||
|
||||
/* == RESULT ==
|
||||
"nb facture"
|
||||
12
|
||||
*/
|
@ -0,0 +1,11 @@
|
||||
-- Query 17
|
||||
|
||||
SELECT
|
||||
ROUND(AVG(consommation.prixcons), 2) AS "prix moyen"
|
||||
FROM
|
||||
consommation;
|
||||
|
||||
/* == RESULT ==
|
||||
"prix moyen"
|
||||
2.86
|
||||
*/
|
@ -0,0 +1,11 @@
|
||||
-- Query 18
|
||||
|
||||
SELECT
|
||||
ROUND(AVG(consommation.prixcons), 2) AS "prix moyen"
|
||||
FROM consommation
|
||||
WHERE REGEXP_INSTR(consommation.libcons, "^Café");
|
||||
|
||||
/* == RESULT ==
|
||||
"prix moyen"
|
||||
1.53
|
||||
*/
|
@ -0,0 +1,18 @@
|
||||
-- Query 19
|
||||
|
||||
SELECT
|
||||
serveur.numserveur,
|
||||
serveur.nomserveur,
|
||||
COUNT(facture.numfacture) AS "nb factures"
|
||||
FROM
|
||||
facture
|
||||
INNER JOIN serveur ON serveur.numserveur = facture.numserveur
|
||||
GROUP BY serveur.numserveur
|
||||
HAVING COUNT(facture.numfacture) >= 5
|
||||
ORDER BY serveur.numserveur DESC;
|
||||
|
||||
/* == RESULT ==
|
||||
"numserveur","nomserveur","nb factures"
|
||||
53,"Pillot Alain",16
|
||||
52,"Martin Cathy",12
|
||||
*/
|
@ -0,0 +1,13 @@
|
||||
-- Query 2: Liste des villes des serveurs
|
||||
SELECT DISTINCT
|
||||
serveur.villeserveur
|
||||
FROM
|
||||
serveur
|
||||
ORDER BY serveur.villeserveur;
|
||||
/* == RESULT ==
|
||||
"VILLESERVEUR"
|
||||
"BAVILLIERS"
|
||||
"BELFORT"
|
||||
"DELLE"
|
||||
"DIJON"
|
||||
*/
|
@ -0,0 +1,48 @@
|
||||
-- Query 20
|
||||
|
||||
SELECT
|
||||
comprend.numfacture,
|
||||
SUM(comprend.qte) AS 'nb conso'
|
||||
FROM
|
||||
comprend
|
||||
GROUP BY comprend.numfacture;
|
||||
|
||||
/* == RESULT ==
|
||||
"numfacture","nb conso"
|
||||
1000,4
|
||||
1001,5
|
||||
1002,2
|
||||
1003,6
|
||||
1004,5
|
||||
1005,7
|
||||
1006,2
|
||||
1007,4
|
||||
1008,5
|
||||
1009,4
|
||||
1010,4
|
||||
1011,1
|
||||
1012,3
|
||||
1013,5
|
||||
1014,2
|
||||
1015,4
|
||||
1016,1
|
||||
1017,5
|
||||
1100,2
|
||||
1101,4
|
||||
1102,3
|
||||
1103,5
|
||||
1104,7
|
||||
1105,5
|
||||
1106,3
|
||||
1107,3
|
||||
1108,2
|
||||
1200,6
|
||||
1201,4
|
||||
1202,3
|
||||
1203,5
|
||||
1204,7
|
||||
1205,5
|
||||
1206,3
|
||||
1207,5
|
||||
1208,2
|
||||
*/
|
@ -0,0 +1,8 @@
|
||||
-- Query 20-2: with sort
|
||||
SELECT
|
||||
comprend.numfacture,
|
||||
SUM(comprend.qte) AS 'nb conso'
|
||||
FROM
|
||||
comprend
|
||||
GROUP BY comprend.numfacture
|
||||
ORDER BY SUM(comprend.qte) DESC, comprend.numfacture;
|
@ -0,0 +1,16 @@
|
||||
-- Query 21
|
||||
|
||||
SELECT
|
||||
serveur.villeserveur as 'ville',
|
||||
COUNT(serveur.numserveur) as 'nb serveurs'
|
||||
FROM
|
||||
serveur
|
||||
GROUP BY serveur.villeserveur;
|
||||
|
||||
/* == RESULT ==
|
||||
"Ville","Nb Serveurs"
|
||||
"BAVILLIERS",1
|
||||
"BELFORT",2
|
||||
"DELLE",1
|
||||
"DIJON",1
|
||||
*/
|
@ -0,0 +1,27 @@
|
||||
-- Query 22: nombre de factures établies chaque jour
|
||||
|
||||
SELECT
|
||||
DATE_FORMAT(facture.datefacture, "%d/%m/%y") AS 'date',
|
||||
COUNT(facture.numfacture) AS 'nb factures'
|
||||
FROM facture
|
||||
GROUP BY facture.datefacture
|
||||
ORDER BY facture.datefacture;
|
||||
|
||||
/* == RESULT ==
|
||||
"Date","Nb factures"
|
||||
"01/12/05",1
|
||||
"02/12/05",2
|
||||
"04/12/05",2
|
||||
"05/12/05",1
|
||||
"06/12/05",1
|
||||
"07/12/05",1
|
||||
"08/12/05",1
|
||||
"09/12/05",4
|
||||
"10/12/05",1
|
||||
"11/12/05",2
|
||||
"12/12/05",2
|
||||
"21/01/06",4
|
||||
"22/01/06",5
|
||||
"21/02/06",4
|
||||
"22/02/06",5
|
||||
*/
|
@ -0,0 +1,20 @@
|
||||
-- Query 23
|
||||
|
||||
SELECT
|
||||
facture.numfacture AS 'facture',
|
||||
DATE_FORMAT(facture.datefacture, "%d/%m/%y") AS 'date',
|
||||
COUNT(comprend.numcons) AS 'nb conso'
|
||||
FROM comprend
|
||||
INNER JOIN facture ON comprend.numfacture = facture.numfacture
|
||||
WHERE facture.datefacture < "20060222"
|
||||
GROUP BY facture.numfacture
|
||||
HAVING `nb conso` > 3
|
||||
ORDER BY SUM(comprend.numcons) DESC;
|
||||
|
||||
/* == RESULT ==
|
||||
"Facture","Date","Nb conso"
|
||||
1003,"04/12/05",6
|
||||
1203,"21/02/06",5
|
||||
1103,"21/01/06",5
|
||||
1200,"21/02/06",4
|
||||
*/
|
@ -0,0 +1,27 @@
|
||||
-- Query 24
|
||||
|
||||
SELECT
|
||||
consommation.numcons,
|
||||
consommation.libcons,
|
||||
(
|
||||
SELECT COUNT(comprend.numfacture)
|
||||
FROM comprend
|
||||
WHERE comprend.numcons = consommation.numcons
|
||||
) AS `nb factures`
|
||||
FROM consommation
|
||||
HAVING `nb factures` >= 2;
|
||||
|
||||
/* == RESULT ==
|
||||
"NUMCONS","LIBCONS","Nb factures"
|
||||
100,"Café",10
|
||||
101,"Café double",13
|
||||
102,"Café crème",11
|
||||
105,"Chocolat",5
|
||||
106,"Bière pression",6
|
||||
107,"Bière 25 Cl",5
|
||||
108,"Bière 33 Cl",13
|
||||
110,"Bière 50 Cl",3
|
||||
122,"Perrier",7
|
||||
124,"Orangina",8
|
||||
130,"Coca Cola",6
|
||||
*/
|
@ -0,0 +1,19 @@
|
||||
-- Query 25: same as query 13, but using a subrequest
|
||||
|
||||
SELECT
|
||||
srv2.numserveur,
|
||||
srv2.nomserveur,
|
||||
srv2.villeserveur
|
||||
FROM serveur srv2
|
||||
WHERE
|
||||
srv2.villeserveur IN (
|
||||
SELECT srv1.villeserveur
|
||||
FROM serveur srv1
|
||||
WHERE srv1.nomserveur = "Durant Pierre"
|
||||
)
|
||||
AND srv2.nomserveur != "Durant Pierre";
|
||||
|
||||
/* == RESULT ==
|
||||
"numserveur","nomserveur","villeserveur"
|
||||
53,"Pillot Alain","BELFORT"
|
||||
*/
|
@ -0,0 +1,17 @@
|
||||
-- Query 26
|
||||
|
||||
SELECT
|
||||
serveur.numserveur,
|
||||
serveur.nomserveur
|
||||
FROM serveur
|
||||
WHERE
|
||||
NOT EXISTS (
|
||||
SELECT *
|
||||
FROM facture
|
||||
WHERE facture.numserveur = serveur.numserveur
|
||||
);
|
||||
|
||||
/* == RESULT ==
|
||||
"numserveur","nomserveur"
|
||||
54,"Séré Alain"
|
||||
*/
|
@ -0,0 +1,22 @@
|
||||
-- Query 27
|
||||
|
||||
SELECT
|
||||
serveur.numserveur,
|
||||
serveur.nomserveur
|
||||
FROM serveur
|
||||
WHERE
|
||||
NOT EXISTS (
|
||||
SELECT *
|
||||
FROM facture
|
||||
WHERE
|
||||
facture.numserveur = serveur.numserveur
|
||||
AND facture.numtable = 1
|
||||
);
|
||||
|
||||
/* == RESULT ==
|
||||
"numserveur","nomserveur"
|
||||
50,"Durant Pierre"
|
||||
51,"Duchemin Paul"
|
||||
52,"Martin Cathy"
|
||||
54,"Séré Alain"
|
||||
*/
|
@ -0,0 +1,17 @@
|
||||
-- Query 28
|
||||
|
||||
SELECT
|
||||
consommation.numcons AS 'numéro',
|
||||
consommation.libcons AS 'libellé consommation'
|
||||
FROM consommation
|
||||
WHERE NOT EXISTS (
|
||||
SELECT comprend.numcons
|
||||
FROM comprend
|
||||
WHERE comprend.numcons = consommation.numcons
|
||||
);
|
||||
|
||||
/* == RESULT ==
|
||||
"Numéro","Libellé Consommation"
|
||||
150,"Jus de tomate"
|
||||
300,"Cocktail"
|
||||
*/
|
@ -0,0 +1,18 @@
|
||||
-- Query 29
|
||||
|
||||
SELECT
|
||||
serveur.numserveur AS 'numéro serveur',
|
||||
serveur.nomserveur AS 'nom serveur',
|
||||
COUNT(facture.numfacture) AS 'nb factures'
|
||||
FROM serveur
|
||||
LEFT OUTER JOIN facture ON facture.numserveur = serveur.numserveur
|
||||
GROUP BY serveur.numserveur;
|
||||
|
||||
/* == RESULT ==
|
||||
"Numéro Serveur","Nom Serveur","Nb factures"
|
||||
50,"Durant Pierre",4
|
||||
51,"Duchemin Paul",4
|
||||
52,"Martin Cathy",12
|
||||
53,"Pillot Alain",16
|
||||
54,"Séré Alain",0
|
||||
*/
|
@ -0,0 +1,17 @@
|
||||
-- Query 3: Liste classée des serveurs par ville
|
||||
SELECT
|
||||
serveur.nomserveur,
|
||||
serveur.villeserveur
|
||||
FROM
|
||||
serveur
|
||||
ORDER BY
|
||||
serveur.villeserveur,
|
||||
serveur.nomserveur DESC;
|
||||
/* == RESULT ==
|
||||
"NOMSERVEUR","VILLESERVEUR"
|
||||
"Martin Cathy","BAVILLIERS"
|
||||
"Pillot Alain","BELFORT"
|
||||
"Durant Pierre","BELFORT"
|
||||
"Duchemin Paul","DELLE"
|
||||
"Séré Alain","DIJON"
|
||||
*/
|
@ -0,0 +1,17 @@
|
||||
-- Query 30: Consommation la moins chère
|
||||
|
||||
SELECT
|
||||
cns1.numcons AS 'numéro',
|
||||
cns1.libcons AS 'libellé consommation',
|
||||
cns1.prixcons AS 'prix le moins élevé'
|
||||
FROM consommation cns1
|
||||
WHERE
|
||||
cns1.prixcons = (
|
||||
SELECT MIN(cns2.prixcons)
|
||||
FROM consommation cns2
|
||||
);
|
||||
|
||||
/* == RESULT ==
|
||||
"Numéro","Libellé Consommation","Prix le moins élevé"
|
||||
100,"Café",1
|
||||
*/
|
@ -0,0 +1,24 @@
|
||||
-- Query 31: Consommations les plus chères servies par Pillot Alain
|
||||
|
||||
SELECT
|
||||
cns1.numcons AS 'numéro',
|
||||
cns1.libcons AS 'libellé consommation',
|
||||
cns1.prixcons AS 'prix'
|
||||
FROM
|
||||
consommation cns1
|
||||
WHERE
|
||||
cns1.prixcons = (
|
||||
SELECT MAX(cns2.prixcons)
|
||||
FROM consommation cns2
|
||||
INNER JOIN comprend ON comprend.numcons = cns2.numcons
|
||||
INNER JOIN facture ON facture.numfacture = comprend.numfacture
|
||||
INNER JOIN serveur ON facture.numserveur = serveur.numserveur
|
||||
WHERE
|
||||
serveur.nomserveur = "Pillot Alain"
|
||||
);
|
||||
|
||||
/* == RESULT ==
|
||||
"Numéro","Libellé Consommation","Prix"
|
||||
110,"Bière 50 Cl",4.5
|
||||
200,"Bière Blonde",4.5
|
||||
*/
|
@ -0,0 +1,27 @@
|
||||
-- Query 32
|
||||
|
||||
SELECT
|
||||
tables.numtable,
|
||||
SUM(comprend.qte * consommation.prixcons) AS prix
|
||||
FROM tables
|
||||
INNER JOIN facture ON facture.numtable = tables.numtable
|
||||
INNER JOIN comprend ON comprend.numfacture = facture.numfacture
|
||||
INNER JOIN consommation ON consommation.numcons = comprend.numcons
|
||||
GROUP BY tables.numtable
|
||||
HAVING prix = (
|
||||
SELECT MAX(prix.prix)
|
||||
FROM (
|
||||
SELECT
|
||||
SUM(comprend.qte * consommation.prixcons) AS prix
|
||||
FROM tables
|
||||
INNER JOIN facture ON facture.numtable = tables.numtable
|
||||
INNER JOIN comprend ON comprend.numfacture = facture.numfacture
|
||||
INNER JOIN consommation ON consommation.numcons = comprend.numcons
|
||||
GROUP BY tables.numtable
|
||||
) AS prix
|
||||
);
|
||||
|
||||
/* == RESULT ==
|
||||
"numtable","prix"
|
||||
5,126.9
|
||||
*/
|
@ -0,0 +1,28 @@
|
||||
-- Query 4: Liste des consommations dont le prix unitaire > 1.5€, classée par libéllé
|
||||
SELECT
|
||||
cns.numcons as 'numéro',
|
||||
cns.libcons as 'libellé',
|
||||
cns.prixcons as 'prix'
|
||||
FROM
|
||||
consommation cns
|
||||
WHERE
|
||||
cns.prixcons > 1.5
|
||||
ORDER BY cns.libcons;
|
||||
/* == RESULT ==
|
||||
"Numéro","Libellé","Prix"
|
||||
107,"Bière 25 Cl",2.5
|
||||
108,"Bière 33 Cl",3
|
||||
110,"Bière 50 Cl",4.5
|
||||
200,"Bière Blonde",4.5
|
||||
106,"Bière pression",2.5
|
||||
102,"Café crème",1.6
|
||||
101,"Café double",2
|
||||
105,"Chocolat",2
|
||||
130,"Coca Cola",2
|
||||
300,"Cocktail",8
|
||||
121,"Fruit pressé",3
|
||||
120,"Jus de fruit",2
|
||||
150,"Jus de tomate",2
|
||||
124,"Orangina",2.7
|
||||
122,"Perrier",2.5
|
||||
*/
|
@ -0,0 +1,16 @@
|
||||
-- Query 5: Liste des factures du 22/02/06
|
||||
SELECT
|
||||
facture.numfacture,
|
||||
facture.numtable,
|
||||
facture.numserveur,
|
||||
DATE_FORMAT(facture.datefacture, '%d/%m/%y') as 'datefact'
|
||||
FROM facture
|
||||
WHERE facture.datefacture = '2006-02-22';
|
||||
/* == RESULT ==
|
||||
"NUMFACTURE","NUMTABLE","NUMSERVEUR","DATEFACT"
|
||||
1204,4,52,"22/02/06"
|
||||
1205,1,53,"22/02/06"
|
||||
1206,3,52,"22/02/06"
|
||||
1207,5,53,"22/02/06"
|
||||
1208,7,51,"22/02/06"
|
||||
*/
|
@ -0,0 +1,11 @@
|
||||
-- Query 6: Liste des serveurs dont le nom contient un i en deuxième position.
|
||||
SELECT serveur.nomserveur
|
||||
FROM serveur
|
||||
WHERE REGEXP_INSTR(serveur.nomserveur, "^.i");
|
||||
-- Alternativement:
|
||||
-- WHERE serveur.nomserveur LIKE "_i%";
|
||||
|
||||
/* == RESULT ==
|
||||
"NOMSERVEUR"
|
||||
"Pillot Alain"
|
||||
*/
|
@ -0,0 +1,13 @@
|
||||
-- Query 7: Liste des serveurs dont le nom commence par un D.
|
||||
SELECT
|
||||
numserveur,
|
||||
nomserveur
|
||||
FROM serveur
|
||||
WHERE serveur.nomserveur LIKE "D%"
|
||||
ORDER BY serveur.nomserveur;
|
||||
|
||||
/* == RESULT ==
|
||||
"NUMSERVEUR","NOMSERVEUR"
|
||||
51,"Duchemin Paul"
|
||||
50,"Durant Pierre"
|
||||
*/
|
@ -0,0 +1,27 @@
|
||||
-- Query 8: Liste des consommations classées par ordre alphabétique sur le libellé
|
||||
SELECT
|
||||
cns.libcons as 'libellé',
|
||||
cns.numcons as 'numéro',
|
||||
cns.prixcons as 'prix'
|
||||
FROM consommation cns
|
||||
ORDER BY cns.libcons;
|
||||
|
||||
/* == RESULT ==
|
||||
"Libellé","Numéro","Prix"
|
||||
"Bière 25 Cl",107,2.5
|
||||
"Bière 33 Cl",108,3
|
||||
"Bière 50 Cl",110,4.5
|
||||
"Bière Blonde",200,4.5
|
||||
"Bière pression",106,2.5
|
||||
"Café",100,1
|
||||
"Café crème",102,1.6
|
||||
"Café double",101,2
|
||||
"Chocolat",105,2
|
||||
"Coca Cola",130,2
|
||||
"Cocktail",300,8
|
||||
"Fruit pressé",121,3
|
||||
"Jus de fruit",120,2
|
||||
"Jus de tomate",150,2
|
||||
"Orangina",124,2.7
|
||||
"Perrier",122,2.5
|
||||
*/
|
@ -0,0 +1,18 @@
|
||||
-- Query 9: Liste des consommations de type bière par prix décroissant et libellé croissant
|
||||
|
||||
SELECT
|
||||
cns.numcons as 'numéro',
|
||||
cns.libcons as 'libellé',
|
||||
cns.prixcons as 'prix'
|
||||
FROM consommation cns
|
||||
WHERE cns.libcons LIKE "Bière%"
|
||||
ORDER BY cns.prixcons DESC, cns.libcons ASC;
|
||||
|
||||
/* == RESULT ==
|
||||
"Numéro","Libellé","Prix"
|
||||
110,"Bière 50 Cl",4.5
|
||||
200,"Bière Blonde",4.5
|
||||
108,"Bière 33 Cl",3
|
||||
107,"Bière 25 Cl",2.5
|
||||
106,"Bière pression",2.5
|
||||
*/
|
Loading…
Reference in new issue