More SQL
by K. Yue
1. More SQL Features
1.1 Prepared Statements
Example:
Try the following code in MySQL prompt.
-- Prepared statements.
SET @sql = "SELECT * FROM toyu.student";
PREPARE stmt FROM @sql;
EXECUTE stmt;
DEALLOCATE PREPARE stmt;
-- with placeholders.
SET @sql = "SELECT * FROM toyu.student WHERE major = ? AND ach >= ?";
PREPARE stmt FROM @sql;
SET @major = 'CSCI';
SET @ach = 38;
EXECUTE stmt USING @major, @ach;
EXECUTE stmt USING 'CSCI', 38;
SET @major = 'CINF';
SET @ach = 15;
EXECUTE stmt USING @major, @ach;
SET @major = 'ITEC';
SET @ach = 25;
EXECUTE stmt USING @major, @ach;
DEALLOCATE PREPARE stmt;
2. Views
CREATE
[OR REPLACE]
[ALGORITHM = {UNDEFINED | MERGE | TEMPTABLE}]
[DEFINER = { user | CURRENT_USER }]
[SQL SECURITY { DEFINER | INVOKER }]
VIEW view_name [(column_list)]
AS select_statement
[WITH [CASCADED | LOCAL] CHECK OPTION]
Example:
Execute the following code and ensure that you understand the result.
CREATE OR REPLACE VIEW school_summary(
schoolCode, schoolName, n_departments) AS
SELECT s.schoolCode, s.schoolName,
COUNT(d.deptCode) AS n_departments
FROM school AS s LEFT JOIN department AS d ON (s.schoolCode = d.schoolCode)
GROUP BY s.schoolCode, s.schoolName;
SHOW CREATE VIEW school_summary;
-- Note something like "ALGORITHM=UNDEFINED DEFINER=`yue`@`localhost` SQL SECURITY DEFINER"
-- (default values) may be added.
DESC school_summary;
SELECT *
FROM school_summary
WHERE n_departments > 0;
DROP VIEW school_summary;
Notes:
3. Stored Subroutines
3.1 Stored Procedures
Example:
Execute the following code and ensure that you understand the result.
-- A very simple stored procedure.
-- Redefine the delimiter to end the procedure.
DELIMITER //
CREATE OR REPLACE PROCEDURE deptInfo(IN dCode VARCHAR(4), OUT numFaculty INT)
BEGIN
-- Display some information.
SELECT d.deptName, d.SchoolCode, t1.n_majors, t2.n_minors
FROM department AS d INNER JOIN
(SELECT COUNT(stuId) AS n_majors
FROM student
WHERE major = dCode) AS t1 INNER JOIN
(SELECT COUNT(stuId) AS n_minors
FROM student
WHERE minor = dCode) AS t2
WHERE d.deptCode = dCode;
-- MySQL does not direct sending output to console.
-- It is necessary to use a SQL statement.
SELECT 'Debuggin comment can be put here.';
SELECT CONCAT('Faculty in the department: ', dCode) AS faculty;
SELECT *
FROM faculty AS f
WHERE f.deptCode = dCode;
SELECT COUNT(f.facId) INTO numFaculty
FROM faculty AS f
WHERE f.deptCode = dCode;
END //
DELIMITER ;
SHOW CREATE PROCEDURE deptInfo;
SET @numFaculty = 0;
SET @dCode = 'CSCI';
CALL deptInfo(@dCode, @numFaculty);
SELECT @dCode, @numFaculty;
SET @dCode = 'ITEC';
CALL deptInfo(@dCode, @numFaculty);
SELECT @dCode, @numFaculty;
DROP PROCEDURE deptInfo;
Note:
Example: using toyu
DELIMITER //
CREATE OR REPLACE PROCEDURE AddNewCourse(
IN course_id INT,
IN rubric CHAR(4),
IN course_number CHAR(4),
IN course_title VARCHAR(80),
IN credits TINYINT
)
BEGIN
INSERT INTO Course (courseId, rubric, number, title, credits)
VALUES (course_id, rubric, course_number, course_title, credits);
END //
DELIMITER ;
CALL AddNewCourse(3009, 'CSCI', '4436', 'Systems Administration', 3);
3.2 Stored Functions
Example:
Execute the following code and ensure that you understand the result.
-- A simple function
DELIMITER //
CREATE OR REPLACE FUNCTION n_major(dCode varchar(4)) RETURNS INT
READS SQL DATA
BEGIN
DECLARE count INT DEFAULT 0;
SELECT COUNT(*) INTO count
FROM student
WHERE major = dCode;
RETURN count;
END //
DELIMITER ;
SHOW CREATE FUNCTION n_major;
SELECT n_major('CSCI');
SELECT n_major('ITEC');
Note:
Example:
The following example functions are included in the script of creating toyu. Function calls are added.
-- get the full name of a student.
DELIMITER //
CREATE OR REPLACE FUNCTION GetStudentFullName(
student_id INT
)
RETURNS VARCHAR(61)
DETERMINISTIC
BEGIN
DECLARE full_name VARCHAR(61);
SELECT CONCAT(fname, ' ', lname) INTO full_name
FROM Student
WHERE stuId = student_id;
RETURN full_name;
END //
DELIMITER ;
SELECT GetStudentFullName(100000);
SELECT GetStudentFullName(100001);
-- get the full name of a department code
DELIMITER //
CREATE OR REPLACE FUNCTION GetDepartmentName(
dept_code CHAR(4)
)
RETURNS VARCHAR(30)
DETERMINISTIC
BEGIN
DECLARE dept_name VARCHAR(30);
SELECT d.deptName INTO dept_name
FROM Department d
WHERE d.deptCode = dept_code;
RETURN dept_name;
END //
DELIMITER ;
SELECT GetDepartmentName('CSCI');
SELECT GetDepartmentName('ITEC');
-- Compute and return the GPA of a student
DELIMITER //
CREATE OR REPLACE FUNCTION GetStudentGPA(
student_id INT
)
RETURNS DECIMAL(3,2)
DETERMINISTIC
BEGIN
DECLARE gpa DECIMAL(3,2);
SELECT ROUND(SUM(g.gradePoint * co.credits) / SUM(co.credits), 2) INTO gpa
FROM Enroll e
JOIN Class c ON e.classId = c.classId
JOIN Course co ON c.courseId = co.courseId
JOIN Grade g ON e.grade = g.grade
WHERE e.stuId = student_id;
RETURN gpa;
END //
DELIMITER ;
SELECT GetStudentGPA(100000);
SELECT GetStudentGPA(100001);
SELECT GetStudentGPA(100002);
-- Return the top n students in a major department with GPA.
DELIMITER //
CREATE OR REPLACE FUNCTION GetTopStudentsInDepartment(
dept_code CHAR(4),
top_count INT
)
RETURNS VARCHAR(1000)
DETERMINISTIC
BEGIN
DECLARE student_list VARCHAR(1000);
SET student_list = '';
WITH temp AS(
SELECT s.stuId, CONCAT(s.fname, ' ', s.lname) AS student,
ROUND(SUM(g.gradePoint * co.credits) / SUM(co.credits), 2) AS gpa
FROM Student s
JOIN Department d ON s.major = d.deptCode
JOIN Enroll e ON s.stuId = e.stuId
JOIN Class c ON e.classId = c.classId
JOIN Course co ON c.courseId = co.courseId
JOIN Grade g ON e.grade = g.grade
WHERE d.deptCode = dept_code
GROUP BY s.stuId, student
ORDER BY gpa DESC
LIMIT top_count)
SELECT GROUP_CONCAT(CONCAT(temp.student, '(', temp.stuId, '):', temp.gpa) SEPARATOR ', ')
INTO student_list
FROM temp
LIMIT 1;
RETURN student_list;
END //
DELIMITER ;
SELECT GetTopStudentsInDepartment('CSCI', 2);
SELECT GetTopStudentsInDepartment('CSCI', 3);
3.3 Cursors
Example:
Execute the following code and ensure that you understand the result. The example is artifically constructed as there are better ways.
-- using cursor.
DELIMITER //
CREATE FUNCTION major_students(dept VARCHAR(4))
RETURNS VARCHAR(1000)
READS SQL DATA
BEGIN
DECLARE result VARCHAR(1000) DEFAULT '';
DECLARE name VARCHAR(41) DEFAULT '';
DECLARE done INT DEFAULT FALSE;
DECLARE cursor_1 CURSOR FOR
SELECT DISTINCT CONCAT(fName, ' ', lName) AS name
FROM student
WHERE major = dept;
DECLARE continue handler FOR NOT FOUND SET done = TRUE;
OPEN cursor_1;
SET result = '';
compute_loop: LOOP
-- This is needed as there are other SQL statements that may set done to true.
SET done = false;
FETCH cursor_1 INTO name;
IF done THEN
LEAVE compute_loop;
END IF;
IF (result <> '') THEN
SET result = CONCAT(result, ', ');
END IF;
SET result = CONCAT(result, name);
END LOOP;
CLOSE cursor_1;
RETURN result;
END //
DELIMITER ;
SELECT major_students('CSCI');
SELECT major_students('CINF');
DROP FUNCTION major_students;
SELECT GROUP_CONCAT(CONCAT(fName, ' ', lName) SEPARATOR ', ') AS majors
FROM student
WHERE major = 'CSCI';
4. Triggers
4.1 The event model for triggers
Advantages of triggers
Disadvantages:
Example:
An example of using trigger is for auditing, e.g. https://vladmihalcea.com/mysql-audit-logging-triggers/
Execute the following code for auditing and ensure that you understand the result.
-- trigger
CREATE TABLE courseUpdate(
cuId INT NOT NULL AUTO_INCREMENT,
`type` CHAR(1),
courseId INT NOT NULL,
rubric CHAR(4) NOT NULL,
number CHAR(4) NOT NULL,
title VARCHAR(80) NOT NULL,
credits TINYINT NULL,
ts TIMESTAMP DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
CONSTRAINT CourseUpdate_courseId_pk PRIMARY KEY (cuId),
CONSTRAINT CourseUpdate_deptCode_fk FOREIGN KEY (rubric)
REFERENCES Department(deptCode));
-- trigger example.
DELIMITER $$
CREATE TRIGGER update_Course AFTER UPDATE ON Course FOR EACH ROW
BEGIN
-- code should be more sophisticated.
INSERT INTO courseUpdate(`type`, courseId, rubric, number, title, credits)
VALUES('U', new.courseId, new.rubric, new.number, new.title, new.credits);
END $$
CREATE TRIGGER insert_Course AFTER INSERT ON Course FOR EACH ROW
BEGIN
-- code should be more sophisticated.
INSERT INTO courseUpdate(`type`, courseId, rubric, number, title, credits)
VALUES('I', new.courseId, new.rubric, new.number, new.title, new.credits);
END $$
DELIMITER ;
SELECT * FROM Course;
UPDATE Course
SET number = 2315
WHERE courseId = 2000;
INSERT INTO Course(courseId, rubric, number, title, credits) VALUES
(4000,'CSCI',3532,'Advanced Data Structures',3);
UPDATE Course
SET number = 3341
WHERE courseId = 4000;
SELECT * FROM course;
SELECT * FROM courseUpdate;
-- Clean up.
DROP TRIGGER update_Course;
DROP TRIGGER insert_Course;
DROP TABLE courseUpdate;
DELETE FROM Course
WHERE CourseId = 4000;
UPDATE Course
SET number = 3333
WHERE courseId = 2000;
SELECT * FROM Course;
5. System Catalog
Example:
mysql> show databases;
+--------------------+
| Database |
+--------------------+
| information_schema |
| mysql |
| performance_schema |
| swim |
| toytu |
| world |
| yue_exp |
+--------------------+
7 rows in set (0.00 sec)
>
5.1 mysql database
Example:
SELECT DISTINCT u.user, u.password
FROM mysql.user AS u
WHERE u.host = 'localhost';
5.2 information_schema database
Example:
mysql> SELECT table_name, table_type, row_format, table_rows, avg_row_length
-> FROM information_schema.tables
-> WHERE table_schema = 'information_schema'
-> ORDER BY table_name DESC;
+---------------------------------------+-------------+------------+------------+----------------+
| table_name | table_type | row_format | table_rows | avg_row_length |
+---------------------------------------+-------------+------------+------------+----------------+
| VIEWS | SYSTEM VIEW | Dynamic | NULL | 0 |
| USER_PRIVILEGES | SYSTEM VIEW | Fixed | NULL | 1986 |
| TRIGGERS | SYSTEM VIEW | Dynamic | NULL | 0 |
| TABLE_PRIVILEGES | SYSTEM VIEW | Fixed | NULL | 2372 |
| TABLE_CONSTRAINTS | SYSTEM VIEW | Fixed | NULL | 2504 |
| TABLESPACES | SYSTEM VIEW | Fixed | NULL | 6951 |
| TABLES | SYSTEM VIEW | Fixed | NULL | 9450 |
| STATISTICS | SYSTEM VIEW | Fixed | NULL | 5753 |
| SESSION_VARIABLES | SYSTEM VIEW | Fixed | NULL | 3268 |
| SESSION_STATUS | SYSTEM VIEW | Fixed | NULL | 3268 |
| SCHEMA_PRIVILEGES | SYSTEM VIEW | Fixed | NULL | 2179 |
| SCHEMATA | SYSTEM VIEW | Fixed | NULL | 3464 |
| ROUTINES | SYSTEM VIEW | Dynamic | NULL | 0 |
| REFERENTIAL_CONSTRAINTS | SYSTEM VIEW | Fixed | NULL | 4814 |
| PROFILING | SYSTEM VIEW | Fixed | NULL | 308 |
| PROCESSLIST | SYSTEM VIEW | Dynamic | NULL | 0 |
| PLUGINS | SYSTEM VIEW | Dynamic | NULL | 0 |
| PARTITIONS | SYSTEM VIEW | Dynamic | NULL | 0 |
| PARAMETERS | SYSTEM VIEW | Dynamic | NULL | 0 |
| KEY_COLUMN_USAGE | SYSTEM VIEW | Fixed | NULL | 4637 |
| INNODB_TRX | SYSTEM VIEW | Fixed | NULL | 4534 |
| INNODB_LOCK_WAITS | SYSTEM VIEW | Fixed | NULL | 599 |
| INNODB_LOCKS | SYSTEM VIEW | Fixed | NULL | 31244 |
| INNODB_CMP_RESET | SYSTEM VIEW | Fixed | NULL | 25 |
| INNODB_CMPMEM_RESET | SYSTEM VIEW | Fixed | NULL | 29 |
| INNODB_CMPMEM | SYSTEM VIEW | Fixed | NULL | 29 |
| INNODB_CMP | SYSTEM VIEW | Fixed | NULL | 25 |
| GLOBAL_VARIABLES | SYSTEM VIEW | Fixed | NULL | 3268 |
| GLOBAL_STATUS | SYSTEM VIEW | Fixed | NULL | 3268 |
| FILES | SYSTEM VIEW | Fixed | NULL | 2677 |
| EVENTS | SYSTEM VIEW | Dynamic | NULL | 0 |
| ENGINES | SYSTEM VIEW | Fixed | NULL | 490 |
| COLUMN_PRIVILEGES | SYSTEM VIEW | Fixed | NULL | 2565 |
| COLUMNS | SYSTEM VIEW | Dynamic | NULL | 0 |
| COLLATION_CHARACTER_SET_APPLICABILITY | SYSTEM VIEW | Fixed | NULL | 195 |
| COLLATIONS | SYSTEM VIEW | Fixed | NULL | 231 |
| CHARACTER_SETS | SYSTEM VIEW | Fixed | NULL | 384 |
+---------------------------------------+-------------+------------+------------+----------------+
37 rows in set (0.08 sec)
Example:
Execute the following code and ensure that you understand the results.
-- System Catalog
-- Getting selected columns from information_schema
SELECT table_name, table_type, row_format, table_rows, avg_row_length
FROM information_schema.tables
WHERE table_schema = 'information_schema'
ORDER BY table_name DESC;
-- databases and tables
SELECT t.TABLE_SCHEMA AS `schema`, COUNT(t.TABLE_NAME) AS num_tables
FROM information_schema.tables AS t
GROUP BY `schema`
ORDER BY num_tables DESC;
SELECT t.TABLE_SCHEMA AS `schema`, t.ENGINE, COUNT(t.TABLE_NAME) AS num_tables
FROM information_schema.tables t
GROUP BY `schema`, t.ENGINE
ORDER BY `schema`, num_tables DESC;
Example:
DROP SCHEMA IF EXISTS dbtool;
CREATE SCHEMA dbtool;
USE dbtool;
-- Return the number of columns of a table in a schema (database) in the output parameter column_count
DELIMITER //
CREATE OR REPLACE PROCEDURE count_columns(
IN schema_name VARCHAR(64),
IN table_name VARCHAR(64),
OUT column_count INT
)
BEGIN
SET @__cc_query = CONCAT('SELECT COUNT(*) INTO @__cc_column_count FROM INFORMATION_SCHEMA.COLUMNS WHERE TABLE_SCHEMA = ? AND TABLE_NAME = ?');
PREPARE stmt FROM @__cc_query;
SET @__cc_schema_name = schema_name;
SET @__cc_table_name = table_name;
EXECUTE stmt USING @__cc_schema_name, @__cc_table_name;
SET column_count = @__cc_column_count;
DEALLOCATE PREPARE stmt;
END //
DELIMITER ;
CALL count_columns('toyu', 'student', @column_count);
SELECT @column_count;
CALL count_columns('swim', 'swimmer', @column_count);
SELECT @column_count;
5.3 performance_schema database