1. Introduction to SQL Server {#introduction}
SQL Server is a relational database management system (RDBMS) developed by Microsoft. It’s designed to store, retrieve, and manage data efficiently.
Key Features:
- Scalability: Handles small to enterprise-level applications
- Security: Built-in security features and authentication
- Integration: Works seamlessly with Microsoft ecosystem
- Performance: Optimized for high-performance operations
What is a Database?
A database is an organized collection of structured information stored electronically in a computer system.
Example: Think of a library catalog system that keeps track of books, authors, and borrowers.
2. Installation and Setup {#installation}
System Requirements:
- Windows Operating System
- Minimum 4GB RAM (8GB+ recommended)
- At least 6GB free disk space
Installation Steps:
- Download SQL Server from Microsoft’s official website
- Choose your edition (Express is free for learning)
- Run the installer and select “Basic” installation
- Download and install SQL Server Management Studio (SSMS)
3. Basic Concepts and Terminology {#basic-concepts}
Essential Terms:
Database: Container that holds related tables and objects
Example: CompanyDB (contains employee, department, project tables)
Table: Collection of related data organized in rows and columns
Example: Employees table with columns: ID, Name, Department, Salary
Row (Record): Single entry in a table
Example: 1, "John Smith", "IT", 50000
Column (Field): Attribute or property of the data
Example: Name, Department, Salary
Primary Key: Unique identifier for each row
Example: EmployeeID (each employee has a unique ID)
Foreign Key: Links two tables together
Example: DepartmentID in Employees table references Departments table
4. SQL Server Management Studio (SSMS) {#ssms}
SSMS is the primary tool for managing SQL Server databases.
Key Components:
- Object Explorer: Navigate databases and objects
- Query Editor: Write and execute SQL commands
- Results Panel: View query results
- Messages Panel: Display system messages and errors
Basic Navigation:
Object Explorer Structure:
├── Databases
│ ├── System Databases
│ └── User Databases
├── Security
├── Server Objects
└── Management
5. Creating and Managing Databases {#databases}
Creating a Database:
Method 1: Using GUI
- Right-click “Databases” in Object Explorer
- Select “New Database”
- Enter database name
- Click “OK”
Method 2: Using SQL Command
CREATE DATABASE CompanyDB;
Using a Database:
USE CompanyDB;
Dropping a Database:
DROP DATABASE CompanyDB;
⚠️ Warning: This permanently deletes the database and all its data!
6. Data Types {#data-types}
Understanding data types is crucial for efficient database design.
Common Data Types:
Numeric Types:
INT -- Integer numbers (-2,147,483,648 to 2,147,483,647)
BIGINT -- Large integers
DECIMAL(p,s) -- Fixed precision numbers
FLOAT -- Floating point numbers
String Types:
VARCHAR(n) -- Variable-length string (up to n characters)
NVARCHAR(n) -- Unicode variable-length string
CHAR(n) -- Fixed-length string
TEXT -- Large text data
Date/Time Types:
DATE -- Date only (YYYY-MM-DD)
DATETIME -- Date and time
TIME -- Time only
TIMESTAMP -- Automatically updated timestamp
Other Types:
BIT -- Boolean (0 or 1)
UNIQUEIDENTIFIER -- GUID
BINARY -- Binary data
Examples:
DECLARE @Age INT = 25;
DECLARE @Name VARCHAR(50) = 'John Doe';
DECLARE @BirthDate DATE = '1998-05-15';
DECLARE @IsActive BIT = 1;
7. Creating Tables {#tables}
Tables are the foundation of your database structure.
Basic Syntax:
CREATE TABLE table_name (
column1 datatype constraints,
column2 datatype constraints,
...
);
Example – Creating an Employees Table:
CREATE TABLE Employees (
EmployeeID INT PRIMARY KEY IDENTITY(1,1),
FirstName VARCHAR(50) NOT NULL,
LastName VARCHAR(50) NOT NULL,
Email VARCHAR(100) UNIQUE,
HireDate DATE NOT NULL,
Salary DECIMAL(10,2),
DepartmentID INT,
IsActive BIT DEFAULT 1
);
Explanation:
IDENTITY(1,1)
: Auto-incrementing number starting at 1NOT NULL
: Field must have a valueUNIQUE
: No duplicate values allowedDEFAULT 1
: Sets default value if none provided
Creating a Departments Table:
CREATE TABLE Departments (
DepartmentID INT PRIMARY KEY IDENTITY(1,1),
DepartmentName VARCHAR(50) NOT NULL,
Location VARCHAR(100),
ManagerID INT
);
8. Basic SQL Operations (CRUD) {#crud-operations}
CRUD stands for Create, Read, Update, Delete – the four basic operations.
CREATE (INSERT) – Adding Data
Single Row Insert:
INSERT INTO Departments (DepartmentName, Location)
VALUES ('Information Technology', 'Building A');
Multiple Rows Insert:
INSERT INTO Departments (DepartmentName, Location)
VALUES
('Human Resources', 'Building B'),
('Finance', 'Building C'),
('Marketing', 'Building A');
Insert with All Columns:
INSERT INTO Employees
VALUES (1, 'John', 'Smith', 'john.smith@company.com', '2023-01-15', 55000, 1, 1);
READ (SELECT) – Retrieving Data
Select All Columns:
SELECT * FROM Employees;
Select Specific Columns:
SELECT FirstName, LastName, Salary
FROM Employees;
Select with Conditions:
SELECT FirstName, LastName, Salary
FROM Employees
WHERE Salary > 50000;
Select with Multiple Conditions:
SELECT FirstName, LastName, Salary
FROM Employees
WHERE Salary > 40000 AND DepartmentID = 1;
Select with Sorting:
SELECT FirstName, LastName, Salary
FROM Employees
ORDER BY Salary DESC;
Select Top Records:
SELECT TOP 5 FirstName, LastName, Salary
FROM Employees
ORDER BY Salary DESC;
UPDATE – Modifying Data
Update Single Record:
UPDATE Employees
SET Salary = 60000
WHERE EmployeeID = 1;
Update Multiple Records:
UPDATE Employees
SET Salary = Salary * 1.1
WHERE DepartmentID = 1;
Update with Multiple Columns:
UPDATE Employees
SET Salary = 65000, DepartmentID = 2
WHERE EmployeeID = 1;
DELETE – Removing Data
Delete Specific Records:
DELETE FROM Employees
WHERE EmployeeID = 1;
Delete with Conditions:
DELETE FROM Employees
WHERE IsActive = 0;
⚠️ Delete All Records (Be Careful!):
DELETE FROM Employees;
9. Data Manipulation Language (DML) {#dml}
Advanced SELECT Queries
LIKE Operator (Pattern Matching):
-- Names starting with 'J'
SELECT * FROM Employees WHERE FirstName LIKE 'J%';
-- Names ending with 'son'
SELECT * FROM Employees WHERE LastName LIKE '%son';
-- Names containing 'an'
SELECT * FROM Employees WHERE FirstName LIKE '%an%';
IN Operator:
SELECT * FROM Employees
WHERE DepartmentID IN (1, 2, 3);
BETWEEN Operator:
SELECT * FROM Employees
WHERE Salary BETWEEN 40000 AND 60000;
IS NULL / IS NOT NULL:
SELECT * FROM Employees WHERE Email IS NOT NULL;
SELECT * FROM Employees WHERE ManagerID IS NULL;
Aggregate Functions
COUNT:
SELECT COUNT(*) AS TotalEmployees FROM Employees;
SELECT COUNT(Email) AS EmployeesWithEmail FROM Employees;
SUM:
SELECT SUM(Salary) AS TotalSalaryBudget FROM Employees;
AVG:
SELECT AVG(Salary) AS AverageSalary FROM Employees;
MIN/MAX:
SELECT MIN(Salary) AS LowestSalary, MAX(Salary) AS HighestSalary
FROM Employees;
GROUP BY and HAVING
GROUP BY:
SELECT DepartmentID, COUNT(*) AS EmployeeCount, AVG(Salary) AS AvgSalary
FROM Employees
GROUP BY DepartmentID;
HAVING (Filter Groups):
SELECT DepartmentID, COUNT(*) AS EmployeeCount
FROM Employees
GROUP BY DepartmentID
HAVING COUNT(*) > 5;
10. Data Definition Language (DDL) {#ddl}
ALTER TABLE – Modifying Table Structure
Add Column:
ALTER TABLE Employees
ADD Phone VARCHAR(15);
Drop Column:
ALTER TABLE Employees
DROP COLUMN Phone;
Modify Column:
ALTER TABLE Employees
ALTER COLUMN FirstName VARCHAR(100);
TRUNCATE vs DELETE
TRUNCATE (Faster, removes all data):
TRUNCATE TABLE Employees;
DELETE (Slower, can use WHERE clause):
DELETE FROM Employees WHERE Salary < 30000;
11. Constraints {#constraints}
Constraints ensure data integrity and enforce business rules.
Types of Constraints:
PRIMARY KEY:
ALTER TABLE Employees
ADD CONSTRAINT PK_Employees PRIMARY KEY (EmployeeID);
FOREIGN KEY:
ALTER TABLE Employees
ADD CONSTRAINT FK_Employees_Department
FOREIGN KEY (DepartmentID) REFERENCES Departments(DepartmentID);
UNIQUE:
ALTER TABLE Employees
ADD CONSTRAINT UK_Employees_Email UNIQUE (Email);
CHECK:
ALTER TABLE Employees
ADD CONSTRAINT CK_Employees_Salary CHECK (Salary > 0);
NOT NULL:
ALTER TABLE Employees
ALTER COLUMN FirstName VARCHAR(50) NOT
NOT NULL:
ALTER TABLE Employees
ALTER COLUMN FirstName VARCHAR(50) NOT NULL;
DEFAULT:
ALTER TABLE Employees
ADD CONSTRAINT DF_Employees_IsActive DEFAULT 1 FOR IsActive;
Example – Creating Table with Multiple Constraints:
CREATE TABLE Projects (
ProjectID INT IDENTITY(1,1) PRIMARY KEY,
ProjectName VARCHAR(100) NOT NULL,
StartDate DATE NOT NULL,
EndDate DATE,
Budget DECIMAL(12,2) CHECK (Budget > 0),
Status VARCHAR(20) DEFAULT 'Active',
ManagerID INT,
CONSTRAINT FK_Projects_Manager
FOREIGN KEY (ManagerID) REFERENCES Employees(EmployeeID),
CONSTRAINT CK_Projects_Dates
CHECK (EndDate IS NULL OR EndDate > StartDate)
);
12. Indexes {#indexes}
Indexes improve query performance by creating faster data access paths.
Types of Indexes:
Clustered Index (One per table):
CREATE CLUSTERED INDEX IX_Employees_EmployeeID
ON Employees (EmployeeID);
Non-Clustered Index:
CREATE NONCLUSTERED INDEX IX_Employees_LastName
ON Employees (LastName);
Composite Index (Multiple columns):
CREATE INDEX IX_Employees_Name
ON Employees (LastName, FirstName);
Unique Index:
CREATE UNIQUE INDEX IX_Employees_Email
ON Employees (Email);
When to Use Indexes:
- ✅ Columns frequently used in WHERE clauses
- ✅ Columns used in JOIN conditions
- ✅ Columns used in ORDER BY
- ❌ Tables with frequent INSERT/UPDATE operations
- ❌ Small tables (few hundred rows)
Example Usage:
-- This query will benefit from an index on LastName
SELECT * FROM Employees WHERE LastName = 'Smith';
-- Create the index
CREATE INDEX IX_Employees_LastName ON Employees (LastName);
13. Views {#views}
Views are virtual tables based on SQL queries. They don’t store data but provide a way to simplify complex queries.
Creating Views:
Simple View:
CREATE VIEW vw_ActiveEmployees AS
SELECT EmployeeID, FirstName, LastName, Email, Salary
FROM Employees
WHERE IsActive = 1;
Complex View with JOINs:
CREATE VIEW vw_EmployeeDepartments AS
SELECT
e.EmployeeID,
e.FirstName + ' ' + e.LastName AS FullName,
e.Email,
e.Salary,
d.DepartmentName,
d.Location
FROM Employees e
INNER JOIN Departments d ON e.DepartmentID = d.DepartmentID
WHERE e.IsActive = 1;
Using Views:
-- Query the view like a regular table
SELECT * FROM vw_ActiveEmployees;
SELECT * FROM vw_EmployeeDepartments
WHERE DepartmentName = 'IT';
Benefits of Views:
- Security: Hide sensitive columns
- Simplicity: Simplify complex queries
- Consistency: Standardize data access
- Abstraction: Hide table structure changes
Modifying Views:
ALTER VIEW vw_ActiveEmployees AS
SELECT EmployeeID, FirstName, LastName, Email, Salary, HireDate
FROM Employees
WHERE IsActive = 1;
14. Stored Procedures {#stored-procedures}
Stored procedures are pre-compiled SQL code blocks that can be executed repeatedly.
Creating Stored Procedures:
Simple Stored Procedure:
CREATE PROCEDURE sp_GetAllEmployees
AS
BEGIN
SELECT * FROM Employees WHERE IsActive = 1;
END;
Stored Procedure with Parameters:
CREATE PROCEDURE sp_GetEmployeesByDepartment
@DepartmentID INT
AS
BEGIN
SELECT EmployeeID, FirstName, LastName, Salary
FROM Employees
WHERE DepartmentID = @DepartmentID AND IsActive = 1;
END;
Stored Procedure with Multiple Parameters:
CREATE PROCEDURE sp_GetEmployeesBySalaryRange
@MinSalary DECIMAL(10,2),
@MaxSalary DECIMAL(10,2)
AS
BEGIN
SELECT EmployeeID, FirstName, LastName, Salary
FROM Employees
WHERE Salary BETWEEN @MinSalary AND @MaxSalary
AND IsActive = 1
ORDER BY Salary DESC;
END;
Stored Procedure with Output Parameters:
CREATE PROCEDURE sp_GetEmployeeCount
@DepartmentID INT,
@EmployeeCount INT OUTPUT
AS
BEGIN
SELECT @EmployeeCount = COUNT(*)
FROM Employees
WHERE DepartmentID = @DepartmentID AND IsActive = 1;
END;
Executing Stored Procedures:
Execute Simple Procedure:
EXEC sp_GetAllEmployees;
Execute with Parameters:
EXEC sp_GetEmployeesByDepartment @DepartmentID = 1;
Execute with Output Parameter:
DECLARE @Count INT;
EXEC sp_GetEmployeeCount @DepartmentID = 1, @EmployeeCount = @Count OUTPUT;
SELECT @Count AS EmployeeCount;
Advanced Stored Procedure Example:
CREATE PROCEDURE sp_InsertEmployee
@FirstName VARCHAR(50),
@LastName VARCHAR(50),
@Email VARCHAR(100),
@HireDate DATE,
@Salary DECIMAL(10,2),
@DepartmentID INT,
@NewEmployeeID INT OUTPUT
AS
BEGIN
BEGIN TRY
-- Check if email already exists
IF EXISTS (SELECT 1 FROM Employees WHERE Email = @Email)
BEGIN
RAISERROR ('Email address already exists', 16, 1);
RETURN;
END
-- Insert the new employee
INSERT INTO Employees (FirstName, LastName, Email, HireDate, Salary, DepartmentID)
VALUES (@FirstName, @LastName, @Email, @HireDate, @Salary, @DepartmentID);
-- Get the new employee ID
SET @NewEmployeeID = SCOPE_IDENTITY();
PRINT 'Employee added successfully with ID: ' + CAST(@NewEmployeeID AS VARCHAR(10));
END TRY
BEGIN CATCH
PRINT 'Error: ' + ERROR_MESSAGE();
END CATCH
END;
15. Functions {#functions}
SQL Server has built-in functions and allows you to create custom functions.
Built-in String Functions:
-- String manipulation examples
SELECT
FirstName,
LEN(FirstName) AS NameLength,
UPPER(FirstName) AS UpperCase,
LOWER(FirstName) AS LowerCase,
LEFT(FirstName, 3) AS FirstThreeChars,
RIGHT(LastName, 3) AS LastThreeChars,
SUBSTRING(Email, 1, CHARINDEX('@', Email) - 1) AS Username
FROM Employees;
Built-in Date Functions:
-- Date manipulation examples
SELECT
HireDate,
GETDATE() AS CurrentDate,
DATEDIFF(YEAR, HireDate, GETDATE()) AS YearsEmployed,
DATEADD(YEAR, 1, HireDate) AS OneYearAfterHire,
YEAR(HireDate) AS HireYear,
MONTH(HireDate) AS HireMonth,
DAY(HireDate) AS HireDay
FROM Employees;
Built-in Mathematical Functions:
-- Math functions
SELECT
Salary,
ROUND(Salary, 0) AS RoundedSalary,
CEILING(Salary / 1000.0) AS CeilingSalaryInThousands,
FLOOR(Salary / 1000.0) AS FloorSalaryInThousands,
ABS(Salary - 50000) AS DifferenceFrom50K
FROM Employees;
Creating User-Defined Functions:
Scalar Function (Returns single value):
CREATE FUNCTION fn_CalculateBonus(@Salary DECIMAL(10,2))
RETURNS DECIMAL(10,2)
AS
BEGIN
DECLARE @Bonus DECIMAL(10,2);
IF @Salary < 40000
SET @Bonus = @Salary * 0.05;
ELSE IF @Salary < 60000
SET @Bonus = @Salary * 0.10;
ELSE
SET @Bonus = @Salary * 0.15;
RETURN @Bonus;
END;
Using the Function:
SELECT
FirstName,
LastName,
Salary,
dbo.fn_CalculateBonus(Salary) AS Bonus
FROM Employees;
Table-Valued Function:
CREATE FUNCTION fn_GetEmployeesByDepartment(@DepartmentID INT)
RETURNS TABLE
AS
RETURN
(
SELECT EmployeeID, FirstName, LastName, Salary
FROM Employees
WHERE DepartmentID = @DepartmentID AND IsActive = 1
);
Using Table-Valued Function:
SELECT * FROM dbo.fn_GetEmployeesByDepartment(1);
16. Advanced Concepts
JOINs – Combining Data from Multiple Tables
INNER JOIN (Returns matching records from both tables):
SELECT
e.FirstName,
e.LastName,
d.DepartmentName
FROM Employees e
INNER JOIN Departments d ON e.DepartmentID = d.DepartmentID;
LEFT JOIN (Returns all records from left table):
SELECT
e.FirstName,
e.LastName,
d.DepartmentName
FROM Employees e
LEFT JOIN Departments d ON e.DepartmentID = d.DepartmentID;
RIGHT JOIN (Returns all records from right table):
SELECT
e.FirstName,
e.LastName,
d.DepartmentName
FROM Employees e
RIGHT JOIN Departments d ON e.DepartmentID = d.DepartmentID;
FULL OUTER JOIN (Returns all records from both tables):
SELECT
e.FirstName,
e.LastName,
d.DepartmentName
FROM Employees e
FULL OUTER JOIN Departments d ON e.DepartmentID = d.DepartmentID;
Subqueries
Subquery in WHERE clause:
SELECT FirstName, LastName, Salary
FROM Employees
WHERE Salary > (
SELECT AVG(Salary)
FROM Employees
);
Subquery with EXISTS:
SELECT DepartmentName
FROM Departments d
WHERE EXISTS (
SELECT 1
FROM Employees e
WHERE e.DepartmentID = d.DepartmentID
);
Common Table Expressions (CTEs)
Simple CTE:
WITH HighEarners AS (
SELECT FirstName, LastName, Salary
FROM Employees
WHERE Salary > 60000
)
SELECT * FROM HighEarners ORDER BY Salary DESC;
Recursive CTE (Employee Hierarchy):
WITH EmployeeHierarchy AS (
-- Anchor: Top-level managers
SELECT EmployeeID, FirstName, LastName, ManagerID, 0 as Level
FROM Employees
WHERE
WITH EmployeeHierarchy AS (
-- Anchor: Top-level managers
SELECT EmployeeID, FirstName, LastName, ManagerID, 0 as Level
FROM Employees
WHERE ManagerID IS NULL
UNION ALL
-- Recursive: Employees with managers
SELECT e.EmployeeID, e.FirstName, e.LastName, e.ManagerID, eh.Level + 1
FROM Employees e
INNER JOIN EmployeeHierarchy eh ON e.ManagerID = eh.EmployeeID
)
SELECT * FROM EmployeeHierarchy ORDER BY Level, LastName;
Window Functions
ROW_NUMBER():
SELECT
FirstName,
LastName,
Salary,
ROW_NUMBER() OVER (ORDER BY Salary DESC) as SalaryRank
FROM Employees;
RANK() and DENSE_RANK():
SELECT
FirstName,
LastName,
Salary,
RANK() OVER (ORDER BY Salary DESC) as Rank,
DENSE_RANK() OVER (ORDER BY Salary DESC) as DenseRank
FROM Employees;
Partitioned Window Functions:
SELECT
FirstName,
LastName,
DepartmentID,
Salary,
AVG(Salary) OVER (PARTITION BY DepartmentID) as DeptAvgSalary,
ROW_NUMBER() OVER (PARTITION BY DepartmentID ORDER BY Salary DESC) as DeptRank
FROM Employees;
17. Error Handling and Transactions
TRY-CATCH Blocks:
BEGIN TRY
BEGIN TRANSACTION;
INSERT INTO Departments (DepartmentName, Location)
VALUES ('Research', 'Building D');
INSERT INTO Employees (FirstName, LastName, Email, HireDate, Salary, DepartmentID)
VALUES ('Jane', 'Doe', 'jane.doe@company.com', GETDATE(), 55000,
(SELECT DepartmentID FROM Departments WHERE DepartmentName = 'Research'));
COMMIT TRANSACTION;
PRINT 'Transaction completed successfully.';
END TRY
BEGIN CATCH
ROLLBACK TRANSACTION;
PRINT 'Error occurred: ' + ERROR_MESSAGE();
PRINT 'Error Number: ' + CAST(ERROR_NUMBER() AS VARCHAR(10));
PRINT 'Error Line: ' + CAST(ERROR_LINE() AS VARCHAR(10));
END CATCH;
Transaction Control:
-- Start a transaction
BEGIN TRANSACTION;
-- Perform operations
UPDATE Employees SET Salary = Salary * 1.1 WHERE DepartmentID = 1;
-- Check if everything is correct
IF @@ROWCOUNT > 0
COMMIT TRANSACTION; -- Make changes permanent
ELSE
ROLLBACK TRANSACTION; -- Undo changes
18. Performance Optimization Tips
Query Optimization:
Use Indexes Effectively:
-- Good: Uses index on LastName
SELECT * FROM Employees WHERE LastName = 'Smith';
-- Bad: Function on column prevents index usage
SELECT * FROM Employees WHERE UPPER(LastName) = 'SMITH';
-- Better: Store data in consistent case or use computed column
SELECT * FROM Employees WHERE LastName = 'Smith';
Use EXISTS instead of IN with subqueries:
-- Slower
SELECT * FROM Departments
WHERE DepartmentID IN (SELECT DepartmentID FROM Employees);
-- Faster
SELECT * FROM Departments d
WHERE EXISTS (SELECT 1 FROM Employees e WHERE e.DepartmentID = d.DepartmentID);
Limit Result Sets:
-- Use TOP to limit results
SELECT TOP 100 * FROM Employees ORDER BY Salary DESC;
-- Use WHERE clauses to filter early
SELECT * FROM Employees
WHERE IsActive = 1 AND HireDate >= '2020-01-01';
19. Best Practices {#best-practices}
Database Design Best Practices:
- Naming Conventions:
-- Tables: Plural nouns Employees, Departments, Projects -- Columns: Descriptive names FirstName (not FName), CreatedDate (not CDate) -- Primary Keys: TableName + ID EmployeeID, DepartmentID -- Foreign Keys: ReferencedTable + ID DepartmentID (in Employees table)
- Data Types:
-- Use appropriate data types VARCHAR(50) for names (not VARCHAR(MAX)) INT for IDs (not VARCHAR) DECIMAL(10,2) for currency (not FLOAT) DATE for dates (not VARCHAR)
- Constraints:
-- Always define primary keys EmployeeID INT PRIMARY KEY IDENTITY(1,1) -- Use foreign keys for referential integrity CONSTRAINT FK_Employee_Department FOREIGN KEY (DepartmentID) REFERENCES Departments(DepartmentID) -- Add check constraints for business rules CONSTRAINT CK_Employee_Salary CHECK (Salary > 0)
SQL Coding Best Practices:
- Formatting:
-- Good formatting SELECT e.FirstName, e.LastName, d.DepartmentName FROM Employees e INNER JOIN Departments d ON e.DepartmentID = d.DepartmentID WHERE e.IsActive = 1 ORDER BY e.LastName, e.FirstName;
- Use Aliases:
-- Clear table aliases SELECT emp.FirstName, dept.DepartmentName FROM Employees emp INNER JOIN Departments dept ON emp.DepartmentID = dept.DepartmentID;
- *Avoid SELECT :
-- Bad SELECT * FROM Employees; -- Good SELECT EmployeeID, FirstName, LastName, Email FROM Employees;
Security Best Practices:
- Use Parameterized Queries:
-- Stored procedure with parameters (prevents SQL injection) CREATE PROCEDURE sp_GetEmployee @EmployeeID INT AS BEGIN SELECT FirstName, LastName, Email FROM Employees WHERE EmployeeID = @EmployeeID; END;
- Principle of Least Privilege:
- Give users only the minimum permissions needed
- Use database roles
- Regularly review and audit permissions
- Regular Backups:
-- Full backup BACKUP DATABASE CompanyDB TO DISK = 'C:\Backups\CompanyDB_Full.bak'; -- Differential backup BACKUP DATABASE CompanyDB TO DISK = 'C:\Backups\CompanyDB_Diff.bak' WITH DIFFERENTIAL;
20. Common Scenarios and Examples
Scenario 1: Employee Management System
Complete Example:
-- Create the database structure
CREATE TABLE Departments (
DepartmentID INT IDENTITY(1,1) PRIMARY KEY,
DepartmentName VARCHAR(50) NOT NULL UNIQUE,
Location VARCHAR(100),
Budget DECIMAL(12,2)
);
CREATE TABLE Employees (
EmployeeID INT IDENTITY(1,1) PRIMARY KEY,
FirstName VARCHAR(50) NOT NULL,
LastName VARCHAR(50) NOT NULL,
Email VARCHAR(100) UNIQUE NOT NULL,
Phone VARCHAR(15),
HireDate DATE NOT NULL,
Salary DECIMAL(10,2) CHECK (Salary > 0),
DepartmentID INT,
ManagerID INT,
IsActive BIT DEFAULT 1,
CONSTRAINT FK_Employee_Department
FOREIGN KEY (DepartmentID) REFERENCES Departments(DepartmentID),
CONSTRAINT FK_Employee_Manager
FOREIGN KEY (ManagerID) REFERENCES Employees(EmployeeID)
);
-- Insert sample data
INSERT INTO Departments (DepartmentName, Location, Budget) VALUES
('Human Resources', 'Building A', 500000),
('Information Technology', 'Building B', 1200000),
('Finance', 'Building C', 800000),
('Marketing', 'Building A', 600000);
INSERT INTO Employees (FirstName, LastName, Email, HireDate, Salary, DepartmentID) VALUES
('John', 'Smith', 'john.smith@company.com', '2020-01-15', 75000, 2),
('Sarah', 'Johnson', 'sarah.johnson@company.com', '2019-03-20', 65000, 1),
('Mike', 'Brown', 'mike.brown@company.com', '2021-06-10', 80000, 2),
('Lisa', 'Davis', 'lisa.davis@company.com', '2020-09-05', 70000, 3),
('David', 'Wilson', 'david.wilson@company.com', '2022-01-12', 60000, 4);
-- Update manager relationships
UPDATE Employees SET ManagerID = 1 WHERE EmployeeID IN (3, 5);
UPDATE Employees SET ManagerID = 2 WHERE EmployeeID = 4;
Common Queries:
1. Get all employees with their department information:
SELECT
e.EmployeeID,
e.FirstName + ' ' + e.LastName AS FullName,
e.Email,
e.Salary,
d.DepartmentName,
d.Location
FROM Employees e
LEFT JOIN Departments d ON e.DepartmentID = d.DepartmentID
WHERE e.IsActive = 1;
2. Find employees earning above average salary:
SELECT
FirstName,
LastName,
Salary,
(SELECT AVG(Salary) FROM Employees WHERE IsActive = 1) AS AvgSalary
FROM Employees
WHERE Salary > (SELECT AVG(Salary) FROM Employees WHERE IsActive = 1)
AND IsActive = 1
ORDER BY Salary DESC;
3. Get department statistics:
SELECT
d.DepartmentName,
COUNT(e.EmployeeID) AS EmployeeCount,
AVG(e.Salary) AS AverageSalary,
MIN(e.Salary) AS MinSalary,
MAX(e.Salary) AS MaxSalary,
SUM(e.Salary) AS TotalSalaryBudget
FROM Departments d
LEFT JOIN Employees e ON d.DepartmentID = e.DepartmentID AND e.IsActive = 1
GROUP BY d.DepartmentID, d.DepartmentName
ORDER BY EmployeeCount DESC;
4. Find employees and their managers:
SELECT
emp.FirstName + ' ' + emp.LastName AS Employee,
emp.Salary AS EmployeeSalary,
mgr.FirstName + ' ' + mgr.LastName AS Manager,
dept.DepartmentName
FROM Employees emp
LEFT JOIN Employees mgr ON emp.ManagerID = mgr.EmployeeID
LEFT JOIN Departments dept ON emp.DepartmentID = dept.DepartmentID
WHERE emp.IsActive = 1
ORDER BY dept.DepartmentName, mgr.LastName, emp.LastName;
21. Troubleshooting Common Issues
Common Errors and Solutions:
1. “Invalid column name”
-- Error: Column doesn't exist
SELECT FirstNam FROM Employees; -- Typo in column name
-- Solution: Check spelling and verify column exists
SELECT FirstName FROM Employees;
2. “Cannot insert NULL value”
-- Error: Trying to insert NULL into NOT NULL column
INSERT INTO Employees (LastName, Email, HireDate, Salary)
VALUES ('Smith', 'john.smith@company.com', '2023-01-15', 50000);
-- Missing required FirstName
-- Solution: Provide all required values
INSERT INTO Employees (FirstName, LastName, Email, HireDate, Salary)
VALUES ('John', 'Smith', 'john.smith@company.com', '2023-01-15', 50000);
3. “Primary Key violation”
-- Error: Trying to insert duplicate primary key
INSERT INTO Employees (EmployeeID, FirstName, LastName, Email, HireDate, Salary)
VALUES (1, 'Jane', 'Doe', 'jane.doe@company.com', '2023-01-15', 45000);
-- EmployeeID 1 already exists
-- Solution: Let IDENTITY handle the ID or use a unique value
INSERT INTO Employees (FirstName, LastName, Email, HireDate, Salary)
VALUES ('Jane', 'Doe', 'jane.doe@company.com', '2023-01-15', 45000);
4. “Foreign Key constraint violation”
-- Error: Referenced value doesn't exist
INSERT INTO Employees (FirstName, LastName, Email, HireDate, Salary, DepartmentID)
VALUES ('Bob', 'Johnson', 'bob.johnson@company.com', '2023-01-15', 55000, 999);
-- Department 999 doesn't exist
-- Solution: Use valid DepartmentID or create the department first
INSERT INTO Employees (FirstName, LastName, Email, HireDate, Salary, DepartmentID)
VALUES ('Bob', 'Johnson', 'bob.johnson@company.com', '2023-01-15', 55000, 1);
5. “Arithmetic overflow error”
-- Error: Value too large for data type
DECLARE @SmallNumber TINYINT = 300; -- TINYINT max is 255
-- Solution: Use appropriate data type
DECLARE @Number SMALLINT = 300; -- SMALLINT can handle this valueF
24. Database Maintenance
Backup and Restore:
Full Backup:
-- Create full backup
BACKUP DATABASE CompanyDB
TO DISK = N'C:\Backup\CompanyDB_Full.bak'
WITH FORMAT, INIT,
NAME = N'CompanyDB Full Backup',
SKIP, NOREWIND, NOUNLOAD, STATS = 10;
Differential Backup:
-- Create differential backup
BACKUP DATABASE CompanyDB
TO DISK = N'C:\Backup\CompanyDB_Diff.bak'
WITH DIFFERENTIAL, FORMAT, INIT,
NAME = N'CompanyDB Differential Backup',
SKIP, NOREWIND, NOUNLOAD, STATS = 10;
Transaction Log Backup:
-- Backup transaction log
BACKUP LOG CompanyDB
TO DISK = N'C:\Backup\CompanyDB_Log.trn'
WITH FORMAT, INIT,
NAME = N'CompanyDB Log Backup',
SKIP, NOREWIND, NOUNLOAD, STATS = 10;
Restore Database:
-- Restore from full backup
RESTORE DATABASE CompanyDB_Restored
FROM DISK = N'C:\Backup\CompanyDB_Full.bak'
WITH FILE = 1,
MOVE N'CompanyDB' TO N'C:\Data\CompanyDB_Restored.mdf',
MOVE N'CompanyDB_Log' TO N'C:\Data\CompanyDB_Restored.ldf',
NOUNLOAD, REPLACE, STATS = 5;
Database Maintenance Tasks:
Update Statistics:
-- Update statistics for all tables
EXEC sp_updatestats;
-- Update statistics for specific table
UPDATE STATISTICS Employees;
Rebuild Indexes:
-- Rebuild all indexes on a table
ALTER INDEX ALL ON Employees REBUILD;
-- Rebuild specific index
ALTER INDEX IX_Employees_LastName ON Employees REBUILD;
Check Database Integrity:
-- Check database consistency
DBCC CHECKDB('CompanyDB');
-- Check specific table
DBCC CHECKTABLE('Employees');
25. Security and User Management
Creating Logins and Users:
-- Create SQL Server login
CREATE LOGIN CompanyUser WITH PASSWORD = 'SecurePassword123!';
-- Create database user
USE CompanyDB;
CREATE USER CompanyUser FOR LOGIN CompanyUser;
-- Grant permissions
GRANT SELECT, INSERT, UPDATE ON Employees TO CompanyUser;
GRANT SELECT ON Departments TO CompanyUser;
Database Roles:
-- Create custom role
CREATE ROLE EmployeeManager;
-- Grant permissions to role
GRANT SELECT, INSERT, UPDATE ON Employees TO EmployeeManager;
GRANT SELECT ON Departments TO EmployeeManager;
-- Add user to role
ALTER ROLE EmployeeManager ADD MEMBER CompanyUser;
Row-Level Security Example:
-- Add security column
ALTER TABLE Employees ADD CreatedBy VARCHAR(50) DEFAULT SYSTEM_USER;
-- Create security policy function
CREATE FUNCTION dbo.fn_SecurityPredicate(@CreatedBy VARCHAR(50))
RETURNS TABLE
WITH SCHEMABINDING
AS
RETURN SELECT 1 AS fn_SecurityPredicate_Result
WHERE @CreatedBy = SYSTEM_USER OR IS_MEMBER('db_owner') = 1;
-- Create security policy
CREATE SECURITY POLICY EmployeeSecurityPolicy
ADD FILTER PREDICATE dbo.fn_SecurityPredicate(CreatedBy) ON dbo.Employees
WITH (STATE = ON);