Class 12Computer Science · Database ManagementFull chapter

SQL: Defining and Querying Data

The whole chapter in one place — read it, then test yourself. Clear notes, a reference sheet, a practice quiz, and worked NCERT solutions & PYQs.

SQL, DDL and DML: Working with Databases

Quick answer SQL is the declarative language every relational database understands, split into DDL commands that define structure and DML commands that move data, and every working session begins with CREATE DATABASE, USE and SHOW.

SQL stands for Structured Query Language. It is the standard language that every relational database — MySQL, Oracle, PostgreSQL, SQL Server — understands. The important idea is that you do not tell SQL how to search. You describe what you want, and the database engine decides how to fetch it. That is why SQL is called a non-procedural or declarative language, unlike Python where you write the loop yourself.

Three mechanical rules first, because they cost marks when forgotten:

  • Every SQL statement ends with a semicolon.
  • Keywords are not case-sensitive. SELECT, select and Select all work. Writing keywords in capitals is only a readability convention — a good one to keep in the exam.
  • Text values go inside single quotes: 'Delhi'. Numbers do not: 52000.

SQL is divided into sub-languages. Two of them are on your syllabus.

Sub-languageFull formWhat it changesCommands
DDLData Definition LanguageThe structure — databases, tables, columns, constraintsCREATE, ALTER, DROP
DMLData Manipulation LanguageThe data stored inside a tableINSERT, UPDATE, DELETE, SELECT

The one line that decides marks in this question: DDL changes the skeleton, DML changes the contents. DROP TABLE is DDL because it removes the table itself. DELETE is DML because it removes rows and leaves the empty table standing. Note that SELECT is grouped under DML in most Class 12 material; a few books list it separately as DQL (Data Query Language). Either grouping is accepted as long as you are consistent.

Database-level commands. A table has to live inside a database, and MySQL needs to be told which database you are working in. These five commands are the start of every practical session.

Worked example. Everything below was run against MySQL 8.0.41. Two databases are created so you can watch one of them disappear:

CREATE DATABASE cs12_sql_basics;
CREATE DATABASE demo_practice;
SHOW DATABASES;

Output:

+-------------------------------+
| Database                      |
+-------------------------------+
| cs12_database_concepts        |
| cs12_sql_basics               |
| cs12_sql_joins_and_aggregates |
| demo_practice                 |
| information_schema            |
| mysql                         |
| performance_schema            |
| sys                           |
+-------------------------------+

Your list will look different, because SHOW DATABASES lists every database on that server. The last four — information_schema, mysql, performance_schema and sys — are MySQL's own system databases. They are always there and you must never modify them. The cs12_ entries were other practice databases that already existed on this machine.

Now remove the throwaway one:

DROP DATABASE demo_practice;
SHOW DATABASES;
+-------------------------------+
| Database                      |
+-------------------------------+
| cs12_database_concepts        |
| cs12_sql_basics               |
| cs12_sql_joins_and_aggregates |
| information_schema            |
| mysql                         |
| performance_schema            |
| sys                           |
+-------------------------------+

demo_practice is gone. Understand what that means: DROP DATABASE deletes the database together with every table and every row inside it, with no confirmation and no undo.

Next, enter the database you want to work in and confirm where you are:

USE cs12_sql_basics;
SELECT DATABASE() AS current_db;
+-----------------+
| current_db      |
+-----------------+
| cs12_sql_basics |
+-----------------+
SHOW TABLES;

0 rows returned — a brand-new database contains no tables at all. We build one in the next section.

Two errors worth recognising. Both were produced deliberately. Creating a database that already exists is rejected — the failure is reported against the second line, the one that repeated the name:

CREATE DATABASE cs12_tmp_check;
CREATE DATABASE cs12_tmp_check;
ERROR 1007 (HY000) at line 2: Can't create database 'cs12_tmp_check'; database exists

And running a table command before choosing a database gives the single most common beginner error — you forgot USE:

SHOW TABLES;
ERROR 1046 (3D000) at line 1: No database selected
CREATE DATABASE CREATE DATABASE database_name; Fails with ERROR 1007 if a database of that name already exists.
SHOW DATABASES / SHOW TABLES SHOW DATABASES; SHOW TABLES; SHOW TABLES only works after USE; otherwise ERROR 1046: No database selected.
USE USE database_name; Not a query and returns no rows — it just sets the current database. Confirm with SELECT DATABASE();
DROP DATABASE DROP DATABASE database_name; Deletes the database and every table inside it. Irreversible.
DDL vs DML DDL: CREATE, ALTER, DROP DML: INSERT, UPDATE, DELETE, SELECT DROP TABLE is DDL (removes the table); DELETE is DML (removes rows only). A standard 1-mark question.
Remember
  • SQL is declarative: you state what data you want, not how to find it. Statements end with a semicolon and keywords are case-insensitive.
  • DDL (CREATE, ALTER, DROP) defines structure; DML (INSERT, UPDATE, DELETE, SELECT) works on the data inside that structure.
  • USE sets the working database. Skip it and any table command fails with 'ERROR 1046: No database selected'.
  • DROP DATABASE removes the database and everything in it permanently — there is no confirmation prompt and no undo.
  • SHOW DATABASES and SHOW TABLES are the two commands that tell you where you are and what exists.

Data Types and Constraints: Creating the Table

Quick answer Every column needs a data type that fixes what it may hold and optional constraints that the database itself enforces, with char(n) fixed-length against varchar(n) variable-length being the storage difference the board asks about most.

A table is a grid of rows and columns. Before MySQL will build one, you must state two things for every column: its data type — what kind of value is allowed — and any constraints — what values are forbidden.

The five data types on your syllabus.

TypeStoresExamplePoint to remember
char(n)Fixed-length text, exactly n characters'Delhi'Always occupies space for n characters; short values are padded with spaces
varchar(n)Variable-length text, up to n characters'Bengaluru'Occupies only what you actually supply, plus a tiny length marker
intWhole numbers, positive or negative101No decimal part. Use for IDs, ages, counts, quantities
floatNumbers with a decimal part52000, 45.75Stored approximately. Fine for marks and salaries at this level
dateA calendar date'2019-06-15'Must be written as 'YYYY-MM-DD', in quotes

char(n) versus varchar(n) — the difference that carries marks. Both hold text, so students assume they are interchangeable. They are not, and the difference is real storage. Here is a demonstration that was actually run. Two columns, same width, and the same value is inserted into both — once plain, once with five trailing spaces:

CREATE TABLE PADDEMO (c CHAR(10), v VARCHAR(10));
INSERT INTO PADDEMO VALUES ('Delhi', 'Delhi');
INSERT INTO PADDEMO VALUES ('Delhi     ', 'Delhi     ');
SELECT CONCAT('[', c, ']') AS char_value,
       CONCAT('[', v, ']') AS varchar_value,
       LENGTH(c) AS char_len,
       LENGTH(v) AS varchar_len
FROM PADDEMO;

Result:

+------------+---------------+----------+-------------+
| char_value | varchar_value | char_len | varchar_len |
+------------+---------------+----------+-------------+
| [Delhi]    | [Delhi]       |        5 |           5 |
| [Delhi]    | [Delhi     ]  |        5 |          10 |
+------------+---------------+----------+-------------+

Read the second row carefully. Both columns were handed 'Delhi' plus five spaces. The varchar column kept all ten characters. The char column returned only Delhi. That is because a char column pads every value out to its full declared width when storing it, and strips the trailing spaces again when reading it back. The padding is invisible to you, but the space is still reserved on disk.

So the examinable statement is: char(10) costs the same for 'Delhi' as it does for 'Chandigarh', because it is fixed-length. varchar(10) costs only what you store. In exchange, char is very slightly faster because every row is the same size. Rule of thumb: use char(n) when every value genuinely has the same length — a grade letter, a two-letter state code, a 6-digit PIN code — and varchar(n) for anything that varies, such as names, cities or email addresses.

Note: CONCAT and LENGTH above are helper functions used only to make the padding visible. They are not part of this chapter's syllabus.

Constraints. A constraint is a rule the database enforces itself. Even if the application code has a bug, bad data is refused at the door.

ConstraintRule enforcedNULL allowed?Duplicates allowed?
NOT NULLThe column must always be given a valueNoYes
UNIQUENo two rows may hold the same valueYes — and more than one NULL is permittedNo
PRIMARY KEYUniquely identifies each rowNoNo

The compact way to remember it: PRIMARY KEY = UNIQUE + NOT NULL. A table may have only one primary key, although that key is allowed to span more than one column.

Worked example — building the table used for the rest of this chapter. A company called Nova keeps employee records:

CREATE TABLE EMPLOYEE (
  EmpID  INT,
  Name   VARCHAR(25) NOT NULL,
  City   CHAR(12),
  Dept   VARCHAR(15),
  Salary FLOAT,
  DOJ    DATE,
  Email  VARCHAR(35) UNIQUE,
  PRIMARY KEY (EmpID)
);
DESCRIBE EMPLOYEE;
+--------+-------------+------+-----+---------+-------+
| Field  | Type        | Null | Key | Default | Extra |
+--------+-------------+------+-----+---------+-------+
| EmpID  | int         | NO   | PRI | NULL    |       |
| Name   | varchar(25) | NO   |     | NULL    |       |
| City   | char(12)    | YES  |     | NULL    |       |
| Dept   | varchar(15) | YES  |     | NULL    |       |
| Salary | float       | YES  |     | NULL    |       |
| DOJ    | date        | YES  |     | NULL    |       |
| Email  | varchar(35) | YES  | UNI | NULL    |       |
+--------+-------------+------+-----+---------+-------+

Read the DESCRIBE output like a checklist. Key reads PRI for the primary key and UNI for the unique column. And look at EmpID: its Null column says NO even though we never wrote NOT NULL there. Declaring it the PRIMARY KEY did that automatically — that is the NOT NULL half of the primary key showing up.

Watch the constraints refuse bad data. Each of these was run against the finished table once the twelve rows of the next section were in place, and each was rejected:

INSERT INTO EMPLOYEE VALUES (101,'Nikhil Verma','Surat','HR',50000,'2024-01-05','nikhil.verma@nova.in');
ERROR 1062 (23000) at line 1: Duplicate entry '101' for key 'employee.PRIMARY'

INSERT INTO EMPLOYEE VALUES (113,NULL,'Surat','HR',50000,'2024-01-05','nikhil.verma@nova.in');
ERROR 1048 (23000) at line 1: Column 'Name' cannot be null

INSERT INTO EMPLOYEE VALUES (113,'Nikhil Verma','Surat','HR',50000,'2024-01-05','diya.nair@nova.in');
ERROR 1062 (23000) at line 1: Duplicate entry 'diya.nair@nova.in' for key 'employee.Email'

INSERT INTO EMPLOYEE (EmpID,Name) VALUES (NULL,'Ghost User');
ERROR 1048 (23000) at line 1: Column 'EmpID' cannot be null

Four different rules, four different refusals: duplicate primary key, missing NOT NULL value, duplicate UNIQUE value, and NULL in a primary key. Note the last one especially — a primary key rejects NULL, but a UNIQUE column accepts it, which we prove in the next section.

CREATE TABLE CREATE TABLE table_name ( col1 datatype constraint, col2 datatype, PRIMARY KEY (col1) ); Comma after every column definition except the last one. A stray or missing comma is the commonest syntax error.
Data types char(n) varchar(n) int float date char(n) fixed-length, varchar(n) variable-length. That contrast is a standard 2-mark question.
NOT NULL col_name datatype NOT NULL Forces a value into the column, but duplicate values are still allowed.
UNIQUE col_name datatype UNIQUE Blocks duplicate values but permits NULL — and permits more than one NULL, since NULL is not a value.
PRIMARY KEY PRIMARY KEY (col_name) -- or inline: col_name datatype PRIMARY KEY Equals UNIQUE plus NOT NULL. Only one per table. It makes the column NOT NULL automatically.
DESCRIBE DESCRIBE table_name; DESC table_name; Both forms work and are identical. Shows structure only — use SELECT to see data.
Remember
  • char(n) is fixed-length and always reserves space for n characters, padding short values with spaces; varchar(n) stores only what you supply. This is a real storage difference, not a formatting one.
  • A DATE value is written year-month-day, as 'YYYY-MM-DD' inside quotes; the Indian-style '15-06-2024' is rejected with ERROR 1292: Incorrect date value.
  • NOT NULL forces a value but still allows duplicates; UNIQUE blocks duplicates but allows NULL; PRIMARY KEY = UNIQUE + NOT NULL.
  • Declaring a column PRIMARY KEY silently makes it NOT NULL — DESCRIBE shows Null as NO even if you never typed NOT NULL.
  • DESCRIBE shows structure only, never data: Field, Type, Null, Key (PRI or UNI), Default and Extra.

Filling and Reshaping the Table: INSERT, ALTER and DROP

Quick answer INSERT loads rows and quietly leaves NULL in any column you omit, while ALTER TABLE reshapes an existing table by adding or removing a column or a primary key and DROP TABLE removes the table outright.

The table exists but it is empty. INSERT is the DML command that puts rows in, and it comes in three forms.

Form 1 — all columns, in order. You give one value for every column, in exactly the order DESCRIBE showed:

INSERT INTO EMPLOYEE VALUES (101,'Aarav Sharma','Delhi','Sales',52000,'2019-06-15','aarav.sharma@nova.in');

Form 2 — chosen columns. You name the columns first, then supply matching values. Any column you leave out is filled with NULL:

INSERT INTO EMPLOYEE (EmpID, Name, City, Dept) VALUES (111,'Rehan Ali','Hyderabad','IT');

Form 3 — several rows at once. One statement, one semicolon, rows separated by commas. A second table is created first, so the example can actually be run:

CREATE TABLE TRAINEE (
  TID      INT,
  TName    VARCHAR(20) NOT NULL,
  Stream   CHAR(10),
  Stipend  FLOAT,
  JoinDate DATE
);
INSERT INTO TRAINEE VALUES (1,'Ananya Das','CS',12000,'2024-01-08'),
                           (2,'Farhan Khan','ECE',10000,'2024-02-19'),
                           (3,'Pooja Bhatt','CS',11000,'2024-03-04');
SELECT * FROM TRAINEE;
+------+-------------+--------+---------+------------+
| TID  | TName       | Stream | Stipend | JoinDate   |
+------+-------------+--------+---------+------------+
|    1 | Ananya Das  | CS     |   12000 | 2024-01-08 |
|    2 | Farhan Khan | ECE    |   10000 | 2024-02-19 |
|    3 | Pooja Bhatt | CS     |   11000 | 2024-03-04 |
+------+-------------+--------+---------+------------+

Worked example — the dataset for the whole chapter. Ten full rows were inserted with Form 1 and two partial rows with Form 2, then read back:

SELECT * FROM EMPLOYEE;
+-------+--------------+-----------+----------+--------+------------+----------------------+
| EmpID | Name         | City      | Dept     | Salary | DOJ        | Email                |
+-------+--------------+-----------+----------+--------+------------+----------------------+
|   101 | Aarav Sharma | Delhi     | Sales    |  52000 | 2019-06-15 | aarav.sharma@nova.in |
|   102 | Diya Nair    | Kochi     | HR       |  47500 | 2020-01-10 | diya.nair@nova.in    |
|   103 | Rohan Mehta  | Mumbai    | Sales    |  61000 | 2018-03-05 | rohan.mehta@nova.in  |
|   104 | Ishita Rao   | Bengaluru | IT       |  78000 | 2021-07-19 | ishita.rao@nova.in   |
|   105 | Kabir Singh  | Delhi     | IT       |  69500 | 2020-11-23 | kabir.singh@nova.in  |
|   106 | Meera Iyer   | Chennai   | HR       |  45000 | 2022-02-14 | meera.iyer@nova.in   |
|   107 | Arjun Patel  | Ahmedabad | Sales    |  58000 | 2017-09-01 | arjun.patel@nova.in  |
|   108 | Sanya Gupta  | Delhi     | Sales    |  52000 | 2023-05-08 | sanya.gupta@nova.in  |
|   109 | Vikram Bose  | Kolkata   | IT       |  84000 | 2016-12-30 | vikram.bose@nova.in  |
|   110 | Neha Joshi   | Pune      | Accounts |  49000 | 2022-08-25 | neha.joshi@nova.in   |
|   111 | Rehan Ali    | Hyderabad | IT       |   NULL | NULL       | NULL                 |
|   112 | Tara Menon   | Jaipur    | Accounts |   NULL | NULL       | NULL                 |
+-------+--------------+-----------+----------+--------+------------+----------------------+

Rows 111 and 112 are the Form 2 inserts. We supplied only four columns, so Salary, DOJ and Email came out as NULL. Two things follow from that, and both are examinable. First, this is why partial inserts fail on a NOT NULL column — there would be nothing to put there. Second, look at Email: it is declared UNIQUE, yet two rows both hold NULL and both were accepted. UNIQUE forbids duplicate values, and NULL is not a value, so the rule never fires.

Dates must be in order. Writing an Indian-style date is rejected outright:

INSERT INTO TRAINEE VALUES (4,'Ravi Kumar','ME',9000,'15-06-2024');
ERROR 1292 (22007) at line 1: Incorrect date value: '15-06-2024' for column 'JoinDate' at row 1

ALTER TABLE — reshaping a table that already has data. This is DDL. Your syllabus needs four operations, and all four are shown running below.

Adding a column. The company decides to record a bonus:

ALTER TABLE EMPLOYEE ADD Bonus FLOAT;
DESCRIBE EMPLOYEE;
+--------+-------------+------+-----+---------+-------+
| Field  | Type        | Null | Key | Default | Extra |
+--------+-------------+------+-----+---------+-------+
| EmpID  | int         | NO   | PRI | NULL    |       |
| Name   | varchar(25) | NO   |     | NULL    |       |
| City   | char(12)    | YES  |     | NULL    |       |
| Dept   | varchar(15) | YES  |     | NULL    |       |
| Salary | float       | YES  |     | NULL    |       |
| DOJ    | date        | YES  |     | NULL    |       |
| Email  | varchar(35) | YES  | UNI | NULL    |       |
| Bonus  | float       | YES  |     | NULL    |       |
+--------+-------------+------+-----+---------+-------+

The new column is appended at the end, and — this is the part students forget — it is NULL for every row that already existed:

SELECT EmpID, Name, Salary, Bonus FROM EMPLOYEE WHERE EmpID IN (101,102,111);
+-------+--------------+--------+-------+
| EmpID | Name         | Salary | Bonus |
+-------+--------------+--------+-------+
|   101 | Aarav Sharma |  52000 |  NULL |
|   102 | Diya Nair    |  47500 |  NULL |
|   111 | Rehan Ali    |   NULL |  NULL |
+-------+--------------+--------+-------+

Removing a column. A Grade column is added and then thrown away again. The COLUMN keyword is optional in MySQL:

ALTER TABLE EMPLOYEE ADD Grade CHAR(1);
ALTER TABLE EMPLOYEE DROP COLUMN Grade;

Dropping a column destroys the data in it permanently — there is no recycle bin.

Removing a primary key.

ALTER TABLE EMPLOYEE DROP PRIMARY KEY;
DESCRIBE EMPLOYEE;
+--------+-------------+------+-----+---------+-------+
| Field  | Type        | Null | Key | Default | Extra |
+--------+-------------+------+-----+---------+-------+
| EmpID  | int         | NO   |     | NULL    |       |
| Name   | varchar(25) | NO   |     | NULL    |       |
| City   | char(12)    | YES  |     | NULL    |       |
| Dept   | varchar(15) | YES  |     | NULL    |       |
| Salary | float       | YES  |     | NULL    |       |
| DOJ    | date        | YES  |     | NULL    |       |
| Email  | varchar(35) | YES  | UNI | NULL    |       |
| Bonus  | float       | YES  |     | NULL    |       |
+--------+-------------+------+-----+---------+-------+

Compare this against the previous DESCRIBE. The PRI against EmpID has gone, exactly as expected. But notice what did not change: Null still reads NO. Dropping the primary key removes only the key. The NOT NULL property it installed stays behind.

Adding a primary key. Notice you do not name the column in DROP PRIMARY KEY (a table has only one), but you must name it when adding:

ALTER TABLE EMPLOYEE ADD PRIMARY KEY (EmpID);

That restores PRI against EmpID. This only works because EmpID values are all different. On a column holding duplicates it is refused — here is that failure, run on a scratch table:

CREATE TABLE PKDEMO (Rno INT, Nm VARCHAR(10));
INSERT INTO PKDEMO VALUES (1,'Asha'),(1,'Bina'),(2,'Chetan');
SELECT * FROM PKDEMO;
+------+--------+
| Rno  | Nm     |
+------+--------+
|    1 | Asha   |
|    1 | Bina   |
|    2 | Chetan |
+------+--------+
ALTER TABLE PKDEMO ADD PRIMARY KEY (Rno);
ERROR 1062 (23000) at line 1: Duplicate entry '1' for key 'pkdemo.PRIMARY'

DROP TABLE. This removes the rows and the structure:

SHOW TABLES;
+---------------------------+
| Tables_in_cs12_sql_basics |
+---------------------------+
| employee                  |
| paddemo                   |
| pkdemo                    |
| trainee                   |
+---------------------------+
DROP TABLE PKDEMO;
DROP TABLE PADDEMO;
SHOW TABLES;
+---------------------------+
| Tables_in_cs12_sql_basics |
+---------------------------+
| employee                  |
| trainee                   |
+---------------------------+

They are gone for good — asking again proves it:

DROP TABLE PKDEMO;
ERROR 1051 (42S02) at line 1: Unknown table 'cs12_sql_basics.pkdemo'
INSERT — all columns INSERT INTO table_name VALUES (v1, v2, v3); Values must be in the same order as DESCRIBE shows, and you must supply one for every column.
INSERT — chosen columns INSERT INTO table_name (col1, col2) VALUES (v1, v2); Every column you leave out becomes NULL. Fails if an omitted column is NOT NULL.
INSERT — many rows INSERT INTO table_name VALUES (1,'A'), (2,'B'), (3,'C'); One statement, one semicolon. Rows separated by commas, not semicolons.
ALTER TABLE — column ALTER TABLE table_name ADD col_name datatype; ALTER TABLE table_name DROP COLUMN col_name; The COLUMN keyword is optional in MySQL. Dropping a column destroys its data permanently.
ALTER TABLE — primary key ALTER TABLE table_name ADD PRIMARY KEY (col_name); ALTER TABLE table_name DROP PRIMARY KEY; Name the column when adding, never when dropping. ADD fails on duplicates or NULLs.
DROP TABLE DROP TABLE table_name; Removes rows AND structure. Contrast DELETE FROM table_name; which keeps the structure.
Remember
  • Any column omitted from an INSERT column list is filled with NULL — which is why a partial insert fails if an omitted column is NOT NULL.
  • A UNIQUE column accepts more than one NULL, because NULL is not a value; a PRIMARY KEY accepts none.
  • ALTER TABLE ADD appends the new column at the end and sets it to NULL for every existing row.
  • ALTER TABLE DROP PRIMARY KEY removes the key but leaves the NOT NULL it created; DESCRIBE still shows Null as NO.
  • ADD PRIMARY KEY is refused with ERROR 1062 if the column already contains duplicate values.
  • DROP TABLE removes rows and structure together; a second attempt gives ERROR 1051: Unknown table.

Querying Data: SELECT, Operators, DISTINCT and ORDER BY

Quick answer SELECT chooses columns and WHERE chooses rows, combined through mathematical, relational and logical operators plus IN, BETWEEN and LIKE, with DISTINCT removing duplicate rows and ORDER BY fixing the sequence.

SELECT is the command you will write most often. Its shape never changes, and the clause order is fixed:

SELECT   which columns
FROM     which table
WHERE    which rows
ORDER BY how to sort

Get that order wrong — ORDER BY before WHERE, for example — and the query is a syntax error. SELECT picks columns, WHERE picks rows. Hold on to that sentence.

Choosing columns, and aliasing. SELECT * means every column. Naming columns instead is called projection:

SELECT Name, Dept, Salary FROM EMPLOYEE;
+--------------+----------+--------+
| Name         | Dept     | Salary |
+--------------+----------+--------+
| Aarav Sharma | Sales    |  52000 |
| Diya Nair    | HR       |  47500 |
| Rohan Mehta  | Sales    |  61000 |
| Ishita Rao   | IT       |  78000 |
| Kabir Singh  | IT       |  69500 |
| Meera Iyer   | HR       |  45000 |
| Arjun Patel  | Sales    |  58000 |
| Sanya Gupta  | Sales    |  52000 |
| Vikram Bose  | IT       |  84000 |
| Neha Joshi   | Accounts |  49000 |
| Rehan Ali    | IT       |   NULL |
| Tara Menon   | Accounts |   NULL |
+--------------+----------+--------+

An alias renames a column in the output only — the table is untouched. Use it when a heading is ugly or when the column is a calculation:

SELECT Name AS "Employee Name", Salary*12 AS Annual_Salary FROM EMPLOYEE;
+---------------+---------------+
| Employee Name | Annual_Salary |
+---------------+---------------+
| Aarav Sharma  |        624000 |
| Diya Nair     |        570000 |
| Rohan Mehta   |        732000 |
| Ishita Rao    |        936000 |
| Kabir Singh   |        834000 |
| Meera Iyer    |        540000 |
| Arjun Patel   |        696000 |
| Sanya Gupta   |        624000 |
| Vikram Bose   |       1008000 |
| Neha Joshi    |        588000 |
| Rehan Ali     |          NULL |
| Tara Menon    |          NULL |
+---------------+---------------+

Two things to take from that. The keyword AS is optional, but an alias containing a space must be quoted. And look at the last two rows: NULL * 12 gave NULL, not 0. Section 5 explains why.

Mathematical operators are +, -, *, / and % (remainder). They work inside the SELECT list and inside WHERE:

SELECT Name, Salary, Salary*0.05 AS PF, Salary-Salary*0.05 AS Take_Home
FROM EMPLOYEE WHERE Dept='IT';
+-------------+--------+------+-----------+
| Name        | Salary | PF   | Take_Home |
+-------------+--------+------+-----------+
| Ishita Rao  |  78000 | 3900 |     74100 |
| Kabir Singh |  69500 | 3475 |     66025 |
| Vikram Bose |  84000 | 4200 |     79800 |
| Rehan Ali   |   NULL | NULL |      NULL |
+-------------+--------+------+-----------+

Relational operators compare two values: =, >, <, >=, <= and <> (not equal, also written !=). Note that equality is a single = in SQL, not == as in Python:

SELECT Name, Dept, Salary FROM EMPLOYEE WHERE Salary > 60000;
+-------------+-------+--------+
| Name        | Dept  | Salary |
+-------------+-------+--------+
| Rohan Mehta | Sales |  61000 |
| Ishita Rao  | IT    |  78000 |
| Kabir Singh | IT    |  69500 |
| Vikram Bose | IT    |  84000 |
+-------------+-------+--------+

Logical operatorsAND, OR, NOT — join conditions. AND needs both true; OR needs at least one:

SELECT Name, Dept, Salary FROM EMPLOYEE WHERE Dept='Sales' AND Salary>55000;
+-------------+-------+--------+
| Name        | Dept  | Salary |
+-------------+-------+--------+
| Rohan Mehta | Sales |  61000 |
| Arjun Patel | Sales |  58000 |
+-------------+-------+--------+

Precedence trap. AND is evaluated before OR, exactly as * is evaluated before +. This query looks like it asks for Delhi employees who are in Sales or HR. It does not:

SELECT Name, City, Dept FROM EMPLOYEE WHERE City='Delhi' AND Dept='Sales' OR Dept='HR';
+--------------+---------+-------+
| Name         | City    | Dept  |
+--------------+---------+-------+
| Aarav Sharma | Delhi   | Sales |
| Diya Nair    | Kochi   | HR    |
| Meera Iyer   | Chennai | HR    |
| Sanya Gupta  | Delhi   | Sales |
+--------------+---------+-------+

Diya Nair is in Kochi and Meera Iyer is in Chennai, yet both appear. MySQL read it as (City='Delhi' AND Dept='Sales') OR Dept='HR'. If you meant the other grouping, use brackets: City='Delhi' AND (Dept='Sales' OR Dept='HR'). When you mix AND with OR, always bracket.

DISTINCT removes duplicate rows. This is the point students most often get wrong, so compare three queries on the same table. Plain SELECT City returns all 12 values including Delhi three times. Now:

SELECT DISTINCT City FROM EMPLOYEE;
+-----------+
| City      |
+-----------+
| Delhi     |
| Kochi     |
| Mumbai    |
| Bengaluru |
| Chennai   |
| Ahmedabad |
| Kolkata   |
| Pune      |
| Hyderabad |
| Jaipur    |
+-----------+

Ten rows. Now add a second column:

SELECT DISTINCT City, Dept FROM EMPLOYEE;
+-----------+----------+
| City      | Dept     |
+-----------+----------+
| Delhi     | Sales    |
| Kochi     | HR       |
| Mumbai    | Sales    |
| Bengaluru | IT       |
| Delhi     | IT       |
| Chennai   | HR       |
| Ahmedabad | Sales    |
| Kolkata   | IT       |
| Pune      | Accounts |
| Hyderabad | IT       |
| Jaipur    | Accounts |
+-----------+----------+

Eleven rows, and Delhi now appears twice. That is the whole lesson: DISTINCT applies to the entire selected row, not to the column written next to it. Delhi/Sales occurred twice in the data and collapsed into one; Delhi/IT is a different row, so it survives separately. You cannot write SELECT DISTINCT City, Dept hoping to de-duplicate only the cities.

IN — shorthand for a chain of ORs.

SELECT Name, City FROM EMPLOYEE WHERE City IN ('Delhi','Mumbai','Pune');
+--------------+--------+
| Name         | City   |
+--------------+--------+
| Aarav Sharma | Delhi  |
| Rohan Mehta  | Mumbai |
| Kabir Singh  | Delhi  |
| Sanya Gupta  | Delhi  |
| Neha Joshi   | Pune   |
+--------------+--------+

NOT IN reverses it and returns the other 7 rows.

BETWEEN — a range, and it includes both ends. That inclusivity is checked constantly in exams:

SELECT Name, Salary FROM EMPLOYEE WHERE Salary BETWEEN 50000 AND 70000;
+--------------+--------+
| Name         | Salary |
+--------------+--------+
| Aarav Sharma |  52000 |
| Rohan Mehta  |  61000 |
| Kabir Singh  |  69500 |
| Arjun Patel  |  58000 |
| Sanya Gupta  |  52000 |
+--------------+--------+

A direct check confirms it: 45000 BETWEEN 45000 AND 60000 returned 1, and 60000 BETWEEN 45000 AND 60000 also returned 1. BETWEEN works on dates too, and is the natural way to ask for a period:

SELECT Name, DOJ FROM EMPLOYEE WHERE DOJ BETWEEN '2019-01-01' AND '2021-12-31';
+--------------+------------+
| Name         | DOJ        |
+--------------+------------+
| Aarav Sharma | 2019-06-15 |
| Diya Nair    | 2020-01-10 |
| Ishita Rao   | 2021-07-19 |
| Kabir Singh  | 2020-11-23 |
+--------------+------------+

ORDER BY — sorting. ASC (ascending) is the default and may be omitted; DESC reverses it. Sort on two columns and the second breaks ties in the first:

SELECT Name, Dept, Salary FROM EMPLOYEE ORDER BY Dept ASC, Salary DESC;
+--------------+----------+--------+
| Name         | Dept     | Salary |
+--------------+----------+--------+
| Neha Joshi   | Accounts |  49000 |
| Tara Menon   | Accounts |   NULL |
| Diya Nair    | HR       |  47500 |
| Meera Iyer   | HR       |  45000 |
| Vikram Bose  | IT       |  84000 |
| Ishita Rao   | IT       |  78000 |
| Kabir Singh  | IT       |  69500 |
| Rehan Ali    | IT       |   NULL |
| Rohan Mehta  | Sales    |  61000 |
| Arjun Patel  | Sales    |  58000 |
| Aarav Sharma | Sales    |  52000 |
| Sanya Gupta  | Sales    |  52000 |
+--------------+----------+--------+

Departments are alphabetical, and within each department salary runs highest first.

LIKE — pattern matching for text. It uses two wildcards, and mixing them up is a classic lost mark:

  • % stands for any number of characters, including none.
  • _ stands for exactly one character.
SELECT Name FROM EMPLOYEE WHERE Name LIKE 'A%';
+--------------+
| Name         |
+--------------+
| Aarav Sharma |
| Arjun Patel  |
+--------------+

Now the underscore. '_a%' means: one character of any kind, then a literal a, then anything at all — in other words, names whose second letter is a:

SELECT Name FROM EMPLOYEE WHERE Name LIKE '_a%';
+--------------+
| Name         |
+--------------+
| Aarav Sharma |
| Kabir Singh  |
| Sanya Gupta  |
| Tara Menon   |
+--------------+

Four names — Aarav, Kabir, Sanya, Tara. Had you written '%a%' instead, you would have got every name containing an a anywhere.

Underscores can also fix a length. '_____' — five underscores — means exactly five characters:

SELECT Name, City FROM EMPLOYEE WHERE City LIKE '_____';
+--------------+-------+
| Name         | City  |
+--------------+-------+
| Aarav Sharma | Delhi |
| Diya Nair    | Kochi |
| Kabir Singh  | Delhi |
| Sanya Gupta  | Delhi |
+--------------+-------+

This also confirms the char behaviour from Section 2. City is char(12), so 'Delhi' is padded out to twelve characters in storage — yet it matches a five-character pattern, because the trailing spaces are stripped when the value is read back.

One honest warning about LIKE in MySQL. Under the default collation on this server (utf8mb4_0900_ai_ci, where ci means case-insensitive), text comparison ignores case. A direct test returned 1 for both 'sharma' LIKE '%SH%' and 'Sharma' = 'SHARMA'. So LIKE 'a%' also matches Arjun. Write your patterns in the natural case and do not rely on case to filter.

SELECT skeleton SELECT DISTINCT col1, col2 FROM table_name WHERE condition ORDER BY col1 DESC; Clause order is fixed. WHERE always comes before ORDER BY, never after.
Aliasing SELECT Name AS Employee, Salary*12 AS "Annual Package" FROM EMPLOYEE; AS is optional. Quote the alias if it contains a space. It renames output only, never the table.
Operators Mathematical: + - * / % Relational: = > = (or !=) Logical: AND OR NOT Equality is a single = , not ==. AND binds tighter than OR, so bracket when you mix them.
IN and BETWEEN col IN ('Delhi','Mumbai','Pune') col BETWEEN 50000 AND 70000 IN is shorthand for a chain of ORs. BETWEEN includes both end values. Both have NOT forms.
LIKE wildcards col LIKE 'A%' -- starts with A col LIKE '%a' -- ends with a col LIKE '%Sh%' -- contains Sh col LIKE '_a%' -- second character is a col LIKE '_____' -- exactly 5 characters % = any number of characters including zero; _ = exactly one. MySQL's default collation ignores case.
DISTINCT and ORDER BY SELECT DISTINCT City, Dept FROM EMPLOYEE; SELECT Name FROM EMPLOYEE ORDER BY Dept ASC, Salary DESC; DISTINCT works on the whole selected row. ASC is the default and can be left out.
Remember
  • SELECT chooses columns, WHERE chooses rows, and the clause order SELECT–FROM–WHERE–ORDER BY is fixed.
  • An alias renames a column in the output only; AS is optional, but an alias containing a space must be quoted.
  • AND is evaluated before OR, so always use brackets when you mix them — otherwise the query silently answers a different question.
  • DISTINCT removes duplicate whole rows, not duplicates in one column: DISTINCT City gave 10 rows but DISTINCT City, Dept gave 11.
  • BETWEEN includes both boundary values, and works on dates as well as numbers.
  • In LIKE, % matches any number of characters (including none) and _ matches exactly one character.

NULL, UPDATE and DELETE

Quick answer NULL means unknown rather than zero or blank, so it fails every comparison and must be tested with IS NULL, while UPDATE edits values in existing rows and DELETE removes whole rows but leaves the table standing.

NULL means the value is not known. It is not zero. It is not an empty string. It is not a space. It is the database saying "nothing was recorded here". In our table, Rehan Ali and Tara Menon joined recently and their salary has not been fixed yet, so Salary is NULL for both.

That distinction is not a technicality — it changes every comparison. A salary of 0 means the person genuinely earns nothing. A salary of NULL means we do not know what they earn. SQL refuses to guess.

The single most common SQL mistake. Every student instinctively writes this:

SELECT Name, Salary FROM EMPLOYEE WHERE Salary = NULL;

Result: 0 rows returned. Not an error message — just nothing. And the reverse fails identically:

SELECT Name, Salary FROM EMPLOYEE WHERE Salary <> NULL;

Also 0 rows returned. Both queries come back empty, even though two rows are sitting right there with NULL salaries. This is why the mistake is so dangerous: you get a silent wrong answer, not a crash.

Why it happens. Comparing anything with an unknown produces an unknown, not true or false. SQL uses three-valued logic. Here are the actual values MySQL returns:

SELECT NULL = NULL AS eq, NULL <> NULL AS ne, NULL IS NULL AS isnull,
       5000 + NULL AS plus, NULL = 0 AS eq_zero, NULL = '' AS eq_empty;
+------+------+--------+------+---------+----------+
| eq   | ne   | isnull | plus | eq_zero | eq_empty |
+------+------+--------+------+---------+----------+
| NULL | NULL |      1 | NULL |    NULL |     NULL |
+------+------+--------+------+---------+----------+

Read that row carefully, because it answers the whole topic. NULL = NULL is not 1 and not 0 — it is NULL. A WHERE clause keeps a row only when the condition comes out TRUE. NULL is not TRUE, so the row is dropped. That is why both queries returned nothing.

The same table proves the two claims that carry marks: NULL = 0 gives NULL, so NULL is not zero, and NULL = '' gives NULL, so NULL is not an empty string. Confirming it against the real data, both of these returned 0 rows as well:

SELECT Name, Salary FROM EMPLOYEE WHERE Salary = 0;
SELECT Name, Salary FROM EMPLOYEE WHERE Salary = '';

The fix: IS NULL and IS NOT NULL. These are the only operators that can test for NULL:

SELECT Name, Salary FROM EMPLOYEE WHERE Salary IS NULL;
+------------+--------+
| Name       | Salary |
+------------+--------+
| Rehan Ali  |   NULL |
| Tara Menon |   NULL |
+------------+--------+
SELECT Name, Salary FROM EMPLOYEE WHERE Salary IS NOT NULL;
+--------------+--------+
| Name         | Salary |
+--------------+--------+
| Aarav Sharma |  52000 |
| Diya Nair    |  47500 |
| Rohan Mehta  |  61000 |
| Ishita Rao   |  78000 |
| Kabir Singh  |  69500 |
| Meera Iyer   |  45000 |
| Arjun Patel  |  58000 |
| Sanya Gupta  |  52000 |
| Vikram Bose  |  84000 |
| Neha Joshi   |  49000 |
+--------------+--------+

Two consequences you will be tested on. First, NULL spreads through arithmetic — 5000 + NULL is NULL, which is why Rehan Ali's annual salary came out NULL in Section 4. Second, a NULL row is quietly excluded from ordinary conditions. WHERE Salary <> 52000 returned only 8 rows: the ten non-NULL salaries minus the two equal to 52000. The NULL rows did not appear, even though "unknown" is arguably not 52000. Similarly NULL IN ('Delhi','Pune') evaluates to NULL, so a NULL never matches an IN list.

Third, in ORDER BY, MySQL treats NULL as the lowest value, so ascending sorts put NULLs first:

SELECT Name, Salary FROM EMPLOYEE ORDER BY Salary ASC;
+--------------+--------+
| Name         | Salary |
+--------------+--------+
| Rehan Ali    |   NULL |
| Tara Menon   |   NULL |
| Meera Iyer   |  45000 |
| Diya Nair    |  47500 |
| Neha Joshi   |  49000 |
| Aarav Sharma |  52000 |
| Sanya Gupta  |  52000 |
| Arjun Patel  |  58000 |
| Rohan Mehta  |  61000 |
| Kabir Singh  |  69500 |
| Ishita Rao   |  78000 |
| Vikram Bose  |  84000 |
+--------------+--------+

UPDATE — changing values in rows that already exist. SET says what to change, WHERE says in which rows. Here are four updates that were actually run, with the number of rows each one changed:

UPDATE EMPLOYEE SET Bonus = Salary * 0.10 WHERE Dept = 'Sales';
   -> rows affected: 4
UPDATE EMPLOYEE SET Salary = Salary + 3000 WHERE City = 'Delhi';
   -> rows affected: 3
UPDATE EMPLOYEE SET Salary = 40000, Bonus = 2000 WHERE EmpID = 111;
   -> rows affected: 1
UPDATE EMPLOYEE SET Bonus = 0 WHERE Bonus IS NULL;
   -> rows affected: 7

The resulting table:

+-------+--------------+-----------+----------+--------+-------+
| EmpID | Name         | City      | Dept     | Salary | Bonus |
+-------+--------------+-----------+----------+--------+-------+
|   101 | Aarav Sharma | Delhi     | Sales    |  55000 |  5200 |
|   102 | Diya Nair    | Kochi     | HR       |  47500 |     0 |
|   103 | Rohan Mehta  | Mumbai    | Sales    |  61000 |  6100 |
|   104 | Ishita Rao   | Bengaluru | IT       |  78000 |     0 |
|   105 | Kabir Singh  | Delhi     | IT       |  72500 |     0 |
|   106 | Meera Iyer   | Chennai   | HR       |  45000 |     0 |
|   107 | Arjun Patel  | Ahmedabad | Sales    |  58000 |  5800 |
|   108 | Sanya Gupta  | Delhi     | Sales    |  55000 |  5200 |
|   109 | Vikram Bose  | Kolkata   | IT       |  84000 |     0 |
|   110 | Neha Joshi   | Pune      | Accounts |  49000 |     0 |
|   111 | Rehan Ali    | Hyderabad | IT       |  40000 |  2000 |
|   112 | Tara Menon   | Jaipur    | Accounts |   NULL |     0 |
+-------+--------------+-----------+----------+--------+-------+

Four points are visible in that table. One: several columns can be set in a single statement, separated by commas — see the third update. Two: the right-hand side of SET uses the row's current value, so Salary = Salary + 3000 is a raise, not a replacement. Three, and worth pausing on: Aarav Sharma's bonus is 5200 while his salary now reads 55000. The bonus was calculated as 10% of his old salary of 52000, because the bonus update ran before the raise. SQL statements execute in the order you write them, and re-running them in a different order gives different data. Four: Tara Menon's salary is still NULL, because Salary + 3000 was never applied to her and NULL cannot be nudged by arithmetic.

UPDATE with no WHERE changes every row. There is no undo and no confirmation. Demonstrated on the small trainee table:

UPDATE TRAINEE SET Stipend = 15000;
   -> rows affected: 3
+-----+-------------+--------+---------+------------+
| TID | TName       | Stream | Stipend | JoinDate   |
+-----+-------------+--------+---------+------------+
|   1 | Ananya Das  | CS     |   15000 | 2024-01-08 |
|   2 | Farhan Khan | ECE    |   15000 | 2024-02-19 |
|   3 | Pooja Bhatt | CS     |   15000 | 2024-03-04 |
+-----+-------------+--------+---------+------------+

All three stipends overwritten. Always write the WHERE first, then go back and add the SET.

DELETE — removing whole rows. DELETE removes entire rows and nothing less. You cannot delete a single column's value with it; to blank one field you use UPDATE ... SET col = NULL. Three deletes, with real counts:

DELETE FROM EMPLOYEE WHERE Salary IS NULL;
   -> rows affected: 1
DELETE FROM EMPLOYEE WHERE Dept = 'Marketing';
   -> rows affected: 0
DELETE FROM EMPLOYEE WHERE City = 'Kochi';
   -> rows affected: 1

The middle one matched nothing and reported 0 rows affected — that is not an error. A DELETE that finds no matching row simply does nothing.

DELETE without WHERE empties the table but keeps it. This was tested properly. A scratch table was created and filled with ten rows:

CREATE TABLE TEMPSTAFF (
  EmpID INT NOT NULL,
  Name  VARCHAR(25) NOT NULL,
  Dept  VARCHAR(15)
);

Then every row was removed with a single statement, and the table list was checked:

DELETE FROM TEMPSTAFF;
   -> rows affected: 10
SELECT * FROM TEMPSTAFF;   -- 0 rows
SHOW TABLES;               -- employee, tempstaff, trainee
DESCRIBE TEMPSTAFF;
+-------+-------------+------+-----+---------+-------+
| Field | Type        | Null | Key | Default | Extra |
+-------+-------------+------+-----+---------+-------+
| EmpID | int         | NO   |     | NULL    |       |
| Name  | varchar(25) | NO   |     | NULL    |       |
| Dept  | varchar(15) | YES  |     | NULL    |       |
+-------+-------------+------+-----+---------+-------+

Every row is gone, but tempstaff is still listed by SHOW TABLES and DESCRIBE still returns its full structure. That is the distinction the board asks for, in one experiment.

CommandTypeRowsStructureDatabase
DELETE FROM t;DMLRemovedKeptKept
DROP TABLE t;DDLRemovedRemovedKept
DROP DATABASE d;DDLRemovedRemovedRemoved
IS NULL / IS NOT NULL col IS NULL col IS NOT NULL The only valid way to test for NULL. Both '= NULL' and '<> NULL' return 0 rows, never an error.
NULL truth values NULL = NULL -> NULL NULL <> NULL -> NULL NULL IS NULL -> 1 5000 + NULL -> NULL WHERE keeps a row only when the condition is TRUE. NULL is not TRUE, so the row is silently dropped.
UPDATE UPDATE table_name SET col1 = value1, col2 = value2 WHERE condition; Omit WHERE and every row is updated. Separate multiple assignments with commas, not AND.
UPDATE with an expression UPDATE EMPLOYEE SET Salary = Salary + 3000 WHERE City = 'Delhi'; The right side uses the row's existing value. NULL + 3000 stays NULL, so NULL rows are unchanged.
DELETE DELETE FROM table_name WHERE condition; Removes whole rows only. To blank one field use UPDATE ... SET col = NULL. A no-match DELETE reports 0 rows, not an error.
DELETE vs DROP DELETE FROM t; -- rows gone, table remains DROP TABLE t; -- rows and table both gone DELETE is DML, DROP TABLE is DDL. After DELETE, SHOW TABLES still lists the table.
Remember
  • NULL means unknown — it is not zero and not an empty string, and MySQL confirms both: NULL = 0 and NULL = '' each evaluate to NULL.
  • WHERE Salary = NULL returns 0 rows silently because NULL = NULL evaluates to NULL, and WHERE keeps a row only when the condition is TRUE. Use IS NULL.
  • NULL propagates through arithmetic (5000 + NULL is NULL) and never matches an IN list, so NULL rows vanish from ordinary conditions like <> 52000.
  • UPDATE ... SET without a WHERE clause changes every row in the table, with no confirmation and no undo.
  • The right side of SET uses the row's current value, so Salary = Salary + 3000 is a raise; statements also run in written order, which is why a bonus computed before a raise uses the old salary.
  • DELETE removes whole rows and keeps the table; DROP TABLE removes the structure too. DELETE is DML, DROP is DDL.

The formula sheet

Every formula in this chapter, in one place — screenshot it before your exam.

CREATE DATABASE database_name;
CREATE DATABASE
SHOW DATABASES; SHOW TABLES;
SHOW DATABASES / SHOW TABLES
USE database_name;
USE
DROP DATABASE database_name;
DROP DATABASE
DDL: CREATE, ALTER, DROP DML: INSERT, UPDATE, DELETE, SELECT
DDL vs DML
CREATE TABLE table_name ( col1 datatype constraint, col2 datatype, PRIMARY KEY (col1) );
CREATE TABLE
char(n) varchar(n) int float date
Data types
col_name datatype NOT NULL
NOT NULL
col_name datatype UNIQUE
UNIQUE
PRIMARY KEY (col_name) -- or inline: col_name datatype PRIMARY KEY
PRIMARY KEY
DESCRIBE table_name; DESC table_name;
DESCRIBE
INSERT INTO table_name VALUES (v1, v2, v3);
INSERT — all columns
INSERT INTO table_name (col1, col2) VALUES (v1, v2);
INSERT — chosen columns
INSERT INTO table_name VALUES (1,'A'), (2,'B'), (3,'C');
INSERT — many rows
ALTER TABLE table_name ADD col_name datatype; ALTER TABLE table_name DROP COLUMN col_name;
ALTER TABLE — column
ALTER TABLE table_name ADD PRIMARY KEY (col_name); ALTER TABLE table_name DROP PRIMARY KEY;
ALTER TABLE — primary key
DROP TABLE table_name;
DROP TABLE
SELECT DISTINCT col1, col2 FROM table_name WHERE condition ORDER BY col1 DESC;
SELECT skeleton
SELECT Name AS Employee, Salary*12 AS "Annual Package" FROM EMPLOYEE;
Aliasing
Mathematical: + - * / % Relational: = > = (or !=) Logical: AND OR NOT
Operators
col IN ('Delhi','Mumbai','Pune') col BETWEEN 50000 AND 70000
IN and BETWEEN
col LIKE 'A%' -- starts with A col LIKE '%a' -- ends with a col LIKE '%Sh%' -- contains Sh col LIKE '_a%' -- second character is a col LIKE '_____' -- exactly 5 characters
LIKE wildcards
SELECT DISTINCT City, Dept FROM EMPLOYEE; SELECT Name FROM EMPLOYEE ORDER BY Dept ASC, Salary DESC;
DISTINCT and ORDER BY
col IS NULL col IS NOT NULL
IS NULL / IS NOT NULL
NULL = NULL -> NULL NULL <> NULL -> NULL NULL IS NULL -> 1 5000 + NULL -> NULL
NULL truth values
UPDATE table_name SET col1 = value1, col2 = value2 WHERE condition;
UPDATE
UPDATE EMPLOYEE SET Salary = Salary + 3000 WHERE City = 'Delhi';
UPDATE with an expression
DELETE FROM table_name WHERE condition;
DELETE
DELETE FROM t; -- rows gone, table remains DROP TABLE t; -- rows and table both gone
DELETE vs DROP

Test yourself

Tap an answer to check it instantly — you'll see why it's right, and what to revise if it isn't.

0 correct · 0/12 answered
Q1

Which of the following is a DML command?

Q2

The EMPLOYEE table has 12 rows, two of which have a NULL Salary. What does SELECT Name, Salary FROM EMPLOYEE WHERE Salary = NULL; return?

Q3

The EMPLOYEE table has 12 rows and 10 distinct cities. How many rows does SELECT DISTINCT City, Dept FROM EMPLOYEE; return?

Q4

On the 12-row EMPLOYEE table (10 salaries recorded, 2 NULL, and 5 salaries lying between 50000 and 70000), how many rows does SELECT Name FROM EMPLOYEE WHERE Salary NOT BETWEEN 50000 AND 70000; return?

Q5

What does SELECT Name, City, Dept FROM EMPLOYEE WHERE City='Delhi' AND Dept='Sales' OR Dept='HR'; return on the 12-row table (3 employees in Delhi, of whom 2 are in Sales; 2 employees in HR, both outside Delhi)?

Q6

The EMPLOYEE table holds these 12 names: Aarav Sharma, Diya Nair, Rohan Mehta, Ishita Rao, Kabir Singh, Meera Iyer, Arjun Patel, Sanya Gupta, Vikram Bose, Neha Joshi, Rehan Ali and Tara Menon. How many rows does SELECT Name FROM EMPLOYEE WHERE Name LIKE '_a%'; return?

Q7

On the 12-row EMPLOYEE table where two employees have NULL salary, which name appears FIRST in the output of SELECT Name, Salary FROM EMPLOYEE ORDER BY Salary;?

Q8

The Salary column holds nine different values, one of which (52000) appears twice, plus two NULLs. How many rows does SELECT DISTINCT Salary FROM EMPLOYEE; return?

Q9

A column is declared City CHAR(12) and another City2 VARCHAR(12). The value 'Pune' is stored in both. Which statement is correct?

Q10

After running ALTER TABLE EMPLOYEE DROP PRIMARY KEY; on a table whose EmpID was the primary key, what does DESCRIBE EMPLOYEE show for the EmpID row?

Q11

Meena wants to remove all 500 rows from the table ORDERS but keep the table so that fresh data can be inserted into it tomorrow. Which command should she use?

Q12

In a table, Email is declared UNIQUE and EmpID is the PRIMARY KEY. Two new rows are inserted using a column list that omits Email. What happens?

NCERT solutions & previous-year questions

Step-by-step model answers — tap a question to reveal the full solution.

NCERT questions 6

1 Differentiate between DDL and DML commands of SQL. Give two examples of each.Introduction to SQL: DDL and DML

SQL commands are grouped by what they act on.

BasisDDLDML
Full formData Definition LanguageData Manipulation Language
Acts onThe structure — databases, tables, columns, constraintsThe data stored inside a table
Effect on rowsDefines where rows can live; does not edit individual valuesAdds, changes or removes rows
ExamplesCREATE, ALTER, DROPINSERT, UPDATE, DELETE, SELECT

The clearest illustration is a DDL command and a DML command run one after the other on the same table. The DDL command reshapes the table and reports no rows changed; the DML command leaves the shape alone and reports how many rows it edited:

ALTER TABLE EMPLOYEE ADD Bonus FLOAT;
   -> rows affected: 0
UPDATE EMPLOYEE SET Salary = Salary + Salary*0.10 WHERE City = 'Delhi';
   -> rows affected: 3

Memory hook: DDL changes the skeleton, DML changes the contents. This is also why DROP TABLE is DDL (it removes the table) while DELETE is DML (it removes rows and leaves the empty table behind).

2 What is the difference between the char(n) and varchar(n) data types? Which one would you choose for a column that stores student names, and why?Data types

char(n) is fixed-length. Whatever you store, the column occupies space for exactly n characters; shorter values are padded with trailing spaces. varchar(n) is variable-length: n is only an upper limit, and the column occupies just as much space as the value you actually supplied.

This can be demonstrated. Two columns of the same width were given the same value — once plain, once with five trailing spaces:

CREATE TABLE PADDEMO (c CHAR(10), v VARCHAR(10));
INSERT INTO PADDEMO VALUES ('Delhi', 'Delhi');
INSERT INTO PADDEMO VALUES ('Delhi     ', 'Delhi     ');
SELECT CONCAT('[', c, ']') AS char_value,
       CONCAT('[', v, ']') AS varchar_value,
       LENGTH(c) AS char_len,
       LENGTH(v) AS varchar_len
FROM PADDEMO;
+------------+---------------+----------+-------------+
| char_value | varchar_value | char_len | varchar_len |
+------------+---------------+----------+-------------+
| [Delhi]    | [Delhi]       |        5 |           5 |
| [Delhi]    | [Delhi     ]  |        5 |          10 |
+------------+---------------+----------+-------------+

In the second row the varchar column kept all ten characters while the char column returned only Delhi. That is because a char column pads values out to the full width on storage and strips the trailing spaces again on retrieval — the padding is hidden, but the space is reserved.

For student names, use varchar — for example varchar(30). Names vary a great deal in length, from 'Ram' to 'Lakshminarayanan', so a fixed-length column would waste space on almost every row. Reserve char(n) for values that genuinely have a constant length, such as a grade letter char(1), a two-letter state code char(2) or a PIN code char(6).

3 Explain the NOT NULL, UNIQUE and PRIMARY KEY constraints. Write a CREATE TABLE statement that uses all three.Constraints

A constraint is a rule enforced by the database itself, so invalid data is refused even if the program inserting it has a bug.

  • NOT NULL — the column must always be given a value. Duplicates are still allowed.
  • UNIQUE — no two rows may hold the same value. NULL is permitted, and more than one NULL is permitted, because NULL is not a value.
  • PRIMARY KEY — uniquely identifies each row. It is UNIQUE + NOT NULL combined. A table can have only one primary key.
CREATE TABLE EMPLOYEE (
  EmpID  INT,
  Name   VARCHAR(25) NOT NULL,
  City   CHAR(12),
  Dept   VARCHAR(15),
  Salary FLOAT,
  DOJ    DATE,
  Email  VARCHAR(35) UNIQUE,
  PRIMARY KEY (EmpID)
);
DESCRIBE EMPLOYEE;
+--------+-------------+------+-----+---------+-------+
| Field  | Type        | Null | Key | Default | Extra |
+--------+-------------+------+-----+---------+-------+
| EmpID  | int         | NO   | PRI | NULL    |       |
| Name   | varchar(25) | NO   |     | NULL    |       |
| City   | char(12)    | YES  |     | NULL    |       |
| Dept   | varchar(15) | YES  |     | NULL    |       |
| Salary | float       | YES  |     | NULL    |       |
| DOJ    | date        | YES  |     | NULL    |       |
| Email  | varchar(35) | YES  | UNI | NULL    |       |
+--------+-------------+------+-----+---------+-------+

Note that EmpID shows Null: NO although NOT NULL was never written for it — declaring it the primary key applied that automatically.

The constraints then refuse bad data. All four of these inserts were rejected:

INSERT INTO EMPLOYEE VALUES (101,'Nikhil Verma','Surat','HR',50000,'2024-01-05','nikhil.verma@nova.in');
ERROR 1062 (23000) at line 1: Duplicate entry '101' for key 'employee.PRIMARY'

INSERT INTO EMPLOYEE VALUES (113,NULL,'Surat','HR',50000,'2024-01-05','nikhil.verma@nova.in');
ERROR 1048 (23000) at line 1: Column 'Name' cannot be null

INSERT INTO EMPLOYEE VALUES (113,'Nikhil Verma','Surat','HR',50000,'2024-01-05','diya.nair@nova.in');
ERROR 1062 (23000) at line 1: Duplicate entry 'diya.nair@nova.in' for key 'employee.Email'

INSERT INTO EMPLOYEE (EmpID,Name) VALUES (NULL,'Ghost User');
ERROR 1048 (23000) at line 1: Column 'EmpID' cannot be null
4 What is meant by a NULL value in SQL? Why does the query WHERE Salary = NULL not work? Write the correct query to list employees whose salary is not known.Meaning of NULL, IS NULL

NULL means the value is unknown or not applicable. It is not zero, not an empty string and not a space. A salary of 0 means the person earns nothing; a salary of NULL means we have not recorded what they earn.

Because NULL is unknown, comparing anything with it produces another unknown rather than true or false. MySQL confirms this directly:

SELECT NULL = NULL AS eq, NULL <> NULL AS ne, NULL IS NULL AS isnull,
       5000 + NULL AS plus, NULL = 0 AS eq_zero, NULL = '' AS eq_empty;
+------+------+--------+------+---------+----------+
| eq   | ne   | isnull | plus | eq_zero | eq_empty |
+------+------+--------+------+---------+----------+
| NULL | NULL |      1 | NULL |    NULL |     NULL |
+------+------+--------+------+---------+----------+

So Salary = NULL evaluates to NULL for every row. A WHERE clause keeps a row only when the condition is TRUE, and NULL is not TRUE — therefore no row is ever returned:

SELECT Name, Salary FROM EMPLOYEE WHERE Salary = NULL;

0 rows returned, and no error message, which is exactly what makes this mistake dangerous. The same table also shows NULL = 0 and NULL = '' both give NULL, proving NULL is neither zero nor a blank string.

The correct query uses IS NULL, the only operator that can test for it:

SELECT Name, Salary FROM EMPLOYEE WHERE Salary IS NULL;
+------------+--------+
| Name       | Salary |
+------------+--------+
| Rehan Ali  |   NULL |
| Tara Menon |   NULL |
+------------+--------+

The opposite test is IS NOT NULL, which returns the other 10 employees.

5 What is the purpose of the DISTINCT clause? Explain aliasing with an example. Illustrate both on an employee table.DISTINCT clause and aliasing

DISTINCT removes duplicate rows from the output of a SELECT. The crucial point is that it applies to the entire selected row, not to a single column. Compare the two queries below on the same 12-row table:

SELECT DISTINCT City FROM EMPLOYEE;
+-----------+
| City      |
+-----------+
| Delhi     |
| Kochi     |
| Mumbai    |
| Bengaluru |
| Chennai   |
| Ahmedabad |
| Kolkata   |
| Pune      |
| Hyderabad |
| Jaipur    |
+-----------+
SELECT DISTINCT City, Dept FROM EMPLOYEE;
+-----------+----------+
| City      | Dept     |
+-----------+----------+
| Delhi     | Sales    |
| Kochi     | HR       |
| Mumbai    | Sales    |
| Bengaluru | IT       |
| Delhi     | IT       |
| Chennai   | HR       |
| Ahmedabad | Sales    |
| Kolkata   | IT       |
| Pune      | Accounts |
| Hyderabad | IT       |
| Jaipur    | Accounts |
+-----------+----------+

The first returns 10 rows, the second 11 — and Delhi now appears twice. Delhi/Sales occurred twice in the data and collapsed into one row, but Delhi/IT is a different combination and is kept. So DISTINCT never de-duplicates just the first column.

Aliasing gives a column or an expression a temporary name in the output. It changes only the heading of the result; the table itself is unchanged. Use the keyword AS (which is optional), and quote the alias if it contains a space:

SELECT Name AS "Employee Name", Salary*12 AS Annual_Salary FROM EMPLOYEE;
+---------------+---------------+
| Employee Name | Annual_Salary |
+---------------+---------------+
| Aarav Sharma  |        624000 |
| Diya Nair     |        570000 |
| Rohan Mehta   |        732000 |
| Ishita Rao    |        936000 |
| Kabir Singh   |        834000 |
| Meera Iyer    |        540000 |
| Arjun Patel   |        696000 |
| Sanya Gupta   |        624000 |
| Vikram Bose   |       1008000 |
| Neha Joshi    |        588000 |
| Rehan Ali     |          NULL |
| Tara Menon    |          NULL |
+---------------+---------------+

Aliasing is essential for calculated columns — without it the heading would read Salary*12, which is unreadable in a report.

6 Write SQL commands to do the following on a table TRAINEE: (a) add a column Mentor of type varchar(20), (b) remove that column, (c) make the existing column TID the primary key, and (d) remove the primary key.ALTER TABLE: add and remove attribute, add and remove primary key

All four operations use ALTER TABLE, which is a DDL command. The starting table has no primary key:

CREATE TABLE TRAINEE (
  TID INT,
  TName VARCHAR(20) NOT NULL,
  Stream CHAR(10),
  Stipend FLOAT,
  JoinDate DATE
);
DESCRIBE TRAINEE;
+----------+-------------+------+-----+---------+-------+
| Field    | Type        | Null | Key | Default | Extra |
+----------+-------------+------+-----+---------+-------+
| TID      | int         | YES  |     | NULL    |       |
| TName    | varchar(20) | NO   |     | NULL    |       |
| Stream   | char(10)    | YES  |     | NULL    |       |
| Stipend  | float       | YES  |     | NULL    |       |
| JoinDate | date        | YES  |     | NULL    |       |
+----------+-------------+------+-----+---------+-------+

(c) Add the primary key and (a) add the column:

ALTER TABLE TRAINEE ADD PRIMARY KEY (TID);
ALTER TABLE TRAINEE ADD Mentor VARCHAR(20);
DESCRIBE TRAINEE;
+----------+-------------+------+-----+---------+-------+
| Field    | Type        | Null | Key | Default | Extra |
+----------+-------------+------+-----+---------+-------+
| TID      | int         | NO   | PRI | NULL    |       |
| TName    | varchar(20) | NO   |     | NULL    |       |
| Stream   | char(10)    | YES  |     | NULL    |       |
| Stipend  | float       | YES  |     | NULL    |       |
| JoinDate | date        | YES  |     | NULL    |       |
| Mentor   | varchar(20) | YES  |     | NULL    |       |
+----------+-------------+------+-----+---------+-------+

Two things to notice. Mentor is appended at the end and is NULL for every existing row. And TID has changed from Null: YES to Null: NO — adding the primary key made the column NOT NULL automatically.

(b) Remove the column and (d) remove the primary key:

ALTER TABLE TRAINEE DROP Mentor;
ALTER TABLE TRAINEE DROP PRIMARY KEY;
DESCRIBE TRAINEE;
+----------+-------------+------+-----+---------+-------+
| Field    | Type        | Null | Key | Default | Extra |
+----------+-------------+------+-----+---------+-------+
| TID      | int         | NO   |     | NULL    |       |
| TName    | varchar(20) | NO   |     | NULL    |       |
| Stream   | char(10)    | YES  |     | NULL    |       |
| Stipend  | float       | YES  |     | NULL    |       |
| JoinDate | date        | YES  |     | NULL    |       |
+----------+-------------+------+-----+---------+-------+

Points that carry marks: the COLUMN keyword is optional, so DROP Mentor and DROP COLUMN Mentor are both accepted. You must name the column when adding a primary key but never when dropping it, because a table has only one. And TID still reads Null: NO after the key is dropped — the key is gone, but the NOT NULL it installed remains.

Previous-year board questions 4

Q1 Ms. Kavita is a database administrator. She wants to remove all the records of the table STOCK but keep its structure so that fresh records can be inserted next month. She also wants to remove another table OLDSTOCK completely from the database. Write the SQL command for each requirement, and state which category (DDL or DML) each command belongs to. (2 marks) CBSE 2023 (board pattern)

(i) To remove all records but keep the structure:

DELETE FROM STOCK;

This is a DML command.

(ii) To remove the table completely:

DROP TABLE OLDSTOCK;

This is a DDL command.

Justification, verified by running it. A table holding 10 rows was emptied with a DELETE carrying no WHERE clause, and the table list was then checked:

DELETE FROM TEMPSTAFF;
   -> rows affected: 10
SELECT * FROM TEMPSTAFF;   -- 0 rows
SHOW TABLES;               -- employee, tempstaff, trainee
DESCRIBE TEMPSTAFF;
+-------+-------------+------+-----+---------+-------+
| Field | Type        | Null | Key | Default | Extra |
+-------+-------------+------+-----+---------+-------+
| EmpID | int         | NO   |     | NULL    |       |
| Name  | varchar(25) | NO   |     | NULL    |       |
| Dept  | varchar(15) | YES  |     | NULL    |       |
+-------+-------------+------+-----+---------+-------+

All rows were removed, yet the table is still listed by SHOW TABLES and DESCRIBE still returns its full structure — exactly what Kavita needs for the STOCK table. By contrast, after DROP TABLE the table cannot be found at all:

DROP TABLE PKDEMO;
ERROR 1051 (42S02) at line 1: Unknown table 'cs12_sql_basics.pkdemo'

Marking note: 1 mark for each correct command; the DDL/DML labels carry the reasoning. Writing DROP TABLE STOCK; for part (i) is the standard error — it would destroy the structure Kavita explicitly wants to keep.

Q2 Consider the table EMPLOYEE with columns EmpID, Name, City, Dept, Salary and DOJ, in which two employees (Rehan Ali and Tara Menon) have not yet been assigned a salary. Predict the output of the following two statements and justify the difference. (2 marks) (i) SELECT Name, Salary FROM EMPLOYEE WHERE Salary = NULL; (ii) SELECT Name, Salary FROM EMPLOYEE WHERE Salary IS NULL; CBSE 2024 (board pattern)

(i) Output: no rows at all (an empty result set).

SELECT Name, Salary FROM EMPLOYEE WHERE Salary = NULL;

0 rows returned — and importantly, no error message.

(ii) Output: the two employees whose salary is unknown.

SELECT Name, Salary FROM EMPLOYEE WHERE Salary IS NULL;
+------------+--------+
| Name       | Salary |
+------------+--------+
| Rehan Ali  |   NULL |
| Tara Menon |   NULL |
+------------+--------+

Justification. NULL represents an unknown value, so any comparison involving it produces an unknown result rather than true or false. SQL confirms this directly:

SELECT NULL = NULL AS eq, NULL IS NULL AS isnull;
+------+--------+
| eq   | isnull |
+------+--------+
| NULL |      1 |
+------+--------+

A WHERE clause retains a row only when its condition evaluates to TRUE. In statement (i) the condition evaluates to NULL for every row, so every row is discarded — including the very rows the user was trying to find. Statement (ii) uses IS NULL, which is a dedicated test that returns TRUE or FALSE and therefore works correctly.

Additional point worth writing: NULL is not zero and not an empty string. Both WHERE Salary = 0 and WHERE Salary = '' also returned 0 rows on this table. The only correct tests for a missing value are IS NULL and IS NOT NULL.

Q3 Rahul created the table TRAINEE with the columns TID (int), TName (varchar(20), not null), Stream (char(10)), Stipend (float) and JoinDate (date), but forgot to declare a primary key. (3 marks) (i) Write the command to make TID the primary key. (ii) Later the company needs to record each trainee's mentor. Write the command to add a column Mentor of type varchar(20). (iii) The mentor scheme is dropped and the table is to be merged with another. Write the commands to remove the Mentor column and to remove the primary key. CBSE 2023 (board pattern)

(i)

ALTER TABLE TRAINEE ADD PRIMARY KEY (TID);

(ii)

ALTER TABLE TRAINEE ADD Mentor VARCHAR(20);

After both commands, DESCRIBE TRAINEE gives:

+----------+-------------+------+-----+---------+-------+
| Field    | Type        | Null | Key | Default | Extra |
+----------+-------------+------+-----+---------+-------+
| TID      | int         | NO   | PRI | NULL    |       |
| TName    | varchar(20) | NO   |     | NULL    |       |
| Stream   | char(10)    | YES  |     | NULL    |       |
| Stipend  | float       | YES  |     | NULL    |       |
| JoinDate | date        | YES  |     | NULL    |       |
| Mentor   | varchar(20) | YES  |     | NULL    |       |
+----------+-------------+------+-----+---------+-------+

(iii)

ALTER TABLE TRAINEE DROP Mentor;
ALTER TABLE TRAINEE DROP PRIMARY KEY;
+----------+-------------+------+-----+---------+-------+
| Field    | Type        | Null | Key | Default | Extra |
+----------+-------------+------+-----+---------+-------+
| TID      | int         | NO   |     | NULL    |       |
| TName    | varchar(20) | NO   |     | NULL    |       |
| Stream   | char(10)    | YES  |     | NULL    |       |
| Stipend  | float       | YES  |     | NULL    |       |
| JoinDate | date        | YES  |     | NULL    |       |
+----------+-------------+------+-----+---------+-------+

Examiner's points. The column name is written in brackets when adding a primary key but must not be written when dropping it, because a table can have only one primary key. The keyword COLUMN in DROP COLUMN Mentor is optional in MySQL, so both forms earn the mark. Also note that TID changed from Null: YES to Null: NO when the key was added, and stayed NO after it was dropped — adding a primary key makes a column NOT NULL, and dropping the key does not undo that.

Caution: part (i) succeeds only if TID currently holds no duplicate values. On a column containing duplicates the command is refused:

ALTER TABLE PKDEMO ADD PRIMARY KEY (Rno);
ERROR 1062 (23000) at line 1: Duplicate entry '1' for key 'pkdemo.PRIMARY'
Q4 Consider the table EMPLOYEE used in this chapter (12 rows; columns EmpID, Name, City, Dept, Salary, DOJ, Email; Rehan Ali and Tara Menon have NULL salary). Write SQL statements for (i) to (v) and give the output of each. (5 marks) (i) Display the name and city of employees whose name begins with 'A'. (ii) Display the name and annual package (Salary*12) of employees of the Sales department, highest package first, with suitable column headings. (iii) Display the different departments in the company, without repetition. (iv) Display the name, city and department of employees who belong to Delhi or Kolkata, arranged alphabetically by name. (v) Display the name and salary of employees whose salary has not been recorded. CBSE 2024 (board pattern)

(i) Names beginning with 'A' — use LIKE with the % wildcard.

SELECT Name, City FROM EMPLOYEE WHERE Name LIKE 'A%';
+--------------+-----------+
| Name         | City      |
+--------------+-----------+
| Aarav Sharma | Delhi     |
| Arjun Patel  | Ahmedabad |
+--------------+-----------+

(ii) Annual package for Sales, descending — needs an alias for the calculated column.

SELECT Name AS Employee, Salary*12 AS "Annual Package"
FROM EMPLOYEE WHERE Dept='Sales' ORDER BY Salary*12 DESC;
+--------------+----------------+
| Employee     | Annual Package |
+--------------+----------------+
| Rohan Mehta  |         732000 |
| Arjun Patel  |         696000 |
| Aarav Sharma |         624000 |
| Sanya Gupta  |         624000 |
+--------------+----------------+

(iii) Distinct departments.

SELECT DISTINCT Dept FROM EMPLOYEE;
+----------+
| Dept     |
+----------+
| Sales    |
| HR       |
| IT       |
| Accounts |
+----------+

(iv) Two cities — IN is the neat way to write it, and ORDER BY sorts the names.

SELECT Name, City, Dept FROM EMPLOYEE
WHERE City IN ('Delhi','Kolkata') ORDER BY Name;
+--------------+---------+-------+
| Name         | City    | Dept  |
+--------------+---------+-------+
| Aarav Sharma | Delhi   | Sales |
| Kabir Singh  | Delhi   | IT    |
| Sanya Gupta  | Delhi   | Sales |
| Vikram Bose  | Kolkata | IT    |
+--------------+---------+-------+

WHERE City='Delhi' OR City='Kolkata' is equally correct and earns the same mark.

(v) Salary not recorded — this must use IS NULL.

SELECT Name, Salary FROM EMPLOYEE WHERE Salary IS NULL;
+------------+--------+
| Name       | Salary |
+------------+--------+
| Rehan Ali  |   NULL |
| Tara Menon |   NULL |
+------------+--------+

Where marks are lost in this question: writing WHERE Salary = NULL in part (v), which returns nothing at all; omitting the alias in part (ii), which leaves an unreadable Salary*12 heading; and forgetting DESC, since ORDER BY is ascending by default.

Part of Priodemy for School

Interactive CBSE lessons, Class 8–12 — free with every school on Priodemy EduSuite. Explore more chapters and labs on the Priodemy for School hub.

Ask AI