-- INNER JOIN
SELECT e.EmployeeID, e.Name, d.DepartmentName
FROM Employees e
INNER JOIN Departments d ON e.DepartmentID = d.DepartmentID;
-- LEFT JOIN
SELECT e.EmployeeID, e.Name, d.DepartmentName
FROM Employees e
LEFT JOIN Departments d ON e.DepartmentID = d.DepartmentID;
-- RIGHT JOIN
SELECT e.EmployeeID, e.Name, d.DepartmentName
FROM Employees e
RIGHT JOIN Departments d ON e.DepartmentID = d.DepartmentID;
-- FULL JOIN
SELECT e.EmployeeID, e.Name, d.DepartmentName
FROM Employees e
FULL JOIN Departments d ON e.DepartmentID = d.DepartmentID;
-- CROSS JOIN
SELECT e.Name, d.DepartmentName
FROM Employees e
CROSS JOIN Departments d;
-- INNER JOIN
SELECT e.EmployeeID, e.Name, d.DepartmentName
FROM Employees e
INNER JOIN Departments d ON e.DepartmentID = d.DepartmentID;
-- LEFT JOIN
SELECT e.EmployeeID, e.Name, d.DepartmentName
FROM Employees e
LEFT JOIN Departments d ON e.DepartmentID = d.DepartmentID;
-- RIGHT JOIN
SELECT e.EmployeeID, e.Name, d.DepartmentName
FROM Employees e
RIGHT JOIN Departments d ON e.DepartmentID = d.DepartmentID;
-- FULL JOIN (MySQL 8.0.14+)
SELECT e.EmployeeID, e.Name, d.DepartmentName
FROM Employees e
FULL JOIN Departments d ON e.DepartmentID = d.DepartmentID;
-- CROSS JOIN
SELECT e.Name, d.DepartmentName
FROM Employees e
CROSS JOIN Departments d;
-- Create table with index
CREATE TABLE Employees (
EmployeeID INT PRIMARY KEY,
Name VARCHAR(50)
);
CREATE NONCLUSTERED INDEX IX_Employees_Name ON Employees(Name);
-- INSERT (index updated)
INSERT INTO Employees VALUES (1, 'John');
-- UPDATE (index updated if Name changes)
UPDATE Employees SET Name = 'Jane' WHERE EmployeeID = 1;
-- DELETE (index updated)
DELETE FROM Employees WHERE EmployeeID = 1;
-- Create table with index
CREATE TABLE Employees (
EmployeeID INT PRIMARY KEY,
Name VARCHAR(50)
);
CREATE INDEX IX_Employees_Name ON Employees(Name);
-- INSERT (index updated)
INSERT INTO Employees VALUES (1, 'John');
-- UPDATE (index updated if Name changes)
UPDATE Employees SET Name = 'Jane' WHERE EmployeeID = 1;
-- DELETE (index updated)
DELETE FROM Employees WHERE EmployeeID = 1;
SELECT EmployeeID, Name, Salary,
ROW_NUMBER() OVER (ORDER BY Salary DESC) AS RowNum,
RANK() OVER (ORDER BY Salary DESC) AS Rank,
DENSE_RANK() OVER (ORDER BY Salary DESC) AS DenseRank
FROM Employees;
SELECT EmployeeID, Name, Salary,
ROW_NUMBER() OVER (ORDER BY Salary DESC) AS RowNum,
RANK() OVER (ORDER BY Salary DESC) AS Rank,
DENSE_RANK() OVER (ORDER BY Salary DESC) AS DenseRank
FROM Employees;
-- Clustered Index (on primary key)
CREATE TABLE Employees (
EmployeeID INT PRIMARY KEY CLUSTERED,
Name VARCHAR(50)
);
-- Non-Clustered Index
CREATE NONCLUSTERED INDEX IX_Employees_Name ON Employees(Name);
-- Clustered Index (InnoDB, implicit with primary key)
CREATE TABLE Employees (
EmployeeID INT PRIMARY KEY,
Name VARCHAR(50)
);
-- Non-Clustered Index
CREATE INDEX IX_Employees_Name ON Employees(Name);
-- Rank employees by salary within department
SELECT EmployeeID, Name, DepartmentID, Salary,
ROW_NUMBER() OVER (PARTITION BY DepartmentID ORDER BY Salary DESC) AS RowNum,
RANK() OVER (PARTITION BY DepartmentID ORDER BY Salary DESC) AS Rank
FROM Employees;
-- Running total of salaries
SELECT EmployeeID, Name, Salary,
SUM(Salary) OVER (ORDER BY EmployeeID) AS RunningTotal
FROM Employees;
-- Rank employees by salary within department
SELECT EmployeeID, Name, DepartmentID, Salary,
ROW_NUMBER() OVER (PARTITION BY DepartmentID ORDER BY Salary DESC) AS RowNum,
RANK() OVER (PARTITION BY DepartmentID ORDER BY Salary DESC) AS Rank
FROM Employees;
-- Running total of salaries
SELECT EmployeeID, Name, Salary,
SUM(Salary) OVER (ORDER BY EmployeeID) AS RunningTotal
FROM Employees;
-- Before optimization
SELECT * FROM Employees e
JOIN Departments d ON e.DepartmentID = d.DepartmentID
WHERE UPPER(e.Name) = 'JOHN';
-- After optimization
CREATE INDEX IX_Employees_Name ON Employees(Name);
SELECT e.EmployeeID, e.Name, d.DepartmentName
FROM Employees e
JOIN Departments d ON e.DepartmentID = d.DepartmentID
WHERE e.Name = 'John';
-- Before optimization
SELECT * FROM Employees e
JOIN Departments d ON e.DepartmentID = d.DepartmentID
WHERE UPPER(e.Name) = 'JOHN';
-- After optimization
CREATE INDEX IX_Employees_Name ON Employees(Name);
SELECT e.EmployeeID, e.Name, d.DepartmentName
FROM Employees e
JOIN Departments d ON e.DepartmentID = d.DepartmentID
WHERE e.Name = 'John';
-- CTE
WITH HighEarners AS (
SELECT EmployeeID, Name, Salary
FROM Employees
WHERE Salary > 50000
)
SELECT e.Name, d.DepartmentName
FROM HighEarners e
JOIN Departments d ON e.EmployeeID = d.DepartmentID;
-- Subquery
SELECT e.Name, d.DepartmentName
FROM (SELECT EmployeeID, Name FROM Employees WHERE Salary > 50000) e
JOIN Departments d ON e.EmployeeID = d.DepartmentID;
-- CTE
WITH HighEarners AS (
SELECT EmployeeID, Name, Salary
FROM Employees
WHERE Salary > 50000
)
SELECT e.Name, d.DepartmentName
FROM HighEarners e
JOIN Departments d ON e.EmployeeID = d.DepartmentID;
-- Subquery
SELECT e.Name, d.DepartmentName
FROM (SELECT EmployeeID, Name FROM Employees WHERE Salary > 50000) e
JOIN Departments d ON e.EmployeeID = d.DepartmentID;
-- Reusable CTE
WITH SalesByRegion AS (
SELECT Region, SUM(Amount) AS TotalSales
FROM Sales
GROUP BY Region
)
SELECT Region, TotalSales, TotalSales * 0.1 AS Tax
FROM SalesByRegion
WHERE TotalSales > 10000;
-- Reusable CTE
WITH SalesByRegion AS (
SELECT Region, SUM(Amount) AS TotalSales
FROM Sales
GROUP BY Region
)
SELECT Region, TotalSales, TotalSales * 0.1 AS Tax
FROM SalesByRegion
WHERE TotalSales > 10000;
-- Recursive CTE for employee hierarchy
WITH EmployeeHierarchy AS (
-- Anchor: Top-level employees (no manager)
SELECT EmployeeID, Name, ManagerID, 0 AS Level
FROM Employees
WHERE ManagerID IS NULL
UNION ALL
-- Recursive: Employees reporting to managers
SELECT e.EmployeeID, e.Name, e.ManagerID, eh.Level + 1
FROM Employees e
INNER JOIN EmployeeHierarchy eh ON e.ManagerID = eh.EmployeeID
)
SELECT EmployeeID, Name, ManagerID, Level
FROM EmployeeHierarchy
ORDER BY Level, Name;
-- Recursive CTE for employee hierarchy
WITH RECURSIVE EmployeeHierarchy AS (
-- Anchor: Top-level employees (no manager)
SELECT EmployeeID, Name, ManagerID, 0 AS Level
FROM Employees
WHERE ManagerID IS NULL
UNION ALL
-- Recursive: Employees reporting to managers
SELECT e.EmployeeID, e.Name, e.ManagerID, eh.Level + 1
FROM Employees e
INNER JOIN EmployeeHierarchy eh ON e.ManagerID = eh.EmployeeID
)
SELECT EmployeeID, Name, ManagerID, Level
FROM EmployeeHierarchy
ORDER BY Level, Name;
-- 1NF: No repeating groups
CREATE TABLE Orders (
OrderID INT PRIMARY KEY,
CustomerID INT,
Product VARCHAR(50)
);
-- 2NF: Remove partial dependency
CREATE TABLE OrderDetails (
OrderID INT,
ProductID INT,
Quantity INT,
PRIMARY KEY (OrderID, ProductID)
);
-- 3NF: Remove transitive dependency
CREATE TABLE Customers (
CustomerID INT PRIMARY KEY,
Name VARCHAR(50),
City VARCHAR(50)
);
-- 1NF: No repeating groups
CREATE TABLE Orders (
OrderID INT PRIMARY KEY,
CustomerID INT,
Product VARCHAR(50)
);
-- 2NF: Remove partial dependency
CREATE TABLE OrderDetails (
OrderID INT,
ProductID INT,
Quantity INT,
PRIMARY KEY (OrderID, ProductID)
);
-- 3NF: Remove transitive dependency
CREATE TABLE Customers (
CustomerID INT PRIMARY KEY,
Name VARCHAR(50),
City VARCHAR(50)
);
-- Demonstrating ACID with a transaction
BEGIN TRANSACTION;
BEGIN TRY
INSERT INTO Orders (OrderID, CustomerID) VALUES (1, 101);
INSERT INTO OrderDetails (OrderID, ProductID, Quantity) VALUES (1, 501, 10);
COMMIT; -- Atomic, Consistent, Isolated, Durable
END TRY
BEGIN CATCH
ROLLBACK; -- Ensures Atomicity on failure
SELECT ERROR_MESSAGE();
END CATCH;
-- Demonstrating ACID with a transaction
START TRANSACTION;
INSERT INTO Orders (OrderID, CustomerID) VALUES (1, 101);
INSERT INTO OrderDetails (OrderID, ProductID, Quantity) VALUES (1, 501, 10);
COMMIT; -- Atomic, Consistent, Isolated, Durable
-- On error: ROLLBACK;
BEGIN TRANSACTION;
BEGIN TRY
UPDATE Accounts
SET Balance = Balance - 100
WHERE AccountID = 1;
UPDATE Accounts
SET Balance = Balance + 100
WHERE AccountID = 2;
COMMIT;
END TRY
BEGIN CATCH
ROLLBACK;
SELECT ERROR_MESSAGE();
END CATCH;
START TRANSACTION;
UPDATE Accounts
SET Balance = Balance - 100
WHERE AccountID = 1;
UPDATE Accounts
SET Balance = Balance + 100
WHERE AccountID = 2;
COMMIT;
-- On error, manually rollback
-- ROLLBACK;
-- EXISTS: Check for orders
SELECT Name
FROM Employees e
WHERE EXISTS (SELECT 1 FROM Orders o WHERE o.EmployeeID = e.EmployeeID);
-- IN: Check for departments
SELECT Name
FROM Employees
WHERE DepartmentID IN (SELECT DepartmentID FROM Departments WHERE Location = 'NY');
-- EXISTS: Check for orders
SELECT Name
FROM Employees e
WHERE EXISTS (SELECT 1 FROM Orders o WHERE o.EmployeeID = e.EmployeeID);
-- IN: Check for departments
SELECT Name
FROM Employees
WHERE DepartmentID IN (SELECT DepartmentID FROM Departments WHERE Location = 'NY');
-- Page 2, 10 rows per page
SELECT EmployeeID, Name, Salary
FROM Employees
ORDER BY Salary DESC
OFFSET 10 ROWS FETCH NEXT 10 ROWS ONLY;
-- Page 2, 10 rows per page
SELECT EmployeeID, Name, Salary
FROM Employees
ORDER BY Salary DESC
LIMIT 10 OFFSET 10;
-- Create table with soft delete
CREATE TABLE Employees (
EmployeeID INT PRIMARY KEY,
Name VARCHAR(50),
IsDeleted BIT DEFAULT 0
);
-- Soft delete
UPDATE Employees
SET IsDeleted = 1
WHERE EmployeeID = 1;
-- Query active records
SELECT * FROM Employees WHERE IsDeleted = 0;
-- Create table with soft delete
CREATE TABLE Employees (
EmployeeID INT PRIMARY KEY,
Name VARCHAR(50),
IsDeleted BOOLEAN DEFAULT FALSE
);
-- Soft delete
UPDATE Employees
SET IsDeleted = TRUE
WHERE EmployeeID = 1;
-- Query active records
SELECT * FROM Employees WHERE IsDeleted = FALSE;
-- Example to avoid deadlock by consistent locking order
BEGIN TRANSACTION;
UPDATE Employees SET Name = 'John' WHERE EmployeeID = 1;
UPDATE Departments SET Name = 'HR' WHERE DepartmentID = 1;
COMMIT;
-- Monitor deadlocks
SELECT * FROM sys.dm_tran_locks WHERE resource_type = 'OBJECT';
-- MySQL: Use consistent locking order
START TRANSACTION;
UPDATE Employees SET Name = 'John' WHERE EmployeeID = 1;
UPDATE Departments SET Name = 'HR' WHERE DepartmentID = 1;
COMMIT;
-- Check for deadlocks (InnoDB)
SHOW ENGINE INNODB STATUS;
-- Using DENSE_RANK
WITH RankedSalaries AS (
SELECT EmployeeID, Name, Salary,
DENSE_RANK() OVER (ORDER BY Salary DESC) AS SalaryRank
FROM Employees
)
SELECT EmployeeID, Name, Salary
FROM RankedSalaries
WHERE SalaryRank = 2;
-- Using MAX
SELECT MAX(Salary) AS SecondHighest
FROM Employees
WHERE Salary < (SELECT MAX(Salary) FROM Employees);
-- Using DENSE_RANK
WITH RankedSalaries AS (
SELECT EmployeeID, Name, Salary,
DENSE_RANK() OVER (ORDER BY Salary DESC) AS SalaryRank
FROM Employees
)
SELECT EmployeeID, Name, Salary
FROM RankedSalaries
WHERE SalaryRank = 2;
-- Using LIMIT
SELECT Salary
FROM Employees
ORDER BY Salary DESC
LIMIT 1 OFFSET 1;
-- Surrogate Key
CREATE TABLE Employees (
EmployeeID INT IDENTITY(1,1) PRIMARY KEY, -- Surrogate
Email VARCHAR(100) UNIQUE -- Natural
);
-- Insert
INSERT INTO Employees (Email) VALUES ('[email protected]');
-- Surrogate Key
CREATE TABLE Employees (
EmployeeID INT AUTO_INCREMENT PRIMARY KEY, -- Surrogate
Email VARCHAR(100) UNIQUE -- Natural
);
-- Insert
INSERT INTO Employees (Email) VALUES ('[email protected]');
-- Audit table
CREATE TABLE SalaryAudit (
AuditID INT IDENTITY PRIMARY KEY,
EmployeeID INT,
OldSalary INT,
NewSalary INT,
ChangeDate DATETIME
);
-- Trigger
CREATE TRIGGER trg_AfterUpdateSalary
ON Employees
AFTER UPDATE
AS
BEGIN
INSERT INTO SalaryAudit (EmployeeID, OldSalary, NewSalary, ChangeDate)
SELECT i.EmployeeID, d.Salary, i.Salary, GETDATE()
FROM inserted i
JOIN deleted d ON i.EmployeeID = d.EmployeeID
WHERE i.Salary != d.Salary;
END;
-- Test
UPDATE Employees SET Salary = 60000 WHERE EmployeeID = 1;
-- Audit table
CREATE TABLE SalaryAudit (
AuditID INT AUTO_INCREMENT PRIMARY KEY,
EmployeeID INT,
OldSalary INT,
NewSalary INT,
ChangeDate DATETIME
);
-- Trigger
DELIMITER //
CREATE TRIGGER trg_AfterUpdateSalary
AFTER UPDATE ON Employees
FOR EACH ROW
BEGIN
IF NEW.Salary != OLD.Salary THEN
INSERT INTO SalaryAudit (EmployeeID, OldSalary, NewSalary, ChangeDate)
VALUES (NEW.EmployeeID, OLD.Salary, NEW.Salary, NOW());
END IF;
END //
DELIMITER ;
-- Test
UPDATE Employees SET Salary = 60000 WHERE EmployeeID = 1;
CREATE TABLE Departments (
DepartmentID INT PRIMARY KEY,
Name VARCHAR(50) NOT NULL
);
CREATE TABLE Employees (
EmployeeID INT PRIMARY KEY,
DepartmentID INT,
Salary INT CHECK (Salary > 0),
FOREIGN KEY (DepartmentID) REFERENCES Departments(DepartmentID) ON DELETE CASCADE
);
-- Transaction for integrity
BEGIN TRANSACTION;
INSERT INTO Departments VALUES (1, 'HR');
INSERT INTO Employees VALUES (1, 1, 50000);
COMMIT;
CREATE TABLE Departments (
DepartmentID INT PRIMARY KEY,
Name VARCHAR(50) NOT NULL
);
CREATE TABLE Employees (
EmployeeID INT PRIMARY KEY,
DepartmentID INT,
Salary INT CHECK (Salary > 0),
FOREIGN KEY (DepartmentID) REFERENCES Departments(DepartmentID) ON DELETE CASCADE
);
-- Transaction for integrity
START TRANSACTION;
INSERT INTO Departments VALUES (1, 'HR');
INSERT INTO Employees VALUES (1, 1, 50000);
COMMIT;
-- Temporary Table
CREATE TABLE #TempEmployees (
EmployeeID INT PRIMARY KEY,
Name VARCHAR(50)
);
INSERT INTO #TempEmployees VALUES (1, 'John');
SELECT * FROM #TempEmployees;
DROP TABLE #TempEmployees;
-- Table Variable
DECLARE @TempTable TABLE (
EmployeeID INT PRIMARY KEY,
Name VARCHAR(50)
);
INSERT INTO @TempTable VALUES (1, 'John');
SELECT * FROM @TempTable;
-- Temporary Table
CREATE TEMPORARY TABLE TempEmployees (
EmployeeID INT PRIMARY KEY,
Name VARCHAR(50)
);
INSERT INTO TempEmployees VALUES (1, 'John');
SELECT * FROM TempEmployees;
DROP TEMPORARY TABLE TempEmployees;
-- Table Variable (not supported, use temporary table)
CREATE TEMPORARY TABLE TempTable (
EmployeeID INT PRIMARY KEY,
Name VARCHAR(50)
);
INSERT INTO TempTable VALUES (1, 'John');
SELECT * FROM TempTable;
-- Index for frequent query
CREATE NONCLUSTERED INDEX IX_Employees_Name_Dept
ON Employees(Name, DepartmentID);
-- Covering index
CREATE NONCLUSTERED INDEX IX_Employees_Cover
ON Employees(Name) INCLUDE (Salary, DepartmentID);
-- Check index usage
SELECT OBJECT_NAME(object_id), index_id, user_seeks, user_scans
FROM sys.dm_db_index_usage_stats;
-- Index for frequent query
CREATE INDEX IX_Employees_Name_Dept
ON Employees(Name, DepartmentID);
-- Covering index (implicit in MySQL)
CREATE INDEX IX_Employees_Cover
ON Employees(Name, Salary, DepartmentID);
-- Check index usage
SELECT TABLE_NAME, INDEX_NAME, NON_UNIQUE
FROM INFORMATION_SCHEMA.STATISTICS
WHERE TABLE_NAME = 'Employees';
-- Using LEAD to find gaps
WITH SequenceCheck AS (
SELECT ID, LEAD(ID) OVER (ORDER BY ID) AS NextID
FROM Orders
)
SELECT ID, NextID, (NextID - ID - 1) AS GapSize
FROM SequenceCheck
WHERE NextID - ID > 1;
-- Using numbers table
WITH Numbers AS (
SELECT ROW_NUMBER() OVER (ORDER BY (SELECT NULL)) AS n
FROM sys.all_objects
)
SELECT n AS MissingID
FROM Numbers n
LEFT JOIN Orders o ON o.ID = n
WHERE o.ID IS NULL AND n <= (SELECT MAX(ID) FROM Orders);
-- Using LEAD to find gaps
WITH SequenceCheck AS (
SELECT ID, LEAD(ID) OVER (ORDER BY ID) AS NextID
FROM Orders
)
SELECT ID, NextID, (NextID - ID - 1) AS GapSize
FROM SequenceCheck
WHERE NextID - ID > 1;
-- Using generated sequence
WITH RECURSIVE Numbers AS (
SELECT 1 AS n
UNION ALL
SELECT n + 1
FROM Numbers
WHERE n < (SELECT MAX(ID) FROM Orders)
)
SELECT n AS MissingID
FROM Numbers n
LEFT JOIN Orders o ON o.ID = n
WHERE o.ID IS NULL;
-- Audit table
CREATE TABLE EmployeeAudit (
AuditID INT IDENTITY PRIMARY KEY,
EmployeeID INT,
Operation VARCHAR(10),
OldSalary INT,
NewSalary INT,
ChangeDate DATETIME,
ChangedBy VARCHAR(50)
);
-- Trigger for UPDATE
CREATE TRIGGER trg_AuditEmployee
ON Employees
AFTER UPDATE
AS
BEGIN
INSERT INTO EmployeeAudit (EmployeeID, Operation, OldSalary, NewSalary, ChangeDate, ChangedBy)
SELECT i.EmployeeID, 'UPDATE', d.Salary, i.Salary, GETDATE(), SUSER_NAME()
FROM inserted i
JOIN deleted d ON i.EmployeeID = d.EmployeeID
WHERE i.Salary != d.Salary;
END;
-- Test
UPDATE Employees SET Salary = 60000 WHERE EmployeeID = 1;
-- Audit table
CREATE TABLE EmployeeAudit (
AuditID INT AUTO_INCREMENT PRIMARY KEY,
EmployeeID INT,
Operation VARCHAR(10),
OldSalary INT,
NewSalary INT,
ChangeDate DATETIME,
ChangedBy VARCHAR(50)
);
-- Trigger for UPDATE
DELIMITER //
CREATE TRIGGER trg_AuditEmployee
AFTER UPDATE ON Employees
FOR EACH ROW
BEGIN
IF NEW.Salary != OLD.Salary THEN
INSERT INTO EmployeeAudit (EmployeeID, Operation, OldSalary, NewSalary, ChangeDate, ChangedBy)
VALUES (NEW.EmployeeID, 'UPDATE', OLD.Salary, NEW.Salary, NOW(), USER());
END IF;
END //
DELIMITER ;
-- Test
UPDATE Employees SET Salary = 60000 WHERE EmployeeID = 1;
-- PIVOT
SELECT * FROM (
SELECT EmployeeID, Month, Sales
FROM Sales
) AS Source
PIVOT (
SUM(Sales)
FOR Month IN (Jan, Feb, Mar)
) AS PivotTable;
-- UNPIVOT
SELECT EmployeeID, Month, Sales
FROM (
SELECT EmployeeID, Jan, Feb, Mar
FROM PivotTable
) AS Source
UNPIVOT (
Sales FOR Month IN (Jan, Feb, Mar)
) AS UnpivotTable;
-- Simulated PIVOT
SELECT EmployeeID,
SUM(CASE WHEN Month = 'Jan' THEN Sales END) AS Jan,
SUM(CASE WHEN Month = 'Feb' THEN Sales END) AS Feb,
SUM(CASE WHEN Month = 'Mar' THEN Sales END) AS Mar
FROM Sales
GROUP BY EmployeeID;
-- Simulated UNPIVOT
SELECT EmployeeID, 'Jan' AS Month, Jan AS Sales FROM PivotTable WHERE Jan IS NOT NULL
UNION
SELECT EmployeeID, 'Feb' AS Month, Feb AS Sales FROM PivotTable WHERE Feb IS NOT NULL
UNION
SELECT EmployeeID, 'Mar' AS Month, Mar AS Sales FROM PivotTable WHERE Mar IS NOT NULL;