πŸ—„οΈ Data Collection & DBMS β€” Complete Revision Notes

PGCP-BDA Β· All 22 sessions fully explained Β· Real-world analogies Β· Code with comments Β· Easy language for all levels
Click any session heading to expand. Use the quick-jump links below to navigate.

44h Theory 46h Lab 30h Self-learning SQL + MongoDB + Cassandra

Quick Jump β€” All Sessions

SQL & Fundamentals
Session 1
Database Concepts β€” File System vs DBMS + Codd's 12 Rules
What is DBMS Β· Why not files Β· Codd's Rules explained Β· 2T
πŸͺ Real-World Analogy β€” The Paper Register Problem Imagine a hospital in the 1980s. Doctors keep patient records in one room. Billing department has duplicate records in another room. Pharmacy keeps their own records. If a patient changes their address, all three rooms need to be updated separately. If you want a report combining all three β€” good luck! This is the file system problem. A DBMS is like hiring a central records management team β€” one system, one truth, everyone queries it together.
What exactly is a File System?

Before databases existed, every application stored its data in its own flat files (think .txt or .csv files stored on disk). Each program read and wrote its own files, with no coordination between programs.

⚠️ Problems with File Systems 1. Data Redundancy: Same customer stored in sales file AND accounts file AND CRM file. 3x storage waste.
2. Data Inconsistency: If customer changes phone number, only one file gets updated β€” now you have three different phone numbers.
3. Difficult Access: To answer "Which customers in Mumbai bought Electronics in Q3?" β€” impossible without custom code.
4. No Concurrent Access: Two people editing the same file = corruption.
5. No Security: Anyone who can see the file can read everything. No row-level or column-level control.
6. No Recovery: Power cut during write = corrupted file, data lost forever.
What is a DBMS?

A Database Management System (DBMS) is software that manages a structured collection of data. It acts as an interface between the user/application and the physical data stored on disk. The DBMS takes care of storage, security, concurrency, recovery, and integrity β€” so you don't have to.

πŸ›οΈ What DBMS gives you

Structured storage, fast querying, multi-user access without conflicts, role-based security, automatic backups and crash recovery.

πŸ”’ ACID Properties

Atomicity β€” all or nothing. Consistency β€” rules always satisfied. Isolation β€” transactions don't interfere. Durability β€” committed data survives crashes.

πŸ”— Types of DBMS

Relational (MySQL, Oracle), Document (MongoDB), Key-Value (Redis), Columnar (Cassandra, HBase), Graph (Neo4j).

RDBMS β€” The Relational Model

In an RDBMS (Relational Database Management System), all data lives in tables β€” rows (tuples) and columns (attributes). Tables relate to each other through keys. You query data using SQL. E.F. Codd invented this model in 1970 at IBM.

TermRelational nameSimple meaningExample
TableRelationA spreadsheet-like gridEmployees table
RowTupleOne record / entityPriya's employee record
ColumnAttributeOne property of the entityemployee_name, salary
Primary KeyCandidate KeyUniquely identifies each rowemployee_id = 101
Foreign KeyReferential constraintLinks one table to anotherdept_id in Employees β†’ id in Departments
Codd's 12 Rules β€” Deeply Explained

E.F. Codd published 12 rules in 1985 to define what a "truly relational" database must do. Think of them as the constitution of RDBMS. Most commercial databases satisfy most but not necessarily all 12 rules.

  • Rule 0 β€” Foundation Rule: The system must manage its data using its relational capabilities exclusively. A product marketed as relational must use its relational features to manage the database β€” it cannot use a non-relational back door.
  • Rule 1 β€” Information Rule: ALL information in a relational database (including table names, column names, and constraints) must be represented as values in tables. No hidden files, no special formats β€” just tables.
  • Rule 2 β€” Guaranteed Access: Every atomic piece of data must be logically addressable by specifying: (1) table name, (2) primary key value, (3) column name. Like a GPS coordinate for any data point.
  • Rule 3 β€” Systematic NULL Treatment: NULL must be supported for representing missing or inapplicable information. NULL is distinct from zero, empty string, or "unknown" text. It means "no value exists here."
  • Rule 4 β€” Active Online Catalog: The database schema (table definitions, column types, constraints) must also be stored in tables β€” called the system catalog or data dictionary. You can query it with SQL just like regular data: SELECT * FROM information_schema.tables;
  • Rule 5 β€” Comprehensive Data SubLanguage: At least one language must exist that supports: data definition, view definition, data manipulation, integrity constraints, authorization, and transaction boundaries. SQL satisfies all of these.
  • Rule 6 β€” View Updating: All views that are theoretically updatable must be updatable by the system. For example, a view joining two tables might not be updatable, but a simple filtered view of one table should be.
  • Rule 7 β€” High-Level Insert, Update, Delete: The system must support set-level (not just row-level) INSERT, UPDATE, and DELETE. Example: UPDATE employees SET dept = 'IT' WHERE dept = 'Computers'; β€” updates many rows at once.
  • Rule 8 β€” Physical Data Independence: Changing how data is physically stored (moving to faster disk, changing indexing strategy, changing file format) must NOT affect the applications or queries. The logical view stays the same.
  • Rule 9 β€” Logical Data Independence: Adding new tables or columns to the database must not break existing applications. A new column added to a table should not crash apps that don't use it.
  • Rule 10 β€” Integrity Independence: Integrity constraints (like "salary must be positive" or "department must exist") must be defined IN the database itself β€” not hardcoded in application code. This way, all apps share the same rules automatically.
  • Rule 11 β€” Distribution Independence: Whether the database is stored on one machine or distributed across 10 servers globally, the user's queries and results should be identical. The distribution is invisible to the end user.
  • Rule 12 β€” Non-Subversion Rule: If there is a low-level interface (row-at-a-time processing), it must not be possible to use it to bypass integrity rules. You can't "sneak around" constraints by using a lower-level API.
βœ… Exam / Interview Tip No commercial RDBMS fully satisfies all 12 rules β€” even Oracle. The rules are a theoretical benchmark. Rule 0 is sometimes called "Rule -1" as it's the meta-rule. Focus on Rules 1, 2, 3, 6, 7, 8, 9 β€” these come up most in exams.
Session 2
Database Storage Structure + Data Collection
Tablespace Β· Datafile Β· Control File Β· Structured vs Unstructured Β· Collection Methods Β· 2T
🏒 Office Building Analogy Think of an Oracle database as a large office building. The Database = the entire building. Each Tablespace = one floor (e.g., Ground Floor for HR, 1st Floor for Accounts). Each Datafile = a physical room on that floor where actual files are stored. The Control File = the building's reception/security desk that knows the layout of everything. The Redo Log = security camera footage that records every change made, useful for recovery.
Oracle Database Storage β€” Layer by Layer

When you install Oracle (or any enterprise RDBMS), data is stored in a hierarchy of logical and physical structures. Understanding this helps you as a DBA (Database Administrator) tune performance and plan storage.

πŸ—‚οΈ Tablespace (Logical)

The highest logical unit. A database has multiple tablespaces: SYSTEM (internal Oracle tables), SYSAUX (auxiliary), USERS (user data), TEMP (temporary sorting), UNDO (rollback data). You can create custom tablespaces per application.

πŸ’Ύ Datafile (Physical)

Each tablespace is made up of one or more .dbf files on disk. This is where bytes actually get written. If a tablespace runs out of space, you add another datafile to it. Multiple datafiles can be on different disks for performance.

πŸ“‹ Control File

A small but critical binary file that records: database name, creation timestamp, list of all datafiles and redo log files, and current checkpoint information. The DB won't start without it. Oracle recommends keeping 3 copies (multiplexing).

πŸ“ Redo Log Files

Record every change made to the database. If the DB crashes, Oracle replays the redo log to recover all committed transactions. Works in groups that cycle through (LGWR writes to them continuously).

🧩 Segments, Extents, Blocks

Inside a datafile: Segment = space for one object (e.g., one table). Extent = a chunk of contiguous blocks. Block (8KB by default) = the smallest unit Oracle reads/writes.

πŸ’­ SGA β€” System Global Area

Memory cache shared by all database users. Contains: Buffer Cache (caches data blocks), Shared Pool (caches SQL execution plans), Redo Log Buffer (buffers before writing to disk).

Structured vs Semi-structured vs Unstructured Data

The world generates different types of data. Understanding the difference determines which storage system you should use for each type.

TypeDefinitionReal ExampleStored inQuery with
StructuredPredefined schema, fixed rows & columns, strict typesBank transactions, Employee records, Product catalogMySQL, Oracle, PostgreSQLSQL β€” straightforward
Semi-structuredNo strict schema, but has self-describing tags or markersJSON from REST APIs, XML config files, Email with headers, CSV exportsMongoDB, Elasticsearch, XML databasesXPath, XQuery, JSONPath
UnstructuredNo predefined format, raw bytes, no schema at allDoctor's handwritten notes, WhatsApp photos, Videos, PDFs, Social media postsHDFS, S3, Azure Blob, NASNLP, image AI, search engines
πŸ“Š Data Volume Today About 80% of all enterprise data is unstructured (emails, documents, images, videos). Only 20% is structured. This is why Big Data and NoSQL exist β€” traditional RDBMS cannot efficiently store or process the 80%.
Data Collection Methods β€” Systematic Data Gathering

Before any analysis or storage, data must be collected. As a data professional, you need to know how data enters your pipeline from various sources.

πŸ“‹ Surveys & Forms

Google Forms, paper forms digitized. Structured data, collected on demand. Good for customer feedback, research. Tool: Google Forms, SurveyMonkey, ODK.

🌐 Web Scraping

Automatically extract data from websites using Python (BeautifulSoup, Scrapy). Amazon price tracking, news headlines, social media trends. Semi-structured HTML parsed to JSON/CSV.

πŸ“‘ IoT Sensors

Temperature sensors, smart meters, GPS trackers, factory machines. Generate continuous time-series data. Volume: billions of readings/day. Need stream processing (Kafka, Flink).

πŸ”Œ APIs

REST APIs from services: Twitter API, Google Maps API, RBI data feeds, payment gateways. Data arrives as JSON/XML. Most reliable β€” structured, real-time.

πŸ“Š Database Integration (ETL)

Pulling data from other operational databases: ERP systems (SAP), CRM (Salesforce), accounting software. Done via SQL queries or JDBC connections.

πŸ“ Log Files

Web server logs (Apache, Nginx), application logs, system event logs. Store every request, error, user action. Massive volume β€” analyzed with ELK Stack (Elasticsearch + Logstash + Kibana).

πŸ” Data Pipeline (How it flows) Source (sensors/APIs/forms) β†’ Ingestion (Kafka/Flume) β†’ Storage (HDFS/S3) β†’ Processing (Spark/Hive) β†’ Analysis (SQL/Python) β†’ Visualization (Tableau/Power BI)
Session 3
Introduction to SQL β€” DDL, DML, DCL Commands
CREATE Β· ALTER Β· DROP Β· INSERT Β· SELECT Β· UPDATE Β· DELETE Β· GRANT Β· REVOKE Β· 2T + 4L + 2SL
🏠 Complete House Analogy Imagine building and living in a house. DDL = the construction crew (build walls, add rooms, demolish). DML = you living in the house (bring in furniture, rearrange, throw things away). DCL = the key-master (give your friend access to the front door, take away the spare key). TCL (bonus) = confirm/undo decisions (sign the final purchase deed or cancel the transaction).
SQL Overview β€” What is SQL?

SQL (Structured Query Language) is the standard language used to interact with relational databases. It was developed in the 1970s by IBM and standardized by ANSI/ISO. All major RDBMS (MySQL, Oracle, PostgreSQL, SQL Server) support SQL with minor dialect differences.

DDL β€” Define Structure+ DML β€” Manipulate Data+ DCL β€” Control Access+ TCL β€” Manage Transactions
DDL β€” Data Definition Language

DDL commands define the structure of the database β€” they create, modify, or delete database objects like tables, indexes, views. DDL commands are auto-committed (cannot be rolled back in most databases).

━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
  CREATE TABLE β€” Define a new table
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
CREATE TABLE Books (
  book_id   INT           NOT NULL,    -- no null allowed
  name      VARCHAR(100) NOT NULL,
  author    VARCHAR(60),               -- nullable
  price     DECIMAL(8,2) DEFAULT 0.00, -- default value
  genre     VARCHAR(30),
  added_on  DATE          DEFAULT CURDATE(),
  PRIMARY KEY (book_id),               -- uniquely identifies row
  CHECK (price >= 0)                   -- price can't be negative
);

━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
  ALTER TABLE β€” Modify existing table structure
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
ALTER TABLE Books ADD COLUMN    publisher  VARCHAR(80);   -- add column
ALTER TABLE Books MODIFY COLUMN name       VARCHAR(150); -- change type
ALTER TABLE Books DROP COLUMN   publisher;                -- remove column
ALTER TABLE Books RENAME TO    Library_Books;            -- rename table

━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
  DROP vs TRUNCATE β€” both delete all data
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
TRUNCATE TABLE Books;  -- removes all rows, keeps table structure (fast)
DROP TABLE    Books;   -- removes ENTIRE table + structure (gone forever)
DML β€” Data Manipulation Language

DML commands work on the data inside the tables. They can be rolled back (unlike DDL). These are the commands you'll use most often day-to-day.

━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
  INSERT β€” Add new rows
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
-- Method 1: Specify all columns in order
INSERT INTO Books VALUES
  (1, 'Clean Code', 'Robert Martin', 499.00, 'Programming', '2024-01-15');

-- Method 2: Specify column names (recommended β€” safe against schema changes)
INSERT INTO Books (book_id, name, author, price)
VALUES (2, 'The Pragmatic Programmer', 'Hunt', 549.00);

-- Method 3: Multiple rows at once (faster than individual inserts)
INSERT INTO Books (book_id, name, price) VALUES
  (3, 'Design Patterns', 699.00),
  (4, 'Refactoring',      599.00),
  (5, 'SICP',             350.00);

━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
  SELECT β€” Query / Read data
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
-- Basic select
SELECT * FROM Books;                        -- all columns, all rows
SELECT name, price FROM Books;             -- specific columns only
SELECT DISTINCT genre FROM Books;          -- unique genres only

-- Filtering with WHERE
SELECT * FROM Books WHERE price > 400;
SELECT * FROM Books WHERE genre = 'Programming' AND price < 600;
SELECT * FROM Books WHERE name LIKE '%Code%'; -- contains 'Code'
SELECT * FROM Books WHERE price BETWEEN 300 AND 600;
SELECT * FROM Books WHERE author IN ('Martin', 'Hunt');
SELECT * FROM Books WHERE publisher IS NULL;  -- find NULL values

━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
  UPDATE β€” Modify existing rows
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
UPDATE Books SET price = 449.00 WHERE book_id = 1;  -- update one row
UPDATE Books SET price = price * 0.9               -- 10% discount to all
WHERE genre = 'Programming';

━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
  DELETE β€” Remove rows (rollbackable unlike TRUNCATE)
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
DELETE FROM Books WHERE book_id = 5;       -- delete one specific row
DELETE FROM Books WHERE price < 100;       -- delete rows matching condition
DCL β€” Data Control Language

DCL manages WHO can do WHAT in the database. In organizations, the DBA creates user accounts and assigns only the necessary permissions (principle of least privilege).

-- Step 1: Create a new DB user
CREATE USER 'dbda_student'@'localhost' IDENTIFIED BY 'secure_pass_2024';

-- Step 2: Grant specific permissions
GRANT SELECT, INSERT ON college_db.* TO 'dbda_student'@'localhost';
GRANT ALL PRIVILEGES ON test_db.* TO 'dbda_student'@'localhost';

-- Step 3: Make permissions take effect
FLUSH PRIVILEGES;

-- Step 4: Revoke permissions when no longer needed
REVOKE DELETE ON college_db.Books FROM 'dbda_student'@'localhost';

-- Check what permissions a user has
SHOW GRANTS FOR 'dbda_student'@'localhost';
Constraints β€” Enforcing Data Integrity

Constraints are rules applied on table columns to ensure data quality. They are enforced automatically by the DBMS β€” no need to write application logic for them.

ConstraintWhat it enforcesExampleWhat happens on violation
PRIMARY KEYUnique + NOT NULL. Only one per table.employee_id INT PRIMARY KEYINSERT rejected with duplicate key error
FOREIGN KEYValue must exist in referenced tabledept_id INT REFERENCES dept(id)INSERT/UPDATE rejected if parent doesn't exist
UNIQUENo duplicate values (NULLs allowed)email VARCHAR(100) UNIQUEINSERT rejected with unique constraint error
NOT NULLValue must be provided, NULL forbiddenname VARCHAR(50) NOT NULLINSERT/UPDATE rejected if NULL passed
CHECKValue must satisfy a logical expressionage INT CHECK (age BETWEEN 18 AND 65)INSERT/UPDATE rejected if condition is false
DEFAULTAuto-fill when value not providedstatus VARCHAR(10) DEFAULT 'active'Column silently filled with default
TCL β€” Transaction Control (Bonus)
-- Transactions ensure ACID compliance
-- BEGIN starts a transaction block
START TRANSACTION;
  UPDATE accounts SET balance = balance - 5000 WHERE acc_no = 'A001';
  UPDATE accounts SET balance = balance + 5000 WHERE acc_no = 'B002';
COMMIT;    -- both updates succeed β†’ save permanently

-- If something goes wrong between the two updates:
ROLLBACK;  -- undo everything β†’ no money lost, no money gained

SAVEPOINT sp1;   -- create a checkpoint mid-transaction
ROLLBACK TO sp1; -- undo back to checkpoint, not all the way
πŸ’‘ DELETE vs TRUNCATE vs DROP
DELETE FROM table WHERE condition β†’ removes specific rows, can be rolled back, slow (logs each row).
TRUNCATE TABLE table β†’ removes ALL rows instantly, minimal logging, cannot be rolled back in most DBs.
DROP TABLE table β†’ removes the table AND its structure completely. Cannot be undone.
Session 4
GROUP BY, ORDER BY, Subqueries & Advanced Joins
Aggregates Β· HAVING Β· Correlated Subqueries Β· All Join Types Β· 2T + 4L + 4SL
πŸ›’ Supermarket Bill Analogy Imagine you're the manager of a supermarket analysing your daily bill. GROUP BY = sort the bill by category (Dairy β‚Ή2000, Vegetables β‚Ή3500, Snacks β‚Ή1800). HAVING = "show me only those categories where we spend more than β‚Ή2000." ORDER BY = "arrange those categories from highest spend to lowest." The WHERE clause is like your filter before you start grouping β€” "only look at bills from this month."
Understanding GROUP BY β€” Step by Step

GROUP BY collapses multiple rows with the same value in a column into a single summary row. It's used with aggregate functions like COUNT, SUM, AVG, MIN, MAX.

πŸ”‘ Order of Execution in SQL (important for understanding WHERE vs HAVING)
1. FROM (get the table) β†’ 2. WHERE (filter rows) β†’ 3. GROUP BY (group them) β†’ 4. HAVING (filter groups) β†’ 5. SELECT (pick columns) β†’ 6. ORDER BY (sort) β†’ 7. LIMIT (paginate)
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
  EMPLOYEES table (what we're working with)
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
  emp_id | name    | dept_id | salary | city
  -------|---------|---------|--------|-------
  1      | Priya   | 10      | 60000  | Mumbai
  2      | Rahul   | 10      | 55000  | Pune
  3      | Anita   | 20      | 70000  | Mumbai
  4      | Vikram  | 20      | 80000  | Delhi
  5      | Sneha   | 30      | 45000  | Pune
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━

-- Q: How many employees in each department?
SELECT dept_id, COUNT(*) AS headcount
FROM employees
GROUP BY dept_id;
-- Result: dept 10β†’2, dept 20β†’2, dept 30β†’1

-- Q: Departments with more than 1 employee AND avg salary above 50000
SELECT dept_id,
       COUNT(*) AS headcount,
       AVG(salary) AS avg_salary,
       MAX(salary) AS highest_sal,
       MIN(salary) AS lowest_sal,
       SUM(salary) AS total_payroll
FROM employees
WHERE city != 'Delhi'    -- filter BEFORE grouping (Delhi excluded)
GROUP BY dept_id
HAVING COUNT(*) > 1      -- filter AFTER grouping (keep dept with 2+ people)
  AND AVG(salary) > 50000
ORDER BY avg_salary DESC; -- sort final output
All Aggregate Functions Explained
FunctionWhat it calculatesIgnores NULL?Example use
COUNT(*)Total number of rows (including NULLs)NoHow many orders today?
COUNT(col)Count of non-NULL values in a columnYesHow many orders have a tracking number?
SUM(col)Total sum of numeric columnYesTotal revenue this month
AVG(col)Arithmetic mean (sum/count-of-non-nulls)YesAverage order value
MIN(col)Smallest value (works on dates too)YesCheapest product, earliest order date
MAX(col)Largest valueYesMost expensive product, latest login
STDDEV(col)Standard deviation β€” how spread out values areYesSalary consistency analysis
VARIANCE(col)Statistical variance (STDDEV squared)YesData science feature analysis
Subqueries β€” Queries inside Queries

A subquery is a complete SELECT statement nested inside another SQL statement. The inner query runs first, its result is passed to the outer query. Think of it as solving a maths problem step by step.

━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
  1. Simple Subquery (runs once, independent)
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
-- Find all employees earning above company average
SELECT name, salary
FROM employees
WHERE salary > (
  SELECT AVG(salary) FROM employees  ← runs once, returns 62000
);
-- The inner query returns 62000, outer becomes: WHERE salary > 62000

━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
  2. Correlated Subquery (runs once PER ROW of outer query)
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
-- Find employees earning MORE than their OWN department's average
-- This is correlated because it references e1.dept_id from outer query
SELECT e1.name, e1.salary, e1.dept_id
FROM  employees e1
WHERE e1.salary > (
  SELECT AVG(e2.salary)
  FROM  employees e2
  WHERE e2.dept_id = e1.dept_id  ← uses outer query's dept_id!
);
-- For Priya (dept 10): inner query calculates avg of dept 10 = 57500
-- For Anita (dept 20): inner query calculates avg of dept 20 = 75000
-- Each row gets its OWN calculation β€” hence "correlated"

━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
  3. Subquery with EXISTS (check if related rows exist)
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
-- Find all departments that HAVE at least one employee
SELECT d.dept_name
FROM departments d
WHERE EXISTS (
  SELECT 1 FROM employees e WHERE e.dept_id = d.id
);
JOINs β€” Combining Data from Multiple Tables

In a normalised database, related data is split across multiple tables. JOINs bring them back together. Think of it as matching puzzle pieces β€” you need matching values on both sides.

━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
  Sample data we'll use for JOIN examples
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
  EMPLOYEES:  emp_id, name, dept_id (1,2,3,4,5)
  DEPARTMENTS: id, dept_name        (10,20,30,99 ← 99 has no employees)
  SALESMAN:   id, name, customer_id, commission
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━

-- INNER JOIN: Only matching rows from BOTH tables
SELECT e.name, d.dept_name, e.salary
FROM  employees e
INNER JOIN departments d ON e.dept_id = d.id;
-- Returns only employees whose dept_id exists in departments table
-- Employees with dept 99 would be excluded

-- LEFT OUTER JOIN: All employees + their dept (NULL if dept missing)
SELECT e.name, d.dept_name
FROM  employees e
LEFT JOIN departments d ON e.dept_id = d.id;
-- Returns ALL employees. If no matching dept, dept_name shows NULL

-- RIGHT OUTER JOIN: All departments + their employees (NULL if no emp)
SELECT e.name, d.dept_name
FROM  employees e
RIGHT JOIN departments d ON e.dept_id = d.id;
-- Returns ALL departments. Dept 99 appears with NULL employee name

-- 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.id;

-- SELF JOIN: Employee-Manager hierarchy (same table joined to itself)
SELECT e.name AS employee, m.name AS manager
FROM  employees e
LEFT JOIN employees m ON e.manager_id = m.emp_id;

-- Salesperson + customer details (from syllabus lab problem)
SELECT c.cust_name, c.city, s.name AS salesman, s.commission
FROM  customers c
JOIN  salesman s ON c.salesman_id = s.id
ORDER BY s.commission DESC;
JOIN TypeReturnsUse caseTip
INNER JOINOnly rows matching in BOTH tablesMost common β€” get related dataDefault JOIN = INNER JOIN
LEFT (OUTER) JOINAll left rows + matched right (NULL if no match)All customers even if they never orderedMost used outer join
RIGHT (OUTER) JOINAll right rows + matched leftAll products even if never soldEquivalent to LEFT JOIN with tables swapped
FULL OUTER JOINAll rows from both sidesAudit mismatches on either sideMySQL doesn't support β€” use UNION of LEFT+RIGHT
SELF JOINTable matched with itselfManager-employee, friend-of-friendRequires aliases (e1, e2)
CROSS JOINEvery row from left Γ— every row from rightGenerate all combinations (sizes Γ— colors)Can produce huge result sets!
πŸ’‘ LIKE Pattern Matching
% = any number of characters (including zero). _ = exactly one character.
name LIKE 'A%' β†’ starts with A. name LIKE '%N' β†’ ends with N.
name LIKE 'A%N' β†’ starts with A, ends with N (matches "Arjun", "Arun").
name LIKE '_a%' β†’ second character is 'a'.
From syllabus lab: "Print employee names who have 'A' as first letter and 'N' as last letter" β†’ WHERE name LIKE 'A%N'
Sessions 5–6
Normal Forms, ER Diagrams & Stored Procedures
1NFΒ·2NFΒ·3NFΒ·BCNF Β· ER Symbols Β· Relational Modelling Β· Stored Procedures Β· 4T+4L+4SL
πŸ—ƒοΈ Filing Cabinet Analogy Imagine a messy office where the same client's address appears in 15 different folders. When the client moves, someone must update all 15 folders. Miss one β€” disaster. Normalization = reorganizing so each fact lives in exactly ONE place. When the client moves, update ONE record. All other references automatically get the correct address.
Why Normalise? β€” The Problems it Solves

Without normalization, databases suffer from three types of anomalies that corrupt data integrity:

πŸ”΄ Insert Anomaly

You can't add a new department unless it already has an employee. Example: Can't record "IT Department" until someone joins it.

πŸ”΄ Update Anomaly

Updating one fact requires changing multiple rows. Miss one row β†’ inconsistent data. Example: Department head changes β€” must update 50 employee rows.

πŸ”΄ Delete Anomaly

Deleting the last employee from a dept also deletes the department's info. Example: Last person in IT quits β†’ IT department info vanishes from DB.

1NF β€” First Normal Form

Rule: Every column must contain atomic (single, indivisible) values. No repeating groups. Every row must be unique.

❌ Violates 1NF

Student: Priya | Courses: "DBMS, OS, Networks"

Problem: The "Courses" column has multiple values in one cell β€” not atomic. Cannot query individual courses.

βœ… Satisfies 1NF

Priya | DBMS
Priya | OS
Priya | Networks

Each course is in its own row. Now you can query "who takes DBMS?"

2NF β€” Second Normal Form

Rule: Must be in 1NF PLUS no partial dependency. Every non-key attribute must depend on the entire primary key β€” not just part of it. This only matters when you have a composite primary key.

❌ Violates 2NF

Table: (student_id, course_id, student_name, marks)
PK = (student_id + course_id)
But student_name depends only on student_id β€” not on the full composite PK. That's a partial dependency.

βœ… Satisfies 2NF

Split into:
Students(student_id, student_name)
Enrollments(student_id, course_id, marks)

Now student_name is in its own table, depending fully on student_id.

3NF β€” Third Normal Form

Rule: Must be in 2NF PLUS no transitive dependency. A non-key attribute should NOT depend on another non-key attribute. All non-key columns must depend DIRECTLY on the primary key.

❌ Violates 3NF

Table: (emp_id, dept_id, dept_name)
PK = emp_id
But dept_name depends on dept_id, not on emp_id directly. That's transitive: emp_id β†’ dept_id β†’ dept_name.

βœ… Satisfies 3NF

Split into:
Employees(emp_id, dept_id)
Departments(dept_id, dept_name)

dept_name is now in its own table, depending directly on dept_id PK.

BCNF β€” Boyce-Codd Normal Form

Rule: Must be in 3NF PLUS every determinant must be a candidate key. It's a stricter version of 3NF that catches edge cases where 3NF still allows anomalies (usually involves multiple overlapping candidate keys). For most practical purposes, 3NF is sufficient.

πŸ“Œ Summary β€” Which NF to aim for?
In practice: aim for 3NF for OLTP databases. For analytics/warehouse, controlled denormalization (going back to 2NF or 1NF) is often done for query performance β€” this is what star/snowflake schemas do.
ER Diagram β€” Entity Relationship Diagram

An ER diagram is a blueprint of your database β€” drawn before you write a single line of SQL. It shows what entities (things) exist, what attributes they have, and how they relate to each other. Think of it as the architect's floor plan before construction begins.

ER SymbolShapeRepresentsExample
EntityRectangleA "thing" that has data β€” becomes a tableStudent, Course, Department
Weak EntityDouble RectangleEntity that can't exist without anotherOrderItem (can't exist without Order)
AttributeEllipse (oval)A property of an entity β€” becomes a columnstudent_id, name, date_of_birth
Key AttributeUnderlined ovalAttribute that uniquely identifies the entitystudent_id
Multi-valued AttributeDouble EllipseAttribute with multiple valuesPhone numbers (person can have 2+ phones)
Derived AttributeDashed EllipseComputed from other attributes, not storedAge (derived from DOB + today's date)
Composite AttributeEllipse with sub-ovalsAttribute made of multiple partsFull Name = FirstName + LastName
RelationshipDiamondAssociation between entitiesEnrolls (Student Enrolls in Course)
Cardinality (1:1)Line labelsOne entity on each sideCitizen has one Passport
Cardinality (1:N)Line labelsOne on left, many on rightDepartment has many Employees
Cardinality (M:N)Line labelsMany on both sidesStudent takes many Courses; Course has many Students
Stored Procedures β€” Reusable SQL Programs

A stored procedure is a named, pre-compiled block of SQL code stored in the database. Instead of sending a long SQL query from your application every time, you just call the procedure name. Think of it like a function in Python β€” you define it once and call it many times with different arguments.

🎯 Why use Stored Procedures?
1. Performance: Pre-compiled β€” the DB doesn't need to parse SQL every time.
2. Security: Grant EXECUTE permission on procedure without exposing underlying tables.
3. Reusability: Write once, call from any application (Java, Python, PHP β€” all call the same procedure).
4. Reduce network traffic: One procedure call instead of 10 individual SQL statements over the network.
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
  Basic Stored Procedure
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
DELIMITER //
CREATE PROCEDURE GetDeptSummary(
  IN  p_dept_id   INT,           -- input parameter (caller provides)
  OUT p_emp_count INT,           -- output parameter (procedure fills)
  OUT p_avg_salary DECIMAL(10,2) -- another output
)
BEGIN
  -- Use IF-ELSE control structure
  IF p_dept_id IS NULL THEN
    SELECT COUNT(*), AVG(salary)
    INTO p_emp_count, p_avg_salary
    FROM employees;              -- all employees if dept not specified
  ELSE
    SELECT COUNT(*), AVG(salary)
    INTO p_emp_count, p_avg_salary
    FROM employees
    WHERE dept_id = p_dept_id;
  END IF;
END //
DELIMITER ;

-- Call the procedure
CALL GetDeptSummary(10, @count, @avg);
SELECT @count AS total_employees, @avg AS average_salary;

━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
  Procedure with CURSOR (process row by row)
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
DELIMITER //
CREATE PROCEDURE GiveRaise()
BEGIN
  DECLARE done INT DEFAULT 0;
  DECLARE v_id INT;
  DECLARE v_salary DECIMAL(10,2);

  -- Declare cursor to loop over employees
  DECLARE emp_cursor CURSOR FOR
    SELECT emp_id, salary FROM employees;

  DECLARE CONTINUE HANDLER FOR NOT FOUND SET done = 1;

  OPEN emp_cursor;
  read_loop: LOOP
    FETCH emp_cursor INTO v_id, v_salary;
    IF done THEN LEAVE read_loop; END IF;
    -- Give 10% raise to those earning below 50000
    IF v_salary < 50000 THEN
      UPDATE employees SET salary = v_salary * 1.10
      WHERE emp_id = v_id;
    END IF;
  END LOOP;
  CLOSE emp_cursor;
END //
DELIMITER ;
Session 7
Views, Triggers, Window Functions & CASE
CREATE VIEW Β· BEFORE/AFTER Triggers Β· RANK() Β· ROW_NUMBER() Β· LEAD/LAG Β· 2T + 4L
πŸͺŸ Three Analogies in One VIEW = a customised window on your data. You see what the window shows β€” but the actual room (table) is unchanged. You can put different windows in different places showing different angles. TRIGGER = a motion sensor alarm. When someone enters a room (INSERT/UPDATE/DELETE), the alarm fires automatically. Window Function = a rolling scoreboard β€” shows each player's rank live while the full player list remains visible.
Views β€” Virtual Tables

A VIEW is a saved SELECT query that looks and behaves like a table. It does NOT store data β€” it runs the underlying query every time you access it. Views are used to: simplify complex queries, restrict columns/rows users can see, and present data in a specific format.

-- Create a view: employees with dept name + only salary > 50000
CREATE VIEW HighEarnerDetails AS
  SELECT
    e.emp_id,
    e.name,
    e.salary,
    e.city,
    d.dept_name
  FROM employees e
  JOIN departments d ON e.dept_id = d.id
  WHERE e.salary > 50000;

-- Use the view exactly like a table
SELECT * FROM HighEarnerDetails WHERE city = 'Mumbai';
SELECT dept_name, COUNT(*) FROM HighEarnerDetails GROUP BY dept_name;

-- Update or drop a view
CREATE OR REPLACE VIEW HighEarnerDetails AS SELECT ...  -- modify
DROP VIEW HighEarnerDetails;                              -- remove

-- From syllabus: View to find employee with highest salary
CREATE VIEW TopEarnerView AS
  SELECT e.name, e.salary, e.dept_id, d.dept_name
  FROM  employees e
  JOIN  departments d ON e.dept_id = d.id
  WHERE e.salary = (SELECT MAX(salary) FROM employees);
Triggers β€” Automatic Actions

A TRIGGER is a stored procedure that automatically executes when a specific event (INSERT, UPDATE, DELETE) occurs on a table. You don't call triggers β€” the database fires them. Used for: audit logging, enforcing complex business rules, maintaining derived data.

Trigger TypeWhen it firesAccess to NEW/OLDUse case
BEFORE INSERTBefore new row is addedNEW (modify it before save)Auto-format data, validate custom rules
AFTER INSERTAfter row is successfully addedNEW (read-only)Log new record, send notification
BEFORE UPDATEBefore row is modifiedOLD (old values), NEW (new values)Prevent unauthorized changes, capture old value
AFTER UPDATEAfter row is successfully modifiedOLD and NEWAudit trail, sync summary tables
BEFORE DELETEBefore row is removedOLD (the row being deleted)Prevent deletion of important records
AFTER DELETEAfter row is deletedOLD (what was deleted)Archive deleted records, cleanup
-- AFTER UPDATE trigger: audit log for salary changes
DELIMITER //
CREATE TRIGGER trg_SalaryAudit
AFTER UPDATE ON employees
FOR EACH ROW
BEGIN
  IF OLD.salary != NEW.salary THEN  -- only log if salary actually changed
    INSERT INTO salary_audit_log
      (emp_id, old_salary, new_salary, changed_by, changed_at)
    VALUES
      (NEW.emp_id, OLD.salary, NEW.salary, USER(), NOW());
  END IF;
END //
DELIMITER ;

-- BEFORE INSERT trigger: auto-uppercase the name
DELIMITER //
CREATE TRIGGER trg_FormatName
BEFORE INSERT ON employees
FOR EACH ROW
BEGIN
  SET NEW.name = UPPER(TRIM(NEW.name));  -- trim spaces + uppercase
END //
DELIMITER ;
Window Functions β€” Analytics Without Losing Rows

Window functions perform calculations across a set of related rows without collapsing them. Unlike GROUP BY (which gives one row per group), window functions give you a result for EVERY row, with that row's "window" context calculated alongside. Extremely powerful for ranking, running totals, and comparisons.

-- The OVER() clause is what makes it a window function
-- PARTITION BY = which "window" each row belongs to
-- ORDER BY = how to sort within each window

SELECT
  name,
  dept_id,
  salary,
  -- Rank within department (ties get same rank, gap follows)
  RANK() OVER(PARTITION BY dept_id ORDER BY salary DESC)       AS dept_rank,
  -- Dense rank (ties get same rank, NO gap)
  DENSE_RANK() OVER(PARTITION BY dept_id ORDER BY salary DESC) AS dense_rank,
  -- Row number (unique sequential number)
  ROW_NUMBER() OVER(ORDER BY salary DESC)                       AS overall_rank,
  -- Running total of salary within dept
  SUM(salary) OVER(PARTITION BY dept_id ORDER BY salary)        AS running_total,
  -- Department total (same for all rows in that dept)
  SUM(salary) OVER(PARTITION BY dept_id)                         AS dept_total,
  -- Salary of next higher earner in same dept
  LEAD(salary, 1) OVER(PARTITION BY dept_id ORDER BY salary)   AS next_salary,
  -- Salary of previous row in same dept
  LAG(salary, 1)  OVER(PARTITION BY dept_id ORDER BY salary)   AS prev_salary
FROM employees;

-- Practical use: Get top earner per department (without subquery)
SELECT * FROM (
  SELECT name, dept_id, salary,
         RANK() OVER(PARTITION BY dept_id ORDER BY salary DESC) AS rnk
  FROM employees
) ranked
WHERE rnk = 1; -- only top earner per dept
CASE Statement β€” Conditional Logic in SQL
-- Simple CASE: map specific values
SELECT name, salary,
  CASE dept_id
    WHEN 10 THEN 'Engineering'
    WHEN 20 THEN 'HR'
    WHEN 30 THEN 'Finance'
    ELSE       'Unknown'
  END AS dept_name,
  -- Searched CASE: conditions
  CASE
    WHEN salary >= 80000 THEN 'Senior'
    WHEN salary >= 50000 THEN 'Mid-level'
    ELSE                       'Junior'
  END AS level
FROM employees;
Data Warehousing & NoSQL
Sessions 8–9
Data Warehousing Concepts
OLTP vs OLAP Β· Star/Snowflake Schema Β· ETL Pipeline Β· Columnar Storage Β· 4T+2L+2SL
πŸͺ Supermarket vs Research Analyst Analogy A supermarket cashier (OLTP) processes one transaction at a time β€” scan item, take payment, print receipt. Fast, one customer, current stock. A market research analyst (OLAP) wants to answer: "Which product category grew fastest across all 500 stores over the last 5 years?" They don't need real-time stock β€” they need years of history, aggregated. These two needs require completely different database designs.
OLTP vs OLAP β€” Deep Comparison
AspectOLTP β€” OperationalOLAP β€” Analytical
Full formOnline Transaction ProcessingOnline Analytical Processing
PurposeRun daily business operationsSupport business intelligence & decisions
Query typeSimple INSERT/UPDATE/DELETE + simple SELECTsComplex multi-table JOINs, aggregations, GROUP BY
Data ageCurrent (minutes old)Historical (months to years)
Data granularityIndividual transactionsAggregated summaries
Schema designHighly normalized (3NF)Denormalized (Star / Snowflake schema)
DB sizeGigabytesTerabytes to Petabytes
Response timeMillisecondsSeconds to minutes (complex queries)
Concurrent usersThousandsTens to hundreds (analysts)
Example systemsBanking apps, e-commerce checkout, hospital recordsAmazon Redshift, Google BigQuery, Snowflake, Hive
Data Warehouse Architecture

A Data Warehouse is a central repository that consolidates data from multiple OLTP systems (ERP, CRM, POS) into one place for analysis. Data is cleaned, transformed, and loaded through an ETL process.

ERP (SAP)→ CRM (Salesforce)→ POS Systems→ ETL (Transform & Clean)→ Data Warehouse→ BI Tools (Tableau)
Star Schema β€” The Most Common Warehouse Design

Named because the diagram looks like a star β€” one central Fact table surrounded by Dimension tables. The Fact table contains measurable numbers (sales amount, quantity). Dimension tables provide context (who, what, when, where).

━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
  STAR SCHEMA for E-commerce Sales Analysis
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━

  FACT_Sales (the star's centre)
  β”œβ”€β”€ sales_id (PK)
  β”œβ”€β”€ amount         ← measure
  β”œβ”€β”€ quantity       ← measure
  β”œβ”€β”€ discount       ← measure
  β”œβ”€β”€ date_id        β†’ DIM_Date
  β”œβ”€β”€ product_id     β†’ DIM_Product
  β”œβ”€β”€ customer_id    β†’ DIM_Customer
  β”œβ”€β”€ store_id       β†’ DIM_Store
  └── promotion_id   β†’ DIM_Promotion

  DIM_Date          DIM_Product       DIM_Customer
  β”œβ”€β”€ date_id (PK)  β”œβ”€β”€ product_id    β”œβ”€β”€ customer_id
  β”œβ”€β”€ day           β”œβ”€β”€ name          β”œβ”€β”€ name
  β”œβ”€β”€ month         β”œβ”€β”€ category      β”œβ”€β”€ city
  β”œβ”€β”€ quarter       β”œβ”€β”€ brand         β”œβ”€β”€ segment
  └── year          └── price         └── age_group

-- Sample analytical query on Star Schema
SELECT
  d.year,
  d.quarter,
  p.category,
  c.city,
  SUM(f.amount) AS total_revenue,
  COUNT(f.sales_id) AS num_transactions,
  AVG(f.amount) AS avg_order_value
FROM  FACT_Sales f
JOIN  DIM_Date d     ON f.date_id = d.date_id
JOIN  DIM_Product p  ON f.product_id = p.product_id
JOIN  DIM_Customer c ON f.customer_id = c.customer_id
WHERE d.year IN (2023, 2024)
GROUP BY d.year, d.quarter, p.category, c.city
ORDER BY total_revenue DESC;
🌟 Star vs Snowflake Schema
Star Schema: Dimension tables are flat (denormalized). Fast query performance, simple joins. Recommended for most cases.
Snowflake Schema: Dimension tables are normalized (split further). Example: DIM_Product β†’ DIM_Category β†’ DIM_Department. Less redundancy but more complex joins, slightly slower queries.
Rule of thumb: Use Star for performance (OLAP). Use Snowflake when storage is critical or dimensions are very large.
ETL β€” Extract, Transform, Load in Detail

πŸ“€ Extract

Pull data from sources: SQL databases (JDBC), flat files (CSV/Excel), APIs (JSON/XML), mainframes. Challenge: different formats, encodings, time zones. Tools: Informatica, Talend, Apache Sqoop.

βš™οΈ Transform

Clean and reshape: handle missing values, standardize formats (dates, phone numbers, addresses), remove duplicates, apply business rules, create surrogate keys, aggregate data. Most complex step β€” 70% of ETL effort.

πŸ“₯ Load

Write to warehouse. Two strategies: Full Load (reload everything β€” simple but slow), Incremental Load (only new/changed records β€” fast but complex). Bulk insert tools used for performance.

OLAP Operations in Detail
OperationWhat it doesExample
Roll-up (aggregation)Move from detail level to summary level. Reduces data.Daily sales β†’ Monthly sales β†’ Annual sales
Drill-down (disaggregation)Move from summary to detail. Increases data.Annual total β†’ Q3 total β†’ September total β†’ Week 3
SliceSelect one value for ONE dimension. Reduces cube to 2D."Show all data for year 2024 only" β€” year dimension fixed
DiceSelect ranges on MULTIPLE dimensions. Sub-cube."2023–2024, Western region, Electronics & Appliances"
Pivot (Rotate)Swap the row and column axes. Changes perspective.Products on rows & months on columns β†’ swap them
Session 10
Introduction to NoSQL
4 Types Β· CAP Theorem Β· When SQL fails Β· Storage Architectures Β· 2T
🏨 Hotel vs House Analogy A house (SQL) has a fixed floor plan. You can't add a new room overnight. Every room is the same shape. Works perfectly for a family, but when 10,000 tourists arrive? A hotel (NoSQL) can quickly add more floors (horizontal scaling), has different room types (flexible schema), and different room sizes for different needs. NoSQL doesn't mean "no structure" β€” it means "Not Only SQL." You might use both.
Why did NoSQL emerge? β€” Limitations of RDBMS at Scale

πŸ“ˆ Scale Problem

Facebook, Google, Amazon have billions of records. Traditional RDBMS scales vertically (bigger machine). But you can't keep buying bigger machines forever. NoSQL scales horizontally (more machines).

πŸ”„ Schema Flexibility

Product catalogs vary wildly β€” a book has ISBN, a shirt has size/color. In SQL, you'd need 50 nullable columns or complex joins. In MongoDB, each document just has the fields it needs.

⚑ Speed at Scale

Redis answers a cache lookup in under 1ms. Cassandra handles 1 million writes/second. These are impossible with a single SQL server under heavy load.

πŸ“Š Unstructured Data

JSON from APIs, sensor readings, user activity logs, social media posts β€” don't fit neatly into rows and columns. Document and key-value stores are far more natural.

4 Types of NoSQL Databases
TypeData ModelReal-world use caseExamplesKey strength
Document StoreJSON/BSON documents, each can have different fieldsProduct catalogs, user profiles, content management systems, e-commerceMongoDB, CouchDB, FirestoreFlexible schema, rich queries on nested data
Key-Value StoreSimple dictionary: key β†’ value (value is opaque blob)Session storage, user carts, rate limiting, real-time leaderboards, pub/sub messagingRedis, DynamoDB, Riak, MemcachedFastest possible lookup (O(1)), massive scale
Column-Oriented (Wide Column)Rows identified by row key, columns grouped in "column families"IoT time-series, activity feeds, recommendation engines, write-heavy analyticsCassandra, HBase, Google BigtableExtraordinary write throughput, linear scaling
Graph DatabaseNodes (entities) connected by edges (relationships)Social networks, fraud detection, recommendation engines, knowledge graphsNeo4j, Amazon Neptune, ArangoDBTraversing relationships is very fast; SQL JOIN chains can't match this
CAP Theorem β€” Every Distributed System's Dilemma

The CAP Theorem (Brewer's Theorem, 2000) states that in a distributed database system, you can guarantee at most 2 out of 3 properties simultaneously:

πŸ”΅ Consistency (C)

Every read receives the most recent write. All nodes show the same data at the same time. If node A writes, node B immediately sees that write.

🟒 Availability (A)

Every request receives a response (not necessarily the most recent data). The system is always up and returns something, even if a node is down.

🟑 Partition Tolerance (P)

The system continues to work even if network communication breaks between nodes (network partition). Essential for any distributed system β€” network failures WILL happen.

πŸ’‘ CAP in Practice β€” Which databases choose what?
Since P (partition tolerance) is unavoidable in distributed systems, the real choice is C vs A:
CP (Consistency + Partition Tolerance): MongoDB (default), HBase, Zookeeper β€” safe for banking, financial data.
AP (Availability + Partition Tolerance): Cassandra, CouchDB, DynamoDB β€” safe for social media, shopping carts where slight staleness is OK.
CA (no partition tolerance): Traditional RDBMS on a single server β€” no distribution.
Session 11
Practical NoSQL Design
Embedding vs Referencing Β· Schema Evolution Β· Column-Oriented Design Β· 2T+2L+2SL
πŸ“ Filing System Analogy Embedding = stapling related documents together in one folder. Open one folder, everything is there. Fast but the folder gets thick. Referencing = writing "see folder 37B" on a sticky note instead of photocopying it. Keeps folders thin, but you have to walk to folder 37B to get the full picture.
Embedding (Denormalized Documents)

Embed related data directly inside the parent document. Best when data is: always accessed together, doesn't change often, has a 1-to-few relationship, or doesn't need independent queries.

-- Example: Blog post with embedded comments
{
  "_id": "post_001",
  "title": "Introduction to MongoDB",
  "author": "Priya Sharma",
  "published": "2024-03-15",
  "tags": ["NoSQL", "MongoDB", "Database"],
  "comments": [                    ← embedded array of sub-documents
    {
      "user": "Rahul",
      "text": "Great article!",
      "date": "2024-03-16",
      "likes": 5
    },
    {
      "user": "Anita",
      "text": "Very helpful for my exam prep",
      "date": "2024-03-17",
      "likes": 3
    }
  ]
}
-- βœ… One query fetches post + all comments
-- ❌ If comments grow to 10,000+, document becomes huge (16MB limit in MongoDB)
Referencing (Normalized Documents)

Store related data in separate collections and reference by ID. Best when: data is accessed independently, there's a 1-to-many or many-to-many relationship, or the related data is frequently updated.

-- User document
{ "_id": "user_101", "name": "Priya", "email": "priya@example.com" }

-- Order documents (separate collection, reference user_id)
{ "_id": "order_501", "user_id": "user_101", "item": "Laptop", "amount": 65000 }
{ "_id": "order_502", "user_id": "user_101", "item": "Mouse",  "amount": 800   }

-- Query with $lookup (like SQL JOIN)
db.orders.aggregate([
  { $lookup: {
      from:         "users",
      localField:   "user_id",
      foreignField: "_id",
      as:           "user_details"
  }}
])
-- βœ… Orders and users can be queried and updated independently
-- ❌ Requires multiple queries (or $lookup which is slower than embedded)
Schema Evolution β€” Adding Fields Without Downtime

One of NoSQL's greatest advantages over SQL: you can add new fields to a collection without ALTER TABLE, without downtime, and without breaking existing applications. Old documents simply don't have the new field.

-- Scenario: You need to add 'loyalty_points' to all users
-- In SQL: ALTER TABLE users ADD loyalty_points INT DEFAULT 0; (might lock table)
-- In MongoDB: Just start inserting new docs with the field!

-- Backfill existing docs that don't have the field:
db.users.updateMany(
  { loyalty_points: { $exists: false } },   -- where field doesn't exist
  { $set: { loyalty_points: 0 } }             -- add it with default 0
);

-- Rename a field across all documents:
db.users.updateMany({}, { $rename: { "phone": "mobile_number" } });

-- Remove a field from all documents:
db.users.updateMany({}, { $unset: { "old_field": "" } });
MongoDB
Session 12
MongoDB β€” CRUD Operations in Depth
Database setup Β· insertOne/Many Β· find with operators Β· update operators Β· deleteOne/Many Β· 2T+4L+4SL
πŸ“¦ Warehouse of Diverse Boxes Imagine a warehouse full of cardboard boxes (documents). Each box can contain completely different items β€” one box has books, another has electronics, another has mixed stuff. MongoDB (the warehouse manager) doesn't open each box to check β€” it just stores and retrieves efficiently. The boxes are grouped by type into "aisles" (collections). You can add a new type of box anytime without redesigning the warehouse.
MongoDB Data Model vs SQL β€” Side by Side
SQL ConceptMongoDB EquivalentNotes
DatabaseDatabaseSame concept, different internal storage
TableCollectionCollections don't enforce schema
Row / RecordDocument (JSON / BSON)Each doc can have different fields
ColumnFieldFields exist only if set on each doc
Primary Key (auto)_id (ObjectId)Auto-generated 12-byte unique ID
IndexIndexSame concept, different syntax
JOIN$lookup (or embedded data)Prefer embedding over $lookup for performance
GROUP BY$group in aggregation pipelineMore powerful β€” can group on complex expressions
WHEREQuery filter document{field: {$operator: value}}
NULLnull or missing fieldAbsence of field β‰  null value in Mongo
CREATE β€” Database Setup + Inserting Documents
-- MongoDB creates DB when you first use it (no CREATE DATABASE command)
use restaurant_db          -- switch to (or create) database

-- Insert a single document
db.restaurants.insertOne({
  name:     "The Spice Route",
  borough:  "Manhattan",
  cuisine:  "Indian",
  address: {
    street: "74 W 47th St",
    zipcode: "10036",
    coord: [-73.9894, 40.7594]   ← nested document
  },
  grades: [                           ← array of sub-documents
    { grade: "A", score: 12, date: new Date("2024-01-15") },
    { grade: "B", score: 27, date: new Date("2023-06-20") }
  ]
})

-- Insert multiple documents at once
db.restaurants.insertMany([
  { name: "Mumbai Magic", borough: "Brooklyn", cuisine: "Indian" },
  { name: "Beijing Bites", borough: "Queens",   cuisine: "Chinese" },
  { name: "Pasta Palace", borough: "Bronx",    cuisine: "Italian" }
])
READ β€” Querying Documents (with all operators)
-- Basic queries
db.restaurants.find({})                          -- all documents
db.restaurants.find({ borough: "Manhattan" })   -- exact match
db.restaurants.findOne({ cuisine: "Indian" })   -- only first match

-- Comparison operators
db.restaurants.find({ "grades.score": { $gt:  90 } }) -- score > 90
db.restaurants.find({ "grades.score": { $gte: 70, $lt: 100 } })

-- From syllabus lab: restaurants NOT American, score > 70, longitude < -65.75
db.restaurants.find({
  cuisine: { $ne: "American" },
  "grades.score": { $gt: 70 },
  "address.coord.0": { $lt: -65.754168 }
})

-- NOT American, grade=A, NOT Brooklyn, sort by cuisine desc
db.restaurants.find({
  cuisine: { $ne: "American" },
  "grades.grade": "A",
  borough: { $ne: "Brooklyn" }
}).sort({ cuisine: -1 })

-- Logical operators
db.restaurants.find({
  $or: [
    { cuisine: "Indian" },
    { cuisine: "Chinese" }
  ]
})
db.restaurants.find({
  $and: [
    { borough: "Manhattan" },
    { "grades.score": { $gt: 80 } }
  ]
})

-- Projection: specify which fields to return (1=include, 0=exclude)
db.restaurants.find(
  { borough: "Manhattan" },
  { name: 1, cuisine: 1, _id: 0 }   -- show name+cuisine, hide _id
)

-- Sort, skip, limit (for pagination)
db.restaurants.find({})
  .sort({ name: 1 })    -- ascending by name
  .skip(20)             -- skip first 20 (page 2)
  .limit(10)            -- return 10 results
UPDATE β€” Modifying Documents (all update operators)
-- $set: add or change specific fields
db.restaurants.updateOne(
  { name: "The Spice Route" },
  { $set: { is_open: true, rating: 4.5 } }
)

-- $inc: increment a number
db.restaurants.updateMany(
  { cuisine: "Indian" },
  { $inc: { review_count: 1 } }   -- add 1 to review_count
)

-- $push: add element to an array
db.restaurants.updateOne(
  { name: "The Spice Route" },
  { $push: { grades: { grade: "A", score: 15, date: new Date() } } }
)

-- Upsert: update if exists, insert if not
db.restaurants.updateOne(
  { name: "New Restaurant" },
  { $set: { cuisine: "Fusion", borough: "Staten Island" } },
  { upsert: true }            -- creates new doc if not found
)
Sessions 13–14
MongoDB Indexing β€” How and Why
IXSCAN vs COLLSCAN Β· Index types Β· Compound index prefix rule Β· explain() Β· 4T+2L+2SL
πŸ“š Book Index Analogy β€” Detailed Imagine searching for the word "Normalization" in a 600-page DBMS textbook. Without index = start at page 1, read every page (very slow). With index = go to index section, find "Normalization β†’ pages 45, 89, 234" (3 page turns). MongoDB works identically. A COLLSCAN (collection scan) reads every document. An IXSCAN uses the index to jump directly to matching documents. On 10 million records, the difference is 30 seconds vs 5 milliseconds.
Why Are Indexes Critical?

Without indexes, every query performs a collection scan (COLLSCAN) β€” examining every single document. As your collection grows to millions of documents, this becomes unbearably slow. Indexes create a separate B-tree data structure that allows O(log n) lookup instead of O(n) scan.

⚠️ Index Trade-offs Reads: Dramatically faster β€” often 1000x+ speedup on large collections.
Writes: Slightly slower β€” every INSERT, UPDATE, DELETE must also update all relevant indexes.
Storage: Each index takes additional disk space.
Rule: Create indexes for columns that appear in WHERE, JOIN, ORDER BY, or $group conditions that run frequently.
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
  Creating Different Types of Indexes
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━

-- 1. Single field index (most common)
db.restaurants.createIndex({ borough: 1 })  -- ascending
db.restaurants.createIndex({ rating: -1 })  -- descending

-- 2. Compound index (multiple fields together)
db.restaurants.createIndex({ borough: 1, cuisine: 1, name: 1 })
-- This compound index helps queries on:
--   (borough)                ← yes, prefix
--   (borough, cuisine)       ← yes, prefix
--   (borough, cuisine, name) ← yes, full index
--   (cuisine)                ← NO! (no borough prefix)
--   (cuisine, name)          ← NO!

-- 3. Unique index β€” prevents duplicates
db.users.createIndex({ email: 1 }, { unique: true })

-- 4. Sparse index β€” only indexes documents WHERE field exists
db.users.createIndex({ phone: 1 }, { sparse: true })
-- Documents without 'phone' field are NOT in this index
-- Useful when field is optional (avoid indexing many nulls)

-- 5. Partial index β€” only index subset matching a condition
db.restaurants.createIndex(
  { borough: 1 },
  { partialFilterExpression: { rating: { $gt: 4.0 } } }
)
-- Only indexes restaurants with rating > 4.0
-- Much smaller index, much faster for queries on popular restaurants

-- 6. Text index β€” for full-text search
db.articles.createIndex({ content: "text", title: "text" })
db.articles.find({ $text: { $search: "MongoDB indexing performance" } })

━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
  Analyzing Query Performance with explain()
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
db.restaurants.find(
  { borough: "Manhattan", cuisine: "Indian" }
).explain("executionStats")

-- Key things to look for in explain() output:
-- winningPlan.stage: "IXSCAN" = βœ… using index, "COLLSCAN" = ❌ full scan
-- totalDocsExamined: should be close to nReturned (ideally equal)
-- executionTimeMillis: how long in milliseconds
-- nReturned: how many docs matched

-- Index management
db.restaurants.getIndexes()          -- list all indexes
db.restaurants.dropIndex("borough_1") -- drop specific index by name
db.restaurants.dropIndexes()          -- drop all except _id
Session 15
MongoDB β€” Aggregation Pipeline
All stages: $matchΒ·$groupΒ·$projectΒ·$sortΒ·$lookupΒ·$unwindΒ·$facet Β· 2T+4L+2SL
🏭 Car Manufacturing Assembly Line A car assembly line has multiple stations. Raw metal enters station 1 (cut into parts). Station 2 paints them. Station 3 assembles. Station 4 installs electronics. Station 5 does quality check. Each station does ONE job, passes to the next. MongoDB's aggregation pipeline works identically β€” each stage transforms documents and passes results downstream. The power comes from chaining these stages.
All Aggregation Pipeline Stages
StagePurposeSQL EquivalentExample
$matchFilter documents (always put early to reduce workload)WHERE{$match: {borough:"Manhattan"}}
$groupGroup documents + compute aggregate valuesGROUP BY{$group: {_id:"$cuisine", count:{$sum:1}}}
$projectInclude/exclude/rename/compute new fieldsSELECT col AS alias{$project: {name:1, price:1, _id:0}}
$sortSort documents by field(s)ORDER BY{$sort: {count:-1}}
$limitKeep only first N documentsLIMIT{$limit: 10}
$skipSkip first N documents (pagination)OFFSET{$skip: 20}
$unwindDeconstruct array β€” one doc per array element(no SQL equivalent){$unwind: "$grades"}
$lookupLeft outer join with another collectionLEFT JOINSee example below
$addFieldsAdd computed fields to documentsSELECT ..., expression AS col{$addFields: {total: {$multiply:["$qty","$price"]}}}
$countCount documents in pipelineCOUNT(*){$count: "total_restaurants"}
$out / $mergeWrite pipeline results to a collectionINSERT INTO ... SELECT{$out: "summary_collection"}
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
  Complete aggregation pipeline example
  From syllabus: restaurants with score > 90
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
db.restaurants.aggregate([
  // Stage 1: filter to Manhattan
  { $match: { borough: "Manhattan" } },

  // Stage 2: explode grades array β€” 1 doc per grade
  { $unwind: "$grades" },

  // Stage 3: only keep grades with score > 90
  { $match: { "grades.score": { $gt: 90 } } },

  // Stage 4: group by cuisine, compute stats
  { $group: {
      _id:       "$cuisine",
      avgScore:  { $avg:  "$grades.score" },
      maxScore:  { $max:  "$grades.score" },
      restCount: { $addToSet: "$name" },  // unique restaurant names
      total:     { $sum: 1 }
  }},

  // Stage 5: add a field counting unique restaurants
  { $addFields: {
      uniqueRestaurants: { $size: "$restCount" }
  }},

  // Stage 6: sort by average score descending
  { $sort: { avgScore: -1 } },

  // Stage 7: only top 5 cuisines
  { $limit: 5 },

  // Stage 8: clean up output
  { $project: {
      cuisine:           "$_id",
      avgScore:          { $round: ["$avgScore", 2] },
      uniqueRestaurants: 1,
      _id:               0
  }}
])

━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
  $lookup β€” Join two collections
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
db.orders.aggregate([
  { $lookup: {
      from:         "customers",  // collection to join
      localField:   "customer_id",// field in orders
      foreignField: "_id",         // field in customers
      as:           "customer_info"// name for joined array
  }},
  { $unwind: "$customer_info" },  // flatten the joined array
  { $project: {
      order_date:    1,
      amount:        1,
      "customer_info.name": 1,
      "customer_info.city": 1
  }}
])
XML, OLAP & Cassandra
Sessions 16–17
XML Data Model, Querying & OLTP vs OLAP Tools
XML structure Β· XPath Β· XQuery Β· XSLT Β· OLAP cube Β· 4T+4L+2SL
πŸ“¬ Structured Letter Analogy XML is like a formal business letter with clearly labeled sections β€” "Dear [Name], Re: [Subject], Body: [Content], Signed: [Name]." Each section has a label that describes what's inside. JSON is a modern leaner format (telegram). XPath = knowing which sentence to highlight. XQuery = asking a question across many letters. XSLT = reformatting the letter into a completely different template (like translating a formal letter into a casual email).
XML β€” eXtensible Markup Language

XML (eXtensible Markup Language) is a text-based format for representing structured data. Every element has an opening and closing tag. It's self-describing (tags explain the data). Widely used for: configuration files, web services (SOAP), data interchange between systems, and document storage.

<?xml version="1.0" encoding="UTF-8"?>
<library>
  <book id="B001" inStock="true">
    <title>Clean Code</title>
    <author>Robert C. Martin</author>
    <price currency="INR">499</price>
    <categories>
      <category>Programming</category>
      <category>Software Engineering</category>
    </categories>
  </book>
  <book id="B002" inStock="false">
    <title>The Pragmatic Programmer</title>
    <author>David Thomas</author>
    <price currency="INR">549</price>
  </book>
</library>

━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
  XPath β€” Navigate the XML tree
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
  /library              β†’ root element
  /library/book         β†’ all book elements
  /library/book[1]      β†’ first book only
  /library/book/@id     β†’ all id attributes
  /library/book[@id='B001']         β†’ book with id B001
  /library/book[@id='B001']/price   β†’ price of book B001
  //title               β†’ all title elements anywhere in doc
  /library/book[price > 500]        β†’ books costing over 500

━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
  XQuery β€” Query language for XML (like SQL for XML)
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
for    $b in doc("library.xml")/library/book
where  $b/price > 400
  and $b/@inStock = "true"
order by $b/price descending
return
  <result>
    <bookTitle>{$b/title/text()}</bookTitle>
    <cost>{$b/price/text()}</cost>
  </result>
XML vs JSON β€” Which to use when?
AspectXMLJSON
VerbosityVerbose (opening + closing tags)Compact
Attributes supportYes (on tags)No native attributes
CommentsSupportedNot supported
Schema validationXSD (XML Schema Definition)JSON Schema
TransformationsXSLT (powerful)JS/Python code
Use todayEnterprise systems, SOAP, config files, Office documentsREST APIs, NoSQL, web applications
Session 18
Introduction to Apache Cassandra
Peer-to-peer ring Β· Consistent hashing Β· Keyspace Β· Replication Β· cqlsh commands Β· 2T+2L+2SL
🌐 WhatsApp Message Delivery Analogy In MongoDB, if the master node goes down, writes stop until a new master is elected (seconds of downtime). Cassandra is like a WhatsApp group β€” if one member's phone is off, the message still gets delivered to everyone else. No one is "the master." Every node is equal. Data is automatically replicated to multiple nodes. Used by: Netflix (for 190+ countries), Instagram (billions of follows), Discord (billions of messages).
Cassandra Architecture β€” Key Concepts

πŸ”΅ Node

A single Cassandra server instance. Typically holds 1-3TB of data. Each node independently handles reads and writes for its data range.

πŸ”„ Ring Topology

All nodes are arranged in a conceptual ring. Each node "owns" a range of hash values. There is NO master β€” every node is equal (peer-to-peer).

#️⃣ Consistent Hashing

Cassandra hashes the partition key to determine which node(s) store the data. Moving data when adding/removing nodes is minimal (only neighboring ranges shift).

πŸ“‹ Commit Log

Every write is first appended to the commit log (sequential disk write β€” very fast). Then written to MemTable (in-memory). Flushed to SSTable periodically.

🏠 Keyspace

= Database in SQL terms. Contains tables. Keyspace-level settings define: replication strategy and replication factor.

πŸ“‘ Replication Factor

How many copies of each data item to keep. RF=3 means 3 nodes each hold a copy. If 1 node dies, 2 copies remain β†’ no data loss.

Cassandra vs MongoDB β€” When to Choose Which
FeatureMongoDBCassandra
ArchitecturePrimary-Secondary (replica sets)Peer-to-peer (masterless ring)
SchemaDynamic (schemaless)Static (schema must be defined)
Query flexibilityHigh (rich queries, aggregation)Limited (must design for query patterns)
Joins$lookup (or embedding)Not supported β€” denormalize instead
Write performanceGoodOutstanding (millions/second)
Best forCMS, catalogs, varied documentsTime-series, IoT, messaging, logs
Consistency modelStrong consistency (CP)Tunable consistency (AP by default)
Used byForbes, eBay, ExpediaNetflix, Apple, Instagram, Discord
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
  Getting Started with cqlsh
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
-- Start the CQL shell
$ cqlsh
$ cqlsh 127.0.0.1 9042    -- with specific host and port

-- Create a keyspace (with replication settings)
CREATE KEYSPACE college_db
WITH REPLICATION = {
  'class': 'SimpleStrategy',     -- for single DC
  'replication_factor': 3         -- 3 copies of all data
};
-- For production (multi-datacenter):
CREATE KEYSPACE prod_db
WITH REPLICATION = {
  'class': 'NetworkTopologyStrategy',
  'datacenter1': 3,
  'datacenter2': 2
};

USE college_db;       -- switch to keyspace
DESCRIBE KEYSPACES;   -- list all keyspaces
DESCRIBE TABLES;      -- list tables in current keyspace
DESCRIBE TABLE employees; -- see table structure
Session 19
Cassandra Table Design β€” Partition & Clustering Keys
Primary key design Β· Index creation Β· Query patterns first approach Β· 2T+2L+2SL
πŸ—„οΈ Filing Warehouse Analogy Imagine 6 warehouses across Mumbai. Partition key decides which warehouse (node) a box goes to β€” based on a hash of the key. Clustering key decides how the boxes inside that warehouse are arranged on shelves (sorted order). When you want a box, you first identify the right warehouse (partition key), then walk to the right shelf (clustering key). You CANNOT search all 6 warehouses simultaneously (no full-table scans).
PRIMARY KEY Design β€” The Most Critical Decision in Cassandra

Unlike SQL where you add indexes after design, Cassandra requires you to design your tables around your queries. The PRIMARY KEY determines: which node stores the data AND how data is sorted on each node. Get this wrong and your queries will either fail or be extremely slow.

Primary Key TypeSyntaxEffectUse when
Simple PKPRIMARY KEY (user_id)user_id is both partition + sort keyData accessed by single unique ID
Compound PKPRIMARY KEY (device_id, timestamp)device_id = partition key; timestamp = clustering key (sorts within partition)Time-series: "get all readings for device X, ordered by time"
Composite PartitionPRIMARY KEY ((country, city), user_id)country+city together determine which node; user_id sorts withinWhen single partition key creates "hot spots" (too much data on one node)
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
  Designing for query: "Get sensor readings for
  device X in descending time order"
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
CREATE TABLE sensor_readings (
  device_id   UUID,        -- partition key: groups all readings for one device
  read_time   TIMESTAMP,  -- clustering key: sorted newest-first
  temperature FLOAT,
  humidity    FLOAT,
  location    TEXT,
  PRIMARY KEY (device_id, read_time)
) WITH CLUSTERING ORDER BY (read_time DESC);

-- βœ… This query is FAST:
SELECT * FROM sensor_readings
WHERE device_id = 12345-uuid
AND   read_time > '2024-01-01'
LIMIT 100;

-- ❌ This query FAILS (no partition key):
SELECT * FROM sensor_readings
WHERE temperature > 30;  -- Cannot scan all partitions

━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
  Table Operations
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
-- Create employees table
CREATE TABLE employees (
  emp_id      UUID PRIMARY KEY,
  first_name  TEXT,
  last_name   TEXT,
  department  TEXT,
  salary      DECIMAL,
  hire_date   DATE,
  is_active   BOOLEAN
);

-- Add a new column (non-breaking change)
ALTER TABLE employees ADD email TEXT;
ALTER TABLE employees ADD skills LIST<TEXT>;

-- Create secondary index (for non-partition key queries)
CREATE INDEX emp_dept_idx ON employees(department);
-- Now you can query: WHERE department = 'Engineering'
-- But use sparingly β€” secondary indexes are expensive at scale

DROP INDEX   emp_dept_idx;
TRUNCATE TABLE employees;  -- delete all rows, keep structure
DROP TABLE   employees;
⚑ Cassandra Design Rules (Never Forget) 1. Design queries first, tables second. Each query pattern needs its own table (denormalize).
2. Partition key is mandatory in WHERE. Without it, Cassandra must scan all nodes.
3. Avoid too many rows per partition (>100K rows/partition = performance issues).
4. No JOINs, no transactions across partitions. Cassandra is built for single-partition operations.
Sessions 20–21
Cassandra CRUD, CQL Datatypes, Collections & UDT
All CQL types Β· LISTΒ·SETΒ·MAP Β· FROZEN Β· UDT Β· BATCH operations Β· 4T+4L
πŸŽ’ Multi-Compartment Bag Analogy CQL Collections = a bag with different compartments. LIST = the main compartment where order matters (books stacked in sequence β€” first in stays first). SET = the zipped side pocket (no duplicates allowed β€” you can't have 2 identical keychains). MAP = a card holder with labeled slots (key β†’ value: "Aadhar" β†’ "1234-5678", "PAN" β†’ "ABCDE1234F"). UDT = a custom-shaped organizer you design yourself.
CQL Datatypes β€” Complete Reference
CategoryCQL TypeDescriptionExample
NumericINT32-bit signed integerage, quantity
BIGINT64-bit signed integeruser_id, timestamp millis
FLOAT32-bit floating pointtemperature, rating
DOUBLE64-bit floating pointlatitude, longitude, precise amounts
DECIMALExact decimal (arbitrary precision)salary, price, financial data
TextTEXT / VARCHARUTF-8 encoded string (no size limit)name, email, description
ASCIIUS-ASCII string onlyStatus codes, old system IDs
BLOBBinary data, no validationImages, files, serialized objects
Date/TimeTIMESTAMPDate + time with millisecond precisioncreated_at, updated_at
DATEDate only (no time)birth_date, hire_date
TIMETime of day onlyoffice_open_time
SpecialUUIDUniversally unique identifierPrimary keys (use uuid() function)
TIMEUUIDVersion 1 UUID (time-based)Sortable by time, for time-series PKs
OtherBOOLEANtrue / falseis_active, is_verified
CollectionsLIST<T>, SET<T>, MAP<K,V>Multiple values in one columnSee examples below
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
  Collections β€” LIST, SET, MAP in action
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
CREATE TABLE student_profile (
  student_id  UUID PRIMARY KEY,
  name        TEXT,
  -- LIST: ordered, allows duplicates (exam scores in order)
  scores      LIST<INT>,
  -- SET: unordered, NO duplicates (enrolled courses)
  courses     SET<TEXT>,
  -- MAP: key-value (certification β†’ date earned)
  certs       MAP<TEXT, DATE>
);

-- Insert with collections
INSERT INTO student_profile (student_id, name, scores, courses, certs)
VALUES (
  uuid(),
  'Priya Sharma',
  [85, 90, 78, 92],                              -- LIST
  {'DBMS', 'OS', 'Networks', 'ML'},              -- SET
  {'AWS_Cloud': '2024-03-15', 'MongoDB': '2024-01-20'} -- MAP
);

-- Append to a LIST
UPDATE student_profile
SET scores = scores + [88]
WHERE student_id = some_uuid;

-- Add to a SET
UPDATE student_profile
SET courses = courses + {'Big Data'}
WHERE student_id = some_uuid;

-- Add to a MAP
UPDATE student_profile
SET certs['Cassandra'] = '2024-06-01'
WHERE student_id = some_uuid;

━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
  UDT β€” User Defined Type (custom complex type)
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
-- Define UDT (like a struct in C)
CREATE TYPE address_type (
  street  TEXT,
  city    TEXT,
  state   TEXT,
  pincode TEXT
);
-- Use in a table (must use FROZEN for non-collection UDT columns)
CREATE TABLE employees (
  emp_id     UUID PRIMARY KEY,
  name       TEXT,
  home_addr  FROZEN<address_type>,    -- single address
  past_addrs LIST<FROZEN<address_type>> -- list of addresses
);

-- Insert with UDT
INSERT INTO employees (emp_id, name, home_addr)
VALUES (
  uuid(),
  'Priya',
  { street: '101 Andheri East', city: 'Mumbai', state: 'MH', pincode: '400069' }
);
BATCH Operations

A BATCH groups multiple DML statements and executes them atomically within a single partition. In Cassandra, BATCH is NOT a full transaction β€” it guarantees atomicity only within the same partition. Cross-partition batches should be avoided as they are slow and add coordinator load.

-- Batch: all operations executed atomically
BEGIN BATCH
  -- Insert new employee
  INSERT INTO employees (emp_id, name, salary)
    VALUES(uuid(), 'Neha Patil', 45000);

  -- Update salary where commission = 0
  UPDATE employees
    SET salary = 20000
    WHERE emp_id = some_uuid;

  -- Change names starting with N to uppercase
  UPDATE employees
    SET name = toUpperCase(name)
    WHERE emp_id = another_uuid;
APPLY BATCH;

-- Logged BATCH (default): safe but slower β€” coordinator logs before applying
-- Unlogged BATCH: faster but not atomic
BEGIN UNLOGGED BATCH
  ...
APPLY BATCH;
Data Management
Session 22
Data-Driven Decisions, Enterprise Data Management & Data Cleaning
Data quality issues Β· Imputation Β· Outlier detection Β· ETL pipelines Β· Normalization vs Standardization Β· 2T+4L+2SL
🧹 Professional Kitchen Analogy A Michelin-star chef doesn't start cooking with ingredients straight from the market. They inspect every vegetable (profile data), throw out the rotten ones (remove nulls/outliers), wash and cut to uniform size (standardize formats), arrange them for efficiency (organize structure), then cook (analyse). "Garbage In = Garbage Out" is real β€” a machine learning model trained on dirty data will give worse predictions than no model at all.
Why Data Quality Matters

According to IBM, bad data costs businesses $3.1 trillion annually in the USA alone. In data pipelines, dirty data causes incorrect analytics, wrong business decisions, failed ML models, and regulatory compliance failures.

The 6 Dimensions of Data Quality

βœ… Completeness

Are all required values present? NULL rate per column should be monitored. Critical fields like customer_id should have 0% NULL.

🎯 Accuracy

Does the data correctly represent reality? Age of 150 = wrong. PIN code "00000" = wrong. Validate against reference data.

πŸ”— Consistency

Is data the same across systems? Customer address in CRM vs ERP should match. "Mumbai" vs "Bombay" = inconsistency.

⏱️ Timeliness

Is data fresh enough for its use? Yesterday's stock prices for real-time trading = useless.

πŸ†” Uniqueness

Are there duplicate records? Same customer entered twice with slightly different names creates double billing.

βœ”οΈ Validity

Does data conform to defined formats and ranges? Email must have @, phone must be 10 digits, date must be a valid date.

Common Data Problems and Solutions
ProblemDetection MethodSolution OptionsSQL/Python example
Missing Values (NULL)COUNT(*) WHERE col IS NULLMean/median imputation, mode for categorical, forward-fill, drop row, flag as "Unknown"UPDATE t SET salary = (SELECT AVG(salary) FROM t) WHERE salary IS NULL
Duplicate RecordsGROUP BY all cols + HAVING COUNT > 1Keep first occurrence, compare timestamps, fuzzy matching for near-duplicatesSELECT name, COUNT(*) FROM customers GROUP BY email HAVING COUNT(*) > 1
OutliersZ-score |z| > 3; IQR method; visual box plotsCap at threshold (winsorization), remove, treat separately, transform (log scale)Z-score: (value - mean) / std_dev
Inconsistent FormatSELECT DISTINCT + manual reviewUPPER()/LOWER(), TRIM(), REGEXP_REPLACE(), standardization tablesUPDATE t SET city = UPPER(TRIM(city))
Wrong Data TypeSchema validation, SELECT WHERECAST, convert pipeline, reject invalid on ingestionSELECT * FROM t WHERE age NOT REGEXP '^[0-9]+$'
Referential IntegrityLEFT JOIN to reference table, check NULLsAdd foreign key constraints, clean orphan recordsSELECT * FROM orders o LEFT JOIN customers c ON ... WHERE c.id IS NULL
Outlier Detection Methods in Detail
πŸ“Š Z-Score Method
Formula: z = (x βˆ’ ΞΌ) / Οƒ
where ΞΌ = mean, Οƒ = standard deviation.
If |z| > 3: that value is an outlier.
Works best when data is normally distributed.
πŸ“¦ IQR Method (Interquartile Range)
Q1 = 25th percentile, Q3 = 75th percentile.
IQR = Q3 - Q1.
Lower fence = Q1 - (1.5 Γ— IQR).
Upper fence = Q3 + (1.5 Γ— IQR).
Values outside fences = outliers. Robust to skewed data.
Normalization vs Standardization β€” Scaling Features

Machine learning algorithms (KNN, SVM, Neural Networks) are sensitive to the scale of features. If "salary" ranges from 10,000 to 1,00,000 and "age" ranges from 22 to 65, the ML model will be dominated by salary. Scaling puts all features on similar ranges.

TechniqueFormulaResult RangeWhen to useSensitive to outliers?
Min-Max Normalization(x βˆ’ min) / (max βˆ’ min)[0, 1] alwaysWhen you know min/max, bounded output needed, KNN, Neural networksYes β€” one extreme outlier shifts everything
Z-score Standardization(x βˆ’ mean) / std_devTypically [βˆ’3, 3], mean=0, SD=1Data has outliers, SVM, Linear/Logistic Regression, PCALess sensitive than min-max
Robust Scaling(x βˆ’ median) / IQRCentered at 0, no fixed rangeHeavy outliers in data, can't remove themNo β€” uses median and IQR
ETL Data Quality Pipeline β€” Step by Step
1
Profile: Run automated statistics on every column β€” NULL count, distinct values, min/max/mean, data type mismatches. Build a "data quality scorecard." Tools: Great Expectations, Deequ, custom SQL.
2
Cleanse: Fix the problems found in profiling β€” handle NULLs, remove duplicates, fix types, standardize values. Document every transformation (data lineage).
3
Validate: Apply business rules β€” "age must be between 18 and 70 for this dataset," "amount cannot be negative," "date cannot be in the future." Reject or flag violating records.
4
Enrich: Add derived columns (age from DOB, full_name from first+last), join with reference data (add country name from country code), compute business metrics.
5
Load: Write to target system with incremental loading strategy. Log load statistics β€” rows inserted, rows rejected, load time. Alert on anomalies.
Data Lineage & Metadata Management

Data lineage tracks the journey of data from origin to destination β€” where did it come from, what transformations were applied, who accessed it. Critical for: debugging data quality issues ("why is this sales figure wrong?"), regulatory compliance (GDPR, HIPAA), and impact analysis ("if I change this ETL rule, what downstream reports are affected?").

πŸ“‹ Incremental Loading Strategies
Full Load: Reload all data every time. Simple but slow and resource-heavy for large datasets. Use for small lookup tables.
Incremental (Timestamp): Load only records where updated_at > last_load_time. Fast. Requires a reliable update timestamp column.
CDC (Change Data Capture): Monitor database transaction logs for changes (INSERT/UPDATE/DELETE) and apply them to the target. Near real-time, no polling delay. Tools: Debezium, Oracle GoldenGate.