SQL — Table of Contents
Overview Why SQL Is Non-Negotiable SQL Command Categories Execution Order
Core Concepts (Q1–Q9) Basics, JOINs, Keys
Data Manipulation (Q10–Q14) DELETE, TRUNCATE, UNION
Aggregation (Q15–Q18) GROUP BY, COUNT, HAVING
Indexes & Performance (Q19–Q21) Indexes, B-tree vs Hash
Normalization (Q22–Q25) 1NF–BCNF, Anomalies
Transactions & ACID (Q26–Q28) ACID, Isolation Levels
Advanced SQL (Q29–Q32) Window Functions, Views, Triggers
Jump to: Overview Commands Exec Order Basics/JOINs DML Aggregation Indexes Normalization ACID Advanced
🗄 Part 2 · Phase 2 of 6 · April 2025

SQL Mastery
32 Real Interview Questions

Every SQL question you'll face in fresher IT interviews — fully answered with real queries, JOIN diagrams, normalization walkthroughs, ACID deep-dives, and window function examples. No fluff. All signal.

JOINs Normalization ACID Indexes Window Functions Subqueries Stored Procedures Views & Triggers Transactions GROUP BY / HAVING
✍ The Tech Intel ⏱ ~40 min read 📋 32 Questions · All Answered 💻 Every Query Written Out

SQL is the most universally tested technical skill at service companies like Infosys. Every project involves data. Data lives in databases. Infosys technical interviewers almost always ask at least 3–5 SQL questions — and those questions are predictable. This phase covers every single one of them.

SQL is also one of the few technical skills where knowing the why matters as much as the how. Why does WHERE filter before GROUP BY? Why does normalization reduce anomalies? Why does an index hurt writes? Understanding the reasoning behind the rules is what separates a candidate who memorized SQL from one who can actually use it in production.

🧭 Why SQL 📋 Commands 🔗 JOINs ✂ DML 📊 Aggregation 🔍 Indexes 📐 Normalization ⚛ ACID 🪟 Window Fns
Overview

🧭 Why SQL Is a Fresher Non-Negotiable

⚡ Why SQL Is Always Tested

SQL is the language of data — and data is the center of every enterprise application Infosys builds. A weak SQL answer signals: "I can't work with real data," which disqualifies you from 80% of IT service projects. Interviewers ask 3–5 SQL questions in every technical round. The good news: those questions are highly predictable. JOINs, normalization, GROUP BY/HAVING, indexes, and ACID appear in virtually every fresher SQL interview. Master these, and SQL becomes a guaranteed scoring section.

"Data is the new oil. SQL is the refinery. Every application that touches a database — which is almost every application — runs on SQL at its core."
— Joe Celko · SQL Pioneer · Author of "SQL for Smarties" · ANSI SQL Committee Member
Reference

📋 SQL Command Categories — Know All 5

CategoryFull NameCommandsPurposeAuto-Commit?
DDLData Definition LanguageCREATE, ALTER, DROP, TRUNCATE, RENAMEDefine and modify database schema structureYes — implicit commit
DMLData Manipulation LanguageINSERT, UPDATE, DELETEAdd, change, or remove data in tablesNo — requires explicit COMMIT
DQLData Query LanguageSELECTRetrieve data from tablesN/A — read-only
DCLData Control LanguageGRANT, REVOKEManage permissions and access rightsYes — implicit commit
TCLTransaction Control LanguageCOMMIT, ROLLBACK, SAVEPOINT, SET TRANSACTIONControl transaction boundariesN/A — manages commits
Critical Reference

⚙ SQL Execution Order — Written vs Executed

⚡ Why Execution Order Is a Top Interview Topic

Most SQL bugs — and many interview mistakes — come from confusing the order SQL is written with the order SQL is actually executed. You can't use a SELECT alias in a WHERE clause because SELECT hasn't run yet when WHERE is processed. You can't filter aggregates with WHERE — that's what HAVING is for. The execution order explains why all these rules exist.

SQL Logical Execution Order (different from written order)
Written: SELECT → FROM → WHERE → GROUP BY → HAVING → ORDER BY → LIMIT

Executed: ① FROM / JOIN (identify source tables, perform joins)
② WHERE (filter individual rows — before grouping)
③ GROUP BY (group filtered rows into buckets)
④ HAVING (filter groups — after aggregation)
⑤ SELECT (compute expressions, apply aliases)
⑥ DISTINCT (remove duplicate rows if requested)
⑦ ORDER BY (sort the final result — aliases available here)
⑧ LIMIT/OFFSET (return subset of rows)

Practical consequences:

Questions 1–9

🔗 Core Concepts, JOINs & Keys

⚡ Why JOINs Are Always the #1 SQL Question

JOINs are the heart of relational databases. The entire reason relational data is split into multiple tables — to reduce redundancy — means you need JOINs to bring it back together for queries. Every Infosys technical interview asks at least one JOIN question. The most common ask: "Explain the difference between INNER JOIN and LEFT JOIN," often followed by "Write a query to get all employees and their department names, including employees without a department."

The 6 JOIN Types — Visual Reference

INNER JOIN

Rows matching in both tables only

LEFT JOIN

All left rows + matching right (NULLs if no match)

RIGHT JOIN

All right rows + matching left (NULLs if no match)

FULL OUTER JOIN

All rows from both, NULLs where no match

CROSS JOIN

Every row × every row (Cartesian product)

SELF JOIN

Table joined to itself using aliases

SQL (Structured Query Language) is the standard language for managing and querying relational databases. SQL keywords are NOT case-sensitiveSELECT, select, and Select are identical. However, data values inside rows may be case-sensitive depending on the column's collation setting (e.g., WHERE name = 'Alice' vs 'alice' may behave differently based on the DB configuration).

Quick category recap:

  • DDL (Data Definition Language): structures the database — CREATE TABLE, ALTER TABLE, DROP TABLE. These auto-commit; you can't roll them back in most databases.
  • DML (Data Manipulation Language): changes data — INSERT, UPDATE, DELETE. These can be rolled back inside a transaction.
  • DQL (Data Query Language): retrieves data — just SELECT. Read-only.
💡 Common interview trap: "Is TRUNCATE DDL or DML?" — TRUNCATE is DDL (it auto-commits, resets identity, and doesn't log individual rows), even though it "removes data" like DML. Most interviewers accept either answer if you explain it clearly.

Tables: Employees (emp_id, name, dept_id) and Departments (dept_id, dept_name)

-- INNER JOIN: only employees who have a matching department
SELECT e.name, d.dept_name
FROM employees e
INNER JOIN departments d ON e.dept_id = d.dept_id;
-- Result: employees WITH a dept only. John (dept=NULL) excluded.

-- LEFT JOIN: all employees, even those without a department
SELECT e.name, d.dept_name
FROM employees e
LEFT JOIN departments d ON e.dept_id = d.dept_id;
-- Result: John (no dept) appears with dept_name = NULL

-- RIGHT JOIN: all departments, even those with no employees
SELECT e.name, d.dept_name
FROM employees e
RIGHT JOIN departments d ON e.dept_id = d.dept_id;
-- Result: "Research" dept (no employees yet) appears with name = NULL

-- FULL OUTER JOIN: everything from both sides
SELECT e.name, d.dept_name
FROM employees e
FULL OUTER JOIN departments d ON e.dept_id = d.dept_id;
-- MySQL doesn't support FULL OUTER JOIN directly — use UNION:
SELECT e.name, d.dept_name FROM employees e LEFT JOIN departments d ON e.dept_id = d.dept_id
UNION
SELECT e.name, d.dept_name FROM employees e RIGHT JOIN departments d ON e.dept_id = d.dept_id;

-- CROSS JOIN: every employee paired with every department
SELECT e.name, d.dept_name FROM employees e CROSS JOIN departments d;
-- 5 employees × 4 departments = 20 rows. Rarely useful. Be careful!

-- SELF JOIN: find employees and their manager (same table)
SELECT e.name AS employee, m.name AS manager
FROM employees e
LEFT JOIN employees m ON e.manager_id = m.emp_id;
⚠ The most common interview mistake: writing LEFT JOIN but expecting INNER JOIN behavior — forgetting that NULL rows are included. Always ask: "Do I want unmatched rows?"

WHERE filters individual rows — it runs before GROUP BY, so it operates on raw table data. You cannot use aggregate functions in WHERE because the aggregates haven't been computed yet.

HAVING filters groups — it runs after GROUP BY, so it operates on grouped/aggregated data. You must use HAVING for conditions on aggregate results.

-- WRONG: cannot use aggregate in WHERE
SELECT dept_id, COUNT(*) FROM employees
WHERE COUNT(*) > 3  -- ❌ Error: WHERE can't see aggregates
GROUP BY dept_id;

-- CORRECT: filter groups after aggregation using HAVING
SELECT dept_id, COUNT(*) AS emp_count
FROM employees
WHERE salary > 30000          -- filters rows BEFORE grouping
GROUP BY dept_id
HAVING COUNT(*) > 3;          -- filters GROUPS after aggregation
-- "Find departments (with avg salary > 30k) having more than 3 employees"
💡 Memory trick: WHERE = rows, HAVING = groups. Or: "WHERE comes before GROUP BY in execution, HAVING comes after." You can use both in the same query — WHERE filters first, then grouping, then HAVING filters groups.
PropertyPrimary KeyUnique KeyForeign Key
Uniqueness✅ Must be unique✅ Must be unique❌ Can repeat (references parent PK)
NULL allowed?❌ Never NULL✅ One NULL allowed (most DBs)✅ Yes (if reference is optional)
Count per tableExactly oneMultiple allowedMultiple allowed
Index created?Yes — clustered index (InnoDB)Yes — non-clusteredRecommended but not auto-created everywhere
PurposeRow identity — the "address" of a rowAlternate uniqueness (email, phone)Relational link — reference another table
CREATE TABLE employees (
    emp_id    INT PRIMARY KEY,       -- PK: unique + not null + clustered
    email     VARCHAR(100) UNIQUE,    -- Unique key: unique but nullable
    dept_id   INT,
    FOREIGN KEY (dept_id) REFERENCES departments(dept_id)
        ON DELETE SET NULL             -- if dept deleted, set to NULL
);
🧠 ON DELETE options for foreign keys: CASCADE (delete children when parent deleted), SET NULL (set FK to null), RESTRICT / NO ACTION (reject the parent deletion if children exist), SET DEFAULT (set FK to a default value).

A subquery (nested query) is a SELECT statement embedded inside another SQL statement. The inner query runs first; its result is used by the outer query.

-- Regular subquery: find employees earning above average salary
SELECT name, salary
FROM employees
WHERE salary > (SELECT AVG(salary) FROM employees);
-- Inner query runs ONCE, returns 58000. Outer query uses that value.

A correlated subquery references a column from the outer query — the inner query re-executes for each row of the outer query. This is powerful but often slow.

-- Correlated subquery: employees earning more than avg in THEIR OWN dept
SELECT e.name, e.salary, e.dept_id
FROM employees e
WHERE e.salary > (
    SELECT AVG(salary)
    FROM employees
    WHERE dept_id = e.dept_id  -- references outer query's row
);
-- Inner query runs ONCE PER OUTER ROW — can be slow on large tables
💡 Correlated subqueries are often replaceable with JOINs or window functions for much better performance. For the example above, use a window function: AVG(salary) OVER (PARTITION BY dept_id).

This is the single most commonly asked SQL query in fresher interviews. Know all three approaches:

-- Method 1: Nested MAX (classic, easy to explain)
SELECT MAX(salary) AS second_highest
FROM employees
WHERE salary < (SELECT MAX(salary) FROM employees);

-- Method 2: LIMIT + OFFSET (simple, MySQL/PostgreSQL)
SELECT DISTINCT salary
FROM employees
ORDER BY salary DESC
LIMIT 1 OFFSET 1;  -- skip the highest, take next one

-- Method 3: DENSE_RANK (best — handles Nth and ties gracefully)
SELECT salary
FROM (
    SELECT salary,
           DENSE_RANK() OVER (ORDER BY salary DESC) AS rnk
    FROM employees
) ranked
WHERE rnk = 2;  -- change 2 to N for Nth highest

-- Why DENSE_RANK is best: handles ties correctly
-- If salaries are [100, 100, 80, 60]:
-- Nested MAX gives 80 ✓ but LIMIT/OFFSET gives 100 (the tie) ✗
-- DENSE_RANK gives 80 (correctly 2nd unique salary) ✓
⚠ Always ask the interviewer: "Do you want the second highest unique salary value, or the second highest row?" The answer changes which method to use. DENSE_RANK handles unique values correctly; LIMIT/OFFSET gets the second row.

This classic question tests self-join knowledge. The trick: join the employees table to itself — once as "employee", once as "manager."

-- Table: employees(emp_id, name, salary, manager_id)
-- manager_id references emp_id in the same table

SELECT
    e.name   AS employee_name,
    e.salary AS employee_salary,
    m.name   AS manager_name,
    m.salary AS manager_salary
FROM employees e
JOIN employees m ON e.manager_id = m.emp_id
WHERE e.salary > m.salary;

-- We alias the same table twice: e = employee row, m = manager row
-- JOIN matches each employee to their manager's record
-- WHERE filters to only keep cases where employee earns more

Why LEFT JOIN instead of JOIN matters here: if you use JOIN (INNER), employees with no manager (CEO/top-level) are excluded — because they have no matching manager row. If the question asks to include all employees, use LEFT JOIN.

-- Step 1: Find which emails have duplicates
SELECT email, COUNT(*) AS cnt
FROM users
GROUP BY email
HAVING COUNT(*) > 1;

-- Step 2: See all duplicate rows
SELECT * FROM users
WHERE email IN (
    SELECT email FROM users
    GROUP BY email HAVING COUNT(*) > 1
);

-- Step 3: Delete duplicates, keep the one with smallest id (MySQL)
DELETE u1 FROM users u1
INNER JOIN users u2
    ON u1.email = u2.email  -- same email
    AND u1.id > u2.id;      -- u1 has higher id → delete u1, keep u2

-- Alternative: PostgreSQL / modern SQL using CTE + ROW_NUMBER
WITH ranked AS (
    SELECT id, ROW_NUMBER() OVER (PARTITION BY email ORDER BY id) AS rn
    FROM users
)
DELETE FROM users WHERE id IN (SELECT id FROM ranked WHERE rn > 1);
PropertyCHAR(n)VARCHAR(n)
StorageFixed — always stores exactly n bytesVariable — stores actual length + 1-2 bytes overhead
PaddingPads with spaces if shorter than nNo padding
PerformanceSlightly faster for fixed-length dataSlightly slower due to length calculation
Max size255 bytes (MySQL)65,535 bytes (MySQL)
Best forTruly fixed-length: country codes (US), status (Y/N), MD5 hashes, fixed-format codesEverything else: names, emails, descriptions, user input
-- Use CHAR for fixed-length, predictable data
country_code CHAR(2)         -- Always 2 chars: US, IN, UK
gender       CHAR(1)         -- Always 1 char: M, F
md5_hash     CHAR(32)        -- Always 32 hex chars

-- Use VARCHAR for variable-length data
first_name   VARCHAR(50)     -- Could be 2 chars or 50
email        VARCHAR(255)    -- Highly variable
description  VARCHAR(1000)   -- Could be empty or long
💡 Rule of thumb: use VARCHAR for everything unless you're certain the length is always the same. The minor performance benefit of CHAR rarely justifies its usage for variable data.
Questions 10–14

✂ Data Manipulation — DELETE, TRUNCATE, UNION & Subqueries

⚡ Why DML Questions Trip Up Freshers

The differences between DELETE, TRUNCATE, and DROP are among the most commonly asked SQL questions — and the most frequently answered incorrectly. Similarly, UNION vs UNION ALL and subquery types (correlated vs regular) are reliable interview topics. These aren't just theoretical — getting them wrong in production can cause data loss or severe performance problems.

PropertyDELETETRUNCATEDROP
What is removed?Specific rows (with WHERE) or all rowsALL rows in the tableEntire table including its structure
Table structure kept?✅ Yes✅ Yes — empty table remains❌ No — table is gone
WHERE clause?✅ Yes — conditional delete❌ No — always all rows❌ N/A
Transaction logLogs each row deletion (fully logged)Minimal logging (only page deallocation)No row logging
Rollback possible?✅ Yes — in a transaction⚠ Depends on DB (MySQL: yes; some: no)❌ No
Auto-increment reset?❌ Counter continues✅ Resets to 1N/A
Speed (large tables)Slow — row-by-row with loggingFast — drops and recreates internallyFastest
Triggers fired?✅ Yes❌ No❌ No
CategoryDMLDDLDDL
-- DELETE: remove specific rows (rollbackable)
DELETE FROM employees WHERE dept_id = 5;
ROLLBACK;  -- ✅ Works — changes are undone

-- TRUNCATE: remove all rows fast (structure remains)
TRUNCATE TABLE employees;
-- ✅ Table still exists, just empty. Auto-increment reset to 1.

-- DROP: destroy the table entirely
DROP TABLE employees;
-- ❌ Cannot rollback in most databases. Table is gone.
⚠ Critical production mistake: using TRUNCATE when you meant to DELETE a subset. TRUNCATE removes ALL rows and can't be filtered with WHERE. Always double-check your intent.
PropertyUNIONUNION ALL
DuplicatesRemoves duplicates (applies DISTINCT internally)Keeps all rows including duplicates
PerformanceSlower — must sort/hash to find duplicatesFaster — no deduplication overhead
Result size≤ sum of individual results= exact sum of individual results
When to useWhen you need unique combined resultsWhen duplicates are OK (or impossible), or when performance matters
-- UNION: combine active and inactive customers (remove duplicates)
SELECT name, email FROM active_customers
UNION
SELECT name, email FROM inactive_customers;
-- If same person is in both tables, they appear ONCE

-- UNION ALL: combine all sales records (duplicates fine — different dates)
SELECT sale_id, amount, 2023 AS year FROM sales_2023
UNION ALL
SELECT sale_id, amount, 2024 AS year FROM sales_2024;
-- All records kept. Faster because no deduplication needed.

-- Requirements: both queries must have SAME number of columns
-- and compatible data types in each position
💡 Default to UNION ALL unless you specifically need deduplication. It's always at least as fast as UNION, often significantly faster on large result sets. Don't pay the performance cost of deduplication if you don't need it.

This is a question interviewers ask to check whether you truly understand relational database design — not just syntax.

DimensionJOINUNION
Combining directionHorizontal — adds columnsVertical — adds rows
How it worksMerges related rows from multiple tables based on a key relationshipStacks rows from multiple result sets on top of each other
Result shapeMore columns than either inputSame columns, more rows
Requires key?Yes — needs a matching condition (ON clause)No — just needs matching column count and types
Real-world use"Get each employee's department name" — merging related data"Get all customers from 2023 and 2024" — combining similar data
🧠 Think of it visually: JOIN expands a table sideways (more columns). UNION stacks tables on top of each other (more rows). They solve completely different problems.

SQL injection is an attack where malicious user input is inserted into a SQL query string, causing the database to execute unintended commands — stealing data, deleting records, or bypassing authentication.

-- VULNERABLE: string concatenation — NEVER DO THIS
String query = "SELECT * FROM users WHERE username='" + username + "'";
-- If username = "admin' OR '1'='1"
-- Query becomes: SELECT * FROM users WHERE username='admin' OR '1'='1'
-- '1'='1' is always true → returns ALL users → attacker logs in as anyone

-- EVEN WORSE: DROP TABLE injection
-- username = "'; DROP TABLE users; --"
-- Query: SELECT * FROM users WHERE username=''; DROP TABLE users; --'
-- The users table is DELETED

Prevention (in order of importance):

  1. Parameterized queries / Prepared statements — always: the query structure is compiled separately from user input. Input can never change the query structure.
  2. Use an ORM — Hibernate, SQLAlchemy, Django ORM use parameterized queries by default.
  3. Input validation and sanitization — validate type, length, format. Reject unexpected characters.
  4. Principle of least privilege — DB user should only have SELECT on read-only operations, not DROP or ALTER.
  5. Web Application Firewall (WAF) — second line of defense (not a primary defense).
-- SAFE: Parameterized query (Java PreparedStatement)
PreparedStatement stmt = conn.prepareStatement(
    "SELECT * FROM users WHERE username = ?");
stmt.setString(1, username); // user input is treated as DATA, not SQL code
ResultSet rs = stmt.executeQuery();
⚠ OWASP consistently ranks SQL injection as a top-1 or top-3 web vulnerability year after year. Never interpolate user input directly into SQL strings — not even for "internal" applications.

This tests date function knowledge — which varies slightly by database.

-- MySQL
SELECT name, joining_date
FROM employees
WHERE joining_date >= CURDATE() - INTERVAL 30 DAY;

-- PostgreSQL
SELECT name, joining_date
FROM employees
WHERE joining_date >= CURRENT_DATE - INTERVAL '30 days';

-- SQL Server
SELECT name, joining_date
FROM employees
WHERE joining_date >= DATEADD(DAY, -30, GETDATE());

-- Oracle
SELECT name, joining_date
FROM employees
WHERE joining_date >= SYSDATE - 30;
💡 In an interview, use MySQL syntax unless told otherwise (most Infosys questions assume MySQL). Always mention: "This depends on the database — MySQL uses CURDATE() while PostgreSQL uses CURRENT_DATE."
Questions 15–18

📊 Aggregation, GROUP BY & COUNT Variants

⚡ Why Aggregation Is Tested in Every SQL Interview

Every business report, dashboard, and analytics feature relies on aggregation. "How many orders were placed per customer?" — GROUP BY. "Which departments have more than 5 employees?" — HAVING. "What's the total revenue by region?" — SUM + GROUP BY. These patterns appear in every real project. Interviewers test them because they're fundamental to data-driven work.

-- Table: orders(order_id, customer_id, product_id, amount)
-- Some rows: order_id=1, customer_id=NULL, product_id=101, amount=50
--            order_id=2, customer_id=5,    product_id=101, amount=80
--            order_id=3, customer_id=5,    product_id=202, amount=30

SELECT
    COUNT(*)                    AS total_rows,       -- 3 (counts ALL rows, including NULLs)
    COUNT(customer_id)          AS non_null_cust,    -- 2 (row 1 has NULL customer_id, excluded)
    COUNT(DISTINCT customer_id) AS unique_customers, -- 1 (customer_id=5 appears twice, counted once)
    COUNT(DISTINCT product_id)  AS unique_products   -- 2 (products 101 and 202)
FROM orders;

Key rules:

  • COUNT(*): counts every row — NULLs included. Use to count total records.
  • COUNT(col): counts only non-NULL values in that column. Use to count how many rows have a value.
  • COUNT(DISTINCT col): counts unique non-NULL values. Use to count distinct customers, products, etc.
⚠ Performance: COUNT(*) is optimized by most DBs (can use index metadata). COUNT(DISTINCT col) requires a full scan and sorting/hashing — much slower on large tables.
-- employees(emp_id, name, dept_id, salary, hire_date)

-- Aggregates per department
SELECT
    dept_id,
    COUNT(*)          AS employee_count,
    SUM(salary)       AS total_salary,
    AVG(salary)       AS avg_salary,
    MIN(salary)       AS min_salary,
    MAX(salary)       AS max_salary,
    MIN(hire_date)    AS earliest_hire,
    MAX(hire_date)    AS latest_hire
FROM employees
GROUP BY dept_id
ORDER BY avg_salary DESC;

-- Rule: every column in SELECT must either:
-- 1. Be in the GROUP BY clause, OR
-- 2. Be inside an aggregate function (COUNT, SUM, AVG, MIN, MAX)
-- Violating this is an error in strict SQL (though MySQL allows it with caveats)

Combining WHERE and HAVING:

-- Departments with > 3 employees, excluding interns (salary < 20000)
SELECT dept_id, COUNT(*) AS emp_count, AVG(salary) AS avg_sal
FROM employees
WHERE salary >= 20000           -- filter rows BEFORE grouping
GROUP BY dept_id
HAVING COUNT(*) > 3             -- filter GROUPS after aggregation
ORDER BY avg_sal DESC;
PropertyStored ProcedureFunction
ReturnsCan return 0 or more result sets or OUT parametersMust return exactly one value
Used in SELECT?❌ Cannot be used directly in SELECT/WHERE✅ Can be used in SELECT, WHERE, HAVING
DML allowed?✅ Can INSERT, UPDATE, DELETE⚠ Restricted (deterministic functions only in MySQL)
CallingCALL proc_name() or EXEC proc_nameSELECT func_name()
Transaction control✅ Can include COMMIT/ROLLBACK❌ Cannot manage transactions
Main purposeEncapsulate complex multi-step business logicCompute and return a single value
-- Stored Procedure: give a raise to all employees in a dept
DELIMITER //
CREATE PROCEDURE give_raise(IN dept INT, IN pct DECIMAL(5,2))
BEGIN
    UPDATE employees
    SET salary = salary * (1 + pct / 100)
    WHERE dept_id = dept;
END //
DELIMITER ;

CALL give_raise(3, 10.0);  -- 10% raise for dept 3

-- Function: calculate tax for a given salary
CREATE FUNCTION calc_tax(salary DECIMAL(10,2))
RETURNS DECIMAL(10,2) DETERMINISTIC
RETURN salary * 0.3;

SELECT name, salary, calc_tax(salary) AS tax FROM employees;

Benefits of stored procedures: performance (cached execution plan), reduced network traffic (single CALL vs many SQL statements), centralized business logic, reusability.

A view is a virtual table defined by a SELECT query. The data is not physically stored — it's recomputed from the underlying tables every time the view is queried.

-- Create a view for frequently queried employee-department data
CREATE VIEW emp_dept_view AS
SELECT e.emp_id, e.name, e.salary, d.dept_name
FROM employees e
JOIN departments d ON e.dept_id = d.dept_id;

-- Now query the view like a regular table
SELECT * FROM emp_dept_view WHERE dept_name = 'Engineering';

-- The database internally rewrites this as the full JOIN query

Benefits of views:

  • Security: expose only certain columns/rows to specific users without granting access to base tables
  • Simplicity: hide complex JOIN logic behind a simple table-like interface
  • Consistency: business logic defined once, used everywhere

A materialized view physically stores the query result — it's a pre-computed snapshot. Much faster for complex aggregations. Needs explicit refresh when underlying data changes.

-- PostgreSQL materialized view (pre-computes expensive aggregation)
CREATE MATERIALIZED VIEW monthly_sales_summary AS
SELECT DATE_TRUNC('month', order_date) AS month,
       SUM(amount) AS total_sales
FROM orders GROUP BY 1;

-- Refresh when data changes (manual or scheduled)
REFRESH MATERIALIZED VIEW monthly_sales_summary;
💡 Use regular views for simplicity and security. Use materialized views when a query takes seconds to run and the data doesn't change frequently — for example, monthly aggregations on a large sales table.
Questions 19–21

🔍 Indexes & Query Performance

⚡ Why Index Questions Appear in Every Senior-Ish Fresher Interview

Indexes are where theoretical database knowledge meets real-world performance engineering. Interviewers ask about indexes to gauge whether you understand the fundamental read/write trade-off. A candidate who says "just add an index on every column" reveals they don't understand databases. A candidate who explains B-tree structure, clustered vs non-clustered, and when NOT to index demonstrates production-level thinking.

A database index is a separate data structure (typically a B-tree) that the database maintains alongside a table to speed up data retrieval on indexed columns — similar to how a book index lets you jump to a page without reading the whole book.

-- Create an index on email column (frequently searched)
CREATE INDEX idx_employees_email ON employees(email);

-- Composite index: speeds up queries filtering by both columns together
CREATE INDEX idx_dept_salary ON employees(dept_id, salary);

-- Unique index: enforces uniqueness AND speeds up lookups
CREATE UNIQUE INDEX idx_unique_email ON employees(email);

Advantages:

  • Dramatically faster SELECT with WHERE, JOIN, ORDER BY, GROUP BY on indexed columns
  • Turns O(n) full table scan into O(log n) B-tree lookup
  • Unique indexes enforce data integrity at the database level

Disadvantages:

  • Slower writes: every INSERT/UPDATE/DELETE must also update all affected indexes — can be 2–5x slower for write-heavy tables
  • Extra disk space: each index is a copy of data in a different order — can be 10–50% of table size
  • Maintenance overhead: index fragmentation over time requires periodic maintenance (REBUILD/REORGANIZE)
⚠ "When should you NOT add an index?" — on small tables (full scan faster), on columns with very high write frequency, on low-cardinality columns (boolean, gender — only 2-3 distinct values, not selective enough), on columns never used in WHERE/JOIN/ORDER BY.
PropertyClustered IndexNon-Clustered Index
Data storageTable rows are physically sorted and stored in key orderSeparate B-tree with pointers to the actual row locations
Count per tableOnly ONE — rows can only be sorted one way physicallyMultiple allowed (up to 999 in SQL Server)
Default onPrimary key (in MySQL InnoDB)All other indexes
Range queriesVery fast — consecutive rows are physically adjacentSlower — may require many random disk seeks
Lookup costOne hop — data IS the index leafTwo hops — index leaf has a pointer to the data row

Visual analogy:

  • Clustered index: like a physical dictionary — words are stored in alphabetical order ON THE PAGE ITSELF. Finding "algorithm" means opening to the A section; the definition is right there.
  • Non-clustered index: like a book's index at the back — you look up "algorithm" in the index, which gives you a page number (pointer), then you flip to that page to read the content.
-- In MySQL InnoDB: Primary Key = Clustered Index
CREATE TABLE employees (
    emp_id INT PRIMARY KEY,  -- THIS is the clustered index
    name   VARCHAR(100),
    email  VARCHAR(100)
);
-- Rows are physically stored sorted by emp_id
-- When you SELECT * FROM employees WHERE emp_id = 42:
-- One B-tree traversal → directly at the row data

CREATE INDEX idx_email ON employees(email); -- non-clustered
-- SELECT * WHERE email = 'x@y.com':
-- 1. Traverse email index B-tree → find pointer (emp_id=42)
-- 2. Traverse PRIMARY KEY B-tree → find actual row data
-- This second lookup is called a "bookmark lookup" or "index scan back"
🧠 Choosing the right clustering key matters enormously for performance. Use a monotonically increasing integer (like auto-increment id) as PK/clustered index — new rows always go to the end, no page splits. Never use UUIDs as clustered indexes — random ordering causes constant page splits and fragmentation.
PropertyB-tree IndexHash Index
StructureBalanced tree — data sorted in leaf nodesHash table — data in hash buckets
Equality lookupO(log n)O(1) average
Range queries✅ Excellent — data is ordered (BETWEEN, <, >, LIKE 'A%')❌ Impossible — hash destroys ordering
ORDER BY acceleration✅ Yes — already sorted❌ No
Prefix matching (LIKE 'A%')✅ Yes❌ No
Memory usageHigher — stores all key values in tree nodesLower per entry — just hash and pointer
Default in MySQL InnoDB✅ Yes — all indexes are B-tree❌ Not supported in InnoDB
When to useAlmost always — the safe default choiceEquality-only lookups where range is never needed (session lookup by ID, cache lookups)
💡 In practice: always use B-tree (the default in MySQL, PostgreSQL, Oracle). Hash indexes only make sense in very specific in-memory use cases (like MEMORY engine in MySQL, or PostgreSQL's hash indexes for specific equality-only workloads). If you're not sure, use B-tree.
Questions 22–25

📐 Normalization — 1NF through BCNF

⚡ Why Normalization Is a Core Design Skill

Normalization is the art of designing database schemas that don't lie to you — schemas where data is stored in exactly one place, with no hidden contradictions. A poorly normalized schema leads to update anomalies (updating one fact requires touching 50 rows), insertion anomalies (can't add data without unrelated data), and deletion anomalies (losing one fact deletes another). Every Infosys database project uses normalized schemas — knowing why and how to normalize is non-negotiable.

Normalization Progression — Visual Reference

1NF

Atomic values. Each cell = one value. No repeating groups. Each row uniquely identifiable.

2NF

1NF + No partial dependency. Every non-key attribute depends on the WHOLE composite primary key.

3NF

2NF + No transitive dependency. Non-key attributes must not depend on other non-key attributes.

BCNF

Stricter 3NF. Every determinant must be a candidate key. Eliminates remaining anomalies.

Starting point — un-normalized order data:

OrderID | CustomerName | CustomerCity | Products              | ProductPrices
--------|--------------|--------------|------------------------|--------------
1       | Alice        | Mumbai       | Laptop, Mouse          | 60000, 500
2       | Bob          | Delhi        | Keyboard               | 800

Step 1 — Apply 1NF: Make all values atomic (one value per cell). No multi-valued attributes.

OrderID | LineNum | CustomerName | CustomerCity | Product  | Price
--------|---------|--------------|--------------|----------|-------
1       | 1       | Alice        | Mumbai       | Laptop   | 60000
1       | 2       | Alice        | Mumbai       | Mouse    | 500
2       | 1       | Bob          | Delhi        | Keyboard | 800

-- PK = (OrderID, LineNum) — composite key
-- All cells are atomic ✅ — 1NF satisfied
-- Problem: CustomerName and CustomerCity only depend on OrderID (not on LineNum)
-- This is a PARTIAL DEPENDENCY → violates 2NF

Step 2 — Apply 2NF: Remove partial dependencies. Split into Orders and OrderItems tables.

-- Orders table: non-key attrs that depend ONLY on OrderID
Orders: (OrderID PK | CustomerName | CustomerCity)
1       | Alice        | Mumbai
2       | Bob          | Delhi

-- OrderItems table: non-key attrs that depend on (OrderID, LineNum)
OrderItems: (OrderID FK | LineNum | Product | Price)
1           | 1       | Laptop   | 60000
1           | 2       | Mouse    | 500
2           | 1       | Keyboard | 800

-- 2NF satisfied: every non-key attribute depends on the WHOLE key ✅
-- Problem: CustomerCity depends on CustomerName (not directly on OrderID)
-- If Alice moves, ALL her orders need updating → UPDATE ANOMALY

Step 3 — Apply 3NF: Remove transitive dependencies (CustomerCity depends on CustomerName, not OrderID directly).

-- Customers table: CustomerName determines CustomerCity
Customers: (CustomerID PK | CustomerName | CustomerCity)
1          | Alice        | Mumbai
2          | Bob          | Delhi

-- Orders table: reference Customer by ID
Orders: (OrderID PK | CustomerID FK)
1       | 1
2       | 2

-- 3NF satisfied: no transitive dependencies ✅
-- Each fact is stored in exactly one place

BCNF (Boyce-Codd Normal Form) is a stricter form of 3NF. The rule: for every functional dependency A → B, A must be a candidate key (or a superkey).

A table can be in 3NF but not BCNF if it has a functional dependency where the left-hand side is not a candidate key. This happens when there are overlapping candidate keys.

-- Classic BCNF violation example:
-- Table: CourseRegistration(Student, Course, Teacher)
-- Functional dependencies:
--   (Student, Course) → Teacher  [a student in a course has one teacher]
--   Teacher → Course              [each teacher teaches only one course]

-- Candidate keys: (Student, Course) and (Student, Teacher)
-- FD: Teacher → Course  violates BCNF because "Teacher" alone is not a candidate key
-- Teacher determines Course but is not a candidate key

-- BCNF fix: decompose into two tables:
TeacherCourse: (Teacher PK | Course)
             → Teacher → Course dependency now has Teacher as PK ✅

StudentTeacher: (Student | Teacher | FK references TeacherCourse)
-- Now both dependencies are preserved without BCNF violation
🧠 In practice: 3NF is usually sufficient for OLTP systems. BCNF is theoretically superior but decomposing into BCNF sometimes loses the ability to enforce certain constraints using only primary keys and foreign keys. Most textbooks and interviews only require explaining up to 3NF; BCNF is a bonus.

Anomalies are problems that arise from data redundancy in poorly designed (un-normalized) schemas. All three are prevented by proper normalization.

-- Un-normalized table (violates 2NF):
-- Orders(OrderID, CustomerName, CustomerCity, Product, Price)
OrderID | CustomerName | CustomerCity | Product  | Price
1       | Alice        | Mumbai       | Laptop   | 60000
2       | Alice        | Mumbai       | Mouse    | 500
3       | Bob          | Delhi        | Keyboard | 800

Insertion Anomaly: Cannot add a new customer until they place an order.

-- Want to register Charlie from Kolkata as a customer (no orders yet)
-- IMPOSSIBLE: OrderID is NOT NULL and there's no product to attach
-- We can't store Charlie's city until he buys something ← ANOMALY

Update Anomaly: Changing Alice's city requires updating EVERY row with Alice's name.

-- Alice moves from Mumbai to Pune
UPDATE Orders SET CustomerCity = 'Pune' WHERE CustomerName = 'Alice';
-- Must update rows 1 AND 2. If we miss row 2 → inconsistency
-- Two rows say Alice lives in different cities ← DATA CORRUPTION

Deletion Anomaly: Deleting an order can accidentally delete customer information.

-- Bob cancels his only order (OrderID=3)
DELETE FROM Orders WHERE OrderID = 3;
-- Bob's existence (name + city) is now GONE from the database
-- We've lost information about a customer because he had no orders ← ANOMALY
💡 All three anomalies are solved by normalization — specifically by storing each fact in exactly one place. Alice's city in a Customers table, updated once. Charlie can exist in Customers without any Orders. Bob persists in Customers even with no Orders.

Denormalization is the intentional introduction of redundancy into a database schema to improve read performance. You're reversing normalization — combining tables, duplicating columns, storing derived values — to reduce the number of JOINs needed for frequently executed queries.

When to denormalize:

  • Data warehouses / analytics (OLAP): reports run complex queries over billions of rows. The cost of JOINs is prohibitive. Star/snowflake schemas deliberately denormalize for read performance.
  • Read-heavy, write-light tables: if a table is read 10,000 times and written once a day, the redundancy cost is trivial versus the query speed gain.
  • Caching pre-computed aggregates: storing total_order_count directly on the customer row instead of counting orders on each request.

When NOT to denormalize:

  • OLTP systems where data changes frequently — redundancy causes update anomalies
  • When you haven't profiled to confirm JOINs are the bottleneck — premature optimization
🧠 Denormalization should always be a deliberate architectural decision, not an accident of poor design. If you're denormalizing an OLTP system, you need a clear answer for "how do we keep the redundant data consistent when the source changes?" The answer is usually: a trigger, an application-level update, or an eventual consistency model.
Questions 26–28

⚛ Transactions, ACID & Isolation Levels

⚡ Why ACID Is a Non-Negotiable Interview Topic

ACID properties are what make databases trustworthy for critical operations — banking transactions, booking systems, inventory management. If you're building software at Infosys for financial or enterprise clients, you'll need to understand exactly what guarantees the database provides and when they can fail. "What is ACID?" is asked in virtually every Infosys technical interview for database-related roles.

Scenario: transferring ₹1000 from Account A (balance: ₹5000) to Account B (balance: ₹2000)

START TRANSACTION;
UPDATE accounts SET balance = balance - 1000 WHERE id = 'A';
UPDATE accounts SET balance = balance + 1000 WHERE id = 'B';
COMMIT;

A — Atomicity: "All or nothing"
If the system crashes after the first UPDATE (deducted from A) but before the second UPDATE (not yet added to B), the transaction is automatically rolled back. Neither UPDATE persists. The ₹1000 doesn't vanish into thin air. Implemented via: undo logs (for rollback).

C — Consistency: "Valid state to valid state"
Before the transaction: A=5000, B=2000, total=7000. After: A=4000, B=3000, total=7000. The total always equals 7000 — money is conserved. The database enforces all constraints (NOT NULL, foreign keys, CHECK constraints) throughout. Implemented via: constraint checking.

I — Isolation: "As if transactions ran one at a time"
If another transaction reads Account A's balance concurrently during the transfer, it either sees the original 5000 (before transfer) or 4000 (after commit) — never an inconsistent intermediate state like 5000 deducted but B not yet credited. Implemented via: locking or MVCC.

D — Durability: "Committed data survives crashes"
Once COMMIT succeeds, the transfer is permanent. Even if the server loses power 1 millisecond after the COMMIT, the data is safe. Implemented via: redo logs (WAL — Write-Ahead Log), which persist changes to durable storage before confirming the commit.

-- Complete transaction pattern
START TRANSACTION;  -- or BEGIN / BEGIN TRANSACTION

UPDATE accounts SET balance = balance - 500 WHERE id = 1;
UPDATE accounts SET balance = balance + 500 WHERE id = 2;

-- Check if business rule satisfied (application layer)
-- IF error → ROLLBACK, otherwise → COMMIT

COMMIT;    -- make all changes permanent (durable)
-- or
ROLLBACK; -- undo ALL changes since START TRANSACTION

-- SAVEPOINT: partial rollback
START TRANSACTION;
INSERT INTO orders VALUES (101, 5, 'pending');
SAVEPOINT after_order;
INSERT INTO payments VALUES (201, 101, 500);
-- Payment fails
ROLLBACK TO after_order;   -- undo payment only, keep order
COMMIT;                    -- order committed, payment rolled back

Transaction states:

  • Active: transaction is executing
  • Partially Committed: last statement executed, changes not yet written to disk
  • Committed: changes durably written — cannot be rolled back
  • Failed: an error occurred during execution
  • Aborted: rolled back — database restored to pre-transaction state

Isolation levels control how much concurrent transactions can "see" each other's in-progress changes. Higher isolation = fewer anomalies but lower concurrency and throughput.

Three anomalies to know:

  • Dirty Read: reading uncommitted data from another transaction (data that might still be rolled back)
  • Non-Repeatable Read: reading the same row twice in a transaction and getting different values (another transaction committed a change between reads)
  • Phantom Read: running the same query twice and getting different rows (another transaction inserted/deleted rows that match your WHERE clause)
Isolation LevelDirty ReadNon-Repeatable ReadPhantom ReadPerformance
Read Uncommitted✅ Possible✅ Possible✅ PossibleFastest
Read Committed❌ Prevented✅ Possible✅ PossibleFast
Repeatable Read❌ Prevented❌ Prevented✅ PossibleModerate
Serializable❌ Prevented❌ Prevented❌ PreventedSlowest
-- Set isolation level for current session
SET SESSION TRANSACTION ISOLATION LEVEL READ COMMITTED;

-- Default isolation levels:
-- MySQL InnoDB: REPEATABLE READ (with snapshot isolation for reads)
-- PostgreSQL: READ COMMITTED
-- Oracle: READ COMMITTED
-- SQL Server: READ COMMITTED
💡 In practice: Read Committed is the right default for most OLTP systems — it prevents dirty reads while maintaining good concurrency. Use Serializable only for financial-critical operations (like inventory deduction) where phantom reads would cause real damage.
Questions 29–32

🪟 Advanced SQL — Window Functions, CTEs & Triggers

⚡ Why Window Functions Are a Modern SQL Must-Know

Window functions were added in SQL:2003 and are now available in all major databases. They're the single biggest productivity boost in SQL for analytics. Before window functions: complex self-joins or correlated subqueries to compute running totals, rankings, or comparisons to group averages. With window functions: one clean query. Infosys interviews increasingly test these for DSE and SP roles. Know RANK, DENSE_RANK, ROW_NUMBER, LAG, LEAD, and SUM/AVG OVER.

A window function performs calculations across a set of related rows (a "window") without collapsing them into a single output row — unlike GROUP BY which collapses. The window is defined by the OVER() clause.

-- employees(emp_id, dept_id, name, salary)

-- Running total of salaries ordered by emp_id
SELECT
    emp_id, name, salary,
    SUM(salary) OVER (ORDER BY emp_id) AS running_total
FROM employees;

-- Result:
-- emp_id | name  | salary | running_total
-- 1      | Alice | 60000  | 60000
-- 2      | Bob   | 45000  | 105000
-- 3      | Carol | 70000  | 175000   ← each row keeps its data + cumulative sum

-- Salary vs department average (PARTITION BY)
SELECT
    emp_id, name, dept_id, salary,
    AVG(salary) OVER (PARTITION BY dept_id) AS dept_avg,
    salary - AVG(salary) OVER (PARTITION BY dept_id) AS diff_from_avg
FROM employees;
-- Without PARTITION BY: window is the entire table
-- With PARTITION BY dept_id: separate window per department
🧠 The key insight: GROUP BY produces ONE row per group. Window functions produce ONE row per ORIGINAL row — each row keeps its data AND gains a computed value from its window. This is why they're so powerful for analytics.
-- Scenario: 4 employees with salaries [90k, 85k, 85k, 70k]
SELECT
    name, salary,
    ROW_NUMBER() OVER (ORDER BY salary DESC) AS row_num,
    RANK()       OVER (ORDER BY salary DESC) AS rnk,
    DENSE_RANK() OVER (ORDER BY salary DESC) AS dense_rnk
FROM employees;

-- Result:
-- name  | salary | row_num | rnk | dense_rnk
-- Alice | 90000  |    1    |  1  |     1
-- Bob   | 85000  |    2    |  2  |     2    ← tie
-- Carol | 85000  |    3    |  2  |     2    ← same dense_rnk as Bob
-- Dave  | 70000  |    4    |  4  |     3    ← RANK skips 3, DENSE_RANK doesn't
FunctionTie behaviorUse when
ROW_NUMBER()Arbitrary unique number — no tiesYou need unique row identification, pagination, deduplication
RANK()Ties get same rank; next rank skipsSports rankings (tied 2nd place, no 3rd), leaderboards where gaps matter
DENSE_RANK()Ties get same rank; no skippingFinding Nth highest salary (no gaps), medal rankings where all should be accounted for
-- Classic use: top-3 salaries per department using DENSE_RANK
SELECT * FROM (
    SELECT emp_id, name, dept_id, salary,
           DENSE_RANK() OVER (PARTITION BY dept_id ORDER BY salary DESC) AS dr
    FROM employees
) ranked
WHERE dr <= 3;  -- top-3 salaries in each department

LAG() and LEAD() access values from previous or subsequent rows within the same window — without a self-join.

-- monthly_sales(month, total_sales)
-- Calculate month-over-month sales change
SELECT
    month,
    total_sales,
    LAG(total_sales, 1, 0)  OVER (ORDER BY month) AS prev_month_sales,
    total_sales - LAG(total_sales, 1, 0) OVER (ORDER BY month) AS mom_change,
    LEAD(total_sales, 1)    OVER (ORDER BY month) AS next_month_sales
FROM monthly_sales;

-- LAG(col, offset, default): value from 'offset' rows BEFORE current row
-- LEAD(col, offset, default): value from 'offset' rows AFTER current row
-- The third arg is the default value when there's no previous/next row

-- Result:
-- month   | total_sales | prev_month | mom_change | next_month
-- 2025-01 |   100000    |     0      |   100000   |   120000
-- 2025-02 |   120000    |   100000   |    20000   |    95000
-- 2025-03 |    95000    |   120000   |   -25000   |    NULL
💡 LAG/LEAD replace complex self-joins for time-series analysis. Before window functions, this required joining the table to itself on month = prev_month — messy and slow. With LAG, it's one clean query.

A trigger is a stored program that automatically fires in response to a DML event (INSERT, UPDATE, or DELETE) on a specific table — BEFORE or AFTER the event.

-- Audit trigger: log every salary change
DELIMITER //
CREATE TRIGGER salary_audit
AFTER UPDATE ON employees
FOR EACH ROW
BEGIN
    IF NEW.salary != OLD.salary THEN
        INSERT INTO salary_changes(emp_id, old_sal, new_sal, changed_at)
        VALUES(OLD.emp_id, OLD.salary, NEW.salary, NOW());
    END IF;
END //
DELIMITER ;

-- BEFORE trigger: validate data before inserting
CREATE TRIGGER validate_salary
BEFORE INSERT ON employees
FOR EACH ROW
BEGIN
    IF NEW.salary < 0 THEN
        SIGNAL SQLSTATE '45000'
        SET MESSAGE_TEXT = 'Salary cannot be negative';
    END IF;
END //

Good uses of triggers:

  • Audit logging (who changed what and when)
  • Enforcing complex business rules that CHECK constraints can't handle
  • Automatically maintaining derived/summary columns
  • Preventing invalid operations with BEFORE triggers

When to avoid triggers:

  • Complex business logic — triggers are invisible to application developers, making debugging nightmarish
  • Performance-critical paths — triggers add overhead to every DML operation
  • Cascading triggers (trigger calls trigger calls trigger) — virtually impossible to reason about
  • When application-layer logic would serve just as well
⚠ "Hidden logic" problem: a developer runs UPDATE employees SET salary = 50000 — and doesn't realize a trigger is quietly running 3 other queries behind the scenes. This makes triggers a debugging and maintenance hazard. Use them sparingly and document them explicitly.
· · ·
Summary

📋 SQL Quick-Review Cheatsheet

12 SQL Rules to Know Cold
1. SQL execution order: FROM → WHERE → GROUP BY → HAVING → SELECT → ORDER BY → LIMIT
2. WHERE filters ROWS (before grouping); HAVING filters GROUPS (after aggregation)
3. Cannot use SELECT aliases in WHERE (not computed yet); CAN use in ORDER BY
4. INNER JOIN: only matched rows · LEFT JOIN: all left + nulls for unmatched right
5. UNION removes duplicates (slow); UNION ALL keeps all rows (fast) — default to UNION ALL
6. JOIN = horizontal (more columns) · UNION = vertical (more rows)
7. Primary Key: unique + NOT NULL + one per table · Unique Key: unique but nullable
8. DELETE: logged, rollbackable, WHERE allowed · TRUNCATE: fast, DDL, resets identity
9. Clustered index: physical row order (one per table) · Non-clustered: separate B-tree (many allowed)
10. Index speeds reads but slows writes (must be maintained on every INSERT/UPDATE/DELETE)
11. Normalization: 1NF (atomic) → 2NF (no partial dep) → 3NF (no transitive dep) → BCNF
12. ACID: Atomicity (all-or-nothing), Consistency (valid states), Isolation (concurrent), Durability (persists)

Window Functions Quick Reference

FunctionWhat It ComputesClassic Use Case
ROW_NUMBER()Unique sequential number per row in windowPagination, deduplication (keep row 1 per group)
RANK()Rank with gaps after ties (1,1,3,4)Leaderboards where gaps matter
DENSE_RANK()Rank without gaps after ties (1,1,2,3)Nth highest salary, medal rankings
SUM() OVERRunning total or group totalCumulative sales, group subtotals
AVG() OVERMoving average or group averageCompare each row to group average
LAG(col, n)Value from n rows before currentMonth-over-month change, previous value
LEAD(col, n)Value from n rows after currentNext period preview, lookahead analysis
FIRST_VALUE()First value in the windowDays since first purchase, starting value
NTILE(n)Divides rows into n equal bucketsQuartiles (NTILE 4), percentiles (NTILE 100)

Up Next: Phase 3 — OOPs

32 OOPs interview questions — 4 pillars with code, abstract vs interface, SOLID principles, design patterns, and every Java-specific question Infosys asks.

Phase 3: OOPs →