Project

Mastering SQL: From Beginner to Advanced

A comprehensive project to build a strong foundation in SQL and advance to higher-level database operations.

Empty image or helper icon

Mastering SQL: From Beginner to Advanced

Description

This project revolves around mastering SQL, starting from understanding its basic application, going through its syntax, exploring various commands, and gradually moving towards more advanced database functions. Each curriculum item will focus on a new aspect of SQL, building on the knowledge from the previous topics. The project is an ideal roadmap for beginners aspiring to be proficient in SQL, providing practical exercises and real-world examples to gain hands-on experience.

Understanding SQL and its Application

In order to understand SQL (Structured Query Language) and its applications, we need to first set up an SQL environment and then, we will write some SQL queries to illustrate its application.

SQL Environment Setup

For this tutorial, you will need:

  • A system with Python 3.6 (or later) installed or any local/online SQL database environment.
  • sqlite3 - Comes pre-installed with Python standard library. So if your system has Python, sqlite3 is already there.

To check whether your Python environment is set up correctly, you can use the following command on your command line:

python --version

To check whether sqlite3 library is available or not:

In Python interpreter

import sqlite3  

try:  
    sqlite_Connection = sqlite3.connect('temp.db')   
    cursor = sqlite_Connection.cursor()  
    print("\n Database created and connected successfully to SQLite")    
    
    cursor.close()  
    
except sqlite3.Error as error:  
    print("\nError while connecting to sqlite", error)  

finally:  
    if (sqlite_Connection):  
        sqlite_Connection.close()  
        print("\nThe SQLite connection is closed") 
    

SQL Implementation

We will create a database, create a table and then perform some basic DML operations (INSERT, SELECT, UPDATE etc).

Database Creation

import sqlite3

conn = sqlite3.connect('test.db')

print ("Opened database successfully")

Table Creation

import sqlite3

conn = sqlite3.connect('test.db')
print ("Opened database successfully")

conn.execute('''CREATE TABLE COMPANY
         (ID INT PRIMARY KEY     NOT NULL,
         NAME           TEXT    NOT NULL,
         AGE            INT     NOT NULL,
         ADDRESS        CHAR(50),
         SALARY         REAL);''')

print ("Table created successfully")

conn.close()

Insert Operation

import sqlite3

conn = sqlite3.connect('test.db')
print ("Opened database successfully")

conn.execute("INSERT INTO COMPANY (ID,NAME,AGE,ADDRESS,SALARY) \
      VALUES (1, 'Paul', 32, 'California', 20000.00)")

conn.commit()
print ("Records created successfully")
conn.close()

Select Operation

import sqlite3

conn = sqlite3.connect('test.db')
print ("Opened database successfully")

cursor = conn.execute("SELECT id, name, address, salary from COMPANY")
for row in cursor:
   print ("ID = ", row[0])
   print ("NAME = ", row[1])
   print ("ADDRESS = ", row[2])
   print ("SALARY = ", row[3], "\n")

print ("Operation done successfully")
conn.close()

That's all for the initial practical application of SQL. You can explore more SQL operations such as UPDATE, DELETE, SQL Joins, and Clauses to fully understand SQL and its wide range of applications.

SQL Syntax Basics

Here, we are going to cover some common and necessary operations in SQL that every developer should know.

Select

The SELECT statement is used to select data from a database. It returns this data as a set of records in a result table. The data returned is stored in a result table, sometimes called the result set.

SELECT * FROM Customers;

In the above example ('*' denotes all), records from all fields of the Customers table will be selected.

Where

The WHERE clause is used to filter records. It is used to extract only those records that fulfill a specified condition.

SELECT * FROM Customers
WHERE Country='Germany';

In the above example, the query selects all fields for every record in the Customers table where the Country is 'Germany'.

Insert Into

The INSERT INTO statement is used to insert new records in a table. It is important to mention that SQL injections should be avoided by using parameterized queries or prepared statements.

INSERT INTO Customers (CustomerName, ContactName, Address, City, PostalCode, Country)
VALUES ('Cardinal', 'Tom B. Erichsen', 'Skagen 21', 'Stavanger', '4006', 'Norway');

In the above example, you are inserting data into specific columns using the column names and their respective values.

Update

The UPDATE statement is used to modify the existing records in a table. It is always a good idea to use the WHERE clause to specify which record(s) one wants to update.

UPDATE Customers
SET ContactName = 'Alfred Schmidt', City= 'Frankfurt'
WHERE CustomerID = 1;

In the example above, you update the ContactName and City of the Customer with a CustomerID of 1.

Delete

The DELETE statement is used to delete existing records in a table. It is always a good practice to use the WHERE clause to specify which record(s) are to be deleted.

DELETE FROM Customers WHERE CustomerName='Alfreds Futterkiste';

In the above example, the query deletes records from the Customers table where the CustomerName is 'Alfreds Futterkiste'.

Note: All the table names and column names used are hypothetical and can be replaced as per the database.

Also, all the SQL queries are to be executed in an SQL client connected to an SQL database. You can choose any SQL client of your preference and connect it to a database by providing credentials.

Learning SQL Data Types and Variables

SQL data types are the attributes that specify the type of data that the object can hold. SQL variables are used to store data temporarily in a program.

SQL Data Types

In SQL, data types can be grouped into four main categories:

  1. Numeric Data Types.
  2. Date and Time Data Types.
  3. Character and String Data Types.
  4. Unicode Character String Data Types.

Here are some practical examples of using each data type category:

CREATE TABLE DataTypeDemo
(
    ID INT,                           -- Numeric Data Type
    Name VARCHAR(255),                -- String Data Type
    BirthDate DATE,                   -- Date and Time Data Type
    Grade CHAR(1)                     -- Unicode Character String Data Type
);

SQL Variables

SQL variables are used to store data. You can use SQL variables to manipulate data in SQL statements or control program flow.

Here is a practical example of using SQL variables:

DECLARE @StudentGrade CHAR(1);       -- Variable declaration
SET @StudentGrade = 'A';             -- Variable initialization
SELECT * FROM DataTypeDemo WHERE Grade = @StudentGrade;  -- Variable usage

In the example above, @StudentGrade is a variable that holds a character. It is initialized to 'A'. Then, it is used in a SELECT statement to get all students who have Grade A.

Table with Various Data Types

Let’s create a table that uses a variety of data types to fully demonstrate their functions:

CREATE TABLE Employee
(
    EmployeeId INT NOT NULL,                   -- Integer data type
    FirstName VARCHAR(255) NOT NULL,           -- Variable character data type
    LastName VARCHAR(255) NOT NULL,            -- Variable character data type
    BirthDate DATE,                            -- Date data type
    JoiningDate DATETIME,                      -- Datetime data type
    Salary DECIMAL(10,2),                      -- Decimal data type
    IsActive BOOLEAN,                          -- Boolean data type
    ProfilePic BLOB                            -- Blob data type for holding large binary data such as images or files.
);

Variables in WHERE Clause

Now, let's look at an example of how you can use variables with WHERE clause:

DECLARE @BirthYear INT;    -- Declaring variable
SET @BirthYear = 1980;     -- Setting variable

-- Using variable
SELECT * 
FROM Employee 
WHERE YEAR(BirthDate) = @BirthYear;

In the SQL code above, @BirthYear is a variable used to store birth years. The query will return all employees who were born in the year stored in the @BirthYear variable.

Finally, mastering SQL data types and variables involves hands-on experience and continuous practice. Referencing data type documentation and implementing variables within your SQL scripts will support this learning.

Sure! Below is a practical implementation of using SQL commands, specifically SELECT, UPDATE, DELETE, and INSERT, focusing on their application in real-life contexts.

Create an SQL Table

Before we run our SQL commands, we will need an SQL table for practical demonstration. We will create an Employees table as follows:

CREATE TABLE Employees (
    ID INT PRIMARY KEY,
    Name NVARCHAR(50),
    Position NVARCHAR(50),
    Salary FLOAT,
    HireDate DATE
);

SQL SELECT

The SELECT statement is used to select data from a database. The data returned is stored in a result table, sometimes called the result-set.

Consider we want to select all records from the Employees table. The SQL command would be:

SELECT * FROM Employees;

If we want to select only the Names and HireDates of the employees:

SELECT Name, HireDate FROM Employees;

SQL UPDATE

The UPDATE statement is used to modify the existing records in a table.

Assume we want to give a 10% salary raise to an employee whose ID is 3. The SQL command would be:

UPDATE Employees
SET Salary = Salary * 1.1
WHERE ID = 3;

If we want to update the Position of all employees to 'Assistant' who were hired after 2019-01-01:

UPDATE Employees
SET Position = 'Assistant'
WHERE HireDate > '2019-01-01';

SQL DELETE

The DELETE statement is used to delete existing records in a table.

Suppose an employee leaves the company and we need to delete their record. If the ID of this employee is 5, the SQL command should be:

DELETE FROM Employees
WHERE ID = 5;

If we want to delete all 'Assistant' positions from Employees table:

DELETE FROM Employees
WHERE Position = 'Assistant';

SQL INSERT

The INSERT INTO statement is used to insert new records in a table.

For example, we are hiring a new employee. His details are: ID=10, Name='Robert', Position='Manager', Salary=85000, HireDate= '2021-08-30'. The SQL command would be:

INSERT INTO Employees (ID, Name, Position, Salary, HireDate)
VALUES (10, 'Robert', 'Manager', 85000, '2021-08-30');

If we are hiring multiple employees:

INSERT INTO Employees (ID, Name, Position, Salary, HireDate)
VALUES (11, 'Alice', 'Developer', 80000, '2021-08-29'),
       (12, 'Cindy', 'Designer', 75000, '2021-08-28');

Please ensure you replace 'Employees' with your actual table name in the given examples.

Understanding SQL Joins and Their Application

SQL Joins are used to combine rows from two or more tables, based on a related column between them. This provides the ability to pull data together from different sources or tables in a database.

Let's consider the following two tables, Students and Courses:

Students Table

ID NAME
1 John
2 Mary
3 Tim

Courses Table

ID COURSE STUDENT_ID
1 Mathematics 1
2 English 1
3 Biology 2
4 Mathematics 3
5 Biology 3

INNER JOIN

The INNER JOIN keyword selects records that have matching values in both tables.

SELECT Students.NAME, Courses.COURSE
FROM Students
INNER JOIN Courses 
ON Students.ID = Courses.STUDENT_ID;

This query will result in:

NAME COURSE
John Mathematics
John English
Mary Biology
Tim Mathematics
Tim Biology

LEFT JOIN (or LEFT OUTER JOIN)

The LEFT JOIN keyword returns all records from the left table (Students), and the matched records from the right table (Courses). The result is NULL on the right side, if there is no match.

SELECT Students.NAME, Courses.COURSE
FROM Students
LEFT JOIN Courses 
ON Students.ID = Courses.STUDENT_ID;

In this case, if there are students who are not enrolled in any course, their names will still appear in the result, with NULL for their courses.

RIGHT JOIN (or RIGHT OUTER JOIN)

The RIGHT JOIN keyword returns all records from the right table (Courses), and the matched records from the left table (Students). The result is NULL on the left side, if there is no match.

SELECT Students.NAME, Courses.COURSE
FROM Students
RIGHT JOIN Courses 
ON Students.ID = Courses.STUDENT_ID;

In this case, if there are courses that have no students enrolled, these courses will still appear in the result, with NULL for the student names.

FULL JOIN (or FULL OUTER JOIN)

The FULL JOIN keyword returns all records when there is a match in either left (Students) or right (Courses) table records.

SELECT Students.NAME, Courses.COURSE
FROM Students
FULL JOIN Courses 
ON Students.ID = Courses.STUDENT_ID;

Here, if there are students who are not enrolled in any course, or there are courses that don't have any students, they will appear in the result with NULL in the respective columns.

In summary, SQL joins are powerful tools that enable you to pull together data stored in different tables in a cohesive manner. Together with other SQL statements, they make your interaction with the data much more powerful and flexible.

Exploring Advanced SQL Queries and Subqueries

In this notebook, we will explore the advanced SQL queries that involve:

  • Complex WHERE conditions (multiple conditions, IN, BETWEEN, LIKE)
  • Advanced JOIN usage (multi-table joins, self-joins)
  • Grouping data with SQL (GROUP BY, HAVING)
  • Subqueries in SQL

Without further ado, let's start implementing those queries using python and its sqlite3 library.

Note: Assuming the SQLite database is already set up and a connection is established. The database includes two main tables: "employees" and "departments".

import sqlite3
connection = sqlite3.connect("company_database.db")
cursor = connection.cursor()

Complex WHERE conditions

Multiple Conditions

The WHERE clause can be combined with AND, OR, and NOT operators.

# Selecting employees who have a salary greater than 50000 and hired after 2015.
query = """
SELECT * 
FROM employees 
WHERE salary > 50000 AND hire_date > '2015-01-01';
"""
cursor.execute(query)
result = cursor.fetchall()

IN Operator

The IN operator allows you to specify multiple values in a WHERE clause.

# Selecting employees whose department id is either 1, 2 or 3.
query = """
SELECT * 
FROM employees 
WHERE department_id IN (1, 2, 3);
"""
cursor.execute(query)
result = cursor.fetchall()

BETWEEN Operator

The BETWEEN operator selects values within a range.

# Selecting employees who were hired between 2015 and 2020.
query = """
SELECT * 
FROM employees 
WHERE hire_date BETWEEN '2015-01-01' AND '2020-12-31';
"""
cursor.execute(query)
result = cursor.fetchall()

LIKE Operator

The LIKE operator is used to search for a specified pattern.

# Selecting employees whose name starts with 'A'.
query = """
SELECT * 
FROM employees 
WHERE first_name LIKE 'A%';
"""
cursor.execute(query)
result = cursor.fetchall()

Advanced JOIN Usage

Multi-table Joins

You can join more than two tables.

# Assuming there's a third table "positions".
query = """
SELECT E.first_name, D.department_name, P.position_name 
FROM employees E 
JOIN departments D ON E.department_id = D.department_id 
JOIN positions P ON E.position_id = P.position_id;
"""
cursor.execute(query)
result = cursor.fetchall()

Self-Joins

A self join is a regular join, but the table is joined with itself.

# Select employees who have the same positions.
query = """
SELECT A.first_name, B.first_name, A.position_id 
FROM employees A, employees B 
WHERE A.position_id = B.position_id 
AND A.employee_id <> B.employee_id;
"""
cursor.execute(query)
result = cursor.fetchall()

Grouping data with SQL

GROUP BY

The GROUP BY statement groups rows that have the same values in specified columns.

# Group by department and count the number of employees in each department.
query = """
SELECT department_id, COUNT(employee_id) 
FROM employees 
GROUP BY department_id;
"""
cursor.execute(query)
result = cursor.fetchall()

HAVING

The HAVING clause was added to SQL because the WHERE keyword could not be used with aggregate functions.

# Select departments with more than 10 employees.
query = """
SELECT department_id, COUNT(employee_id) 
FROM employees 
GROUP BY department_id 
HAVING COUNT(employee_id) > 10;
"""
cursor.execute(query)
result = cursor.fetchall()

Subqueries in SQL

A subquery is a SQL query nested inside a larger query.

# Find the average salary of the department that has the highest average salary.
query = """
SELECT AVG(salary) 
FROM employees 
WHERE department_id = 
    (SELECT department_id 
    FROM employees 
    GROUP BY department_id 
    ORDER BY AVG(salary) DESC 
    LIMIT 1);
"""
cursor.execute(query)
result = cursor.fetchall()

Don't forget to close the connection once you've finished executing your queries.

connection.close()

This covers some of the advanced SQL queries including complex WHERE clauses, advanced JOINS, GROUP BY, HAVING, and subqueries in SQL. After executing these queries, you'll be able to fetch, analyze, and summarize your data in high granularity.

Introduction to Database Normalization and Transactions in SQL

Database normalization is a vital concept in SQL. It is a systematic approach of decomposing tables to eliminate data redundancy and undesirable characteristics like insertion, update and deletion anomalies. Transactions, on the other hand, are units or sequences of work accomplished in a logical order, whether in a manual fashion by a user or automatically by some sort of a database program.

Database Normalization - Implementation

Assuming we've got a table Orders with the columns OrderID, Product, Quantity, Price, Total, CustomerID, CustomerName, CustomerAddress.

CREATE TABLE Orders (
    OrderID int,
    Product varchar(50),
    Quantity int,
    Price decimal(5,2),
    Total as Quantity * Price, 
    CustomerID int,
    CustomerName varchar(50),
    CustomerAddress varchar(100)
);

This table contains data redundancy and is not in the normalized form. Let's normalize it. We will divide this table into two tables i.e., Orders and Customers.

CREATE TABLE Customers (
    CustomerID int,
    CustomerName varchar(50),
    CustomerAddress varchar(100)
);
CREATE TABLE Orders (
    OrderID int,
    Product varchar(50),
    Quantity int,
    Price decimal(5,2),
    Total as Quantity * Price, 
    CustomerID int
    FOREIGN KEY (CustomerID) REFERENCES Customers(CustomerID)
);

This decomposition eliminates redundancy, reduces anomalies, and ensures data integrity.

Transactions - Implementation

Transactions are used to manage data manipulation operations and to ensure data integrity even in cases of system failure.

Let's see a simple implementation. We are updating two tables with relation. If something goes wrong in between, we should be able to revert the changes back to keep the integrity of data.

First, let's add some data to the tables:

INSERT INTO Customers 
    (CustomerID, CustomerName, CustomerAddress)
VALUES 
    (1, 'John Doe', '123 ABC St'),
    (2, 'Jane Doe', '456 DEF St');    
    
INSERT INTO Orders 
    (OrderID, Product, Quantity, Price, CustomerID)
VALUES
    (1, 'Product A', 2, 20.50, 1),
    (2, 'Product B', 1, 10.00, 2);

Assuming you want to update customer's address and related order's price, you will use transaction to ensure data integrity.

BEGIN TRANSACTION
    UPDATE Customers
    SET CustomerAddress = '789 GHI St'
    WHERE CustomerID = 1;

    UPDATE Orders
    SET Price = 25.00
    WHERE OrderID = 1 and CustomerID = 1;

    IF @@ERROR <> 0
    BEGIN
        ROLLBACK TRANSACTION
        RAISERROR('Error encountered. Transaction Rolled back',16,1)
    END
COMMIT TRANSACTION

Here, if any of the update operation fails, all previous operations are rolled back, ensuring data consistency. If all operations are successful, changes are committed to the database.

Stored Procedures and Triggers Implementation in SQL

Stored Procedures

Stored procedures are SQL statements or a series of SQL commands that are stored in the database catalog - much like a small script. The stored procedure executes these procedures when it is called by a trigger, another stored procedure or a client application.

1. Creating a Stored Procedure

Here's an example of creating a stored procedure that fetches employee data from an Employee table.

CREATE PROCEDURE Fetch_EmployeeData 
AS
SELECT * FROM Employee;
GO

In this example, the stored procedure named Fetch_EmployeeData fetches all records from the Employee table.

2. Calling a Stored Procedure

Stored procedures can be executed by using the EXEC keyword followed by the stored procedure name.

EXEC Fetch_EmployeeData;

Triggers

Triggers are special types of stored procedures that are executed automatically when an event happens (e.g., data modification or database operation).

1. Creating a Trigger

Here's an example of creating a trigger that logs an entry in LogTable when data is inserted into the Employee table.

CREATE TRIGGER Log_EmployeeInsert
ON Employee
AFTER INSERT
AS
INSERT INTO LogTable 
SELECT 'New Employee data inserted on ' + CONVERT(VARCHAR(20), GETDATE());
GO

In this example, the trigger named Log_EmployeeInsert logs an entry into LogTable whenever a new record is inserted into the Employee table.

2. Deleting a Trigger

Triggers can be deleted using the DROP TRIGGER statement.

DROP TRIGGER Log_EmployeeInsert;

Remember, the use of stored procedures and triggers can greatly increase the efficiency and security of SQL-based applications by minimizing the need to expose SQL commands to the user and consolidating potentially complex operations into a single, streamlined process.

Remember, stored procedures and triggers are reusable and transparent to other SQL commands and applications. This means that they can be modified independently of the application code. Also bear in mind that these are powerful features and should be used judiciously - overuse of triggers, for instance, can lead to complex interdependencies that might be hard to keep track of during the development process.