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:

  1. Use of the DELIMITER command to redefine '//' as the delimiter indicating the end of the stored procedure. Otherwise, the default ';' is the delimiter indicating the end of the stored procedure.
  2. Two parameters for this procedure: one using IN and one using OUT as the parameter passing mechanism.
  3. A procedure does not return any value and accomplish its goal through side effects.
  4. Side effects include:
    1. Return SELECT results in the procedure body.
    2. Copy numFaculty out upon completion.

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

  1. The event driven model suits certain kinds of tasks better.
  2. Can ease the implementation of consistency check.
  3. Can ease the implementation of business logic and integrity check.

Disadvantages:

  1. Relatively invisible, and possibly overlooked by developers.
  2. Relatively difficult to debug.
  3. Potential performance issues.
  4. Potential complicated interactions.

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