From 65f0644609cf6ae693de518a40ef05f380fb1088 Mon Sep 17 00:00:00 2001 From: NemCaBong <89348778+NemCaBong@users.noreply.github.com> Date: Sun, 28 May 2023 14:18:52 +0700 Subject: [PATCH 1/5] Create sol-2.sql --- sol-2.sql | 22 ++++++++++++++++++++++ 1 file changed, 22 insertions(+) create mode 100644 sol-2.sql diff --git a/sol-2.sql b/sol-2.sql new file mode 100644 index 0000000..faa198b --- /dev/null +++ b/sol-2.sql @@ -0,0 +1,22 @@ +# Write your MySQL query statement below +SELECT 'High Salary' as category, +COUNT( + account_id +) as accounts_count +FROM Accounts +WHERE income > 50000 +UNION +SELECT 'Average Salary' as category, +COUNT( + account_id +) as accounts_count +FROM Accounts +WHERE income >= 20000 AND income <= 50000 +UNION +SELECT 'Low Salary' as category, +COUNT( + account_id +) as accounts_count +FROM Accounts +WHERE income < 20000 + From 8bf84a601a64ba95de71e0a15d324b772273663a Mon Sep 17 00:00:00 2001 From: NemCaBong <89348778+NemCaBong@users.noreply.github.com> Date: Sun, 28 May 2023 14:21:26 +0700 Subject: [PATCH 2/5] Create sol-1.sql --- sol-1.sql | 11 +++++++++++ 1 file changed, 11 insertions(+) create mode 100644 sol-1.sql diff --git a/sol-1.sql b/sol-1.sql new file mode 100644 index 0000000..449457c --- /dev/null +++ b/sol-1.sql @@ -0,0 +1,11 @@ +# Write your MySQL query statement below + +SELECT stock_name, +SUM( +CASE + WHEN operation = 'Buy' THEN -price + WHEN operation = 'Sell' THEN price +END ) as capital_gain_loss +FROM Stocks +GROUP BY stock_name + From d9900b9f4e145a0323b1257b16c639d1320d8f1a Mon Sep 17 00:00:00 2001 From: NemCaBong <89348778+NemCaBong@users.noreply.github.com> Date: Thu, 1 Jun 2023 11:31:02 +0700 Subject: [PATCH 3/5] Create create-db-engineerpro.sql --- create-db-engineerpro.sql | 143 ++++++++++++++++++++++++++++++++++++++ 1 file changed, 143 insertions(+) create mode 100644 create-db-engineerpro.sql diff --git a/create-db-engineerpro.sql b/create-db-engineerpro.sql new file mode 100644 index 0000000..88ff71d --- /dev/null +++ b/create-db-engineerpro.sql @@ -0,0 +1,143 @@ +USE engineerpro; +CREATE TABLE Professor ( + prof_id INT PRIMARY KEY NOT NULL AUTO_INCREMENT, + prof_lname VARCHAR(50), + prof_fname VARCHAR(50) +); +CREATE TABLE Student ( + stud_id INT PRIMARY KEY NOT NULL AUTO_INCREMENT, + stud_fname VARCHAR(50) NOT NULL, + stud_lname VARCHAR(50) NOT NULL, + stud_street VARCHAR(255), + stud_city VARCHAR(50), + stud_zip VARCHAR(10) +); +CREATE TABLE Course( + course_id INT PRIMARY KEY NOT NULL AUTO_INCREMENT, + course_name VARCHAR(255) NOT NULL +); +CREATE TABLE IF NOT EXISTS Room( + room_id INT PRIMARY KEY NOT NULL AUTO_INCREMENT, + room_loc VARCHAR(50) NOT NULL, + room_cap VARCHAR(50) NOT NULL, + class_id INT NULL +); +CREATE TABLE IF NOT EXISTS Class ( + class_id INT PRIMARY KEY NOT NULL AUTO_INCREMENT, + class_name VARCHAR(255) NOT NULL, + prof_id INT NOT NULL, + course_id INT NOT NULL, + room_id INT NOT NULL-- add constraint later +); +CREATE TABLE Enroll( + stud_id INT NOT NULL, + class_id INT NOT NULL, + grade VARCHAR(3) NOT NULL, + PRIMARY KEY (stud_id, class_id) +); +-- Add constraint to Class table +ALTER TABLE Class ADD CONSTRAINT fk_class_professor_prof_id +FOREIGN KEY (prof_id) REFERENCES Professor (prof_id); + +ALTER TABLE Class ADD CONSTRAINT fk_class_course_course_id +FOREIGN KEY (course_id) REFERENCES Course (course_id); + +ALTER TABLE Class ADD CONSTRAINT fk_class_room_room_id +FOREIGN KEY (room_id) REFERENCES Room (room_id); +-- end + + +-- add constraint to Enroll table +ALTER TABLE Enroll ADD CONSTRAINT fk_enroll_student_stud_id +FOREIGN KEY (stud_id) REFERENCES Student (stud_id); + +ALTER TABLE Enroll ADD CONSTRAINT fk_enroll_class_class_id +FOREIGN KEY (class_id) REFERENCES Class (class_id); +-- end + +-- add constraint to Room table +ALTER TABLE Room ADD CONSTRAINT fk_room_class_class_id +FOREIGN KEY (class_id) REFERENCES Class (class_id); +-- end + +-- INSERT +INSERT INTO Professor (prof_lname, prof_fname) +VALUES + ('Smith', 'John'), + ('Johnson', 'Emily'), + ('Brown', 'Michael'), + ('Davis', 'Sarah'), + ('Miller', 'David'), + ('Wilson', 'Jennifer'), + ('Anderson', 'Christopher'), + ('Thomas', 'Jessica'), + ('Taylor', 'Matthew'), + ('Moore', 'Amanda'); + +INSERT INTO Student (stud_fname, stud_lname, stud_street, stud_city, stud_zip) +VALUES + ('Emma', 'Johnson', '123 Main St', 'New York', '10001'), + ('Oliver', 'Williams', '456 Elm St', 'Los Angeles', '90001'), + ('Sophia', 'Jones', '789 Oak St', 'Chicago', '60601'), + ('Liam', 'Brown', '234 Maple St', 'Houston', '77001'), + ('Ava', 'Davis', '567 Pine St', 'Philadelphia', '19101'), + ('Noah', 'Wilson', '890 Cedar St', 'Phoenix', '85001'), + ('Isabella', 'Lee', '321 Birch St', 'San Antonio', '78201'), + ('Mason', 'Taylor', '654 Willow St', 'San Diego', '92101'), + ('Charlotte', 'Clark', '987 Spruce St', 'Dallas', '75201'), + ('Elijah', 'Moore', '654 Rose St', 'Austin', '78701'); + +INSERT INTO Course (course_name) +VALUES + ('Mathematics'), + ('English'), + ('History'), + ('Science'), + ('Art'), + ('Computer Science'), + ('Physics'), + ('Biology'), + ('Chemistry'), + ('Music'); + +-- set class id null than update later on +INSERT INTO Room (room_loc, room_cap, class_id) +VALUE + ('Building A, Room 101', '30',NULL), + ('Building A, Room 102', '25',NULL), + ('Building B, Room 201', '40',NULL), + ('Building B, Room 202', '35',NULL), + ('Building C, Room 301', '20',NULL), + ('Building C, Room 302', '15',NULL), + ('Building D, Room 401', '30',NULL), + ('Building D, Room 402', '25',NULL), + ('Building E, Room 501', '40',NULL), + ('Building E, Room 502', '35',NULL); + +INSERT INTO Class (class_name, prof_id, course_id, room_id) +VALUES + ('Class 1', 1, 1, 1), + ('Class 2', 2, 2, 2), + ('Class 3', 3, 3, 3), + ('Class 4', 4, 4, 4), + ('Class 5', 5, 5, 5), + ('Class 6', 6, 6, 6), + ('Class 7', 7, 7, 7), + ('Class 8', 8, 8, 8), + ('Class 9', 9, 9, 9), + ('Class 10',10,10,10); + +INSERT INTO Enroll (stud_id, class_id, grade) +VALUES + (1, 1, 'A'), + (2, 2, 'B'), + (3, 3, 'C'), + (4, 4, 'A'), + (5, 5, 'B'), + (6, 6, 'C'), + (7, 7, 'A'), + (8, 8, 'B'), + (9, 9, 'C'), + (10, 10, 'A'); + + From ba49aa1ab959a71698a4ba44e45ef105ba84deb2 Mon Sep 17 00:00:00 2001 From: NemCaBong <89348778+NemCaBong@users.noreply.github.com> Date: Fri, 2 Jun 2023 09:38:47 +0700 Subject: [PATCH 4/5] Create query_for_engineerpro_db --- query_for_engineerpro_db | 112 +++++++++++++++++++++++++++++++++++++++ 1 file changed, 112 insertions(+) create mode 100644 query_for_engineerpro_db diff --git a/query_for_engineerpro_db b/query_for_engineerpro_db new file mode 100644 index 0000000..c3f0ed2 --- /dev/null +++ b/query_for_engineerpro_db @@ -0,0 +1,112 @@ +-- query for engineerpro db + +-- 1. +USE engineerpro; +SELECT + CONCAT(stud_fname, " ", stud_lname) as stud_name, + CONCAT(prof_fname, " ", prof_lname) as prof_name, + cl.class_id +FROM Professor pr +JOIN Class cl -- class of prof + ON pr.prof_id = cl.prof_id +JOIN Enroll en -- make sure that classes have been enrolled + ON en.class_id = cl.class_id +JOIN Student st -- make sure the stud has been enrolled + ON en.stud_id = st.stud_id; + +-- 2 +SELECT + DISTINCT course_name, + CONCAT(prof_fname, " ", prof_lname) as prof_name +FROM Professor pr +JOIN Class cl + ON pr.prof_id = cl.prof_id +JOIN Course cr + ON cr.course_id = cl.course_id; + +-- 3 +SELECT + course_name, + CONCAT(stud_fname, " ", stud_lname) as stud_name +FROM Student st +JOIN Enroll en + ON st.stud_id = en.stud_id +JOIN Class cl + ON cl.class_id = en.class_id +JOIN Course cr + ON cr.course_id = cl.course_id; + +-- 4 +SELECT +( +CASE + WHEN grade = 'A' THEN 10 + WHEN grade = 'B' THEN 8 + WHEN grade = 'C' THEN 6 + WHEN grade = 'D' THEN 4 + WHEN grade = 'E' THEN 2 + WHEN grade = 'F' THEN 0 +END +) as grade +FROM Enroll; + +-- 5 +SELECT + CONCAT(stud_fname, " ", stud_lname) as stud_name, + AVG( + CASE + WHEN grade = 'A' THEN 10 + WHEN grade = 'B' THEN 8 + WHEN grade = 'C' THEN 6 + WHEN grade = 'D' THEN 4 + WHEN grade = 'E' THEN 2 + WHEN grade = 'F' THEN 0 + END + ) as academic_ability +FROM Enroll en +JOIN Student st + ON st.stud_id = en.stud_id +GROUP BY CONCAT(stud_fname, " ", stud_lname); + +-- 6 +SELECT + class_name, + AVG( + CASE + WHEN grade = 'A' THEN 10 + WHEN grade = 'B' THEN 8 + WHEN grade = 'C' THEN 6 + WHEN grade = 'D' THEN 4 + WHEN grade = 'E' THEN 2 + WHEN grade = 'F' THEN 0 + END + ) as grade +FROM Enroll en +JOIN Class cl + ON cl.class_id = en.class_id +GROUP BY class_name; +-- 7 +SELECT + course_name, + AVG( + CASE + WHEN grade = 'A' THEN 10 + WHEN grade = 'B' THEN 8 + WHEN grade = 'C' THEN 6 + WHEN grade = 'D' THEN 4 + WHEN grade = 'E' THEN 2 + WHEN grade = 'F' THEN 0 + END + ) as grade +FROM Enroll en +JOIN Class cl + ON cl.class_id = en.class_id +JOIN Course cr + ON cr.course_id = cl.class_id +GROUP BY course_name; + + + + + + From 0217ab5fb11dc5c5ec0bc7699afe526bff7181ba Mon Sep 17 00:00:00 2001 From: NemCaBong <89348778+NemCaBong@users.noreply.github.com> Date: Mon, 5 Jun 2023 11:54:51 +0700 Subject: [PATCH 5/5] Create redis_gin_go_excersise --- redis_gin_go_excersise | 216 +++++++++++++++++++++++++++++++++++++++++ 1 file changed, 216 insertions(+) create mode 100644 redis_gin_go_excersise diff --git a/redis_gin_go_excersise b/redis_gin_go_excersise new file mode 100644 index 0000000..b7026ab --- /dev/null +++ b/redis_gin_go_excersise @@ -0,0 +1,216 @@ +package main + +import ( + "fmt" + "log" + "net/http" + "strconv" + "sync" + "time" + + "github.com/gin-gonic/gin" + "github.com/go-redis/redis/v8" + "github.com/golang/groupcache/lru" +) + +var ( + redisClient *redis.Client +) + +// khởi tạo 1 biến mutex (khóa) +// Mutex là cơ chế đồng bộ hóa đảm bảo chỉ 1 gorountine (luồng) +// được thực hiện trong 1 thời điểm +var mutex = &sync.Mutex{} + +var lruCache *lru.Cache + +// 1 func để khởi tạo ra 1 redis Client mới +// và thông báo error nếu có +// func không có trả về +// mà thay đổi trực tiếp global variable: redisClient +func initRedis() { + // return a new client to the redis server + redisClient = redis.NewClient(&redis.Options{ + Addr: "localhost:6379", + Password: "", + DB: 0, + }) + // sử dụng Ping để xem server có chạy và kết nối thành công hay không + // .Result() để trả lại kết quả của lệnh Ping + // đưa context.Context vào trong lệnh đễ provide context + _, err := redisClient.Ping(redisClient.Context()).Result() + if err != nil { + log.Fatal("Failed to connect to Redis", err) + } + + // cho lru cache voi max la 10 entries + lruCache = lru.New(10) +} +func generateSessionID() string { + // tạo ra 1 session ID ngẫu nhiên + return fmt.Sprintf("%d", time.Now().UnixNano()) +} +func logInHandler(c *gin.Context) { + // lấy ra key từ trong POST urlencoded form + username := c.PostForm("username") + // password := c.PostForm("password") + // gin.H là 1 cái map <=> key-value, vs key là string + // JSON: serialize struct thành JSON trong response body + // ShouldBindJSON sẽ bind struct pointer và xem nó có thành JSON đc không + // if err := c.ShouldBindJSON(&user); err != nil { + // c.JSON(http.StatusBadRequest, gin.H{ + // "error": "Invalid request", // không được thì coi như request k hợp lệ + // }) + // return + // } + sessionID := generateSessionID() + // HSet được sử dụng để lưu trữ một cặp khóa-giá trị trong một hash (bảng băm) của Redis + // khóa là sessionID còn "username"->field, username->value + // err := redisClient.HSet(redisClient.Context(), sessionID, "username", username, "password", password).Err() + err := redisClient.Set(redisClient.Context(), sessionID, username, 0).Err() + if err != nil { + c.JSON(http.StatusInternalServerError, gin.H{"error": "Failed to create session"}) + return + } + // username, _ = redisClient.HGet(redisClient.Context(), sessionID, "username").Result() + // password, _ = redisClient.HGet(redisClient.Context(), sessionID, "password").Result() + // lưu + c.JSON(http.StatusOK, gin.H{ + "message": "Login successful", + "session_id": sessionID, + "username": username, + }) +} + +func main() { + initRedis() + // tạo ra 1 Object Router để định nghĩa + // các route xử lý HTTP + // có sẵn middleware: + // logger: ghi lại http và thông tin như phg thức đường dẫn + // recovery: xử lý panic, lỗi, đbảo sever không crash + router := gin.Default() + router.POST("/login", logInHandler) + router.GET("/ping", pingHandler) + router.GET("/top", topUserHandler) + router.GET("/count", countHandler) + // run attaches the router to a http.Server + // start listening and serving http requests + + err := router.Run("localhost:8080") + if err != nil { + log.Fatal(err) + } + +} +func pingHandler(c *gin.Context) { + // the sessionID will be saved as session_id + sessionID := c.Query("session_id") + // log.Printf("SessionID: %T\n", sessionID) + // Check if the session ID is valid + // look up in the redis cached + // Result() sẽ trả về giá trị value với key cung cấp và err + userName, err := redisClient.Get(redisClient.Context(), sessionID).Result() + // nếu không thấy có trong redis cache thì => sessionID k hợp lệ + if err == redis.Nil { + c.JSON(http.StatusUnauthorized, gin.H{ + "error": "invalid session ID", + "username": userName, + }) + return + } else if err != nil { + // nếu err có trả về gì đó + // có lỗi với hệ thống + c.JSON(http.StatusInternalServerError, gin.H{ + "error": "failed to retrieve session", + }) + return + } + + // đặt khóa để chỉ 1 người /ping được 1 lúc mà thôi + mutex.Lock() + defer mutex.Unlock() // mở lock sau khi thực hiện hết + + // tạo ra callCountKey cho lru.Cache lấy để get trong cache + callCountKey := fmt.Sprintf("call_count:%s", userName) + // Chúng ta sử dụng lru.Cache để lưu trữ và lấy ra số lần gọi của mỗi user + // sử dụng redis là nơi lưu chính cho session info(id, username) + // nhưng dùng lru.Cache để lưu call count giúp cải thiện perf + // The LRU cache acts as a local cache that holds a subset of the data in memory and provides faster access. + // giảm số redis query + // lru.Cache.Get() sẽ lấy khóa callCountKey có dạng call_count: ch được gọi bao giờ + // đây là lần 1 + callCount = 1 + } else { + // đổi từ string sang int + count, _ := strconv.Atoi(callCount.(string)) + if count >= 2 { + // nếu đây là ping lần thứ >= 2 => không cho + c.JSON(http.StatusTooManyRequests, gin.H{ + "error": "rate limit exceeded", + }) + return + } else { + // nếu không thì thêm 1 vào coi như thêm 1 lần ping + callCount = count + 1 + } + } + // add lần gọi ping này vào hyperloglog + err = redisClient.PFAdd(redisClient.Context(), "myset", sessionID).Err() + if err != nil { + c.JSON(http.StatusInternalServerError, gin.H{ + "error": "failed to increment ping count", + }) + return + } + // sau khi đã cập nhật callCount + // chúng ta sẽ lưu nó lại vào cache tiếp + // với key là: callCountKey và val: callCount + lruCache.Add(callCountKey, callCount) + + // Sleep với 5 giây để mô phỏng thời gian xử lý + time.Sleep(5 * time.Second) + c.JSON(http.StatusOK, gin.H{ + "message": "ping successfully", + }) +} + +func topUserHandler(c *gin.Context) { + // sử dụng hàm ZRevRangeWithScores + //trả về một danh sách các thành viên trong một Sorted Set + // theo thứ tự giảm dần (theo score) cùng với các điểm số tương ứng + sortedSet, err := redisClient.ZRevRangeWithScores(redisClient.Context(), "myset", 0, 9).Result() + if err != nil { + c.JSON(http.StatusInternalServerError, gin.H{ + "error": "failed to retrieve top 10 users calling API", + }) + } + var topUsers []string + for _, val := range sortedSet { + // lấy phần tử trong sortedSet = val.Member + // rồi ép kiểu về string cho vào slice + topUsers = append(topUsers, val.Member.(string)) + } + c.JSON(http.StatusOK, gin.H{ + "top_users": topUsers, + }) +} + +func countHandler(c *gin.Context) { + // hàm này sử dụng để trả về + // số ng gọi API + // sử dụng hyperloglog cho kết quả gần đúng + count, err := redisClient.PFCount(redisClient.Context(), "myset").Result() + if err != nil { + c.JSON(http.StatusInternalServerError, gin.H{"error": "failed to retrieve count"}) + return + } + + c.JSON(http.StatusOK, gin.H{"count": count}) +}