Database Management Systems

Unit 2: Relational Query Language — DDL & DML

From CREATE TABLE to SELECT mastery — the SQL commands that build, fill, protect, and query every database on Earth.

🏢 Oracle & PostgreSQL  |  📝 15 MCQs (Bloom's)  |  🔬 5 Lab Exercises  |  💼 Interview Prep

Section 1

Why This Chapter Pays Your Salary

SQL is the single most in-demand technical skill in Indian IT. TCS NQT, Infosys InfyTQ, Wipro NLTH, Cognizant GenC — every campus placement test has a SQL section. Every backend developer, data analyst, QA engineer, and DBA writes SQL daily. This chapter teaches you the two pillars: DDL (building the structure) and DML (working with the data).

🏢 Industry Snapshot

IRCTC — Their reservation system runs ~200 DDL migration scripts per release cycle. Every table (trains, coaches, seats, passengers, bookings, payments) was designed with the constraints you'll learn in this chapter. A missing NOT NULL on pnr_number once caused a production bug affecting 50,000 bookings.

Razorpay — Processes ₹7 lakh crore annually. Every payment is an INSERT + UPDATE wrapped in a transaction. Their DBA team reviews every ALTER TABLE because adding a column to a 500-million-row table can lock it for hours if done wrong.

TCS / Infosys — 80% of freshers are assigned to database-related projects in their first year. Your SQL skills from this chapter directly determine your project allocation and early career trajectory.

🇮🇳 IRCTC🇮🇳 Razorpay🇮🇳 TCS🇮🇳 PhonePe🇮🇳 Infosys🇮🇳 Flipkart
SQL was invented in 1974 by Donald Chamberlin and Raymond Boyce at IBM. 50+ years later, it remains the most widely used database language. Every major DBMS — Oracle, PostgreSQL, MySQL, SQL Server, SQLite — speaks SQL. Learning SQL is the best ROI of any technical skill.

📦 Schema Setup — Hospital Management System

All examples in this chapter use a Hospital Management System domain. Here's the complete schema we'll build step by step:

Domain: Hospital Management
Tables we will create in this chapter:
  departments   — Hospital departments (Cardiology, Orthopedics, etc.)
  doctors       — Doctor profiles with specialization
  patients      — Patient records with Aadhaar linkage
  appointments  — OPD appointments (patient + doctor + date)
  prescriptions — Medicines prescribed per appointment
  medicines     — Medicine catalog with stock
  bills         — Patient billing records
Section 2

Learning Outcomes — Bloom's Taxonomy

Bloom's LevelOutcome Statement
L1 — RememberList all DDL commands (CREATE, ALTER, DROP, TRUNCATE, RENAME) and DML commands (INSERT, UPDATE, DELETE); recall Oracle vs PostgreSQL data type differences
L2 — UnderstandExplain the difference between DELETE, TRUNCATE, and DROP; describe how COMMIT, ROLLBACK, and SAVEPOINT control transactions; explain SELECT execution order
L3 — ApplyWrite CREATE TABLE with all constraint types (PK, FK, UNIQUE, CHECK, NOT NULL, DEFAULT); write INSERT/UPDATE/DELETE with subqueries; write SELECT with functions
L4 — AnalyzeAnalyze query execution order to debug incorrect results; compare Oracle vs PostgreSQL syntax differences and choose the appropriate one
L5 — EvaluateEvaluate constraint design choices for a production database; justify when to use TRUNCATE vs DELETE; critique a GRANT/REVOKE security configuration
L6 — CreateDesign a complete schema with DDL, populate with realistic DML, configure role-based access with DCL, and write complex queries using functions and CASE expressions
Section 3

Concept Explanations

Part A — Data Definition Language (DDL)

3.1 DDL Overview — Building the Blueprint

📌 DDL — Commands That Define Database Structure

📌 WHAT IT IS

DDL commands create, modify, and destroy database objects (tables, views, indexes, sequences). They define the schema — the blueprint. DDL statements are auto-committed in Oracle (no ROLLBACK possible).

⚙️ DDL COMMANDS AT A GLANCE
CommandPurposeAuto-Commit?
CREATECreate new database object✅ Yes (Oracle) / No (PG)
ALTERModify existing object structure✅ Yes (Oracle) / No (PG)
DROPDelete entire object permanently✅ Yes (Oracle) / No (PG)
TRUNCATERemove all rows, keep structure✅ Yes (Oracle) / No (PG)
RENAMEChange object name✅ Yes (Oracle) / No (PG)
COMMENTAdd metadata comments to objects✅ Yes (Oracle) / No (PG)
🔀 ORACLE vs PostgreSQL

Key difference: In Oracle, ALL DDL statements trigger an implicit COMMIT — any pending DML (INSERT/UPDATE) is automatically committed before the DDL executes. In PostgreSQL, DDL is transactional — you CAN rollback a CREATE TABLE inside a transaction block. This is a PostgreSQL superpower.

3.2 Data Types — Choosing the Right Container

CategoryOraclePostgreSQLWhen to Use
IntegerNUMBER(10)INTEGER / BIGINTIDs, counts, quantities — no decimals
DecimalNUMBER(10,2)NUMERIC(10,2) / DECIMALMoney (₹), percentages, GPA — exact precision
FloatBINARY_FLOATREAL / DOUBLE PRECISIONScientific calculations — approximate, faster
Short StringVARCHAR2(100)VARCHAR(100)Names, emails, codes — variable length
Fixed StringCHAR(12)CHAR(12)Aadhaar (12 digits), PAN (10 chars) — fixed width
Long TextCLOBTEXTDescriptions, notes, medical history — unlimited length
DateDATE (has time!)DATE (date only)Appointment date, DOB. ⚠️ Oracle DATE includes time!
TimestampTIMESTAMPTIMESTAMPExact moment — login time, transaction time
BooleanNUMBER(1) ⚠️BOOLEANTrue/false flags. Oracle has NO native BOOLEAN in SQL!
BinaryBLOBBYTEAImages, PDFs, biometric data
JSONJSON (21c+)JSONBAPI responses, flexible attributes. JSONB is indexed in PG.

"Use FLOAT for storing money." Never! FLOAT/REAL are approximate types — 0.1 + 0.2 might equal 0.30000000000000004. For financial data (₹ amounts, account balances), ALWAYS use NUMBER(p,s) (Oracle) or NUMERIC(p,s) (PostgreSQL). One floating-point rounding error on a ₹10 crore transaction = ₹1000+ discrepancy.

3.3 CREATE TABLE — Building Tables

SQL — Oracle & PostgreSQL
-- ═══════════════════════════════════════
-- Example 1: Hospital Departments Table
-- Business Context: Master table for hospital departments
-- Industry: Every hospital HIS (AIIMS, Apollo, Fortis)
-- ═══════════════════════════════════════

CREATE TABLE departments (
    dept_id     NUMBER(5)      PRIMARY KEY,
    dept_name   VARCHAR2(50)   NOT NULL UNIQUE,
    building    VARCHAR2(30)   DEFAULT 'Main Block',
    floor_no    NUMBER(2)      CHECK (floor_no BETWEEN 0 AND 15),
    is_active   NUMBER(1)      DEFAULT 1 CHECK (is_active IN (0, 1))
);

-- PostgreSQL version differences:
-- NUMBER(5) → INTEGER
-- VARCHAR2   → VARCHAR
-- NUMBER(1)  → BOOLEAN DEFAULT TRUE
SQL
-- ═══════════════════════════════════════
-- Example 2: Doctors Table (with FK)
-- Business Context: Doctor profiles linked to departments
-- ═══════════════════════════════════════

CREATE TABLE doctors (
    doctor_id       NUMBER(10)    PRIMARY KEY,
    doctor_name     VARCHAR2(100) NOT NULL,
    specialization  VARCHAR2(50)  NOT NULL,
    phone           VARCHAR2(10)  UNIQUE,
    email           VARCHAR2(100) UNIQUE,
    dept_id         NUMBER(5)     NOT NULL,
    salary          NUMBER(10,2)  CHECK (salary > 0),
    joining_date    DATE           DEFAULT SYSDATE,
    CONSTRAINT fk_doc_dept FOREIGN KEY (dept_id) 
        REFERENCES departments(dept_id)
        ON DELETE SET NULL
);
SQL
-- ═══════════════════════════════════════
-- Example 3: Patients Table
-- Business Context: Patient master with Aadhaar linkage
-- ═══════════════════════════════════════

CREATE TABLE patients (
    patient_id    NUMBER(10)    GENERATED ALWAYS AS IDENTITY PRIMARY KEY,
    patient_name  VARCHAR2(100) NOT NULL,
    aadhaar_no    CHAR(12)      UNIQUE,
    phone         VARCHAR2(10)  NOT NULL,
    dob           DATE,
    gender        CHAR(1)       CHECK (gender IN ('M', 'F', 'O')),
    blood_group   VARCHAR2(5),
    address       VARCHAR2(300),
    reg_date      DATE           DEFAULT SYSDATE
);

-- INSERT sample data
INSERT INTO departments VALUES (1, 'Cardiology', 'Main Block', 3, 1);
INSERT INTO departments VALUES (2, 'Orthopedics', 'Main Block', 2, 1);
INSERT INTO departments VALUES (3, 'Neurology', 'East Wing', 4, 1);
INSERT INTO departments VALUES (4, 'Pediatrics', 'West Wing', 1, 1);
INSERT INTO departments VALUES (5, 'General Medicine', 'Main Block', 1, 1);

INSERT INTO doctors VALUES (101, 'Dr. Anil Sharma', 'Cardiologist', '9876543210', 'anil@hospital.com', 1, 250000, DATE '2018-06-15');
INSERT INTO doctors VALUES (102, 'Dr. Priya Patel', 'Orthopedic Surgeon', '9876543211', 'priya@hospital.com', 2, 220000, DATE '2019-03-01');
INSERT INTO doctors VALUES (103, 'Dr. Rajesh Kumar', 'Neurologist', '9876543212', 'rajesh@hospital.com', 3, 280000, DATE '2017-08-20');
INSERT INTO doctors VALUES (104, 'Dr. Sneha Reddy', 'Pediatrician', '9876543213', 'sneha@hospital.com', 4, 180000, DATE '2021-01-10');
INSERT INTO doctors VALUES (105, 'Dr. Amit Gupta', 'General Physician', '9876543214', 'amit@hospital.com', 5, 160000, DATE '2022-07-01');

COMMIT;

3.4 Integrity Constraints — The Guard Rails

📌 Constraints — Rules the DBMS Enforces Automatically

📌 WHAT THEY ARE

Constraints are rules defined on columns or tables that the DBMS enforces on every INSERT, UPDATE, and DELETE. If a DML operation violates a constraint, the DBMS rejects the entire operation. No bad data enters the database — ever.

ConstraintWhat It EnforcesNULL Allowed?How Many Per Table?
NOT NULLColumn must have a value❌ NoAny column
UNIQUENo duplicate values✅ One NULL (Oracle: multiple)Multiple
PRIMARY KEYNOT NULL + UNIQUE❌ NoExactly 1
FOREIGN KEYValue must exist in referenced table✅ Yes (unless NOT NULL)Multiple
CHECKCustom boolean condition✅ (NULL passes CHECK)Multiple
DEFAULTAuto-fills value when none providedAny column
Foreign Key — ON DELETE Actions
SQL
-- ON DELETE CASCADE: Delete parent → auto-delete all children
CONSTRAINT fk_appt_patient FOREIGN KEY (patient_id)
    REFERENCES patients(patient_id)
    ON DELETE CASCADE;  -- Delete patient → all their appointments deleted

-- ON DELETE SET NULL: Delete parent → set FK to NULL in children
CONSTRAINT fk_doc_dept FOREIGN KEY (dept_id)
    REFERENCES departments(dept_id)
    ON DELETE SET NULL;  -- Delete dept → doctor.dept_id becomes NULL

-- ON DELETE RESTRICT (default): Refuse to delete parent if children exist
-- ORA-02292: integrity constraint violated - child record found
Industry constraint naming convention: Always name your constraints explicitly. Format: pk_tablename, fk_child_parent, chk_tablename_column, uq_tablename_column. At Infosys and TCS projects, unnamed constraints in code reviews are sent back for correction — debugging SYS_C00321456 vs chk_salary_positive is the difference between 5 minutes and 2 hours.

3.5 ALTER TABLE, DROP, TRUNCATE

SQL — ALTER TABLE
-- ═══════════════════════════════════════
-- Example 4: Modifying table structure
-- Business Context: Hospital adds new fields as requirements evolve
-- ═══════════════════════════════════════

-- ADD column
ALTER TABLE patients ADD (
    emergency_contact  VARCHAR2(10),
    insurance_id       VARCHAR2(20)
);

-- MODIFY column (change data type or size)
ALTER TABLE patients MODIFY (phone VARCHAR2(15));  -- Oracle
-- ALTER TABLE patients ALTER COLUMN phone TYPE VARCHAR(15);  -- PostgreSQL

-- DROP column
ALTER TABLE patients DROP COLUMN insurance_id;

-- ADD constraint to existing table
ALTER TABLE doctors ADD CONSTRAINT chk_salary_min
    CHECK (salary >= 50000);

-- DROP constraint
ALTER TABLE doctors DROP CONSTRAINT chk_salary_min;

-- RENAME column
ALTER TABLE patients RENAME COLUMN phone TO mobile_no;

-- RENAME table
ALTER TABLE patients RENAME TO patient_records;  -- Oracle
-- ALTER TABLE patients RENAME TO patient_records;  -- Same in PG

DROP vs TRUNCATE vs DELETE

FeatureDELETE FROM tTRUNCATE TABLE tDROP TABLE t
RemovesSpecified rows (WHERE) or all rowsAll rows onlyEntire table + structure
Structure remains?✅ Yes✅ Yes (empty table)❌ No — table gone
WHERE clause?✅ Yes❌ No❌ No
Rollback? (Oracle)✅ Yes (if not committed)❌ No (auto-commit DDL)❌ No (auto-commit DDL)
Rollback? (PG)✅ Yes✅ Yes (inside txn!)✅ Yes (inside txn!)
Fires triggers?✅ Yes (per-row)❌ No❌ No
Resets identity/seq?❌ No✅ Yes (restarts from 1)
Speed (1M rows)🐢 Slow (logged per row)⚡ Very fast (minimal log)⚡ Instant
Reclaims space?❌ Not immediately✅ Yes✅ Yes

"I used TRUNCATE instead of DELETE and now I can't rollback!" (Oracle). TRUNCATE is DDL in Oracle — it auto-commits. You cannot undo it. Use DELETE with WHERE when you need the safety net of ROLLBACK. Only use TRUNCATE when you're 100% sure you want to empty the table permanently. PostgreSQL is more forgiving — TRUNCATE inside a transaction block CAN be rolled back.

Part B — Data Manipulation Language (DML)

3.6 INSERT, UPDATE, DELETE

SQL — INSERT
-- ═══════════════════════════════════════
-- Example 5: Inserting patient records
-- Business Context: New patient registration at OPD counter
-- ═══════════════════════════════════════

-- Single row INSERT (explicit columns — BEST PRACTICE)
INSERT INTO patients (patient_name, aadhaar_no, phone, dob, gender, blood_group, address)
VALUES ('Rahul Verma', '234567890123', '9811234567', 
        DATE '1990-05-15', 'M', 'B+', '45 MG Road, Pune');

-- Multiple rows (PostgreSQL syntax — also works in Oracle 23c+)
INSERT INTO patients (patient_name, phone, gender, blood_group) VALUES
    ('Priya Singh',    '9822345678', 'F', 'A+'),
    ('Amit Joshi',     '9833456789', 'M', 'O+'),
    ('Sneha Kulkarni', '9844567890', 'F', 'AB-'),
    ('Rajesh Gupta',   '9855678901', 'M', 'O-');

-- INSERT from SELECT (copy data from another table)
CREATE TABLE senior_doctors AS
    SELECT * FROM doctors WHERE salary > 200000;

-- Oracle: INSERT ALL (multi-table insert)
INSERT ALL
    INTO cardio_patients (patient_id, name) VALUES (id, name)
    INTO patient_audit (patient_id, action) VALUES (id, 'REGISTERED')
SELECT patient_id AS id, patient_name AS name FROM patients
WHERE reg_date = TRUNC(SYSDATE);
SQL — UPDATE
-- ═══════════════════════════════════════
-- Example 6: Updating records
-- Business Context: Salary revision, patient info updates
-- ═══════════════════════════════════════

-- Single column update
UPDATE doctors SET salary = 270000 WHERE doctor_id = 102;

-- Multiple column update
UPDATE patients 
SET phone = '9999888877', address = '12 Baner Road, Pune'
WHERE patient_id = 1;

-- UPDATE with subquery (give 10% raise to all Cardiology doctors)
UPDATE doctors 
SET salary = salary * 1.10
WHERE dept_id = (SELECT dept_id FROM departments WHERE dept_name = 'Cardiology');

-- Correlated UPDATE (set bonus based on department average)
UPDATE doctors d
SET salary = salary + (SELECT AVG(salary) * 0.05 FROM doctors 
                        WHERE dept_id = d.dept_id);
SQL — DELETE
-- ═══════════════════════════════════════
-- Example 7: Deleting records
-- Business Context: Removing cancelled appointments, old records
-- ═══════════════════════════════════════

-- DELETE specific rows
DELETE FROM appointments WHERE status = 'CANCELLED';

-- DELETE with subquery
DELETE FROM appointments 
WHERE doctor_id IN (
    SELECT doctor_id FROM doctors WHERE dept_id = 5
);

-- DELETE all rows (table structure remains)
DELETE FROM appointments;  -- Can be rolled back!

3.7 Transaction Control — COMMIT, ROLLBACK, SAVEPOINT

📌 Transactions — All or Nothing

📌 WHAT IT IS

A transaction is a unit of work that either completely succeeds (COMMIT) or completely fails (ROLLBACK). There is no middle state. This is the "Atomicity" in ACID.

🌍 REAL-WORLD ANALOGY

UPI payment: ₹500 from your PhonePe to shopkeeper. Two operations: (1) Debit ₹500 from your account, (2) Credit ₹500 to shopkeeper. If step 1 succeeds but step 2 fails, the DBMS rolls back step 1 too — money returns to your account. No money ever "disappears."

SQL — Transaction Control
-- ═══════════════════════════════════════
-- Example 8: Hospital billing transaction
-- Business Context: Generate bill + update patient status atomically
-- ═══════════════════════════════════════

-- Start transaction (implicit in Oracle; explicit BEGIN in PG)
INSERT INTO bills (bill_id, patient_id, amount, bill_date, status)
VALUES (5001, 1, 15000, SYSDATE, 'GENERATED');

SAVEPOINT after_bill;  -- Bookmark this point

UPDATE patients SET address = 'Updated Address' WHERE patient_id = 1;

-- Oops, wrong update! Rollback to savepoint (NOT everything)
ROLLBACK TO after_bill;  -- Bill INSERT preserved, address UPDATE undone

UPDATE appointments SET status = 'COMPLETED' WHERE appointment_id = 301;

COMMIT;  -- Both the bill INSERT and appointment UPDATE are now permanent
FeatureOraclePostgreSQL
Auto-commitOFF by default (you must COMMIT explicitly)ON by default (each statement auto-commits unless inside BEGIN...END)
DDL commits?✅ Yes — DDL triggers implicit COMMIT of pending DML❌ No — DDL is transactional, can be rolled back inside a transaction
Read ConsistencyMVCC — readers never block writers, writers never block readersMVCC — same as Oracle. Both use snapshot isolation.
Explicit txn startImplicit (first DML starts a txn)BEGIN; ... COMMIT;

3.8 Data Control Language — GRANT & REVOKE

SQL — DCL
-- ═══════════════════════════════════════
-- Example 9: Hospital access control
-- Business Context: Different staff have different data access
-- Industry: RBI mandates role-based DB access for banks
-- ═══════════════════════════════════════

-- Create roles
CREATE ROLE doctor_role;
CREATE ROLE billing_role;
CREATE ROLE admin_role;

-- Grant object privileges to roles
GRANT SELECT ON patients TO doctor_role;
GRANT SELECT, UPDATE ON appointments TO doctor_role;
GRANT SELECT, INSERT ON prescriptions TO doctor_role;

GRANT SELECT, INSERT, UPDATE ON bills TO billing_role;
GRANT SELECT ON patients (patient_id, patient_name) TO billing_role;
-- Billing staff sees name/ID but NOT Aadhaar, blood group, etc.

GRANT ALL PRIVILEGES ON patients TO admin_role;

-- Assign roles to users
GRANT doctor_role TO dr_anil;
GRANT billing_role TO cashier_ravi;

-- WITH GRANT OPTION (user can further grant to others)
GRANT SELECT ON patients TO dr_anil WITH GRANT OPTION;

-- Revoke privileges
REVOKE INSERT ON prescriptions FROM doctor_role;
System Privileges vs Object Privileges. System privileges: CREATE TABLE, CREATE USER, ALTER SESSION — control what users can DO. Object privileges: SELECT, INSERT, UPDATE, DELETE, EXECUTE — control what users can do on SPECIFIC objects. In production, never grant system privileges directly to application users.

Part C — SQL Basic Operations (SELECT Mastery)

3.9 SELECT Statement — The Most Important SQL Command

📌 SELECT Execution Order — Not What You'd Expect

📌 WHAT IT IS

The SELECT statement retrieves data. But SQL executes its clauses in a DIFFERENT order than how you write them:

Execution Order
WRITTEN ORDER:          EXECUTION ORDER:
1. SELECT               1. FROM       ← Which tables?
2. FROM                 2. WHERE      ← Filter rows
3. WHERE                3. GROUP BY   ← Group remaining rows
4. GROUP BY             4. HAVING     ← Filter groups
5. HAVING               5. SELECT     ← Choose columns
6. ORDER BY             6. ORDER BY   ← Sort output

-- WHY THIS MATTERS: You can't use a column alias in WHERE
-- because WHERE executes BEFORE SELECT!
-- ❌ WRONG: SELECT salary * 12 AS annual FROM doctors WHERE annual > 200000
-- ✅ RIGHT: SELECT salary * 12 AS annual FROM doctors WHERE salary * 12 > 200000
SQL — SELECT Operations
-- ═══════════════════════════════════════
-- Example 10: WHERE clause operators
-- ═══════════════════════════════════════

-- Comparison operators
SELECT doctor_name, salary FROM doctors WHERE salary > 200000;
SELECT * FROM doctors WHERE salary BETWEEN 150000 AND 250000;
SELECT * FROM doctors WHERE dept_id IN (1, 3, 5);

-- Pattern matching
SELECT * FROM doctors WHERE doctor_name LIKE 'Dr. A%';  -- starts with 'Dr. A'
SELECT * FROM patients WHERE phone LIKE '98%';        -- phone starts with 98
SELECT * FROM patients WHERE patient_name LIKE '_a%'; -- 2nd char is 'a'

-- NULL handling
SELECT * FROM patients WHERE aadhaar_no IS NULL;
SELECT * FROM patients WHERE aadhaar_no IS NOT NULL;
-- ⚠️ NEVER use = NULL or != NULL — always use IS NULL / IS NOT NULL

-- Sorting
SELECT doctor_name, salary FROM doctors 
ORDER BY salary DESC, doctor_name ASC;

-- NULLS FIRST / LAST (Oracle and PostgreSQL)
SELECT * FROM patients ORDER BY aadhaar_no NULLS LAST;

-- DISTINCT
SELECT DISTINCT dept_id FROM doctors;
SELECT DISTINCT specialization, dept_id FROM doctors;

-- FETCH FIRST (Oracle 12c+ / SQL Standard)
SELECT * FROM doctors ORDER BY salary DESC
FETCH FIRST 3 ROWS ONLY;

-- PostgreSQL: LIMIT
-- SELECT * FROM doctors ORDER BY salary DESC LIMIT 3;
Oracle ROWNUM for pagination: Before Oracle 12c, WHERE ROWNUM <= 3 was used. This is legacy syntax. Use FETCH FIRST n ROWS ONLY (Oracle 12c+) or LIMIT (PostgreSQL). ROWNUM has unintuitive behavior with ORDER BY — it filters BEFORE sorting unless wrapped in a subquery.

3.10 SQL Functions — String, Numeric, Date, Conversion

String Functions

SQL — String Functions
-- ═══════════════════════════════════════
-- Example 11: Text processing in queries
-- ═══════════════════════════════════════

SELECT 
    UPPER(doctor_name)                            AS name_upper,
    LOWER(email)                                  AS email_lower,
    INITCAP('RAHUL SHARMA')                       AS proper_case,   -- Rahul Sharma
    LENGTH(doctor_name)                            AS name_len,
    SUBSTR(phone, 1, 3)                            AS area_code,     -- First 3 digits
    INSTR(email, '@')                               AS at_position,   -- Position of @
    CONCAT('Dr. ', doctor_name)                     AS formal_name,
    REPLACE(phone, '987', '***')                   AS masked_phone,
    TRIM('  Rahul  ')                               AS trimmed,       -- 'Rahul'
    LPAD(dept_id, 5, '0')                          AS padded_id      -- '00001'
FROM doctors;

Numeric Functions

SQL — Numeric Functions
SELECT
    ROUND(15678.456, 2)    AS rounded,     -- 15678.46
    TRUNC(15678.456, 2)    AS truncated,   -- 15678.45
    MOD(17, 5)              AS remainder,   -- 2
    ABS(-250)               AS absolute,    -- 250
    CEIL(4.2)               AS ceiling,     -- 5
    FLOOR(4.8)              AS floor_val,   -- 4
    POWER(2, 10)            AS power_val,   -- 1024
    SQRT(144)               AS sqrt_val     -- 12
FROM dual;  -- Oracle uses DUAL; PostgreSQL: just SELECT without FROM

Date Functions

SQL — Date Functions
-- ═══════════════════════════════════════
-- Example 12: Date calculations in hospital context
-- ═══════════════════════════════════════

SELECT
    SYSDATE                                       AS today,          -- Oracle
    -- CURRENT_DATE                                  AS today,          -- PostgreSQL
    ADD_MONTHS(SYSDATE, 6)                        AS follow_up,      -- 6 months later
    MONTHS_BETWEEN(SYSDATE, DATE '2024-01-15')  AS months_since,
    NEXT_DAY(SYSDATE, 'MONDAY')                   AS next_monday,
    LAST_DAY(SYSDATE)                              AS month_end
FROM dual;

-- Calculate patient age
SELECT patient_name,
    TRUNC(MONTHS_BETWEEN(SYSDATE, dob) / 12) AS age
FROM patients
WHERE dob IS NOT NULL;

-- Format dates
SELECT 
    TO_CHAR(SYSDATE, 'DD-Mon-YYYY HH24:MI') AS formatted_date,
    TO_DATE('15-Jun-2025', 'DD-Mon-YYYY')   AS parsed_date,
    EXTRACT(YEAR FROM SYSDATE)                AS current_year,
    EXTRACT(MONTH FROM SYSDATE)               AS current_month
FROM dual;

Conversion & NULL Functions

SQL — Conversion & NULL Handling
-- ═══════════════════════════════════════
-- Example 13: Handling NULLs and type conversion
-- ═══════════════════════════════════════

SELECT
    -- NVL: replace NULL with default value (Oracle)
    NVL(aadhaar_no, 'NOT PROVIDED')          AS aadhaar_display,
    -- COALESCE: ANSI standard (works in both Oracle & PG)
    COALESCE(aadhaar_no, phone, 'NO ID')   AS primary_id,
    -- NVL2: if not null → value1; if null → value2 (Oracle)
    NVL2(aadhaar_no, 'Verified', 'Pending') AS kyc_status,
    -- NULLIF: returns NULL if two values are equal
    NULLIF(blood_group, 'Unknown')           AS cleaned_bg
FROM patients;

-- CASE expression (works everywhere — the SQL "if-else")
SELECT doctor_name, salary,
    CASE
        WHEN salary > 250000 THEN 'Senior'
        WHEN salary > 180000 THEN 'Mid-Level'
        ELSE 'Junior'
    END AS seniority_level
FROM doctors;

-- Oracle DECODE (legacy — use CASE instead)
SELECT DECODE(gender, 'M', 'Male', 'F', 'Female', 'O', 'Other') AS gender_text
FROM patients;
Oracle's DECODE function is Oracle-specific and less readable than CASE. CASE is ANSI-standard SQL that works in ALL databases. Industry standard: always use CASE. DECODE exists only for backward compatibility with legacy Oracle code.

3.11 Window Functions — The Interview Darling

Window functions are asked in EVERY backend/data engineer interview at Swiggy, Zomato, Razorpay, Flipkart, and all product companies. Master ROW_NUMBER, RANK, DENSE_RANK, LAG, and LEAD — they separate junior SQL writers from professionals.
SQL — Window Functions
-- ═══════════════════════════════════════
-- Example 14: Ranking and analytics without GROUP BY
-- Business Context: Doctor salary ranking per department
-- ═══════════════════════════════════════

SELECT 
    doctor_name, dept_id, salary,
    ROW_NUMBER() OVER (ORDER BY salary DESC)                  AS overall_rank,
    ROW_NUMBER() OVER (PARTITION BY dept_id ORDER BY salary DESC) AS dept_rank,
    RANK()       OVER (ORDER BY salary DESC)                  AS rank_with_gaps,
    DENSE_RANK() OVER (ORDER BY salary DESC)                  AS dense_rank_val
FROM doctors;

-- Output (sample):
-- doctor_name     | dept | salary  | overall | dept_rank | rank | dense
-- Dr. Rajesh Kumar| 3    | 280000  | 1       | 1         | 1    | 1
-- Dr. Anil Sharma | 1    | 250000  | 2       | 1         | 2    | 2
-- Dr. Priya Patel | 2    | 220000  | 3       | 1         | 3    | 3
-- Dr. Sneha Reddy | 4    | 180000  | 4       | 1         | 4    | 4
-- Dr. Amit Gupta  | 5    | 160000  | 5       | 1         | 5    | 5
FunctionTies?Example (salaries: 100, 100, 80)
ROW_NUMBER()No ties — unique number1, 2, 3 (arbitrary for ties)
RANK()Ties get same rank, gaps after1, 1, 3 (skip 2)
DENSE_RANK()Ties get same rank, no gaps1, 1, 2 (no skip)
SQL — LAG and LEAD
-- ═══════════════════════════════════════
-- Example 15: Compare with previous/next row
-- Business Context: Salary comparison with previous hire
-- ═══════════════════════════════════════

SELECT 
    doctor_name, joining_date, salary,
    LAG(salary) OVER (ORDER BY joining_date)  AS prev_salary,
    LEAD(salary) OVER (ORDER BY joining_date) AS next_salary,
    salary - LAG(salary) OVER (ORDER BY joining_date) AS salary_diff
FROM doctors;

-- LAG(col, n, default) — look N rows back (default 1)
-- LEAD(col, n, default) — look N rows forward (default 1)
Section 4

Industry Problems

🏢 Industry Problem #1 — Hospital OPD Schema with All Constraint Types

Organization: Multi-specialty hospital (Fortis-scale)

Scenario: Design and create the complete OPD schema with every constraint type. Test each constraint by intentionally violating it.

Your Task: Write DDL + sample DML. Then write 5 queries that would FAIL due to constraint violations.

💡 Complete Solution
SQL
-- Complete schema (abbreviated — referencing tables from Section 3)
CREATE TABLE appointments (
    appt_id      NUMBER(10)    GENERATED ALWAYS AS IDENTITY PRIMARY KEY,
    patient_id   NUMBER(10)    NOT NULL REFERENCES patients(patient_id),
    doctor_id    NUMBER(10)    NOT NULL REFERENCES doctors(doctor_id),
    appt_date    DATE           NOT NULL,
    appt_time    VARCHAR2(5),  -- HH:MM format
    status       VARCHAR2(15)  DEFAULT 'SCHEDULED'
                 CHECK (status IN ('SCHEDULED','COMPLETED','CANCELLED','NO_SHOW')),
    fee          NUMBER(8,2)   CHECK (fee >= 0),
    CONSTRAINT uq_appt_slot UNIQUE (doctor_id, appt_date, appt_time)
);

-- Constraint violation tests:
-- 1. PK violation:
--    INSERT INTO departments VALUES (1, 'Test', 'X', 1, 1);
--    → ORA-00001: unique constraint violated (dept_id=1 exists)

-- 2. FK violation:
--    INSERT INTO doctors VALUES (110,'Dr.X','GP','9999','x@h.com',99,100000,SYSDATE);
--    → ORA-02291: integrity constraint violated (dept_id=99 doesn't exist)

-- 3. CHECK violation:
--    UPDATE doctors SET salary = -5000 WHERE doctor_id = 101;
--    → ORA-02290: check constraint violated (salary > 0)

-- 4. NOT NULL violation:
--    INSERT INTO patients (patient_name, phone) VALUES (NULL, '9999');
--    → ORA-01400: cannot insert NULL into (patient_name NOT NULL)

-- 5. UNIQUE violation:
--    INSERT INTO doctors VALUES (110,'Dr.Y','GP','9876543210','y@h.com',1,100000,SYSDATE);
--    → ORA-00001: unique constraint violated (phone already used by Dr. Anil)

DBA Insight: In production, each constraint violation error is caught by the application layer (Java, Node.js) and translated into user-friendly messages. "Phone number already registered" is much better than "ORA-00001: SYS_C0012345." This is why constraint naming conventions matter.

🏢 Industry Problem #2 — Transaction Safety for Billing

Organization: Hospital billing department

Scenario: When generating a patient bill: (1) INSERT into bills, (2) UPDATE appointment status to COMPLETED, (3) UPDATE medicine stock (decrease quantity). All three MUST succeed together or all fail together.

Your Task: Write the transaction with SAVEPOINT usage, demonstrating partial rollback.

💡 Complete Solution
SQL — Transaction with SAVEPOINT
-- Step 1: Insert bill
INSERT INTO bills (bill_id, patient_id, amount, bill_date, status)
VALUES (6001, 1, 8500.00, SYSDATE, 'GENERATED');
SAVEPOINT sp_bill;

-- Step 2: Mark appointment completed
UPDATE appointments SET status = 'COMPLETED' WHERE appt_id = 301;
SAVEPOINT sp_appointment;

-- Step 3: Decrease medicine stock
UPDATE medicines SET stock_qty = stock_qty - 5 WHERE medicine_id = 201;

-- If stock went negative (business rule violation):
-- ROLLBACK TO sp_appointment;  ← Undo only the stock update
-- Handle manually: alert pharmacist, then retry

-- If everything is OK:
COMMIT;  -- All 3 changes permanently saved

-- If catastrophic failure:
-- ROLLBACK;  ← Undo EVERYTHING since last commit

🏢 Industry Problem #3 — Role-Based Access Control for Hospital Staff

Organization: Hospital IT department

Scenario: Implement access control: doctors see patient medical data (NOT billing). Billing staff see amounts (NOT medical history). Admin sees everything. Lab technicians can INSERT lab results but not modify patient records.

💡 Complete Solution
SQL — Role-Based Access
-- Create roles
CREATE ROLE role_doctor;
CREATE ROLE role_billing;
CREATE ROLE role_lab_tech;
CREATE ROLE role_admin;

-- Doctor: read patients, manage appointments & prescriptions
GRANT SELECT ON patients TO role_doctor;
GRANT SELECT, INSERT, UPDATE ON appointments TO role_doctor;
GRANT SELECT, INSERT ON prescriptions TO role_doctor;
-- NO access to bills table

-- Billing: access bills and limited patient info
GRANT SELECT, INSERT, UPDATE ON bills TO role_billing;
-- Create view for limited patient access
CREATE VIEW patient_billing_view AS
    SELECT patient_id, patient_name, phone FROM patients;
GRANT SELECT ON patient_billing_view TO role_billing;

-- Lab technician: INSERT results only
GRANT INSERT ON lab_results TO role_lab_tech;
GRANT SELECT ON patients TO role_lab_tech; -- Need to look up patient

-- Admin: full access
GRANT ALL PRIVILEGES ON patients TO role_admin;
GRANT ALL PRIVILEGES ON doctors TO role_admin;
GRANT ALL PRIVILEGES ON bills TO role_admin;

-- Assign to users
GRANT role_doctor TO dr_anil, dr_priya;
GRANT role_billing TO cashier_ravi;

DBA Insight: RBI mandates that banking databases must implement role-based access control with audit trails. The same principle applies to hospital data under India's proposed Digital Information Security in Healthcare Act (DISHA). Every SELECT on patient records should be logged.

Section 5

Lab Exercises

Exercise 1: Create Complete Hospital Schema

⏱ 45 minutes🟢 Beginner

Task: Create all 7 tables for the hospital system (departments, doctors, patients, appointments, prescriptions, medicines, bills). Each table must have: at least one PK, named constraints, appropriate data types, and at least one CHECK constraint. INSERT 5+ rows per table.

Deliverable: Complete DDL + DML script that runs without errors on Oracle SQL Developer or PostgreSQL (pgAdmin).

Hints: Create tables in dependency order (parent before child). Use GENERATED ALWAYS AS IDENTITY for auto-increment PKs.

Extension: Add a patient_history table that tracks all visits over time (one patient, many visits).

Exercise 2: Constraint Violation Lab

⏱ 30 minutes🟢 Beginner

Task: Using the hospital schema, write 6 INSERT/UPDATE statements that intentionally fail — one for each constraint type (PK, FK, UNIQUE, NOT NULL, CHECK, and FK DELETE restriction). Document the exact Oracle error code and message for each.

Deliverable: Table with columns: Constraint Type | SQL Statement | Error Code | Error Message | Fix

Hints: Try inserting a doctor with dept_id=99 (FK fail). Try setting salary to -1 (CHECK fail).

Exercise 3: SAVEPOINT Transaction Lab

⏱ 40 minutes🟡 Intermediate

Task: Simulate a hospital billing workflow using transactions:

  1. INSERT a new patient → SAVEPOINT sp1
  2. INSERT an appointment → SAVEPOINT sp2
  3. INSERT a prescription with wrong medicine_id (fails)
  4. ROLLBACK TO sp2 (undo prescription, keep appointment)
  5. INSERT correct prescription → COMMIT

Verify: After each step, run SELECT to confirm what data exists.

Deliverable: Transaction script with interleaved SELECT verification queries.

Exercise 4: SQL Functions Workout

⏱ 50 minutes🟡 Intermediate

Task: Write the following queries using the hospital schema:

  1. Display doctor names in UPPERCASE with their salary formatted as ₹X,XX,XXX (use TO_CHAR)
  2. Find patients who registered in the last 30 days (use date arithmetic)
  3. Calculate each doctor's age from DOB (use MONTHS_BETWEEN and TRUNC)
  4. Display patient Aadhaar with middle 4 digits masked: XXXX****XXXX (use SUBSTR, LPAD)
  5. Create a seniority classification using CASE: Senior (salary>250K), Mid (180K-250K), Junior (<180K)
  6. Use COALESCE to display "No Aadhaar" for patients with NULL aadhaar_no

Deliverable: 6 queries with sample output for each.

Exercise 5: Window Functions — Doctor Ranking Report

⏱ 45 minutes🔴 Advanced

Task: Write queries using window functions on the doctors table:

  1. Rank all doctors by salary (ROW_NUMBER, RANK, DENSE_RANK) — show all three
  2. Rank doctors within each department by salary (PARTITION BY dept_id)
  3. Find the highest-paid doctor in each department (use ROW_NUMBER + subquery, keep rank=1)
  4. For each doctor (ordered by joining_date), show previous doctor's salary using LAG
  5. Calculate running total of salaries ordered by joining_date using SUM() OVER

Deliverable: 5 queries with formatted output tables.

Extension: Use NTILE(4) to divide doctors into salary quartiles.

Section 6

MCQ Assessment Bank — 15 Questions

Hover to reveal answer and explanation.

Q1

Which SQL command is used to add a new column to an existing table?

  1. MODIFY TABLE
  2. ALTER TABLE ... ADD
  3. UPDATE TABLE ... ADD COLUMN
  4. INSERT COLUMN INTO
B. ALTER TABLE table_name ADD (column_name datatype) adds a new column. MODIFY TABLE doesn't exist. UPDATE is DML (changes data, not structure). INSERT adds rows, not columns. ALTER is the DDL command for structural changes.
🏢 TCS NQT and Oracle OCA (1Z0-071) frequently test DDL command syntax.
L1 — RememberDDL
Q2

Which constraint allows NULL values but does not allow duplicate non-NULL values?

  1. PRIMARY KEY
  2. NOT NULL
  3. UNIQUE
  4. FOREIGN KEY
C. UNIQUE constraint prevents duplicate values but allows NULLs (one NULL in most DBMS, multiple NULLs in PostgreSQL). PRIMARY KEY = NOT NULL + UNIQUE (no NULLs allowed). NOT NULL only prevents NULLs, doesn't prevent duplicates.
🏢 This distinction is tested in every SQL assessment — Infosys SP, Wipro NLTH, GATE CS.
L1 — RememberConstraints
Q3

What is the correct SQL data type to store Indian currency amounts (e.g., ₹12,345.50) in Oracle?

  1. FLOAT
  2. BINARY_FLOAT
  3. NUMBER(10,2)
  4. VARCHAR2(15)
C. NUMBER(10,2) stores exact decimal values with 2 decimal places — perfect for currency. FLOAT/BINARY_FLOAT are approximate types that can introduce rounding errors (₹0.01 discrepancies on large sums). VARCHAR2 stores text, not numbers — you can't do arithmetic on it. NEVER use floating-point for money.
🏢 RBI guidelines require exact arithmetic for financial calculations — FLOAT is prohibited for monetary data in banking systems.
L1 — RememberData Types
Q4

What is the difference between DELETE and TRUNCATE in Oracle?

  1. They are identical
  2. DELETE is DML (can be rolled back, fires triggers, can use WHERE); TRUNCATE is DDL (auto-commits, no triggers, removes ALL rows, faster, resets identity)
  3. TRUNCATE can use WHERE clause
  4. DELETE is faster than TRUNCATE
B. Key differences: DELETE is DML → logged per-row, can rollback, fires triggers, can filter with WHERE. TRUNCATE is DDL → minimal logging, auto-commits (Oracle), no triggers, always removes ALL rows, resets identity sequences. TRUNCATE is much faster for large tables (doesn't log each row deletion). In PostgreSQL, TRUNCATE CAN be rolled back inside a transaction.
🏢 This is the #1 most asked SQL interview question at Indian IT companies. Know every difference.
L2 — UnderstandDML vs DDL
Q5

In what order does SQL execute the clauses of a SELECT statement?

  1. SELECT → FROM → WHERE → GROUP BY → HAVING → ORDER BY
  2. FROM → WHERE → GROUP BY → HAVING → SELECT → ORDER BY
  3. FROM → SELECT → WHERE → GROUP BY → HAVING → ORDER BY
  4. SELECT → FROM → ORDER BY → WHERE → GROUP BY → HAVING
B. SQL execution order: FROM (identify tables) → WHERE (filter rows) → GROUP BY (group remaining) → HAVING (filter groups) → SELECT (choose/compute columns) → ORDER BY (sort). This is why you can't use a column alias defined in SELECT within the WHERE clause — WHERE executes before SELECT.
🏢 Understanding execution order is critical for debugging complex queries. Asked at Flipkart, Razorpay, and every data engineering interview.
L2 — UnderstandSELECT
Q6

Why does Oracle's DDL trigger an implicit COMMIT?

  1. It's a bug in Oracle
  2. Because DDL changes the data dictionary (system catalog), and Oracle's architecture requires these changes to be immediately permanent to maintain system integrity. This means any pending DML is also committed.
  3. PostgreSQL does the same thing
  4. Only DROP triggers implicit COMMIT
B. Oracle's data dictionary (metadata about tables, columns, constraints) is stored in system tables. DDL modifies these system tables, and Oracle requires data dictionary changes to be immediately durable. As a side effect, any uncommitted DML in the same session is also committed. PostgreSQL handles this differently — DDL is transactional in PG.
🏢 This Oracle-specific behavior catches many developers off guard. Know it for OCA certification.
L2 — UnderstandOracle
Q7

Write a query to find doctors whose salary is between ₹1,50,000 and ₹2,50,000 (inclusive) and who belong to departments 1, 3, or 5.

  1. SELECT * FROM doctors WHERE salary BETWEEN 150000 AND 250000 AND dept_id IN (1,3,5)
  2. SELECT * FROM doctors WHERE salary > 150000 AND salary < 250000 OR dept_id IN (1,3,5)
  3. SELECT * FROM doctors WHERE salary IN (150000, 250000) AND dept_id BETWEEN 1 AND 5
  4. SELECT * FROM doctors WHERE salary BETWEEN 150000 TO 250000
A. BETWEEN is inclusive (≥ AND ≤). IN checks membership in a set. Option B uses OR without parentheses — this changes precedence (AND binds tighter than OR), returning wrong results. Option C: IN (150000, 250000) matches ONLY those two exact values, not the range. Option D: BETWEEN uses AND not TO.
🏢 WHERE clause with BETWEEN + IN combination is a standard SQL test pattern in campus placements.
L3 — ApplyWHERE
Q8

Write a query to display each doctor's name, salary, and annual salary (salary × 12), with a custom label based on salary range using CASE.

  1. Cannot be done in one query
  2. SELECT doctor_name, salary, salary * 12 AS annual_salary, CASE WHEN salary > 250000 THEN 'Senior' WHEN salary > 180000 THEN 'Mid' ELSE 'Junior' END AS level FROM doctors
  3. SELECT doctor_name, IF salary > 250000 'Senior' FROM doctors
  4. SELECT doctor_name, salary, DECODE(salary, 'Senior') FROM doctors
B. CASE is the standard SQL conditional expression. It evaluates WHEN conditions in order and returns the first match. IF is not valid in SQL SELECT (it's for PL/SQL procedural code). DECODE is Oracle-specific and less flexible than CASE (can only do equality checks, not ranges). Always prefer CASE over DECODE for new code.
🏢 CASE expressions appear in almost every real-world report query. Practice them extensively.
L3 — ApplyCASE
Q9

A developer creates a table and inserts 5 rows without committing. Then they execute ALTER TABLE t ADD (col NUMBER). What happens to the 5 inserted rows in Oracle?

  1. The rows are lost (rolled back)
  2. The rows are permanently saved because ALTER (DDL) triggers an implicit COMMIT in Oracle — the 5 rows and the new column are both committed
  3. Only the ALTER is committed, not the INSERTs
  4. Oracle throws an error
B. In Oracle, DDL triggers implicit COMMIT of any pending transactions in the session. The 5 uncommitted INSERTs are permanently saved, then the ALTER executes and is also committed. This is a critical gotcha — if you intended to rollback those rows, it's too late after any DDL. In PostgreSQL, this doesn't happen because DDL is transactional.
🏢 This scenario has caused production data issues at multiple Indian IT companies. Mandatory knowledge for Oracle DBAs.
L4 — AnalyzeTransactions
Q10

A query SELECT DISTINCT city FROM patients ORDER BY patient_name fails. Analyze why.

  1. DISTINCT and ORDER BY can't be used together
  2. When using DISTINCT, the ORDER BY column must appear in the SELECT list — because DISTINCT collapses rows, the DB can't sort by a column that's been removed from the result set
  3. ORDER BY must come before DISTINCT
  4. The table doesn't have a city column
B. With DISTINCT, multiple rows might collapse into one. If two patients from "Pune" have different names ("Amit" and "Priya"), which name should Pune be sorted by? It's ambiguous, so the DBMS rejects it. Fix: SELECT DISTINCT city FROM patients ORDER BY city. The ORDER BY column must be in the SELECT list when DISTINCT is used.
🏢 This is a debugging scenario that data analysts encounter weekly. Understanding SQL execution order helps diagnose these errors instantly.
L4 — AnalyzeSELECT
Q11

What is the difference between ROW_NUMBER(), RANK(), and DENSE_RANK() when there are ties?

  1. They all produce the same result
  2. ROW_NUMBER: unique sequential (1,2,3 — no ties). RANK: same rank for ties, then gaps (1,1,3). DENSE_RANK: same rank for ties, no gaps (1,1,2)
  3. ROW_NUMBER handles ties, RANK doesn't
  4. They can only be used with GROUP BY
B. For salaries 100K, 100K, 80K: ROW_NUMBER → 1,2,3 (arbitrary order for ties). RANK → 1,1,3 (skip rank 2). DENSE_RANK → 1,1,2 (no skip). Use ROW_NUMBER when you need exactly one row per rank (e.g., "top 1 per department"). Use DENSE_RANK when ranking for leaderboards (no gaps looks better). Window functions use OVER(), NOT GROUP BY.
🏢 Window functions are asked in 90% of data engineering interviews at Indian product companies (Swiggy, Flipkart, Razorpay).
L4 — AnalyzeWindow
Q12

A hospital developer proposes storing patient allergies as a comma-separated string: allergies VARCHAR2(500) with values like "Penicillin,Aspirin,Dust". Evaluate this approach.

  1. Good — saves space
  2. Poor design — violates 1NF (atomicity). Searching for patients allergic to "Aspirin" requires LIKE '%Aspirin%' which is slow, can't use indexes, and may match "AspirinExtra". Create a separate patient_allergies(patient_id, allergy) table instead
  3. This is industry standard
  4. It depends on the DBMS
B. Comma-separated values: (1) violate 1NF (not atomic), (2) can't be indexed efficiently, (3) LIKE searches are O(n) and error-prone, (4) can't enforce referential integrity (no FK to allergy master table), (5) counting/grouping is impossible without string parsing. Correct design: patient_allergies(patient_id FK, allergy_code FK, severity) with FK to an allergies master table.
🏢 This is one of the most common design mistakes in college projects and is called out in every code review at professional companies.
L5 — EvaluateDesign
Q13

A startup uses Oracle for their fintech app. The DBA proposes using TRUNCATE instead of DELETE for monthly data cleanup of a 50-million-row transaction_logs table. The cleanup should be reversible in case of mistakes. Evaluate this proposal.

  1. Good — TRUNCATE is faster
  2. Risky — TRUNCATE in Oracle cannot be rolled back (auto-commit DDL). For reversible cleanup, either: (a) Use DELETE with COMMIT after verification, or (b) First backup using CREATE TABLE backup AS SELECT, then TRUNCATE, or (c) Use partitioning — DROP PARTITION for old months (instant, can be recovered from backup)
  3. TRUNCATE is always better than DELETE
  4. Use DROP TABLE instead
B. TRUNCATE is indeed 100× faster for large tables, but it's irreversible in Oracle. For a fintech app handling financial data, irreversible operations are extremely risky. Best approach: partition the table by month, then DROP old partitions — fast as TRUNCATE but recoverable from the standard backup. This is how Razorpay and PhonePe handle log cleanup.
🏢 This is a real DBA decision at every fintech. Know the trade-offs for interviews and certification exams.
L5 — EvaluateDBA
Q14

Design the complete DDL for a medicine inventory system: medicines (with expiry tracking), suppliers, purchase_orders, and stock_movements (in/out). Include all constraint types.

  1. Single table with all data
  2. medicines(med_id PK, name NOT NULL, category, unit_price CHECK>0, stock_qty CHECK>=0, expiry_date, supplier_id FK). suppliers(supplier_id PK, name NOT NULL UNIQUE, gst_no UNIQUE, phone). purchase_orders(po_id PK, supplier_id FK, order_date DEFAULT SYSDATE, total CHECK>0, status CHECK IN('PENDING','RECEIVED','CANCELLED')). stock_movements(movement_id PK, med_id FK, type CHECK IN('IN','OUT'), qty CHECK>0, movement_date, reference_id)
  3. Store everything in JSON
  4. Use spreadsheets instead
B. Well-designed schema: PK on every table, FK for referential integrity, CHECK for business rules (price>0, qty>=0, valid status values), NOT NULL for mandatory fields, UNIQUE for natural identifiers (GST number), DEFAULT for auto-populated dates. stock_movements tracks both incoming (purchase) and outgoing (prescription) with type column — this is the standard inventory pattern.
🏢 Pharmacy inventory is a common project assignment at TCS and Infosys. This schema pattern applies to any inventory system.
L6 — CreateSchema Design
Q15

Design a complete GRANT/REVOKE access control plan for a bank database with 4 roles: teller (INSERT deposits/withdrawals), loan_officer (SELECT customer, INSERT/UPDATE loans), auditor (SELECT ALL tables, no modifications), and DBA (full access). Include role creation and assignment SQL.

  1. Give everyone full access
  2. CREATE ROLE teller/loan_officer/auditor/dba_role. GRANT INSERT ON transactions TO teller. GRANT SELECT ON customers, INSERT,UPDATE ON loans TO loan_officer. GRANT SELECT ON customers, accounts, transactions, loans TO auditor (no INSERT/UPDATE/DELETE). GRANT ALL PRIVILEGES ON ALL tables TO dba_role. Assign: GRANT teller TO user1; etc.
  3. Use application-level security only, no DB roles
  4. Let the network firewall handle security
B. Defense-in-depth: DB-level roles enforce least privilege at the data layer. Tellers can't see loan details, auditors can't modify data, loan officers can't see raw transactions. This is NOT replaceable by application-level or network security alone — if an application is compromised, DB roles are the last line of defense. RBI explicitly mandates database-level access control for banking systems.
🏢 This is a real security architecture question in DBA and backend developer interviews at Indian banks and fintechs.
L6 — CreateDCL
Section 7

Chapter Summary

RELATIONAL QUERY LANGUAGE — DDL & DML │ ├── DDL (Data Definition Language) — Structure │ ├── CREATE TABLE: define columns, types, constraints │ ├── ALTER TABLE: ADD/MODIFY/DROP column, ADD/DROP constraint │ ├── DROP TABLE: destroy table entirely │ ├── TRUNCATE: remove all rows, keep structure │ ├── RENAME: change object name │ └── ⚠️ Oracle: DDL auto-commits! PG: DDL is transactional │ ├── DATA TYPES │ ├── Numeric: NUMBER/INTEGER, DECIMAL (money!), FLOAT (never for ₹) │ ├── String: VARCHAR2/VARCHAR, CHAR, CLOB/TEXT │ ├── Date: DATE (Oracle has time!), TIMESTAMP │ ├── Boolean: NUMBER(1) Oracle / BOOLEAN PG │ └── 🆕 JSON/JSONB (PostgreSQL) for flexible data │ ├── CONSTRAINTS — Guard Rails │ ├── NOT NULL, UNIQUE, PRIMARY KEY, FOREIGN KEY, CHECK, DEFAULT │ ├── FK actions: CASCADE, SET NULL, RESTRICT │ └── Always NAME your constraints (pk_xxx, fk_xxx, chk_xxx) │ ├── DML (Data Manipulation Language) — Data │ ├── INSERT: single row, multi-row, INSERT SELECT, INSERT ALL │ ├── UPDATE: with WHERE, with subquery, correlated update │ ├── DELETE: with WHERE, with subquery │ └── DELETE vs TRUNCATE: rollback, triggers, speed, identity │ ├── TCL (Transaction Control) — ACID │ ├── COMMIT: make changes permanent │ ├── ROLLBACK: undo all changes since last commit │ ├── SAVEPOINT: bookmark for partial rollback │ └── Oracle: auto-commit OFF | PG: auto-commit ON (use BEGIN) │ ├── DCL (Data Control) — Security │ ├── GRANT: give privileges (SELECT, INSERT, UPDATE, DELETE) │ ├── REVOKE: remove privileges │ ├── ROLE: group privileges, assign to users │ └── System vs Object privileges │ ├── SELECT — Query Mastery │ ├── Execution order: FROM→WHERE→GROUP BY→HAVING→SELECT→ORDER BY │ ├── WHERE: =, !=, BETWEEN, IN, LIKE, IS NULL, EXISTS │ ├── DISTINCT, FETCH FIRST / LIMIT, ORDER BY ASC/DESC │ └── NULLS FIRST/LAST │ ├── FUNCTIONS │ ├── String: UPPER, LOWER, SUBSTR, INSTR, CONCAT, TRIM, LPAD │ ├── Numeric: ROUND, TRUNC, MOD, ABS, CEIL, FLOOR │ ├── Date: SYSDATE, ADD_MONTHS, MONTHS_BETWEEN, TO_CHAR, EXTRACT │ ├── Conversion: TO_CHAR, TO_DATE, TO_NUMBER, CAST │ ├── NULL: NVL, NVL2, COALESCE, NULLIF │ └── Conditional: CASE (preferred), DECODE (legacy Oracle) │ └── 🆕 WINDOW FUNCTIONS ├── ROW_NUMBER(), RANK(), DENSE_RANK() ├── LAG(), LEAD() ├── SUM/AVG/COUNT() OVER (PARTITION BY ... ORDER BY ...) └── Every backend/data interview asks these

🎯 3 SQL Skills This Chapter Unlocks

  1. Schema Design — You can now CREATE a complete database from scratch with proper data types, constraints, and relationships. This is the first skill tested on any DBA or backend project.
  2. Transaction Safety — You understand COMMIT, ROLLBACK, SAVEPOINT, and why ACID matters. This knowledge prevents data corruption in production systems handling ₹ crores daily.
  3. Query Writing — SELECT with functions, CASE, NVL, and window functions. These are the exact skills tested in TCS NQT, Infosys SP, and every product company SQL round.

📋 SQL Quick Reference

DDL:  CREATE TABLE t (col TYPE CONSTRAINT);
      ALTER TABLE t ADD (col TYPE);
      ALTER TABLE t MODIFY (col NEW_TYPE);
      ALTER TABLE t DROP COLUMN col;
      DROP TABLE t;    TRUNCATE TABLE t;

DML:  INSERT INTO t (cols) VALUES (vals);
      UPDATE t SET col = val WHERE condition;
      DELETE FROM t WHERE condition;

TCL:  COMMIT;  ROLLBACK;  SAVEPOINT sp;  ROLLBACK TO sp;

DCL:  GRANT SELECT ON t TO user;
      REVOKE INSERT ON t FROM user;
      CREATE ROLE r;  GRANT r TO user;

KEY FUNCTIONS:
  String:  UPPER() LOWER() SUBSTR(s,start,len) LENGTH() TRIM() LPAD()
  Number:  ROUND(n,d) TRUNC(n,d) MOD(a,b) ABS() CEIL() FLOOR()
  Date:    SYSDATE  ADD_MONTHS(d,n)  MONTHS_BETWEEN(d1,d2)
           TO_CHAR(d,'DD-Mon-YYYY')  TO_DATE('str','fmt')
  NULL:    NVL(expr,default)  COALESCE(a,b,c)  NULLIF(a,b)
  Window:  ROW_NUMBER() OVER (PARTITION BY x ORDER BY y)
           RANK()  DENSE_RANK()  LAG(col,n)  LEAD(col,n)
Section 8

Interview & Career Preparation

Q1: What is the difference between DDL and DML?

Model Answer: DDL (Data Definition Language) defines database structure — CREATE, ALTER, DROP, TRUNCATE. DML (Data Manipulation Language) works with data — INSERT, UPDATE, DELETE, SELECT. Key difference: DDL auto-commits in Oracle (cannot rollback); DML can be rolled back until COMMIT. DDL changes the schema (blueprint); DML changes the instance (actual data).

Q2: What are integrity constraints? Name all types.

Model Answer: Integrity constraints are rules enforced by the DBMS to maintain data correctness: (1) NOT NULL — mandatory value, (2) UNIQUE — no duplicates, (3) PRIMARY KEY — NOT NULL + UNIQUE, one per table, (4) FOREIGN KEY — referential integrity (value must exist in parent table), (5) CHECK — custom business rule (e.g., salary > 0), (6) DEFAULT — auto-fills when no value provided. Industry practice: always name constraints explicitly.

Q3: Explain DELETE vs TRUNCATE vs DROP.

Model Answer: DELETE: DML, removes specific rows (WHERE), can rollback, fires triggers, logged per row — slow on large tables. TRUNCATE: DDL, removes ALL rows, auto-commits (Oracle), no triggers, minimal logging, resets identity — very fast. DROP: DDL, removes entire table and structure — table no longer exists. Use DELETE for selective removal, TRUNCATE for emptying, DROP for destroying.

Q4: What is a SAVEPOINT?

Model Answer: SAVEPOINT creates a named checkpoint within a transaction. ROLLBACK TO savepoint undoes changes after that point but preserves changes before it. Useful for multi-step transactions where partial failure shouldn't undo everything. Example: billing transaction — INSERT bill (SAVEPOINT), UPDATE stock — if stock update fails, rollback only the stock update, keep the bill.

Q5: What is the SELECT execution order?

Model Answer: FROM → WHERE → GROUP BY → HAVING → SELECT → ORDER BY. Written order is different from execution order. This explains why you can't use a column alias in WHERE (WHERE executes before SELECT where the alias is defined). You CAN use an alias in ORDER BY because it executes after SELECT. Understanding this order is critical for debugging queries.

Q6: When would you use NVL vs COALESCE?

Model Answer: NVL(expr, default) replaces NULL with a default — Oracle-specific, takes exactly 2 arguments. COALESCE(a, b, c, ...) returns the first non-NULL value from a list — ANSI-standard (works everywhere), takes multiple arguments. Use COALESCE for portability and when you have multiple fallback values. NVL is slightly faster in Oracle (no short-circuit evaluation overhead).

Q7: What is ON DELETE CASCADE?

Model Answer: When a parent row is deleted, ON DELETE CASCADE automatically deletes all child rows referencing it. Example: deleting a patient auto-deletes all their appointments. Without it, deleting a parent with existing children throws an FK violation error. Alternatives: ON DELETE SET NULL (sets FK to NULL), ON DELETE RESTRICT (default — blocks the delete). Use CASCADE for ownership relationships (patient → appointments), SET NULL for optional relationships.

Q8: Explain window functions with an example.

Model Answer: Window functions perform calculations across rows related to the current row without collapsing them (unlike GROUP BY). Example: ROW_NUMBER() OVER (PARTITION BY dept_id ORDER BY salary DESC) assigns a rank within each department. Three key functions: ROW_NUMBER (unique, no ties), RANK (ties with gaps), DENSE_RANK (ties without gaps). LAG/LEAD access previous/next rows. Used for: top-N per group, running totals, comparing with previous row.

Q9: What is GRANT and REVOKE?

Model Answer: GRANT gives privileges to users/roles: GRANT SELECT, INSERT ON patients TO doctor_role. REVOKE removes them: REVOKE INSERT ON patients FROM doctor_role. Two types: System privileges (CREATE TABLE, CREATE USER) and Object privileges (SELECT, INSERT on specific tables). WITH GRANT OPTION allows the grantee to further grant to others. Roles group related privileges for easier management.

Q10: How does Oracle differ from PostgreSQL? (Top 5 differences)

Model Answer: (1) DDL: Oracle auto-commits DDL; PostgreSQL DDL is transactional. (2) Data types: Oracle uses VARCHAR2, NUMBER; PG uses VARCHAR, INTEGER, BOOLEAN (Oracle has no SQL BOOLEAN). (3) Pagination: Oracle uses FETCH FIRST / ROWNUM; PG uses LIMIT. (4) Empty string: Oracle treats '' as NULL; PG distinguishes between '' and NULL. (5) Date: Oracle DATE includes time component; PG DATE is date-only (use TIMESTAMP for date+time). Both use MVCC for concurrency.

🎓 Certification Path

  • Oracle Database SQL Certified Associate (1Z0-071) — Covers everything in this chapter. The single most valuable SQL certification in Indian IT.
  • HackerRank SQL Certificate — Free. Proves basic-to-intermediate SQL. Add to LinkedIn immediately.
  • LeetCode SQL 50 — 50 curated problems covering SELECT, JOINs, subqueries, window functions. Industry interview preparation gold standard.