Core SQL concepts every interview starts with — NULLs, keys, constraints, data types and aliases.
SQL (Structured Query Language) is a standardized programming language used for managing and manipulating relational databases. It enables users to create, read, update, and delete data in database systems.
SELECT first_name, last_name, email FROM employees WHERE department = 'Engineering' ORDER BY last_name;
A Primary Key uniquely identifies each record in a table and cannot contain NULL values. A Foreign Key is a field that links to the Primary Key of another table, establishing relationships between tables.
CREATE TABLE employees (
employee_id INT PRIMARY KEY,
first_name VARCHAR(50),
last_name VARCHAR(50),
email VARCHAR(100)
);
CREATE TABLE orders (
order_id INT PRIMARY KEY,
order_date DATE,
employee_id INT,
FOREIGN KEY (employee_id) REFERENCES employees(employee_id)
);
Database normalization is the process of organizing data to reduce redundancy and improve data integrity. It involves dividing large tables into smaller, related tables and defining relationships between them.
CREATE TABLE employees (
employee_id INT,
name VARCHAR(100),
department VARCHAR(100),
dept_location VARCHAR(100),
skill1 VARCHAR(50),
skill2 VARCHAR(50),
skill3 VARCHAR(50)
);
CREATE TABLE employees (
employee_id INT PRIMARY KEY,
name VARCHAR(100),
department_id INT
);
CREATE TABLE departments (
department_id INT PRIMARY KEY,
name VARCHAR(100),
location VARCHAR(100)
);
CREATE TABLE employee_skills (
employee_id INT,
skill VARCHAR(50),
PRIMARY KEY (employee_id, skill)
);
NULL represents missing, unknown, or inapplicable data. It's not the same as empty string ('') or zero (0).
-- Sample data INSERT INTO employees (id, name, phone, salary) VALUES (1, 'John Doe', NULL, 50000), -- phone is unknown (2, 'Jane Smith', '', 60000), -- phone is empty string (3, 'Bob Wilson', '555-1234', 0); -- phone exists, salary is 0 -- Different behaviors SELECT * FROM employees WHERE phone IS NULL; -- Returns John only SELECT * FROM employees WHERE phone = ''; -- Returns Jane only SELECT * FROM employees WHERE salary = 0; -- Returns Bob only
-- Use COALESCE to provide default values SELECT name, COALESCE(phone, 'No phone') as contact FROM employees; -- Use NULLIF to treat specific values as NULL SELECT name, NULLIF(salary, 0) as actual_salary FROM employees;
Clustered indexes determine the physical order of data in a table, while non-clustered indexes are separate structures that point to the data.
-- Clustered index (only one per table, SQL Server syntax) CREATE CLUSTERED INDEX idx_employees_id ON employees(id); -- Non-clustered index (multiple per table) CREATE NONCLUSTERED INDEX idx_employees_email ON employees(email); CREATE NONCLUSTERED INDEX idx_employees_dept_salary ON employees(department, salary);
Constraints enforce rules on data columns to maintain data integrity and accuracy.
CREATE TABLE employees (
-- NOT NULL: Column cannot contain NULL
id INT NOT NULL,
-- UNIQUE: All values must be different
email VARCHAR(100) UNIQUE,
-- PRIMARY KEY: Unique identifier for table
employee_id INT PRIMARY KEY,
-- FOREIGN KEY: Links to another table
department_id INT,
CONSTRAINT fk_department
FOREIGN KEY (department_id)
REFERENCES departments(id),
-- CHECK: Validates data based on condition
salary DECIMAL(10,2) CHECK (salary >= 0),
age INT CHECK (age >= 18 AND age <= 65),
-- DEFAULT: Sets default value if not specified
hire_date DATE DEFAULT CURRENT_DATE,
status VARCHAR(20) DEFAULT 'active'
);
-- Add NOT NULL constraint
ALTER TABLE employees MODIFY COLUMN name VARCHAR(100) NOT NULL;
-- Add UNIQUE constraint
ALTER TABLE employees ADD CONSTRAINT uc_email UNIQUE (email);
-- Add CHECK constraint
ALTER TABLE employees ADD CONSTRAINT chk_salary
CHECK (salary >= 0);
-- Add FOREIGN KEY constraint
ALTER TABLE employees ADD CONSTRAINT fk_dept
FOREIGN KEY (department_id) REFERENCES departments(id);
-- Add DEFAULT constraint
ALTER TABLE employees ALTER COLUMN status
SET DEFAULT 'active';
Aliases give temporary names to tables, columns, or expressions, improving query readability and enabling self-joins.
-- Basic column aliases
SELECT
first_name || ' ' || last_name AS full_name,
salary * 12 AS annual_salary,
department AS dept,
COUNT(*) AS employee_count
FROM employees
GROUP BY department;
-- Aliases with special characters or spaces
SELECT
name AS "Employee Name",
salary AS "Monthly Salary (USD)",
hire_date AS "Date Hired"
FROM employees;
-- Aliases in ORDER BY
SELECT name, salary * 1.1 AS new_salary
FROM employees
ORDER BY new_salary DESC;
-- Simple table aliases
SELECT e.name, d.department_name
FROM employees e
JOIN departments d ON e.department_id = d.department_id;
-- Self-join using aliases (essential!)
SELECT
e.name AS employee_name,
m.name AS manager_name
FROM employees e
LEFT JOIN employees m ON e.manager_id = m.employee_id;
-- Multiple table aliases in complex queries
SELECT
c.name AS customer_name,
o.order_date,
p.name AS product_name,
oi.quantity,
s.name AS supplier_name
FROM customers c
JOIN orders o ON c.customer_id = o.customer_id
JOIN order_items oi ON o.order_id = oi.order_id
JOIN products p ON oi.product_id = p.product_id
JOIN suppliers s ON p.supplier_id = s.supplier_id;
-- Subquery in FROM clause requires alias
SELECT dept_stats.department, dept_stats.avg_salary
FROM (
SELECT
department,
AVG(salary) AS avg_salary,
COUNT(*) AS emp_count
FROM employees
GROUP BY department
) AS dept_stats
WHERE dept_stats.emp_count > 10;
-- CTE aliases
WITH department_summary AS (
SELECT
department,
AVG(salary) AS avg_sal,
MAX(salary) AS max_sal
FROM employees
GROUP BY department
)
SELECT * FROM department_summary
WHERE avg_sal > 60000;
SQL data types define what kind of data a column can hold. Choosing the right type affects storage, performance, and data integrity.
CREATE TABLE example_numbers (
-- Integer types
id INT PRIMARY KEY, -- 4 bytes, -2B to 2B
small_id SMALLINT, -- 2 bytes, -32K to 32K
big_id BIGINT, -- 8 bytes, huge range
-- Exact numeric (decimal)
price DECIMAL(10, 2), -- 10 total digits, 2 decimal places
precise NUMERIC(15, 5), -- Same as DECIMAL
-- Floating point (approximate)
weight FLOAT, -- 4 bytes, ~7 digits precision
measurement DOUBLE PRECISION, -- 8 bytes, ~15 digits precision
rating REAL -- 4 bytes, ~6 digits precision
-- Specialized
serial_id SERIAL, -- Auto-incrementing integer (PostgreSQL)
money_amount MONEY -- Currency type (SQL Server/PostgreSQL)
);
CREATE TABLE example_strings (
-- Fixed length (padded with spaces)
country_code CHAR(2), -- Exactly 2 characters
ssn CHAR(9), -- Exactly 9 characters
-- Variable length
name VARCHAR(100), -- Max 100 characters
description TEXT, -- Very large text (unlimited in some DBs)
-- Unicode support
unicode_name NVARCHAR(100), -- SQL Server unicode
postgres_unicode VARCHAR(100), -- PostgreSQL (always unicode)
-- Specialized
email VARCHAR(255) CHECK (email LIKE '%@%'),
url VARCHAR(2083), -- Max URL length
json_data JSON, -- JSON document (PostgreSQL/MySQL 5.7+)
xml_data XML -- XML document
);
CREATE TABLE example_dates (
-- Date only
birth_date DATE, -- '2023-12-25'
-- Time only
meeting_time TIME, -- '14:30:00'
meeting_time_tz TIME WITH TIME ZONE, -- '14:30:00-05'
-- Date and time
created_at TIMESTAMP, -- '2023-12-25 14:30:00'
updated_at TIMESTAMPTZ, -- With timezone
-- Specialized
appointment TIMESTAMP(0), -- No fractional seconds
interval_field INTERVAL, -- Time interval '1 day 2 hours'
-- Database-specific
year_field YEAR, -- MySQL only
datetime_field DATETIME, -- MySQL/SQL Server
smalldatetime_field SMALLDATETIME -- SQL Server
);