diff --git a/Week3/assignment/exercise1-normalization.md b/Week3/assignment/exercise1-normalization.md new file mode 100644 index 000000000..99d31d1c2 --- /dev/null +++ b/Week3/assignment/exercise1-normalization.md @@ -0,0 +1,136 @@ +# Exercise 3.1 - SQL Normalization + +Original table: + +member_id | member_name | member_address | dinner_id | dinner_date | venue_code | venue_description | food_code | food_description +--------- | ----------- | -------------- | --------- | ----------- | ---------- | ----------------- | ---------- | ---------------- +... | ... | ... | ... | ... | ... | ... | C1, C2 | Curry, Cake +... | ... | ... | ... | ... | ... | ... | P1, T1, M1 | Pie, Tea, Mousse +etc. + +--- + +## 1. Which columns violate 1NF? + +1NF (First Normal Form) requires: + +- Each field contains **atomic** (indivisible) values. +- No repeating groups or lists in a single column. +- All values in a column are of the same type. + +In this table, 1NF is violated by: + +- **food_code** - contains multiple values separated by commas, e.g. `C1, C2`, `P1, T1, M1`. +- **food_description** - also contains multiple values separated by commas, e.g. `Curry, Cake`, `Pie, Tea, Mousse`. + +Additionally, **dinner_date** is stored in multiple different formats (`2020-03-15`, `20-03-2020`, `Mar 25 '20`), which is bad practice, but they are still single scalar values. The main 1NF violation is the multi-valued food columns. + +--- + +## 2. Which entities can be extracted? + +From the table we can identify the following entities: + +1. **Member** + - member_id + - member_name + - member_address + +2. **Dinner (Event)** + - dinner_id + - dinner_date + - venue_code (the place where the dinner is held) + +3. **Venue** + - venue_code + - venue_description + +4. **Food** + - food_code + - food_description + +5. **MemberDinner / Attendance** + - which member attended which dinner + (many-to-many relationship between Member and Dinner) + +6. **DinnerFood / Menu** + - which food items were served at a given dinner + (many-to-many relationship between Dinner and Food) + +--- + +## 3. Tables and columns for a 3NF-compliant solution + +Below is a 3NF schema. + +### 1. Table `members` + +- **member_id** (PK) +- member_name +- member_address + +Functional dependency: +`member_id → member_name, member_address` +No transitive dependencies. + +--- + +### 2. Table `venues` + +- **venue_code** (PK) +- venue_description + +Functional dependency: +`venue_code → venue_description` + +--- + +### 3. Table `dinners` + +- **dinner_id** (PK) +- dinner_date (DATE type) +- venue_code (FK → venues.venue_code) + +Functional dependency: +`dinner_id → dinner_date, venue_code` + +--- + +### 4. Table `foods` + +- **food_code** (PK) +- food_description + +Functional dependency: +`food_code → food_description` + +--- + +### 5. Table `member_dinners` (attendance) + +- **member_id** (FK → members.member_id) +- **dinner_id** (FK → dinners.dinner_id) +- (PK is the combination (member_id, dinner_id)) + +This table represents which member attended which dinner (many-to-many). + +--- + +### 6. Table `dinner_foods` (menu per dinner) + +- **dinner_id** (FK → dinners.dinner_id) +- **food_code** (FK → foods.food_code) +- (PK is the combination (dinner_id, food_code)) + +This table replaces the multi-valued fields `food_code` and `food_description` by storing one row per dinner–food combination. + +--- + +## Final 3NF schema + +- `members(member_id PK, member_name, member_address)` +- `venues(venue_code PK, venue_description)` +- `dinners(dinner_id PK, dinner_date, venue_code FK)` +- `foods(food_code PK, food_description)` +- `member_dinners(member_id FK, dinner_id FK, PK(member_id, dinner_id))` +- `dinner_foods(dinner_id FK, food_code FK, PK(dinner_id, food_code))` \ No newline at end of file diff --git a/Week3/assignment/index.js b/Week3/assignment/index.js new file mode 100644 index 000000000..61874975d --- /dev/null +++ b/Week3/assignment/index.js @@ -0,0 +1,71 @@ +require("dotenv").config() +const { MongoClient, ObjectId } = require("mongodb") + +const uri = process.env.MONGODB_URI + +if (!uri) { + throw new Error("Please set MONGODB_URI in your .env file") +} + +async function main() { + const client = new MongoClient(uri) + + try { + await client.connect() + console.log("Connected to MongoDB Atlas") + + const db = client.db("databaseWeek3") + const episodes = db.collection("bob_ross_episodes") + + const newEpisode = { + title: "My Custom Joy of Painting Episode", + season: 100, + episode: 1, + elements: ["happy little trees", "mountain", "lake"], + aired_on: new Date("2025-01-01"), + } + + const insertResult = await episodes.insertOne(newEpisode) + console.log("Inserted new episode with _id:", insertResult.insertedId) + + + const insertedId = insertResult.insertedId + const foundById = await episodes.findOne({ _id: insertedId }) + + const cursor = episodes.find({ elements: "happy little trees" }) + const episodesWithTrees = await cursor.toArray() + + console.log( + `Episodes with "happy little trees": ${episodesWithTrees.length}` + ) + + const updateResult = await episodes.updateOne( + { _id: insertedId }, + { + $set: { + title: "Updated Custom Joy of Painting Episode", + updated_at: new Date(), + }, + } + ) + + console.log( + `Updated episodes: matched ${updateResult.matchedCount}, modified ${updateResult.modifiedCount}` + ) + + const updatedDoc = await episodes.findOne({ _id: insertedId }) + + const deleteResult = await episodes.deleteOne({ _id: insertedId }) + console.log(`Deleted documents: ${deleteResult.deletedCount}`) + } catch (err) { + console.error("MongoDB error:", err) + } finally { + await client.close() + console.log("Connection to MongoDB closed") + } +} + +main().catch((err) => { + console.error(err) + process.exit(1) +}) \ No newline at end of file diff --git a/Week3/assignment/package-lock.json b/Week3/assignment/package-lock.json new file mode 100644 index 000000000..f71cceef5 --- /dev/null +++ b/Week3/assignment/package-lock.json @@ -0,0 +1,308 @@ +{ + "name": "assignment", + "version": "1.0.0", + "lockfileVersion": 3, + "requires": true, + "packages": { + "": { + "name": "assignment", + "version": "1.0.0", + "license": "ISC", + "dependencies": { + "dotenv": "^17.2.3", + "mongodb": "^6.21.0", + "mysql2": "^3.15.3" + } + }, + "node_modules/@mongodb-js/saslprep": { + "version": "1.3.2", + "resolved": "https://registry.npmjs.org/@mongodb-js/saslprep/-/saslprep-1.3.2.tgz", + "integrity": "sha512-QgA5AySqB27cGTXBFmnpifAi7HxoGUeezwo6p9dI03MuDB6Pp33zgclqVb6oVK3j6I9Vesg0+oojW2XxB59SGg==", + "license": "MIT", + "dependencies": { + "sparse-bitfield": "^3.0.3" + } + }, + "node_modules/@types/webidl-conversions": { + "version": "7.0.3", + "resolved": "https://registry.npmjs.org/@types/webidl-conversions/-/webidl-conversions-7.0.3.tgz", + "integrity": "sha512-CiJJvcRtIgzadHCYXw7dqEnMNRjhGZlYK05Mj9OyktqV8uVT8fD2BFOB7S1uwBE3Kj2Z+4UyPmFw/Ixgw/LAlA==", + "license": "MIT" + }, + "node_modules/@types/whatwg-url": { + "version": "11.0.5", + "resolved": "https://registry.npmjs.org/@types/whatwg-url/-/whatwg-url-11.0.5.tgz", + "integrity": "sha512-coYR071JRaHa+xoEvvYqvnIHaVqaYrLPbsufM9BF63HkwI5Lgmy2QR8Q5K/lYDYo5AK82wOvSOS0UsLTpTG7uQ==", + "license": "MIT", + "dependencies": { + "@types/webidl-conversions": "*" + } + }, + "node_modules/aws-ssl-profiles": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/aws-ssl-profiles/-/aws-ssl-profiles-1.1.2.tgz", + "integrity": "sha512-NZKeq9AfyQvEeNlN0zSYAaWrmBffJh3IELMZfRpJVWgrpEbtEpnjvzqBPf+mxoI287JohRDoa+/nsfqqiZmF6g==", + "license": "MIT", + "engines": { + "node": ">= 6.0.0" + } + }, + "node_modules/bson": { + "version": "6.10.4", + "resolved": "https://registry.npmjs.org/bson/-/bson-6.10.4.tgz", + "integrity": "sha512-WIsKqkSC0ABoBJuT1LEX+2HEvNmNKKgnTAyd0fL8qzK4SH2i9NXg+t08YtdZp/V9IZ33cxe3iV4yM0qg8lMQng==", + "license": "Apache-2.0", + "engines": { + "node": ">=16.20.1" + } + }, + "node_modules/denque": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/denque/-/denque-2.1.0.tgz", + "integrity": "sha512-HVQE3AAb/pxF8fQAoiqpvg9i3evqug3hoiwakOyZAwJm+6vZehbkYXZ0l4JxS+I3QxM97v5aaRNhj8v5oBhekw==", + "license": "Apache-2.0", + "engines": { + "node": ">=0.10" + } + }, + "node_modules/dotenv": { + "version": "17.2.3", + "resolved": "https://registry.npmjs.org/dotenv/-/dotenv-17.2.3.tgz", + "integrity": "sha512-JVUnt+DUIzu87TABbhPmNfVdBDt18BLOWjMUFJMSi/Qqg7NTYtabbvSNJGOJ7afbRuv9D/lngizHtP7QyLQ+9w==", + "license": "BSD-2-Clause", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://dotenvx.com" + } + }, + "node_modules/generate-function": { + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/generate-function/-/generate-function-2.3.1.tgz", + "integrity": "sha512-eeB5GfMNeevm/GRYq20ShmsaGcmI81kIX2K9XQx5miC8KdHaC6Jm0qQ8ZNeGOi7wYB8OsdxKs+Y2oVuTFuVwKQ==", + "license": "MIT", + "dependencies": { + "is-property": "^1.0.2" + } + }, + "node_modules/iconv-lite": { + "version": "0.7.0", + "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.7.0.tgz", + "integrity": "sha512-cf6L2Ds3h57VVmkZe+Pn+5APsT7FpqJtEhhieDCvrE2MK5Qk9MyffgQyuxQTm6BChfeZNtcOLHp9IcWRVcIcBQ==", + "license": "MIT", + "dependencies": { + "safer-buffer": ">= 2.1.2 < 3.0.0" + }, + "engines": { + "node": ">=0.10.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/express" + } + }, + "node_modules/is-property": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/is-property/-/is-property-1.0.2.tgz", + "integrity": "sha512-Ks/IoX00TtClbGQr4TWXemAnktAQvYB7HzcCxDGqEZU6oCmb2INHuOoKxbtR+HFkmYWBKv/dOZtGRiAjDhj92g==", + "license": "MIT" + }, + "node_modules/long": { + "version": "5.3.2", + "resolved": "https://registry.npmjs.org/long/-/long-5.3.2.tgz", + "integrity": "sha512-mNAgZ1GmyNhD7AuqnTG3/VQ26o760+ZYBPKjPvugO8+nLbYfX6TVpJPseBvopbdY+qpZ/lKUnmEc1LeZYS3QAA==", + "license": "Apache-2.0" + }, + "node_modules/lru-cache": { + "version": "7.18.3", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-7.18.3.tgz", + "integrity": "sha512-jumlc0BIUrS3qJGgIkWZsyfAM7NCWiBcCDhnd+3NNM5KbBmLTgHVfWBcg6W+rLUsIpzpERPsvwUP7CckAQSOoA==", + "license": "ISC", + "engines": { + "node": ">=12" + } + }, + "node_modules/lru.min": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/lru.min/-/lru.min-1.1.3.tgz", + "integrity": "sha512-Lkk/vx6ak3rYkRR0Nhu4lFUT2VDnQSxBe8Hbl7f36358p6ow8Bnvr8lrLt98H8J1aGxfhbX4Fs5tYg2+FTwr5Q==", + "license": "MIT", + "engines": { + "bun": ">=1.0.0", + "deno": ">=1.30.0", + "node": ">=8.0.0" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wellwelwel" + } + }, + "node_modules/memory-pager": { + "version": "1.5.0", + "resolved": "https://registry.npmjs.org/memory-pager/-/memory-pager-1.5.0.tgz", + "integrity": "sha512-ZS4Bp4r/Zoeq6+NLJpP+0Zzm0pR8whtGPf1XExKLJBAczGMnSi3It14OiNCStjQjM6NU1okjQGSxgEZN8eBYKg==", + "license": "MIT" + }, + "node_modules/mongodb": { + "version": "6.21.0", + "resolved": "https://registry.npmjs.org/mongodb/-/mongodb-6.21.0.tgz", + "integrity": "sha512-URyb/VXMjJ4da46OeSXg+puO39XH9DeQpWCslifrRn9JWugy0D+DvvBvkm2WxmHe61O/H19JM66p1z7RHVkZ6A==", + "license": "Apache-2.0", + "dependencies": { + "@mongodb-js/saslprep": "^1.3.0", + "bson": "^6.10.4", + "mongodb-connection-string-url": "^3.0.2" + }, + "engines": { + "node": ">=16.20.1" + }, + "peerDependencies": { + "@aws-sdk/credential-providers": "^3.188.0", + "@mongodb-js/zstd": "^1.1.0 || ^2.0.0", + "gcp-metadata": "^5.2.0", + "kerberos": "^2.0.1", + "mongodb-client-encryption": ">=6.0.0 <7", + "snappy": "^7.3.2", + "socks": "^2.7.1" + }, + "peerDependenciesMeta": { + "@aws-sdk/credential-providers": { + "optional": true + }, + "@mongodb-js/zstd": { + "optional": true + }, + "gcp-metadata": { + "optional": true + }, + "kerberos": { + "optional": true + }, + "mongodb-client-encryption": { + "optional": true + }, + "snappy": { + "optional": true + }, + "socks": { + "optional": true + } + } + }, + "node_modules/mongodb-connection-string-url": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/mongodb-connection-string-url/-/mongodb-connection-string-url-3.0.2.tgz", + "integrity": "sha512-rMO7CGo/9BFwyZABcKAWL8UJwH/Kc2x0g72uhDWzG48URRax5TCIcJ7Rc3RZqffZzO/Gwff/jyKwCU9TN8gehA==", + "license": "Apache-2.0", + "dependencies": { + "@types/whatwg-url": "^11.0.2", + "whatwg-url": "^14.1.0 || ^13.0.0" + } + }, + "node_modules/mysql2": { + "version": "3.15.3", + "resolved": "https://registry.npmjs.org/mysql2/-/mysql2-3.15.3.tgz", + "integrity": "sha512-FBrGau0IXmuqg4haEZRBfHNWB5mUARw6hNwPDXXGg0XzVJ50mr/9hb267lvpVMnhZ1FON3qNd4Xfcez1rbFwSg==", + "license": "MIT", + "dependencies": { + "aws-ssl-profiles": "^1.1.1", + "denque": "^2.1.0", + "generate-function": "^2.3.1", + "iconv-lite": "^0.7.0", + "long": "^5.2.1", + "lru.min": "^1.0.0", + "named-placeholders": "^1.1.3", + "seq-queue": "^0.0.5", + "sqlstring": "^2.3.2" + }, + "engines": { + "node": ">= 8.0" + } + }, + "node_modules/named-placeholders": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/named-placeholders/-/named-placeholders-1.1.3.tgz", + "integrity": "sha512-eLoBxg6wE/rZkJPhU/xRX1WTpkFEwDJEN96oxFrTsqBdbT5ec295Q+CoHrL9IT0DipqKhmGcaZmwOt8OON5x1w==", + "license": "MIT", + "dependencies": { + "lru-cache": "^7.14.1" + }, + "engines": { + "node": ">=12.0.0" + } + }, + "node_modules/punycode": { + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/punycode/-/punycode-2.3.1.tgz", + "integrity": "sha512-vYt7UD1U9Wg6138shLtLOvdAu+8DsC/ilFtEVHcH+wydcSpNE20AfSOduf6MkRFahL5FY7X1oU7nKVZFtfq8Fg==", + "license": "MIT", + "engines": { + "node": ">=6" + } + }, + "node_modules/safer-buffer": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/safer-buffer/-/safer-buffer-2.1.2.tgz", + "integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==", + "license": "MIT" + }, + "node_modules/seq-queue": { + "version": "0.0.5", + "resolved": "https://registry.npmjs.org/seq-queue/-/seq-queue-0.0.5.tgz", + "integrity": "sha512-hr3Wtp/GZIc/6DAGPDcV4/9WoZhjrkXsi5B/07QgX8tsdc6ilr7BFM6PM6rbdAX1kFSDYeZGLipIZZKyQP0O5Q==" + }, + "node_modules/sparse-bitfield": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/sparse-bitfield/-/sparse-bitfield-3.0.3.tgz", + "integrity": "sha512-kvzhi7vqKTfkh0PZU+2D2PIllw2ymqJKujUcyPMd9Y75Nv4nPbGJZXNhxsgdQab2BmlDct1YnfQCguEvHr7VsQ==", + "license": "MIT", + "dependencies": { + "memory-pager": "^1.0.2" + } + }, + "node_modules/sqlstring": { + "version": "2.3.3", + "resolved": "https://registry.npmjs.org/sqlstring/-/sqlstring-2.3.3.tgz", + "integrity": "sha512-qC9iz2FlN7DQl3+wjwn3802RTyjCx7sDvfQEXchwa6CWOx07/WVfh91gBmQ9fahw8snwGEWU3xGzOt4tFyHLxg==", + "license": "MIT", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/tr46": { + "version": "5.1.1", + "resolved": "https://registry.npmjs.org/tr46/-/tr46-5.1.1.tgz", + "integrity": "sha512-hdF5ZgjTqgAntKkklYw0R03MG2x/bSzTtkxmIRw/sTNV8YXsCJ1tfLAX23lhxhHJlEf3CRCOCGGWw3vI3GaSPw==", + "license": "MIT", + "dependencies": { + "punycode": "^2.3.1" + }, + "engines": { + "node": ">=18" + } + }, + "node_modules/webidl-conversions": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/webidl-conversions/-/webidl-conversions-7.0.0.tgz", + "integrity": "sha512-VwddBukDzu71offAQR975unBIGqfKZpM+8ZX6ySk8nYhVoo5CYaZyzt3YBvYtRtO+aoGlqxPg/B87NGVZ/fu6g==", + "license": "BSD-2-Clause", + "engines": { + "node": ">=12" + } + }, + "node_modules/whatwg-url": { + "version": "14.2.0", + "resolved": "https://registry.npmjs.org/whatwg-url/-/whatwg-url-14.2.0.tgz", + "integrity": "sha512-De72GdQZzNTUBBChsXueQUnPKDkg/5A5zp7pFDuQAj5UFoENpiACU0wlCvzpAGnTkj++ihpKwKyYewn/XNUbKw==", + "license": "MIT", + "dependencies": { + "tr46": "^5.1.0", + "webidl-conversions": "^7.0.0" + }, + "engines": { + "node": ">=18" + } + } + } +} diff --git a/Week3/assignment/package.json b/Week3/assignment/package.json new file mode 100644 index 000000000..c7166dcc3 --- /dev/null +++ b/Week3/assignment/package.json @@ -0,0 +1,17 @@ +{ + "name": "assignment", + "version": "1.0.0", + "main": "index.js", + "scripts": { + "test": "echo \"Error: no test specified\" && exit 1" + }, + "keywords": [], + "author": "", + "license": "ISC", + "description": "", + "dependencies": { + "dotenv": "^17.2.3", + "mongodb": "^6.21.0", + "mysql2": "^3.15.3" + } +} diff --git a/Week3/assignment/sql-injection.md b/Week3/assignment/sql-injection.md new file mode 100644 index 000000000..5d5f71d87 --- /dev/null +++ b/Week3/assignment/sql-injection.md @@ -0,0 +1,16 @@ +# Exercise 3.3 — SQL Injection + +Original function: + +```js +function getPopulation(Country, name, code, cb) { + // assuming that connection to the database is established and stored as conn + conn.query( + `SELECT Population FROM ${Country} WHERE Name = '${name}' and code = '${code}'`, + function (err, result) { + if (err) cb(err); + if (result.length == 0) cb(new Error("Not found")); + cb(null, result[0].name); + } + ); +} \ No newline at end of file diff --git a/Week3/assignment/transaction.js b/Week3/assignment/transaction.js new file mode 100644 index 000000000..ca9c5a5a9 --- /dev/null +++ b/Week3/assignment/transaction.js @@ -0,0 +1,81 @@ +const mysql = require("mysql2/promise") + +async function main() { + const connection = await mysql.createConnection({ + host: "localhost", + user: "root", + password: "1234", + database: "dbweek3", + }) + + const FROM_ACCOUNT = 101 + const TO_ACCOUNT = 102 + const AMOUNT = 1000.0 + + try { + await connection.beginTransaction() + + const [rowsFrom] = await connection.execute( + "SELECT balance FROM account WHERE account_number = ? FOR UPDATE", + [FROM_ACCOUNT] + ) + const [rowsTo] = await connection.execute( + "SELECT balance FROM account WHERE account_number = ? FOR UPDATE", + [TO_ACCOUNT] + ) + + if (rowsFrom.length === 0 || rowsTo.length === 0) { + throw new Error("One of the accounts does not exist.") + } + + const fromBalance = parseFloat(rowsFrom[0].balance) + + if (fromBalance < AMOUNT) { + throw new Error("Insufficient funds on source account.") + } + + await connection.execute( + "UPDATE account SET balance = balance - ? WHERE account_number = ?", + [AMOUNT, FROM_ACCOUNT] + ) + + await connection.execute( + "UPDATE account SET balance = balance + ? WHERE account_number = ?", + [AMOUNT, TO_ACCOUNT] + ) + + await connection.execute( + ` + INSERT INTO account_changes (account_number, amount, remark) + VALUES (?, ?, ?), (?, ?, ?) + `, + [ + FROM_ACCOUNT, + -AMOUNT, + `Transfer to account ${TO_ACCOUNT}`, + TO_ACCOUNT, + AMOUNT, + `Transfer from account ${FROM_ACCOUNT}`, + ] + ) + + await connection.commit() + console.log( + `Transferred ${AMOUNT} from ${FROM_ACCOUNT} to ${TO_ACCOUNT} successfully.` + ) + } catch (err) { + console.error("Error during transaction, rolling back:", err.message) + try { + await connection.rollback() + } catch (rollbackErr) { + console.error("Error during rollback:", rollbackErr.message) + } + } finally { + await connection.end() + } +} + +main().catch((err) => { + console.error(err) + process.exit(1) +}) \ No newline at end of file diff --git a/Week3/assignment/transactions-create-tables.js b/Week3/assignment/transactions-create-tables.js new file mode 100644 index 000000000..1c712ba83 --- /dev/null +++ b/Week3/assignment/transactions-create-tables.js @@ -0,0 +1,47 @@ + +const mysql = require("mysql2/promise") + +async function main() { + const connection = await mysql.createConnection({ + host: "localhost", + user: "root", + password: "1234", + database: "dbweek3", + }) + + try { + await connection.execute("SET FOREIGN_KEY_CHECKS = 0") + await connection.execute("DROP TABLE IF EXISTS account_changes") + await connection.execute("DROP TABLE IF EXISTS account") + await connection.execute("SET FOREIGN_KEY_CHECKS = 1") + + await connection.execute(` + CREATE TABLE account ( + account_number INT PRIMARY KEY, + balance DECIMAL(10, 2) NOT NULL DEFAULT 0 + ) + `) + + await connection.execute(` + CREATE TABLE account_changes ( + change_number INT AUTO_INCREMENT PRIMARY KEY, + account_number INT NOT NULL, + amount DECIMAL(10, 2) NOT NULL, + changed_date DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP, + remark VARCHAR(255), + FOREIGN KEY (account_number) REFERENCES account(account_number) + ) + `) + + console.log("Tables 'account' and 'account_changes' created successfully.") + } catch (err) { + console.error("Error creating tables:", err) + } finally { + await connection.end() + } +} + +main().catch((err) => { + console.error(err) + process.exit(1) +}) \ No newline at end of file diff --git a/Week3/assignment/transactions-insert-values.js b/Week3/assignment/transactions-insert-values.js new file mode 100644 index 000000000..fc93c4692 --- /dev/null +++ b/Week3/assignment/transactions-insert-values.js @@ -0,0 +1,46 @@ +const mysql = require("mysql2/promise") + +async function main() { + const connection = await mysql.createConnection({ + host: "localhost", + user: "root", + password: "1234", + database: "dbweek3", + }) + + try { + await connection.execute("DELETE FROM account_changes") + await connection.execute("DELETE FROM account") + + await connection.execute( + ` + INSERT INTO account (account_number, balance) + VALUES + (101, 5000.00), + (102, 2000.00), + (103, 3000.00) + ` + ) + + await connection.execute( + ` + INSERT INTO account_changes (account_number, amount, remark) + VALUES + (101, 5000.00, 'Initial deposit'), + (102, 2000.00, 'Initial deposit'), + (103, 3000.00, 'Initial deposit') + ` + ) + + console.log("Sample data inserted into 'account' and 'account_changes'.") + } catch (err) { + console.error("Error inserting sample data:", err) + } finally { + await connection.end() + } +} + +main().catch((err) => { + console.error(err) + process.exit(1) +}) \ No newline at end of file