วันจันทร์ที่ 8 เมษายน พ.ศ. 2562

การสร้าง Restful API สำหรับการเข้าถึงฐานข้อมูล (Express.js & Node.js)


ในบทความนี้จะเป็นวิธีการสร้าง Restful API ที่มีหน้าที่ให้ Front-end (Angular) เรียกใช้งานเพื่อเข้าถึงฐานข้อมูลในรูปแบบการทำงานต่างๆ

ซึ่งไฟล์หลักๆที่จะกล่าวถึงในบทความนี้ก็คือ app.js และ api.js (/routes/api.js)


app.js

var createError = require('http-errors');
var express = require('express');
var path = require('path');
var cookieParser = require('cookie-parser');
var bodyParser = require('body-parser')
var logger = require('morgan');
var favicon = require('serve-favicon');

var indexRouter = require('./routes/index');
var usersRouter = require('./routes/users');
var apiRouter = require('./routes/api').router;

var app = express();

// view engine setup
app.set('views', path.join(__dirname, 'views'));
app.set('view engine', 'ejs');

app.use(logger('dev'));
app.use(bodyParser.urlencoded({ extended: false }))
app.use(bodyParser.json())
app.use(bodyParser.text())
app.use(cookieParser());
app.use(express.static(path.join(__dirname, 'public')));
app.use(favicon(__dirname + '/public/images/favicon.ico'));

app.use('/', indexRouter);
app.use('/users', usersRouter);
app.use('/api', apiRouter);

// catch 404 and forward to error handler
app.use(function(req, res, next) {
  next(createError(404));
});

// error handler
app.use(function(err, req, res, next) {
  // set locals, only providing error in development
  res.locals.message = err.message;
  res.locals.error = req.app.get('env') === 'development' ? err : {};

  // render the error page
  res.status(err.status || 500);
  res.render('error');
});

module.exports = app;

ไฟล์นี้จะเป็นไฟล์หลักในการทำงานของ Express (เฉพาะการประกาศการใช้งาน router และ module ต่างๆที่จำเป็น)
จะเห็นได้ว่า มีการประกาศตัวแปรที่ชื่อว่า app ซึ่งเป็น express object และเมื่อเราทำการนำ module ต่างๆ เช่น bodyParser, cookieParser ใส่ลงไป app แล้วส่วนต่อไปจะเป็นการใส่ router ที่ใช้ในการ Handling APIต่างๆที่สร้างขึ้นมา
โดย /routes/api.js ก็คือไฟล์หลักที่กำหนด path Restful API ที่ใช้ในระบบ NBLOGIC

api.js

ส่วนแรกจะเป็นส่วนของการ Import Module เข้ามาใช้งานภายในไฟล์นี้
var express = require('express');
var router = express.Router();
const request = require('request')
const {PythonShell} =  require('python-shell');
var cors = require('cors');
var mysql = require('mysql');
const nblogic_node = '/home/ec2-user/nblogic/routes/nblogic_node.py';
var _ = require('lodash');
require('dotenv').config()

ส่วนต่อไปจะเป็นส่วนที่กำหนด option ของ MySQL ที่เราจะทำการสร้าง Connection แบบ Pool ไว้

var connection = mysql.createPool({
  localAddress: process.env.DB_HOST,
  user: process.env.DB_USER,
  password: process.env.DB_PASS,
  database: "nblogic",
  port: 3306,
 socketPath: '/var/lib/mysql/mysql.sock',
 multipleStatements: true
});

ต่อไปจะเป็นตัวอย่างของ ส่วนการทำงาน (Function) ที่ขอข้อมูลของนักศึกษา (Student)

const sqlQuery = (query, data) => {
 return new Promise((resolve, reject) => {
  data?connection.query(query, data, (error, result) => {
   queryHandle(resolve, reject, result, error)
  }):connection.query(query, (error, result) => {
   queryHandle(resolve, reject, result, error)
  })
 })
}
const getUserData = (username) => {
 return sqlQuery(`SELECT * FROM student WHERE student.Student_ID = ${username}`)
}
const getUserAttemps = (username) => {
 return sqlQuery(`SELECT attemps.Course_id, Name, Year, Semester, Grade FROM attemps INNER JOIN course 
  ON course.Course_id = attemps.Course_id WHERE attemps.Student_ID = ${username}
  `)
}
router.get('/db/user/:userId', cors(), (req, res, next) => {
 var userId = req.params.userId;
 getUserData(userId).catch(error => {
  res.status(500).send(error)
 }).then(data_result => {
  return data_result
 }).then(data_result => {
  getUserAttemps(userId).catch(error => {
   res.status(500).send(error)
  }).then(attemp_result => {
   res.send({"User_Bio": data_result[0], "Attemps": attemp_result});
  })
 })
});

ก่อนที่จะพูดถึงในส่วนของ function ของการ login ต่อไปจะกล่าวการเขียนสคริปต์ Python
ซึ่งเป็นการเขียนสคริปต์ เพื่อเรียกใช้ module NBLOGIC ของ Python เพื่อที่จะใช้งานในส่วนการเข้าสู่ระบบของ KLOGIC ซึ่งจะได้มาซึ่งข้อมูลของนักศึกษาเกือบทั้งหมด

import sys
import json
import nblogic

nb = nblogic.KLOGIC(apiMode=True)

args = sys.argv
username = args[1]
password = args[2]
mode = args[3]

if(nb.authentication(username, password)):
    if mode == "authen":
        nb.get_bio()
        nb.get_information()
        nb.get_program_course()
        print(json.dumps(json.loads(nb.json(language="EN"))))
    sys.stdout.flush()

หลังจากได้รับข้อมูลจาก Python(PythonShell) ผ่าน sys.stdout.flush() แล้วก็จะถูกนำไปใช้ในไฟล์ api.js ต่อไป ดังโค้ด

router.post('/klogic', cors(), function(req, res, next) {
 let username = req.body.username;
 let password = req.body.password;
 let api_mode = req.body.api_mode;
 let options = {
  args: [username, password, "authen"],
  pythonPath: '/usr/bin/python3.7',
 }
 console.log(req.body)
 if (username && password) {
  PythonShell.run(nblogic_node, options, function (err, results) {
   if (err) {
    res.status(500).send(err);
   }
   console.log('nblogic_node finished.');
   console.log(results)
   results_json = JSON.parse(results);
   if (results_json !== null) {
    api_mode ? res.send(results_json) : res.render('profile', { title: "NBLOGIC", data: results_json['User_Bio'] });
    let user_bio = results_json['User_Bio']
    var full_course = results_json['User_Summary'];
    sqlQuery("INSERT INTO student SET ?", user_bio).then(result => {
     console.log("INSERT STUDENT SUCCESS");
     return sqlQuery(`UPDATE student SET GPAX = ${user_bio.GPAX} WHERE Student_ID = "${user_bio.Student_ID}"`)
    }).catch(errorHandle).then(result => {
     console.log("UPDATE GPAX SUCCESS");
     return sqlQuery("INSERT IGNORE INTO course (Name, Course_id, Credit_points) VALUES ?", [full_course.all_course.map(course =>
      [course.Name, course.Course_id, course.Credit_points])])
    }).catch(errorHandle).then(result => {
     console.log("INSERT COURSE SUCCESS")
     return sqlQuery("INSERT IGNORE INTO attemps (Student_id, Course_id, Year, Semester, Grade) VALUES ?", [full_course.attemp.map(attemp =>
      [attemp.Student_id, attemp.Course_id, attemp.Year, attemp.Semester, attemp.Grade])])
    }).catch(errorHandle).then(result => {
     console.log("INSERT Attemps SUCCESS")
     return sqlQuery("INSERT IGNORE INTO contains (Program_id, Course_id) VALUES ?", [full_course.all_course.map(course_ =>
      [full_course.User_info.Program_ID, course_.Course_id])])
    }).catch(errorHandle).then(result => {
     console.log("INSERT Contains SUCCESS")
     results_json = null;
    }).catch(errorHandle)
   }
   else {
    res.status(500).send('Something broke!')
   }
  });
 } else {
  res.status(401).send("Unauthorize!")
 }
});

References


การสร้างฐานข้อมูล MySQL สำหรับ NBLOGIC




โดยสำหรับ Relational Database ที่ใช้เก็บข้อมูลจะมีลักษณะตาม ER Diagram ด้านบน

Student Table


CREATE TABLE `student` (`Student_ID` VARCHAR(13) NOT NULL,
 `Thai_Name` VARCHAR(200) NOT NULL,
 `English_Name` VARCHAR(200) NOT NULL,
 `Sex` VARCHAR(20) NOT NULL,
 `Degree` VARCHAR(200) NOT NULL,
 `Major` VARCHAR(200) NOT NULL,
 `Student_Type` VARCHAR(200) NOT NULL,
 `Program` VARCHAR(250) NOT NULL,
 `Program2` VARCHAR(250) NOT NULL,
 `Main_Course` VARCHAR(250) NOT NULL,
 `Year_Enrolled` VARCHAR(250) NOT NULL,
 `Campus` VARCHAR(250) NOT NULL,
 `Account_Number` VARCHAR(20) NOT NULL,
 `Program_ID` VARCHAR(50) NOT NULL,
 `Student_Status` VARCHAR(20) NOT NULL,
 `Year` VARCHAR(50) NOT NULL,
 `ID_Card` VARCHAR(13) NOT NULL,
 `Birth_Date` VARCHAR(50) NOT NULL,
 `Home_Town` VARCHAR(200) NOT NULL,
 `Height` VARCHAR(50) NOT NULL,
 `Weight` VARCHAR(50) NOT NULL,
 `Blood_Group` VARCHAR(10) NOT NULL,
 `Is_the_child_of` VARCHAR(50) NOT NULL,
 `From_total` VARCHAR(20) NOT NULL,
 `Address` VARCHAR(250) NOT NULL,
 `Address_` VARCHAR(250) NOT NULL,
 `Telephone` VARCHAR(20) NOT NULL,
 `Live_with` VARCHAR(50) NOT NULL,
 `Expense` VARCHAR(50) NOT NULL,
 `Funded_by` VARCHAR(50) NOT NULL,
 PRIMARY KEY (`Student_ID`),
 INDEX `FK_student_program` (`Program_ID`),
 CONSTRAINT `FK_student_program` FOREIGN KEY (`Program_ID`) REFERENCES `program` (`Program_id`)
)
COLLATE='utf8_general_ci'
ENGINE=InnoDB;

Program Table

CREATE TABLE `program` (
 `Name` VARCHAR(200) NULL DEFAULT NULL,
 `Program_id` VARCHAR(50) NOT NULL,
 `Faculty` VARCHAR(200) NOT NULL,
 `Department` VARCHAR(200) NOT NULL,
 PRIMARY KEY (`Program_id`)
)
COLLATE='utf8_general_ci'
ENGINE=InnoDB
;

Course Table

CREATE TABLE `course` (
 `Course_id` VARCHAR(50) NOT NULL,
 `Credit_points` INT(11) NULL DEFAULT NULL,
 `Name` VARCHAR(50) NULL DEFAULT NULL,
 PRIMARY KEY (`Course_id`)
)
COLLATE='utf8_general_ci'
ENGINE=InnoDB
;

 Attemps Table

เนื่องจากตาราง Attemps นั้นมีความสัมพันธ์แบบ Many-to-many ทำให้เราต้องสร้าง Table ขึ้นมาใหม่แล้วกำหนด Foreign Key จาก Primary Key ของทั้ง 2 ตารางก็คือ Course_id จากตาราง course และ
Student_id จากตาราง student
CREATE TABLE `attemps` (
 `Course_id` VARCHAR(50) NOT NULL,
 `Student_id` VARCHAR(13) NOT NULL,
 `Year` YEAR NOT NULL,
 `Semester` INT(11) NOT NULL,
 `Grade` VARCHAR(10) NOT NULL,
 PRIMARY KEY (`Course_id`, `Year`, `Semester`, `Student_id`),
 INDEX `FK_attemps_student` (`Student_id`),
 CONSTRAINT `FK_attemps_course` FOREIGN KEY (`Course_id`) REFERENCES `course` (`Course_id`),
 CONSTRAINT `FK_attemps_student` FOREIGN KEY (`Student_id`) REFERENCES `student` (`Student_ID`)
)
COLLATE='utf8_general_ci'
ENGINE=InnoDB
;

Contains Table

เช่นเดียวกันกับตาราง Attemps เราจะต้องสร้าง Table ขึ้นมาใหม่แล้วกำหนด Foreign Key จาก Primary Key ของทั้ง 2 ตารางก็คือ Course_id จากตาราง course และ
Program_id จากตาราง program
CREATE TABLE `contains` (
 `Course_id` VARCHAR(50) NOT NULL,
 `Program_id` VARCHAR(50) NOT NULL,
 INDEX `FK_contains_course` (`Course_id`),
 INDEX `FK_contains_program` (`Program_id`),
 CONSTRAINT `FK_contains_course` FOREIGN KEY (`Course_id`) REFERENCES `course` (`Course_id`),
 CONSTRAINT `FK_contains_program` FOREIGN KEY (`Program_id`) REFERENCES `program` (`Program_id`)
)
COLLATE='utf8_general_ci'
ENGINE=InnoDB
;

วันจันทร์ที่ 25 มีนาคม พ.ศ. 2562

การเตรียม Environment สำหรับการพัฒนา University Web Application (NBLOGIC)

    ในบทความนี้จะเป็นการกล่าวขั้นตอนการเตรียม Environment ที่จำเป็นในการพัฒนา University Web Application โดยในที่นี้ จะมีชื่อ Application ว่า NBLOGIC

    ระบบ NBLOGIC นี้จะใช้เครื่องมือในการเปิดบริการ Web server คือ NodeJS + Express.js
ซึ่ง NodeJS เป็น JavaScript Run-time ที่มีประโยชน์ต่อการพัฒนา Application ที่ใช้ภาษา JavaScript
และ Express.js เป็น Library เสริมที่จะส่วนช่วยให้ Application NodeJS สามารถทำงานเป็น Web Application (Web Server) ได้

   โดยผู้ออกแบบ ได้ทำการเลือกใช้ AWS (Amazon Web Services) เป็นผู้ให้บริการระบบทั้งหมดของ Application และทำการใช้บริการ EC2 (Elastic Compute Cloud) ในการจัดการส่วนของการใช้งานระบบ Cloud Computing ผ่าน Virtual Machine

    ซึ่งระบบปฏิบัติการที่ผู้ออกแบบได้เลือกก็คือ Amazon Linux 2 AMI  (HVM) เพื่อให้ง่ายต่อการเตรียม Environment ในการพัฒนา Web Application


   ขั้นตอนของการเตรียม Environment นั้นที่จะกล่าวถึง จะเริ่มที่การ Remote เข้าไปที่ Instance

1. ทำการ Remote เข้าที่ Instance โดยใช้คำสั่ง
ssh ec2-user@nblogic.ddns.net -i "private-key.pem"
2. เมื่อเชื่อมต่อเข้ามาแล้วก็พบหน้าต่างดังนี้

3. ให้ทำการ update Linux โดยใช้คำสั่ง
sudo yum update
4. เมื่อเสร็จแล้วให้ทำการติดตั้ง MySQL โดยใช้คำสั่ง
sudo yum install mysql-server
5. หลังจากติดตั้ง MySQL เรียบร้อยแล้วให้ทำการเปิดใช้งาน Service mysqld โดยใช้คำสั่ง
sudo service mysqld start
6. ให้ตรวจสอบว่า Service ได้ถูกเปิดใช้งานแล้วโดยใช้คำสั่ง
sudo mysql
 7. เพิ่ม user ของ MySQL โดยใช้คำสั่ง
GRANT ALL PRIVILEGES ON *.* TO 'root'@'localhost' IDENTIFIED BY 'password';
8. เสร็จสิ้นการติดตั้ง MySQL บน Linux ต่อไปจะเป็นการติดตั้ง NodeJS สามารถทำได้โดยใช้ Homebrew เพื่อให้ติดตั้ง package ต่างๆได้ง่ายขึ้น
brew install node
 9. เช็ค Version ของ NodeJS โดยใช้คำสั่ง
node -v
10. ทำการติดตั้ง Express Generator โดยใช้คำสั่ง
npm install express-generator -g
11. สร้าง Express app โดยตั้งชื่อว่า nblogic โดยใช้คำสั่ง
express --view=ejs nblogic
12. ทดสอบการติดตั้งโดยทำการใช้งาน Web server โดยใช้คำสั่ง
cd nblogic
npm install
DEBUG=nblogic:* npm start 
13. แต่เนื่องจากเรายังไม่ได้ทำการตั้งค่า Security Group - inbound rules ของ EC2 หรือก็คือการเปิด Port ให้กับ Instance นั่นเองสามารถทำได้โดยเข้าไปที่ AWS console เลือก Instance


14. จากนั้นให้เลือก Inbound แล้วกด Edit และเพิ่ม rule เข้าไปดังนี้ จากนั้นกด Save

15. เนื่องจากเรายังไม่ได้ตั้งค่า Privileged ให้กับ Port 80 ของ Linux แต่ว่าเราจะใช้วิธี Redirect แทน
เพราะว่า NodeJS ตอนนี้ Listen อยู่ที่ Port 3000 โดยใช้คำสั่ง
sudo iptables -t nat -A PREROUTING -i eth0 -p tcp --dport 80 -j REDIRECT --to-port 3000
16. ทดสอบว่าสามารถเข้าเว็บไซต์ได้หรือไม่ โดยเข้าไปที่ IP ของ Instance











วันอังคารที่ 12 กุมภาพันธ์ พ.ศ. 2562

MySQL on Ubuntu

จากบทความก่อนหน้า ได้เขียนอธิบายวิธีการติดตั้ง SQLite3 บน Ubuntu ไปแล้วนั้น ในบทความนี้ก็จะกล่าวถึงการติดตั้ง MySQL Server บน Ubuntu และวิธีการใช้งาน

ขั้นแรก

อัพเดท Ubuntu's Advanced Packaging Tool (APT) โดยใช้คำสั่ง
sudo apt update

ขั้นที่สอง

ติดตั้ง MySQL โดยใช้คำสั่ง
sudo apt install mysql-server

ขั้นที่สาม 

ตั้งค่า Initialize ของ MySQL โดยใช้คำสั่ง
sudo mysql_secure_installation
โดยหลังจากใช้คำสั่งนี้ ก็จะมีคำถามหลายคำถามขึ้นมา เช่น การตั้ง Password ของ root user

ทดสอบ MySQL Server 

เราสามารถเปิด SQL prompt ได้โดยใช้คำสั่ง
sudo mysql


การสร้าง Database Book, Author, Publisher บน MySQL

 ขั้นตอนแรก

ทำการสร้าง Database โดยใช้คำสั่ง
mysql> CREATE DATABASE BAP;
เมื่อสร้างสำเร็จ ก็ทำการเลือกใช้ Database ที่เพิ่งสร้าง
 mysql> USE BAP;

ขั้นตอนที่สอง สร้าง Table ของ Publisher

โดยใช้คำสั่งจากบทความที่แล้วได้เลย ก็คือ
 mysql> CREATE TABLE publisher (publisher_id INTEGER PRIMARY KEY AUTO_INCREMENT, publisher_name TEXT NOT NULL, location TEXT NOT NULL, publisher_tel TEXT NOT NULL);
ถ้าหากเราใช้ MySQL นั้น ในส่วน field ของ Auto Increment นั้นจะต้องใช้แบบนี้เท่านั้น
AUTO_INCREMENT
ต่างจากที่ใช้ใน SQLite ก็คือ AUTOINCREMENT

ขั้นตอนที่สาม สร้าง Table ของ Author

mysql> CREATE TABLE author (author_id INTEGER PRIMARY KEY AUTO_INCREMENT, author_name TEXT NOT NULL, author_address TEXT, author_tel TEXT, publisher_id INTEGER NOT NULL, FOREIGN KEY(publisher_id) REFERENCES publisher(publisher_id));

ขั้นตอนที่สี่ สร้าง Table ของ Book

mysql> CREATE TABLE book (book_id INTEGER PRIMARY KEY AUTO_INCREMENT, book_name TEXT NOT NULL, release_date TEXT NOT NULL, copy INTEGER NOT NULL, publisher_id INTEGER, FOREIGN KEY(publisher_id) REFERENCES publisher(publisher_id));

ขั้นตอนที่ห้า สร้าง Table ของ Book_Author (M:N Relationship) 

mysql> CREATE TABLE book_author (book_id INTEGER, author_id INTEGER, FOREIGN KEY(book_id) REFERENCES book(book_id),FOREIGN KEY(author_id) REFERENCES author(author_id));

ขั้นตอนสุดท้าย เพิ่มข้อมูลในแต่ละ Table 

Table - Publisher
mysql> INSERT INTO publisher (publisher_name, location, publisher_tel) VALUES ("Pearson Education Limited", "United Kingdom", "020 7010 2336"); 
mysql> INSERT INTO publisher (publisher_name, location, publisher_tel) VALUES ("Mcgraw-hill International Enterprises", "United States", "212-904-2078"); 
mysql> INSERT INTO publisher (publisher_name, location, publisher_tel) VALUES ("Mcgraw-hill International Enterprises", "United States", "212-904-2078"); 
Table -  Book
mysql> INSERT INTO book (book_name, release_date, copy, publisher_id) VALUES ("Computer Organization and Architecture", "2013", "9", 2); 
mysql> INSERT INTO book (book_name, release_date, copy, publisher_id) VALUES ("Concepts of Programming Languages", "2013", "10", 2); 
mysql> INSERT INTO book (book_name, release_date, copy, publisher_id) VALUES ("Programming the World Wide Web", "2013", "7", 2); 
mysql> INSERT INTO book (book_name, release_date, copy, publisher_id) VALUES ("The Intel Microprocessors : Architecture, Programmi", "2008", "8", 2); 
mysql> INSERT INTO book (book_name, release_date, copy, publisher_id) VALUES ("Management", "2015", "13", 2); 
mysql> INSERT INTO book (book_name, release_date, copy, publisher_id) VALUES ("Aircraft Basic Science", "2013", "8", 3); 
mysql> INSERT INTO book (book_name, release_date, copy, publisher_id) VALUES ("Essentials of Corporate Finance", "2002", "3", 3); 
mysql> INSERT INTO book (book_name, release_date, copy, publisher_id) VALUES ("Egypt in Spectacular Cross-Section", "2005", "1", 3); 
mysql> INSERT INTO book (book_name, release_date, copy, publisher_id) VALUES ("Fundamentals of Corporate Finance Alternate Edition 9ED (P)", "2010", "9", 3); 
mysql> INSERT INTO book (book_name, release_date, copy, publisher_id) VALUES ("Corporate Finance : Corporate Principles", "2007", "1", 3);
Table -  Author
mysql> INSERT INTO author (author_name, author_address, publisher_id) VALUES ("William Stallings", "USA", 2); 
mysql> INSERT INTO author (author_name, author_address, publisher_id) VALUES ("Robert W. Sebesta", "Colorado", 2); 
mysql> INSERT INTO author (author_name, author_address, publisher_id) VALUES ("Barry B. Brey", "USA", 2); 
mysql> INSERT INTO author (author_name, author_address, publisher_id) VALUES ("Stephen P. Robbins", "USA", 2); 
mysql> INSERT INTO author (author_name, author_address, publisher_id) VALUES ("Michael J Kroes", "USA", 3); 
mysql> INSERT INTO author (author_name, author_address, publisher_id) VALUES ("Stephen A. Ross", "USA", 3);
Table -  Book Author
mysql> INSERT INTO book_author (book_id, author_id) VALUES (1, 1) -- Computer Organization and Architecture, William Stallings; 
mysql> INSERT INTO book_author (book_id, author_id) VALUES (2,2) -- Concepts of Programming Languages, Robert W. Sebesta; 
mysql> INSERT INTO book_author (book_id, author_id) VALUES (3,2) -- Programming the World Wide Web, Robert W. Sebesta; 
mysql> INSERT INTO book_author (book_id, author_id) VALUES (4,3) -- The Intel Microprocessors : Architecture, Programmi, Barry B. Brey; 
mysql> INSERT INTO book_author (book_id, author_id) VALUES (5,4) -- Management, Stephen P. Robbins; 
mysql> INSERT INTO book_author (book_id, author_id) VALUES (6,5) -- Aircraft Basic Science, Michael J Kroes; 
mysql> INSERT INTO book_author (book_id, author_id) VALUES (7,6) -- Essentials of Corporate Finance, Stephen A. Ross; 
mysql> INSERT INTO book_author (book_id, author_id) VALUES (8,6) -- Egypt in Spectacular Cross-Section, Stephen A. Ross; 
mysql> INSERT INTO book_author (book_id, author_id) VALUES (9,6) -- Fundamentals of Corporate Finance Alternate Edition 9ED (P), Stephen A. Ross; 
mysql> INSERT INTO book_author (book_id, author_id) VALUES (10,6) -- Corporate Finance : Corporate Principles, Stephen A. Ross;

การทดสอบ Query ข้อมูล

 - ข้อมูลทั้งหมดของ Book

mysql> SELECT * FROM book;
ผลลัพธ์ที่ได้

 - ข้อมูลทั้งหมดของ Author

mysql> SELECT * FROM author;
ผลลัพธ์ที่ได้

 - ข้อมูลทั้งหมดของ Publisher

mysql> SELECT * FROM publisher;
ผลลัพธ์ที่ได้

 - ข้อมูลทั้งหมดของ Book Author

mysql> SELECT * FROM book_author;
ผลลัพธ์ที่ได้

= คำถาม =

Can use the same command from SQLite?

ตอบ    สามารถใช้ได้เกือบทั้งหมด แต่ว่าคำสั่งจาก MySQL จะมีให้ใช้เยอะกว่าและมี Data Types ให้เลือกใช้ได้เยอะกว่า

What changes are needed?

ตอบ    เช่นคำสั่ง AUTOINCREMENT ของ SQLite จะต้องเปลี่ยนเป็น AUTO_INCREMENT





Normalization

ในบทความนี้จะพูดถึงการตัวอย่างการทำ Normalization ให้กับ Relation ในรูปแบบต่างๆ โดยมีรูปแบบที่จะนำเสนออยู่ 3 รูปแบบคือ
  1. First Normal Form (1NF)
  2. Second Normal Form (2NF)
  3. Third Normal Form (3NF)

First Normal Form (1NF)

ตัวอย่าง    

    จากตัวอย่าง Relation ของ ClientRental จะเห็นได้ว่าเป็นแบบ Unnormalized table และมี Primary Key คือ clientNo และยังมี Repeating group อยู่ภายใน Unnormalized table นี้ด้วยซึ่งก็คือ
Repeating Group = (propertyNo, pAddress, rentStart, rentFinish, rent, ownerNo, oName)
    ทำให้เห็นได้ว่า จะมีข้อมูลที่เป็น multiple values หรือข้อมูลที่มีหลายค่า อยู่ภายในความสัมพันธ์
จากตัวอย่างจะเห็นว่า มี 2 ค่า สำหรับ propertyNo (PG4 และ PG16) ในลูกค้า (Client) ที่ชื่อว่า John Kay หากต้องการที่จะทำให้ Unnormalized table ให้เป็น 1NF เราจะต้องทำให้มีแต่ข้อมูลที่เป็น single value ภายในแต่ละหลัก และแต่ละแถว โดยสามารถทำได้โดย การนำ Repeating group ออกไป

    ขั้นแรก    เราจะต้องนำ Repeating group ออก ในที่นี้จะนำ property rented details ที่มีลักษณะซ้อนอยู่ในแถวออกไป และนำไปเป็นข้อมูลในแต่ละแถวที่มี Key เหมือนกัน ดังรูปด้านล่าง
    

และรูปด้านล่างก็คือ Functional Dependencies (fd1 ถึง fd6) สำหรับความสัมพันธ์ ClientRental

    ซึ่ง ณ ตอนนี้ความสัมพันธ์ ClientRental นั้นเป็นแบบ 1NF แล้วแต่ว่าจะมีข้อมูลเดิมๆ ซ้ำกันอยู่หลายรอบ หรือที่เรียกว่า Data Redundancy ซึ่งจะทำให้เกิด Update anomalies ได้หากนำไปใช้

    ขั้นที่สอง    เราจะนำ Repeating group (property rented details ) ออก โดยนำข้อมูลที่ซ้ำไปใส่ไว้ใน relation ที่แยกออกไป โดยจะมี Key attribute คือ clientNo ดังรูป

ทำให้เราสามารถสรุป 1NF relations มาได้ดังนี้
Client                              (clientNo, cName)
PropertyRentalOwner     (clientNo, propertyNo, pAddress, rentStart, rentFinish, rent,
ownerNo, oName)

Second Normal Form (2NF)

    ความหมายของ 2NF ก็คือ relation ที่เป็น First normal form และทุก non-primary-key เป็น
fully functionally dependent บน primary key

    โดยการทำ Normalization สำหรับ 1NF relation ไปเป็น 2NF relation จะทำได้โดย นำ partial dependencies ออกไปจาก relation 

   ClientRental relation จะมี functional dependencies ดังนี้

fd1 clientNo, propertyNo ® rentStart, rentFinish (Primary key) 
fd2 clientNo ® cName (Partial dependency) 
fd3 propertyNo ® pAddress, rent, ownerNo, oName (Partial dependency) 
fd4 ownerNo ® oName (Transitive dependency) 
fd5 clientNo, rentStart ® propertyNo, pAddress, rentFinish, rent, ownerNo, oName (Candidate key) 
fd6 propertyNo, rentStart ® clientNo, cName, rentFinish (Candidate key)
และผลจากการเปลี่ยน 1NF เป็น 2NF เป็นไปดังรูปด้านล่าง


ขั้นตอนของการทำ 2NF ก็คือ เราจะทดสอบ ClientRental relation โดยหาว่ามี partial dependencies บน primary key หรือไม่ จากการทดสอบจะเห็นได้ว่า client attribute (cName) เป็น partial dependencies บน primary key และยังมี (pAddress, rent, ownerNo, oName) อีกที่เป็น partial dependencies และ
property rents attrbutes (rentStart และ rentFinish) เป็น fully dependent บน primary key ทั้งหมด

จากการสังเกตุ Partial dependencies ที่มีภายใน relation หมายความว่า relation นี้ไม่ใช่ 2NF โดยการที่จะเปลี่ยนให้เป็น 2NF สามารถทำได้โดย สร้าง relation ใหม่ขึ้นมา ที่ไม่มี non-primary key เป็น partial dependent และประกอบไปด้วย ส่วนของ primary key ที่เป็น fully functionally dependent

ทำให้เราสามารถสรุป 2NF relations มาได้ดังนี้
Client                    (clientNo, cName) 
Rental                   (clientNo, propertyNo, rentStart, rentFinish) 
PropertyOwner     (propertyNo, pAddress, rent, ownerNo, oName)

Third Normal Form (3NF)

    ความหมายของ 3NF ก็คือ relation ที่เป็นทั้ง First normal form และ Second normal form และไม่มี non-primary key attributes  ที่เป็น transitively dependent บน primary key

โดยการทำ Normalization สำหรับ 2NF relation ไปเป็น 3NF relation จะทำได้โดย นำ transitive dependencies ออกไปจาก relation 

จากการทำ 2NF ด้านบน แสดงให้เห็นถึง functional dependencies ของ Client, Rental และ PropertyOwner ได้ดังนี้
Client 
fd2 clientNo ® cName (Primary key) 
Rental 
fd1 clientNo, propertyNo ® rentStart, rentFinish (Primary key) 
fd5' clientNo, rentStart ® propertyNo, rentFinish (Candidate key) 
fd6' propertyNo, rentStart ® clientNo, rentFinish (Candidate key) 
PropertyOwner 
fd3 propertyNo ® pAddress, rent, ownerNo, oName (Primary key) 
fd4 ownerNo ® oName (Transitive dependency)
จะเห็นได้ว่า non-primary key attributes ทุกตัวในความสัมพันธ์ Client และ Rental เป็น functionally dependent บน primary key ของตัวเอง ทำให้เป็น 3NF แล้วสำหรับความสัมพันธ์นี้

และจะเห็นได้  non-primary key attributes ทุกตัวในความสัมพันธ์ PropertyOwner เป็น functionally dependent บน primary key ของตัวเอง ยกเว้น oName ที่เป็น transitively depentdent บน ownerNo
และการที่จำให้ 2NF เป็น 3NF จะต้องทำให้ transitive dependency หายไป และเราจะสร้าง relation ใหม่ขึ้นมา 2 อัน ดังรูปแบบดังนี้
PropertyForRent     (propertyNo, pAddress, rent, ownerNo) 
Owner                     (ownerNo, oName)


ทำให้เราสามารถสรุป 3NF relations มาได้ดังนี้
Client                     (clientNo, cName) 
Rental                     (clientNo, propertyNo, rentStart, rentFinish) 
PropertyForRent     (propertyNo, pAddress, rent, ownerNo) 
Owner                     (ownerNo, oName)

วันอังคารที่ 5 กุมภาพันธ์ พ.ศ. 2562

Functional Dependencies

ความหมาย

    Functional dependency เป็นคุณสมบัติของความหมายหรือความหมายของแอตทริบิวต์ในความสัมพันธ์ (Relation) ความหมายจะเป็นตัวบ่งชี้ว่าแอตทริบิวต์เกี่ยวข้องกันอย่างไร และระบุ Function Dependency ระหว่างแอททริบิวต์
    การที่ Functional dependency มีอยู่ การอ้างอิง (Dependency) จะเป็นข้อจำกัด (Constraint)ระหว่างแอตทริบิวต์

ตัวอย่าง


    จากตัวอย่างในรูป ส่วนของ staffNo และ position ของ staff  ของ SL21 เราจะเห็นได้ว่า position ของ staff เป็น Manager. อธิบายง่ายๆก็คือ staffNo จะเป็นตัวที่สามารถกำหนดหรือบอก position ได้
    โดยสามารถอธิบายได้ดังรูปด้านล่าง

    ในรูป (b) จะเห็นได้ว่าเมื่อนำ attribute มาสลับกัน จะไม่สามารถทำได้ เพราะว่า position จะไม่สามารถบ่งบอกได้ถึง staffNo

    ความสัมพันธ์ระหว่าง staffNo และ position คือแบบ one-to-one (1:1) โดย staff number แต่ละเลขจะมีแค่ 1 position. กลับกัน ความสัมพันธ์ระหว่าง position และ staffNo คือ one-to-many (1:N)
โดยที่จะสามารถมี staff หลายคนใน position เดียวกันได้

    สรุปได้ว่า staffNo จะเป็นตัว determinant ของ functional dependency

Update Anomalies

ความหมาย

    เป็นความสัมพันธ์ที่มีข้อมูลที่มีลักษณะ Redundant หรือว่าซ้ำกัน ซึ่งอาจจะทำให้เกิดปัญหาได้ 
โดยสามารถแบ่งประเภทได้เป็น 3 ประเภทคือ

  1. Insertion Anomalies
  2. Deletion Anomalies
  3. Modification Anomalies

ตัวอย่าง

Insertion Anomalies

    การที่เราจะทำการ insert รายละเอียดของ staff คนใหม่ ไปยังความสัมพันธ์ StaffBranch เราจะต้องรวมรายละเอียดของ branch ที่ staff จะไปอยู่ด้วย. ยกตัวอย่างเช่น เราต้องการ insert รายละเอียดของ staff คนใหม่ที่ เลขที่ branch B007 นั้นหมายถึงเราจะต้องใส่ข้อมูลที่ถูกต้องของ branch นั้นๆ และข้อมูลนั้นจะต้องสอดคล้องกับค่าอื่นๆภายใน branch B007 ด้วย จะเห็นได้ว่าการ insert ครั้งนี้จะยังไม่มีผลกระทบซักเท่าไหร่ เนื่องจากข้อมูลของ B007 นั้นมีอยู่ในความสัมพันธ์ของ Branch
    ในอีกกรณีหนึ่งของการ insert คือเมื่อทำการ insert ข้อมูลของ branch ใหม่ที่ไม่มี staff อยู่ในนั้นเลย ไปยัง StaffBranch จึงจำเป็นต้องใส่ null เข้าไปเป็น attribute ของ staff เช่น staffNo ยังไงก็ตาม staffNo นั้นเป็น Primary Key สำหรับความสัมพันธ์ StaffBranch ดังนั้นการที่เราจะเพิ่ม null เข้าไปยัง staffNo จะเป็นการกระทำที่ไม่ถูกต้อง

Deletion Anomalies

    ถ้าเราต้องการที่จะลบข้อมูลออกจาก StaffBranch โดยข้อมูลที่ต้องการลบออกคือ สมาชิกคนสุดท้ายภายใน branch ข้อมูลของ branch นั้นๆก็จะหายไปด้วย เช่นถ้าเราจะลบข้อมูลของ staff SA9 (Mary Howe) จากใน StaffBranch ข้อมูลของ branch B007 ก็จะหายไปจากฐานข้อมูล โดยการออกแบบความสัมพันธ์จากรูปด้านบนจะแก้ปัญหานี้ได้ เนื่องจากมีการเก็บข้อมูลของ Branch แยกไว้ทำให้เมื่อลบข้อมูลออกจาก StaffBranch แล้วข้อมูลของ branch ก็ยังคงอยู่ในความสัมพันธ์ Branch

Modification Anomalies

    หากเราต้องการที่จะแก้ไขข้อมูลจาก 1 ใน attribute ของ branch หนึ่งภายในความสัมพันธ์ StaffBranch ยกตัวอย่างเช่น ต้องการที่จะแก้ไข address ของ branch B003 เราจะต้อง update ข้อมูลทของ staff ทุกคนที่อยู่ใน branch นั้น โดยหากการแก้ไขครั้งนี้ไม่ได้แก้ไขข้อมูลทุกตัวที่เกี่ยวข้อง ก็จะทำให้เกิดความไม่สอดคล้องกันของฐานข้อมูล เช่น branch B003 อาจจะมีหลาย address ใน staff ที่ต่างกัน

 



วันเสาร์ที่ 26 มกราคม พ.ศ. 2562

SQLite

บทความนี้จะกล่าวถึงการติดตั้งและการใช้งานระบบฐานข้อมูล SQLite 3 ผ่านระบบปฏิบัติการ Ubuntu

ขั้นตอนการติดตั้ง

    เนื่องด้วยผู้เขียนนั้นใช้ระบบปฏิบัติการ Windows 10 เป็นหลักและได้ทำการติดตั้ง Sub-system ไว้แล้ว ซึ่ง Ubuntu Sub-system เป็น Application ที่สามารถติดตั้งได้โดยตรงจาก Microsoft Store
หากสนใจวิธีให้ทำการติดตั้งตามวิธีใน Link (https://docs.microsoft.com/en-us/windows/wsl/install-win10)


  1. ให้ทำการ update apt package โดยใช้คำสั่ง sudo apt-get update
  2. ติดตั้ง SQLite โดยใช้คำสั่ง sudo apt-get install sqlite3
  3. ทำการตรวจสอบว่าติดตั้งได้ถูกต้องโดยใช้คำสั่ง sqlite3 --version

สร้างฐานข้อมูล

เริ่มต้นโดยการสร้างฐานข้อมูลสามารถทำได้โดยเข้าสู่ SQLite ผ่านคำสั่ง
sqlite3 bap.db
โดยชื่อไฟล์ที่ใส่เข้าไปเป็น argument นั่นก็คือชื่อของไฟล์ Database ที่เราต้องการเก็บข้อมูลไว้
หากไม่ได้ใส่ชื่อไว้ การใช้งาน SQLite จะเป็นแบบใช้ memory เท่านั้นไม่ได้เก็บบันทึกข้อมูลไว้

สร้างตาราง


  1. ทำการสร้างตารางของ publisher โดยใช้คำสั่ง

    CREATE TABLE publisher (publisher_id INTEGER PRIMARY KEY AUTOINCREMENT, publisher_name TEXT NOT NULL, location TEXT NOT NULL, publisher_tel TEXT NOT NULL);
  2. ทำการสร้างตารางของ authorโดยใช้คำสั่ง

    CREATE TABLE author (author_id INTEGER PRIMARY KEY AUTOINCREMENT, author_name TEXT NOT NULL, author_address TEXT, author_tel TEXT, author_ssn TEXT UNIQUE, publisher_id INTEGER NOT NULL, FOREIGN KEY(publisher_id) REFERENCES publisher(publisher_id));

    จะเห็นได้ว่าในตารางนี้มีการเพิ่ม FOREIGN KEY เข้าไปเพื่อทำการเชื่อมตาราง Author และ Publisher เข้าด้วยกันโดยใช้ publisher_id เป็น foreign key
  3. ทำการสร้างตารางของ book ใช้คำสั่ง

    CREATE TABLE book (book_id INTEGER PRIMARY KEY AUTOINCREMENT, book_name TEXT NOT NULL, release_date TEXT NOT NULL, copy INTEGER NOT NULL, publisher_id INTEGER, FOREIGN KEY(publisher_id) REFERENCES publisher(publisher_id));

    จะเห็นได้ว่าในตารางนี้มีการเพิ่ม FOREIGN KEY เข้าไปเพื่อทำการเชื่อมตาราง book และ Publisher เข้าด้วยกันโดยใช้ publisher_id เป็น foreign key
  1. ทำการสร้างตารางของ book_author เนื่องจากความสัมพันธ์ของ book และ author เป็นแบบ M:N หรือแบบ Many to Many โดยใช้คำสั่ง

    CREATE TABLE book_author (book_id INTEGER, author_id INTEGER, FOREIGN KEY(book_id) REFERENCES book(book_id),FOREIGN KEY(author_id) REFERENCES author(author_id));

    โดยตารางนี้จะมี key อยู่ 2 อันนั้นก็คือ book_id และ author_id ซึ่งใช้เป็นการเชื่อมกันระหว่างตาราง book และ author

ทดสอบตาราง

ข้อสังเกต หากไม่แน่ใจว่า SQLite ได้ทำการ enable foreign key แล้วหรือไม่ ให้ใช้คำสั่ง
                   PRAGMA foreign_keys;
                 หากค่าที่ส่งกลับมาเป็น 0 แสดงว่า SQLite ยังไม่ได้เปิดใช้งาน Foreign key
                 สามารถเปิดใช้งานได้โดยใช้คำสั่ง
                  PRAGMA foreign_keys = ON;
                หลังจากนั้นให้ทดสอบโดยใช้คำสั่งแรกอีกครั้ง หากได้ค่าที่ส่งกลับเป็น 1 หมายความว่า                          สามารถใช้งาน Foreign Key ได้แล้ว

ทดสอบการเพิ่มข้อมูลสำหรับ publisher

ใช้คำสั่ง
INSERT INTO publisher (publisher_name, publisher_address, publisher_tel) VALUES ("Fah Sai", "Bangkok", "029523875"); 
INSERT INTO publisher (publisher_name, location, publisher_tel) VALUES ("Pearson Education Limited", "United Kingdom", "020 7010 2336"); 
INSERT INTO publisher (publisher_name, location, publisher_tel) VALUES ("Mcgraw-hill International Enterprises", "United States", "212-904-2078");                                                                  

ทดสอบการเพิ่มข้อมูลสำหรับ author

ใช้คำสั่ง
INSERT INTO author (author_name, author_address, publisher_id) VALUES ("William Stallings", "USA", 2); 
INSERT INTO author (author_name, author_address, publisher_id) VALUES ("Robert W. Sebesta", "Colorado", 2); 
INSERT INTO author (author_name, author_address, publisher_id) VALUES ("Barry B. Brey", "USA", 2); 
INSERT INTO author (author_name, author_address, publisher_id) VALUES ("Stephen P. Robbins", "USA", 2); 
INSERT INTO author (author_name, author_address, publisher_id) VALUES ("Michael J Kroes", "USA", 3); 
INSERT INTO author (author_name, author_address, publisher_id) VALUES ("Stephen A. Ross", "USA", 3); 

โดยหากถ้าเราใส่ publisher_id ที่ไม่มีอยู่จริง จะได้รับ Error ดังนี้
 Error: FOREIGN KEY constraint failed
เนื่องจากติดเงื่อนไขของ foreign key นั่นเอง

ทดสอบการเพิ่มข้อมูลสำหรับ book


ใช้คำสั่ง

INSERT INTO book (book_name, release_date, copy, publisher_id) VALUES ("Concepts of Programming Languages", "2013", "10", 2); 
INSERT INTO book (book_name, release_date, copy, publisher_id) VALUES ("Programming the World Wide Web", "2013", "7", 2); 
INSERT INTO book (book_name, release_date, copy, publisher_id) VALUES ("The Intel Microprocessors : Architecture, Programmi", "2008", "8", 2); 
INSERT INTO book (book_name, release_date, copy, publisher_id) VALUES ("Management", "2015", "13", 2); 
INSERT INTO book (book_name, release_date, copy, publisher_id) VALUES ("Aircraft Basic Science", "2013", "8", 3); 
INSERT INTO book (book_name, release_date, copy, publisher_id) VALUES ("Essentials of Corporate Finance", "2002", "3", 3); 
INSERT INTO book (book_name, release_date, copy, publisher_id) VALUES ("Egypt in Spectacular Cross-Section", "2005", "1", 3); 
INSERT INTO book (book_name, release_date, copy, publisher_id) VALUES ("Fundamentals of Corporate Finance Alternate Edition 9ED (P)", "2010", "9", 3); 
INSERT INTO book (book_name, release_date, copy, publisher_id) VALUES ("Corporate Finance : Corporate Principles", "2007", "1", 3);

ทดสอบการเพิ่มข้อมูลสำหรับ book_author


ใช้คำสั่ง
INSERT INTO book_author (book_id, author_id) VALUES (1, 1) -- Computer Organization and Architecture, William Stallings; 
INSERT INTO book_author (book_id, author_id) VALUES (2,2) -- Concepts of Programming Languages, Robert W. Sebesta; 
INSERT INTO book_author (book_id, author_id) VALUES (3,2) -- Programming the World Wide Web, Robert W. Sebesta; 
INSERT INTO book_author (book_id, author_id) VALUES (4,3) -- The Intel Microprocessors : Architecture, Programmi, Barry B. Brey; 
INSERT INTO book_author (book_id, author_id) VALUES (5,4) -- Management, Stephen P. Robbins; 
INSERT INTO book_author (book_id, author_id) VALUES (6,5) -- Aircraft Basic Science, Michael J Kroes; 
INSERT INTO book_author (book_id, author_id) VALUES (7,6) -- Essentials of Corporate Finance, Stephen A. Ross; 
INSERT INTO book_author (book_id, author_id) VALUES (8,6) -- Egypt in Spectacular Cross-Section, Stephen A. Ross; 
INSERT INTO book_author (book_id, author_id) VALUES (9,6) -- Fundamentals of Corporate Finance Alternate Edition 9ED (P), Stephen A. Ross; 
INSERT INTO book_author (book_id, author_id) VALUES (10,6) -- Corporate Finance : Corporate Principles, Stephen A. Ross;
เช่นเดียวกับกรณีของ author หากใส่ id ที่ไม่มีอยู่จริงก็จะได้รับ Contraint Error

ทดสอบการ Query ข้อมูล

การทดสอบสามารถทำโดยใช้ SQL SELECT Statement

ตัวอย่างการ Query ข้อมูลทั้งหมดของ book

SELECT * FROM book; 

ผลลัพธ์ที่ได้ 

1||Computer Organization and Architecture|2013|9|2
2||Concepts of Programming Languages|2013|10|2
3||Programming the World Wide Web|2013|7|2
4||The Intel Microprocessors : Architecture, Programmi|2008|1|2
5||Management|2015|13|2
6||Aircraft Basic Science|2013|8|3
7||Essentials of Corporate Finance|2002|3|3
8||Egypt in Spectacular Cross-Section|2005|1|3
9||Fundamentals of Corporate Finance Alternate Edition 9ED (P)|2010|9|3
10||Corporate Finance : Corporate Principles|2007|1|3

ตัวอย่างการ Query ข้อมูลทั้งหมดของ author

SELECT * FROM author; 

 ผลลัพธ์ที่ได้ 

1||William Stallings|USA|||2
2||Robert W. Sebesta|Colorado|||2
3||Barry B. Brey|USA|||2
4||Stephen P. Robbins|USA|||2
5||Michael J Kroes|USA|||3
6||Stephen A. Ross|USA|||3

ตัวอย่างการ Query ข้อมูลทั้งหมดของ publisher

SELECT * FROM publisher; 

 ผลลัพธ์ที่ได้ 

1||Fah Sai|Bangkok|0971073060
2||Pearson Education Limited|United Kingdom|020 7010 2336
3||Mcgraw-hill International Enterprises|United States|212-904-2078