☰ Chapters
Week 3 · SQL Recap
CT004-3.5-3 Advanced Database Systems · Week 3

SQL: Data Definition & Manipulation

SELECT in depth — filtering, sorting, aggregates, grouping, subqueries, joins, EXISTS — plus INSERT, UPDATE and DELETE.

Contents
    Topic & structure
    Key terms you must be able to use

    DDL, DML, CREATE TABLE, INSERT INTO … VALUES, SELECT … FROM … WHERE, GROUP BY, HAVING, ORDER BY, DISTINCT, IN, NOT IN, LIKE, NULL, IS NOT NULL, COUNT, SUM, AVG, MIN, MAX, JOIN, EXISTS, NOT EXISTS, UPDATE … SET … WHERE.

    Objectives of SQL

    Ideally, a database language should allow a user to:

    SQL is a transform-oriented language with three major components:

    ComponentPurposeExamples
    DDL — Data Definition LanguageDefine database structureCREATE, ALTER, DROP
    DML — Data Manipulation LanguageRetrieve and update dataSELECT, INSERT, UPDATE, DELETE
    DCL — Data Control LanguageControl access to dataGRANT, REVOKE (see Week 8)

    SQL consists of standard English words:

    CREATE TABLE Staff (staffNo VARCHAR(5),
                        lName   VARCHAR(15),
                        salary  DECIMAL(7,2));
    
    INSERT INTO Staff VALUES ('SG16', 'Brown', 8300);
    
    SELECT staffNo, lName, salary
    FROM   Staff
    WHERE  salary > 10000;
    Extra · literals

    A literal is a constant value written in a statement. Non-numeric literals are enclosed in single quotes ('SG16', 'London'); numeric literals are not (8300).

    DreamHome sample data

    All examples use the DreamHome case study (Connolly & Begg). Expand a table to check query results yourself.

    Staff
    staffNofNamelNamepositionsexDOBsalarybranchNo
    SL21JohnWhiteManagerM1-Oct-4530000B005
    SG37AnnBeechAssistantF10-Nov-6012000B003
    SG14DavidFordSupervisorM24-Mar-5818000B003
    SA9MaryHoweAssistantF19-Feb-709000B007
    SG5SusanBrandManagerF3-Jun-4024000B003
    SL41JulieLeeAssistantF13-Jun-659000B005
    Branch
    branchNostreetcitypostcode
    B00522 Deer RdLondonSW1 4EH
    B00716 Argyll StAberdeenAB2 3SU
    B003163 Main StGlasgowG11 9QX
    B00432 Manse RdBristolBS99 1NZ
    B00256 Clover DrLondonNW10 6EU
    PropertyForRent
    propertyNostreetcitytyperoomsrentownerNostaffNobranchNo
    PA1416 HolheadAberdeenHouse6650CO46SA9B007
    PL946 Argyll StLondonFlat4400CO87SL41B005
    PG46 Lawrence StGlasgowFlat3350CO40nullB003
    PG362 Manor RdGlasgowFlat3375CO93SG37B003
    PG2118 Dale RdGlasgowHouse5600CO87SG37B003
    PG165 Novar DrGlasgowFlat4450CO93SG14B003
    Viewing and Client
    clientNopropertyNoviewDatecomment
    CR56PA1424-May-01too small
    CR76PG420-Apr-01too remote
    CR56PG426-May-01null
    CR62PA1414-May-01no dining room
    CR56PG3628-Apr-01null
    clientNofNamelNametelNoprefTypemaxRent
    CR76JohnKay0207-774-5632Flat425
    CR56AlineStewart0141-848-1825Flat350
    CR74MikeRitchie01475-392178House750
    CR62MaryTregear01224-196720Flat600

    The SELECT statement

    SELECT   [DISTINCT | ALL] {* | [columnExpression [AS newName]] [,...]}
    FROM     TableName [alias] [, ...]
    [WHERE   condition]
    [GROUP BY columnList] [HAVING condition]
    [ORDER BY columnList]
    ClauseWhat it does
    SELECTSpecifies which columns appear in the output
    FROMSpecifies the table(s) to be used
    WHEREFilters rows
    GROUP BYForms groups of rows with the same column value
    HAVINGFilters groups subject to some condition
    ORDER BYSpecifies the order of the output
    Rules

    The order of the clauses cannot be changed. Only SELECT and FROM are mandatory.

    Extra · logical processing order

    Although written SELECT-first, the DBMS logically evaluates: FROM → WHERE → GROUP BY → HAVING → SELECT → ORDER BY. This explains why you can’t use an aggregate in WHERE (groups don’t exist yet) but can in HAVING.

    Basic retrieval

    All columns, all rows

    List full details of all staff.

    SELECT staffNo, fName, lName, position, sex, DOB, salary, branchNo
    FROM   Staff;
    
    -- * is an abbreviation for 'all columns'
    SELECT * FROM Staff;

    Result: all 6 rows of the Staff table.

    Specific columns, all rows

    List salaries for all staff, showing only staff number, first and last names, and salary.

    SELECT staffNo, fName, lName, salary
    FROM   Staff;

    Use of DISTINCT

    List the property numbers of all properties that have been viewed.

    SELECT propertyNo FROM Viewing;           -- PA14, PG4, PG4, PA14, PG36
    SELECT DISTINCT propertyNo FROM Viewing;  -- PA14, PG4, PG36

    DISTINCT eliminates duplicate rows from the result.

    Calculated fields

    Produce a list of monthly salaries for all staff.

    SELECT staffNo, fName, lName, salary/12 AS monthlySalary
    FROM   Staff;
    staffNofNamelNamemonthlySalary
    SL21JohnWhite2500.00
    SG37AnnBeech1000.00
    SG14DavidFord1500.00
    SA9MaryHowe750.00
    SG5SusanBrand2000.00
    SL41JulieLee750.00

    Without AS, the column gets a system name (e.g. col4). Use the AS clause to name it.

    Search conditions (WHERE)

    The five basic search conditions: Comparison Range Set membership Pattern match Null

    1. Comparison

    List all staff with a salary greater than 10,000.

    SELECT staffNo, fName, lName, position, salary
    FROM   Staff
    WHERE  salary > 10000;

    Result: SL21 (30000), SG37 (12000), SG14 (18000), SG5 (24000).

    Compound comparison — list addresses of all branch offices in London or Glasgow:

    SELECT *
    FROM   Branch
    WHERE  city = 'London' OR city = 'Glasgow';

    Result: B005 (22 Deer Rd, London), B003 (163 Main St, Glasgow), B002 (56 Clover Dr, London).

    2. Range — BETWEEN

    List all staff with a salary between 20,000 and 30,000.

    SELECT staffNo, fName, lName, position, salary
    FROM   Staff
    WHERE  salary BETWEEN 20000 AND 30000;
    
    -- equivalent:
    WHERE  salary >= 20000 AND salary <= 30000;

    Result: SL21 John White (30000), SG5 Susan Brand (24000).

    3. Set membership — IN

    List all managers and supervisors.

    SELECT staffNo, fName, lName, position
    FROM   Staff
    WHERE  position IN ('Manager', 'Supervisor');
    
    -- equivalent:
    WHERE  position = 'Manager' OR position = 'Supervisor';

    Result: SL21 White (Manager), SG14 Ford (Supervisor), SG5 Brand (Manager).

    4. Pattern matching — LIKE

    Find all owners with the string ‘Glasgow’ in their address.

    SELECT ownerNo, fName, lName, address, telNo
    FROM   PrivateOwner
    WHERE  address LIKE '%Glasgow%';

    Result: CO87 Carol Farrel, CO40 Tina Murphy, CO93 Tony Shaw.

    SymbolMeaningExample
    %Sequence of zero or more characters'%Glasgow%' — contains “Glasgow”
    _Any single character'H____' — exactly 5 chars starting with H

    5. NULL search condition

    List details of all viewings on property PG4 where a comment has not been supplied.

    SELECT clientNo, viewDate
    FROM   Viewing
    WHERE  propertyNo = 'PG4' AND comment IS NULL;

    Result: CR56, 26-May-01. There are 2 viewings for PG4 — one with and one without a comment.

    Common mistake

    You must test for null with the special keyword IS NULL (or IS NOT NULL). comment = NULL never evaluates to true.

    Sorting results (ORDER BY)

    Single column ordering

    SELECT staffNo, fName, lName, salary
    FROM   Staff
    ORDER BY salary DESC;

    Result order: SL21 (30000), SG5 (24000), SG14 (18000), SG37 (12000), SA9 (9000), SL41 (9000).

    Multiple column ordering

    SELECT propertyNo, type, rooms, rent
    FROM   PropertyForRent
    ORDER BY type;

    There are four flats — with no minor sort key, the system arranges them in any order it chooses. To order by rent within type:

    SELECT propertyNo, type, rooms, rent
    FROM   PropertyForRent
    ORDER BY type, rent DESC;
    propertyNotyperoomsrent
    PG16Flat4450
    PL94Flat4400
    PG36Flat3375
    PG4Flat3350
    PA14House6650
    PG21House5600

    type is the major sort key; rent is the minor sort key. Default direction is ASC.

    Aggregate functions

    The ISO standard defines five aggregate functions:

    FunctionReturnsColumn types
    COUNTNumber of values in the columnNumeric and non-numeric
    SUMSum of valuesNumeric only
    AVGAverage of valuesNumeric only
    MINSmallest valueNumeric and non-numeric
    MAXLargest valueNumeric and non-numeric
    Illegal query

    If the SELECT list includes an aggregate and there is no GROUP BY, the SELECT list cannot reference a column outside an aggregate:

    SELECT staffNo, COUNT(salary)   -- ILLEGAL
    FROM   Staff;

    (Which staffNo would go with the single count?)

    Examples

    -- How many properties cost more than £350 per month to rent?
    SELECT COUNT(*) AS myCount
    FROM   PropertyForRent
    WHERE  rent > 350;                         -- 5
    
    -- How many different properties were viewed in May '01?
    SELECT COUNT(DISTINCT propertyNo) AS myCount
    FROM   Viewing
    WHERE  viewDate BETWEEN '1-May-01' AND '31-May-01';   -- 2 (PA14, PG4)
    
    -- Find number of Managers and sum of their salaries.
    SELECT COUNT(staffNo) AS myCount, SUM(salary) AS mySum
    FROM   Staff
    WHERE  position = 'Manager';               -- 2, 54000
    
    -- Find minimum, maximum, and average staff salary.
    SELECT MIN(salary) AS myMin, MAX(salary) AS myMax, AVG(salary) AS myAvg
    FROM   Staff;                              -- 9000, 30000, 17000

    Grouping (GROUP BY and HAVING)

    Use GROUP BY to get sub-totals. SELECT and GROUP BY are closely integrated: each item in the SELECT list must be single-valued per group, so the SELECT clause may only contain:

    Find the number of staff in each branch and their total salaries.

    SELECT   branchNo, COUNT(staffNo) AS myCount, SUM(salary) AS mySum
    FROM     Staff
    GROUP BY branchNo
    ORDER BY branchNo;
    branchNomyCountmySum
    B003354000
    B005239000
    B00719000

    Restricted groupings — HAVING

    For each branch with more than 1 member of staff, find the number of staff and the sum of their salaries.

    SELECT   branchNo, COUNT(staffNo) AS myCount, SUM(salary) AS mySum
    FROM     Staff
    GROUP BY branchNo
    HAVING   COUNT(staffNo) > 1
    ORDER BY branchNo;

    Result: B003 (3, 54000), B005 (2, 39000). B007 is removed because it has only 1 staff member.

    Subqueries

    Some SQL statements can have a SELECT embedded within them. A subselect used in the WHERE or HAVING clause of an outer SELECT is called a subquery or nested query. Subselects may also appear in INSERT, UPDATE and DELETE statements.

    Subquery with equality

    List staff who work in the branch at ‘163 Main St’.

    SELECT staffNo, fName, lName, position
    FROM   Staff
    WHERE  branchNo = (SELECT branchNo
                       FROM   Branch
                       WHERE  street = '163 Main St');

    The inner SELECT finds the branch number ('B003'). The outer SELECT then becomes … WHERE branchNo = 'B003'. Result: SG37 Ann Beech, SG14 David Ford, SG5 Susan Brand.

    Subquery with aggregate

    List all staff whose salary is greater than the average salary, and show by how much.

    SELECT staffNo, fName, lName, position,
           salary - (SELECT AVG(salary) FROM Staff) AS salDiff
    FROM   Staff
    WHERE  salary > (SELECT AVG(salary) FROM Staff);
    Why a subquery?

    You cannot write WHERE salary > AVG(salary) — aggregates aren’t allowed in WHERE. The subquery computes the average (17000) first; the outer query then effectively runs WHERE salary > 17000.

    staffNofNamelNamepositionsalDiff
    SL21JohnWhiteManager13000
    SG14DavidFordSupervisor1000
    SG5SusanBrandManager7000

    Subquery rules

    1. ORDER BY may not be used in a subquery (only in the outermost SELECT).
    2. The subquery SELECT list must consist of a single column name or expression, except for subqueries using EXISTS.
    3. By default, column names refer to the table in the subquery’s FROM clause; you can refer to an outer table using an alias.
    4. When a subquery is an operand in a comparison, it must appear on the right-hand side.
    5. A subquery may not be used as an operand in an expression.

    Nested subquery using IN

    List properties handled by staff at ‘163 Main St’.

    SELECT propertyNo, street, city, postcode, type, rooms, rent
    FROM   PropertyForRent
    WHERE  staffNo IN (SELECT staffNo
                       FROM   Staff
                       WHERE  branchNo = (SELECT branchNo
                                          FROM   Branch
                                          WHERE  street = '163 Main St'));

    The middle query returns several staff numbers, so IN is used rather than =. Result: PG16, PG36, PG21.

    Multi-table queries (joins)

    Simple join

    List names of all clients who have viewed a property, along with any comment supplied.

    SELECT c.clientNo, fName, lName, propertyNo, comment
    FROM   Client c, Viewing v
    WHERE  c.clientNo = v.clientNo;

    Equivalent to the equi-join in relational algebra: only rows with identical clientNo values in both tables are included.

    clientNofNamelNamepropertyNocomment
    CR56AlineStewartPG36
    CR56AlineStewartPA14too small
    CR56AlineStewartPG4
    CR62MaryTregearPA14no dining room
    CR76JohnKayPG4too remote

    Alternative JOIN constructs

    FROM Client c JOIN Viewing v ON c.clientNo = v.clientNo
    FROM Client JOIN Viewing USING (clientNo)
    FROM Client NATURAL JOIN Viewing

    In each case the new FROM replaces the original FROM and WHERE. However, the ON version produces a table with two identical clientNo columns; USING and NATURAL JOIN keep only one.

    Note

    The slides write USING clientNo; standard SQL requires parentheses: USING (clientNo). Also, SQL Server does not support USING or NATURAL JOIN — use ON there.

    Sorting a join

    For each branch, list numbers and names of staff who manage properties, and the properties they manage.

    SELECT   s.branchNo, s.staffNo, fName, lName, propertyNo
    FROM     Staff s, PropertyForRent p
    WHERE    s.staffNo = p.staffNo
    ORDER BY s.branchNo, s.staffNo, propertyNo;
    branchNostaffNofNamelNamepropertyNo
    B003SG14DavidFordPG16
    B003SG37AnnBeechPG21
    B003SG37AnnBeechPG36
    B005SL41JulieLeePL94
    B007SA9MaryHowePA14

    Three-table join

    For each branch, list staff who manage properties, including the city of the branch and the properties they manage.

    SELECT   b.branchNo, b.city, s.staffNo, fName, lName, propertyNo
    FROM     Branch b, Staff s, PropertyForRent p
    WHERE    b.branchNo = s.branchNo AND s.staffNo = p.staffNo
    ORDER BY b.branchNo, s.staffNo, propertyNo;
    
    -- Alternative FROM/WHERE:
    FROM (Branch b JOIN Staff s USING (branchNo)) AS bs
         JOIN PropertyForRent p USING (staffNo)

    Result: same rows as above plus a city column (Glasgow ×3, London, Aberdeen).

    Multiple grouping columns

    Find the number of properties handled by each staff member.

    SELECT   s.branchNo, s.staffNo, COUNT(*) AS myCount
    FROM     Staff s, PropertyForRent p
    WHERE    s.staffNo = p.staffNo
    GROUP BY s.branchNo, s.staffNo
    ORDER BY s.branchNo, s.staffNo;

    Result: B003/SG14 → 1, B003/SG37 → 2, B005/SL41 → 1, B007/SA9 → 1.

    Computing a join (conceptual procedure)

    1. Form the Cartesian product of the tables named in the FROM clause.
    2. If there is a WHERE clause, apply the search condition to each row of the product, keeping rows that satisfy it.
    3. For each remaining row, determine the value of each item in the SELECT list to produce a single result row.
    4. If DISTINCT was specified, eliminate duplicate rows.
    5. If there is an ORDER BY, sort the result.

    SQL has a special format for the Cartesian product:

    SELECT [DISTINCT | ALL] {* | columnList}
    FROM   Table1 CROSS JOIN Table2;

    EXISTS and NOT EXISTS

    Find all staff who work in a London branch.

    SELECT staffNo, fName, lName, position
    FROM   Staff s
    WHERE  EXISTS (SELECT *
                   FROM   Branch b
                   WHERE  s.branchNo = b.branchNo AND city = 'London');

    Result: SL21 John White (Manager), SL41 Julie Lee (Assistant).

    Why the correlation condition matters

    s.branchNo = b.branchNo links each staff row to its own branch (a correlated subquery). If omitted, the subquery SELECT * FROM Branch WHERE city='London' is always true, and the query becomes … WHERE true — listing all staff.

    The same query written as a join:

    SELECT staffNo, fName, lName, position
    FROM   Staff s, Branch b
    WHERE  s.branchNo = b.branchNo AND city = 'London';

    INSERT

    INSERT INTO TableName [(columnList)]
    VALUES (dataValueList);

    The dataValueList must match the columnList:

    INSERT … VALUES (all columns)

    INSERT INTO Staff
    VALUES ('SG16', 'Alan', 'Brown', 'Assistant', 'M', DATE '1957-05-25', 8300, 'B003');

    INSERT using defaults (mandatory columns only)

    INSERT INTO Staff (staffNo, fName, lName, position, salary, branchNo)
    VALUES ('SG44', 'Anne', 'Jones', 'Assistant', 8100, 'B003');
    
    -- or, listing every column and using NULL:
    INSERT INTO Staff
    VALUES ('SG44', 'Anne', 'Jones', 'Assistant', NULL, NULL, 8100, 'B003');

    INSERT … SELECT

    A second form of INSERT copies multiple rows from one or more tables into another:

    INSERT INTO TableName [(columnList)]
    SELECT ...

    Populate StaffPropCount(staffNo, fName, lName, propCnt) using Staff and PropertyForRent.

    INSERT INTO StaffPropCount
      (SELECT s.staffNo, fName, lName, COUNT(*)
       FROM   Staff s, PropertyForRent p
       WHERE  s.staffNo = p.staffNo
       GROUP BY s.staffNo, fName, lName)
    UNION
      (SELECT staffNo, fName, lName, 0
       FROM   Staff
       WHERE  staffNo NOT IN (SELECT DISTINCT staffNo
                              FROM   PropertyForRent));
    staffNofNamelNamepropCount
    SG14DavidFord1
    SL21JohnWhite0
    SG37AnnBeech2
    SA9MaryHowe1
    SG5SusanBrand0
    SL41JulieLee1

    If the second part of the UNION is omitted, staff who currently manage no properties (SL21, SG5) are excluded.

    Extra · NOT IN with NULLs

    PG4 has a null staffNo. In many DBMSs, x NOT IN (…, NULL) evaluates to UNKNOWN, returning no rows. Safer: add WHERE staffNo IS NOT NULL inside the subquery, or use NOT EXISTS.

    UPDATE

    UPDATE TableName
    SET    columnName1 = dataValue1 [, columnName2 = dataValue2 ...]
    [WHERE searchCondition];
    -- Give all staff a 3% pay increase.
    UPDATE Staff SET salary = salary * 1.03;
    
    -- Give all Managers a 5% pay increase.
    UPDATE Staff SET salary = salary * 1.05
    WHERE  position = 'Manager';
    
    -- Promote David Ford (SG14) to Manager and change his salary to £18,000.
    UPDATE Staff SET position = 'Manager', salary = 18000
    WHERE  staffNo = 'SG14';

    DELETE

    DELETE FROM TableName
    [WHERE searchCondition];
    -- Delete all viewings that relate to property PG4.
    DELETE FROM Viewing WHERE propertyNo = 'PG4';
    
    -- Delete all records from the Viewing table.
    DELETE FROM Viewing;

    Quick review questions

    List the objectives of SQL.
    Create the database and relation structures; insert, modify and delete data; perform simple and complex queries — with minimal user effort and a syntax that is easy to learn and portable (ISO standard).
    Describe the importance of SQL.
    It is the standard relational database language (ISO/ANSI), supported by virtually every RDBMS; it is non-procedural (you say what, not how), used for DDL, DML and DCL, and is the basis for application access, reporting tools and portability between systems.
    What are literals?
    Constants used in SQL statements. Non-numeric literals go in single quotes ('London'); numeric literals don’t (350).
    Which commands are used for a range search condition?
    BETWEEN … AND … and NOT BETWEEN (inclusive of endpoints).
    Which command is used for pattern matching?
    LIKE / NOT LIKE, with % (zero or more chars) and _ (one char).
    Which clause is used for single column ordering?
    ORDER BY column [ASC | DESC].
    Name all aggregates usable in a SELECT statement.
    COUNT, SUM, AVG, MIN, MAX.
    List the rules for subqueries.
    No ORDER BY inside; single column in the SELECT list (except with EXISTS); columns default to the subquery’s FROM table (use aliases to reach outer tables); subquery must be on the right-hand side of a comparison; cannot be used as an operand in an expression.
    Differentiate EXISTS and NOT EXISTS.
    EXISTS is true when the subquery returns at least one row; NOT EXISTS is true when it returns no rows. Both return only true/false.