Advanced SQL Query Techniques: Data Retrieval and Manipulation

SELECT Statement Fundamentals

The SELECT statement is fundamental for retrieving data from databases. When used alone, it can access system parameters and configuration settings.

-- View system parameters
SELECT @@port;
SELECT @@basedir;
SELECT @@datadir;
SELECT @@socket;
SELECT @@server_id;
SELECT @@innodb_flush_log_at_trx_commit;
SHOW VARIABLES LIKE 'innodb%';

For comprehensive MySQL function documentation, refer to the official manual:

https://dev.mysql.com/doc/refman/5.7/en/func-op-summary-ref.html

SQL Statement Structure

MySQL follows this logical order when processing queries:

SELECT columns
FROM tables
WHERE conditions
GROUP BY conditions
HAVING conditions
ORDER BY conditions
LIMIT number;

Understanding Table Structures

Before querying, it's essential to understand the table structuers. Here's the locations table structure:

DESCRIBE locations;

+-------------+----------+------+-----+---------+----------------+
| Field       | Type     | Null | Key | Default | Extra          |
+-------------+----------+------+-----+---------+----------------+
| ID          | int(11)  | NO   | PRI | NULL    | auto_increment |
| Name        | char(35) | NO   |     |         |                |
| CountryCode | char(3)  | NO   | MUL |         |                |
| Region      | char(20) | NO   |     |         |                |
| Population  | int(11)  | NO   |     | 0       |                |
+-------------+----------+------+-----+---------+----------------+

Quick ways to understand table structures:

DESCRIBE locations;
SHOW CREATE TABLE locations;
SELECT * FROM locations LIMIT 5;

Basic SELECT with FROM Clause

Retrieve all information from a table:

SELECT id, name, countrycode, region, population FROM locations;
-- Or using wildcard
SELECT * FROM locations;

Retrieve specific columns:

SELECT name, population FROM locations;

Filtering Data with WHERE Clause

Filter data based on conditions:

-- Cities in China with population
SELECT name, population FROM locations WHERE countrycode = 'CHN';

-- Cities with population less than 100
SELECT name, population FROM locations WHERE population < 100;

-- Chinese cities with population over 8 million
SELECT name, population FROM locations 
WHERE countrycode = 'CHN' AND population > 8000000;

-- Cities in China or USA
SELECT name, population FROM locations 
WHERE countrycode = 'CHN' OR countrycode = 'USA';

-- Cities with population between 5M and 6M
SELECT name, population FROM locations 
WHERE population > 5000000 AND population < 6000000;
-- Alternative syntax
SELECT name, population FROM locations 
WHERE population BETWEEN 5000000 AND 6000000;

-- Country codes starting with 'CH'
SELECT * FROM locations WHERE countrycode LIKE 'CH%';

Note: Avoid patterns like '%CH%' as they typically don't use indexes and perform poorly. For frequent searches, consider using Elasticsearch.

Alternative to OR conditions:

SELECT name, population FROM locations 
WHERE countrycode IN ('CHN', 'USA');

Grouping Data with GROUP BY

GROUP BY combines rows with identical values in specified columns:

-- Count cities per country
SELECT countrycode, COUNT(id) FROM locations GROUP BY countrycode;

-- Total population per country
SELECT countrycode, SUM(population) FROM locations GROUP BY countrycode;

-- Count distinct regions per country
SELECT countrycode, COUNT(DISTINCT region) FROM locations GROUP BY countrycode;

-- Total population per Chinese region
SELECT region, SUM(population) FROM locations 
WHERE countrycode = 'CHN' GROUP BY region;

-- Count cities per Chinese region
SELECT region, COUNT(name) FROM locations 
WHERE countrycode = 'CHN' GROUP BY region;

-- List cities per Chinese region
SELECT region, GROUP_CONCAT(name) FROM locations 
WHERE countrycode = 'CHN' GROUP BY region;

-- Formatted output
SELECT CONCAT(region, ":", GROUP_CONCAT(name)) FROM locations 
WHERE countrycode = 'CHN' GROUP BY region;

Sorting and Limiting Results

Combine GROUP BY with HAVING, ORDER BY, and LIMIT:

-- Countries with population over 50M, sorted by population
SELECT countrycode, SUM(population) FROM locations 
GROUP BY countrycode 
HAVING SUM(population) > 50000000 
ORDER BY SUM(population) DESC;

-- Top 3 countries by population over 50M
SELECT countrycode, SUM(population) FROM locations 
GROUP BY countrycode 
HAVING SUM(population) > 50000000 
ORDER BY SUM(population) DESC 
LIMIT 3 OFFSET 0;

-- Alternative pagination syntax
SELECT countrycode, SUM(population) FROM locations 
GROUP BY countrycode 
HAVING SUM(population) > 50000000 
ORDER BY SUM(population) DESC 
LIMIT 3, 3; -- Skip 3, show 3

Practice Exercises

-- Chinese regions with total population under 1M
SELECT region, SUM(population) FROM locations 
WHERE countrycode = 'CHN' 
GROUP BY region 
HAVING SUM(population) < 1000000;

-- All Chinese cities sorted by population
SELECT * FROM locations WHERE countrycode = 'CHN' 
ORDER BY population DESC;

-- Chinese regions by total population
SELECT region, SUM(population) FROM locations 
WHERE countrycode = 'CHN' 
GROUP BY region 
ORDER BY SUM(population) DESC;

-- Top 3 Chinese regions with population over 5M
SELECT region, SUM(population) FROM locations 
WHERE countrycode = 'CHN' 
GROUP BY region 
HAVING SUM(population) > 5000000 
ORDER BY SUM(population) DESC 
LIMIT 3;

Combining Result Sets

UNION and UNION ALL combine multiple result sets:

-- UNION ALL (includes duplicates)
SELECT * FROM locations WHERE countrycode = 'CHN'
UNION ALL 
SELECT * FROM locations WHERE countrycode = 'USA';

-- UNION (removes duplicates)
SELECT * FROM locations WHERE countrycode = 'CHN'
UNION 
SELECT * FROM locations WHERE countrycode = 'USA';

Multi-Table Joins

Joins combine data from multiple tables:

-- Create sample tables
CREATE DATABASE education;
USE education;

CREATE TABLE students (
    student_id INT PRIMARY KEY AUTO_INCREMENT,
    name VARCHAR(50),
    age TINYINT,
    gender ENUM('M', 'F')
);

CREATE TABLE courses (
    course_id INT PRIMARY KEY,
    course_name VARCHAR(50),
    instructor_id INT
);

CREATE TABLE enrollments (
    student_id INT,
    course_id INT,
    grade INT
);

CREATE TABLE instructors (
    instructor_id INT PRIMARY KEY,
    name VARCHAR(50)
);

-- Insert sample data
INSERT INTO students (name, age, gender) VALUES
('Zhang San', 20, 'M'),
('Li Si', 21, 'F'),
('Wang Wu', 19, 'M');

INSERT INTO instructors (instructor_id, name) VALUES
(101, 'Professor Zhang'),
(102, 'Professor Li');

INSERT INTO courses (course_id, course_name, instructor_id) VALUES
(1001, 'Database Systems', 101),
(1002, 'Web Development', 102);

INSERT INTO enrollments (student_id, course_id, grade) VALUES
(1, 1001, 85),
(1, 1002, 78),
(2, 1002, 92),
(3, 1001, 88);

Join examples:

-- Count courses taken by Zhang San
SELECT s.name, COUNT(e.course_id)
FROM students s
JOIN enrollments e ON s.student_id = e.student_id
WHERE s.name = 'Zhang San';

-- Courses taken by Zhang San
SELECT s.name, GROUP_CONCAT(c.course_name)
FROM students s
JOIN enrollments e ON s.student_id = e.student_id
JOIN courses c ON e.course_id = c.course_id
WHERE s.name = 'Zhang San'
GROUP BY s.name;

-- Students taught by Professor Li
SELECT i.name AS instructor, GROUP_CONCAT(s.name), COUNT(s.name)
FROM instructors i
JOIN courses c ON i.instructor_id = c.instructor_id
JOIN enrollments e ON c.course_id = e.course_id
JOIN students s ON e.student_id = s.student_id
WHERE i.name = 'Professor Li'
GROUP BY i.name;

-- Average grade for Professor Li's courses
SELECT i.name, AVG(e.grade)
FROM instructors i
JOIN courses c ON i.instructor_id = c.instructor_id
JOIN enrollments e ON c.course_id = e.course_id
WHERE i.name = 'Professor Li'
GROUP BY c.course_id;

-- Average grade per instructor and course
SELECT i.name AS instructor, c.course_name, AVG(e.grade)
FROM instructors i
JOIN courses c ON i.instructor_id = c.instructor_id
JOIN enrollments e ON c.course_id = e.course_id
GROUP BY i.name, c.course_name
ORDER BY AVG(e.grade);

-- Students failing Professor Li's course
SELECT i.name AS instructor, s.name AS student, e.grade
FROM instructors i
JOIN courses c ON i.instructor_id = c.instructor_id
JOIN enrollments e ON c.course_id = e.course_id
JOIN students s ON e.student_id = s.student_id
WHERE i.name = 'Professor Li' AND e.grade < 60;

-- All instructors' failing students
SELECT i.name, GROUP_CONCAT(CONCAT(s.name, ":", e.grade))
FROM instructors i
JOIN courses c ON i.instructor_id = c.instructor_id
JOIN enrollments e ON c.course_id = e.course_id
JOIN students s ON e.student_id = s.student_id
WHERE e.grade < 60
GROUP BY i.instructor_id;

Using Aliases

Aliases improve query readability:

-- Table aliases
SELECT t.name AS instructor, GROUP_CONCAT(CONCAT(st.name, ":", e.grade))
FROM instructors AS t
JOIN courses AS c ON t.instructor_id = c.instructor_id
JOIN enrollments AS e ON c.course_id = e.course_id
JOIN students AS st ON e.student_id = st.student_id
WHERE e.grade < 60
GROUP BY t.instructor_id;

-- Column aliases
SELECT t.name AS instructor_name, 
       GROUP_CONCAT(CONCAT(st.name, ":", e.grade)) AS failing_students
FROM instructors AS t
JOIN courses AS c ON t.instructor_id = c.instructor_id
JOIN enrollments AS e ON c.course_id = e.course_id
JOIN students AS st ON e.student_id = st.student_id
WHERE e.grade < 60
GROUP BY t.instructor_id;

Metadata Queries

Access database metadata through information_schema:

-- Table metadata structure
DESCRIBE information_schema.tables;

+-----------------------+---------------------+------+-----+---------+-------+
| Field                 | Type                | Null | Key | Default | Extra |
+-----------------------+---------------------+------+-----+---------+-------+
| TABLE_CATALOG         | varchar(512)        | YES  |     | NULL    |       |
| TABLE_SCHEMA          | varchar(64)         | NO   |     |         |       |
| TABLE_NAME            | varchar(64)         | NO   |     |         |       |
| TABLE_TYPE            | varchar(64)         | NO   |     |         |       |
| ENGINE                | varchar(64)         | NO   |     |         |       |
| VERSION               | bigint(21) unsigned | YES  |     | NULL    |       |
| ROW_FORMAT            | varchar(10)         | NO   |     |         |       |
| TABLE_ROWS            | bigint(21) unsigned | YES  |     | NULL    |       |
| AVG_ROW_LENGTH        | bigint(21) unsigned | YES  |     | NULL    |       |
| DATA_LENGTH           | bigint(21) unsigned | YES  |     | NULL    |       |
| MAX_DATA_LENGTH       | bigint(21) unsigned | YES  |     | NULL    |       |
| INDEX_LENGTH          | bigint(21) unsigned | YES  |     | NULL    |       |
| DATA_FREE             | bigint(21) unsigned | YES  |     | NULL    |       |
| AUTO_INCREMENT        | bigint(21) unsigned | YES  |     | NULL    |       |
| CREATE_TIME           | datetime            | YES  |     | NULL    |       |
| UPDATE_TIME           | datetime            | YES  |     | NULL    |       |
| CHECK_TIME            | datetime            | YES  |     | NULL    |       |
| TABLE_COLLATION       | varchar(32)         | NO   |     |         |       |
| CHECKSUM              | bigint(21) unsigned | YES  |     | NULL    |       |
| CREATE_OPTIONS        | varchar(255)        | YES  |     | NULL    |       |
| COMMENT               | varchar(2048)       | NO   |     |         |       |
| MAX_INDEX_LENGTH      | bigint(21) unsigned | YES  |     | NULL    |       |
| INSERT_METHOD         | varchar(6)          | YES  |     | NULL    |       |
| DATA_DIRECTORY        | varchar(255)        | YES  |     | NULL    |       |
| INDEX_DIRECTORY       | varchar(255)        | YES  |     | NULL    |       |
| TRAINING_INSTANCE     | varchar(255)        | YES  |     | NULL    |       |
+-----------------------+---------------------+------+-----+---------+-------+

-- All databases and tables
SELECT table_schema, table_name FROM information_schema.tables;

-- All tables in world database
SELECT table_schema, GROUP_CONCAT(table_name) 
FROM information_schema.tables 
GROUP BY table_schema \G;

-- All InnoDB tables
SELECT table_schema, table_name, ENGINE 
FROM information_schema.tables 
WHERE ENGINE = 'InnoDB';

-- Calculate table size (KB) = (avg_row_length * rows + index_length) / 1024
SELECT table_name, 
       (AVG_ROW_LENGTH * TABLE_ROWS + INDEX_LENGTH) / 1024 AS size_kb 
FROM information_schema.TABLES 
WHERE table_schema = 'education' AND table_name = 'students';

-- Total size of education database
SELECT table_schema, 
       SUM((AVG_ROW_LENGTH * TABLE_ROWS + INDEX_LENGTH)) / 1024 AS total_kb 
FROM information_schema.TABLES 
WHERE table_schema = 'education';

-- Database sizes sorted by size
SELECT table_schema, 
       SUM((AVG_ROW_LENGTH * TABLE_ROWS + INDEX_LENGTH)) / 1024 AS total_kb 
FROM information_schema.TABLES 
GROUP BY table_schema 
ORDER BY total_kb DESC;

Dynamic SQL with CONCAT()

Generate SQL statements dynamically:

-- Backup all tables in world database
SELECT CONCAT("mysqldump -uroot -p123 ", table_schema, " ", table_name, 
             " >/backups/", table_schema, "_", table_name, ".sql")
FROM information_schema.tables;

-- Discard tablespace for all tables in education database
SELECT CONCAT("ALTER TABLE ", table_schema, ".", table_name, " DISCARD TABLESPACE;")
FROM information_schema.tables 
WHERE table_schema = 'education';

SHOW Commands

Various SHOW commands provide database information:

SHOW DATABASES;           -- List databases
SHOW TABLES;              -- List tables in current database
SHOW CREATE DATABASE db;  -- Show database creation statement
SHOW CREATE TABLE tbl;    -- Show table creation statement
SHOW PROCESSLIST;         -- Current connections
SHOW CHARSET;             -- Supported character sets
SHOW COLLATION;           -- Supported collations
SHOW GRANTS FOR user;     -- User permissions
SHOW VARIABLES LIKE '%var%'; -- System variables
SHOW ENGINES;             -- Supported storage engines
SHOW INDEX FROM tbl;      -- Table indexes
SHOW ENGINE InnoDB STATUS\G -- InnoDB engine status
SHOW BINARY LOGS;         -- Binary log files
SHOW MASTER STATUS;       -- Current binary log info
SHOW SLAVE STATUS\G;      -- Replication slave status
SHOW STATUS LIKE '%stat%'; -- Database status

GROUP BY Considerations

MySQL's sql_mode affects GROUP BY behavior:

-- Check current sql_mode
SELECT @@sql_mode;

-- Example with ONLY_FULL_GROUP_BY enabled
SELECT user, GROUP_CONCAT(host) FROM mysql.user GROUP BY User;

-- Solution 1: Use functions
SELECT user, GROUP_CONCAT(host) FROM mysql.user GROUP BY User;

-- Solution 2: Disable ONLY_FULL_GROUP_BY
-- In configuration file:
sql_mode=STRICT_TRANS_TABLES,NO_ZERO_IN_DATE,NO_ZERO_DATE,ERROR_FOR_DIVISION_BY_ZERO,NO_AUTO_CREATE_USER,NO_ENGINE_SUBSTITUTION

Tags: MySQL sql Data Query Language Database Queries Table Joins

Posted on Thu, 13 Aug 2026 16:50:58 +0000 by REDFOXES06