[go: up one dir, main page]

0% found this document useful (0 votes)
18 views23 pages

Design and Implement Sample Schema and Insert Data...

Uploaded by

20CE033 Dhruvi
Copyright
© © All Rights Reserved
We take content rights seriously. If you suspect this is your content, claim it here.
Available Formats
Download as PDF, TXT or read online on Scribd
0% found this document useful (0 votes)
18 views23 pages

Design and Implement Sample Schema and Insert Data...

Uploaded by

20CE033 Dhruvi
Copyright
© © All Rights Reserved
We take content rights seriously. If you suspect this is your content, claim it here.
Available Formats
Download as PDF, TXT or read online on Scribd
You are on page 1/ 23

The following code provides a comprehensive set of SQL commands for an Oracle SQL

command-line environment, covering schema design, data manipulation, constraints, various


function types, joins, subqueries, and an introduction to PL/SQL concepts like cursors and
triggers.
Important Notes for Oracle SQL Command Line:
●​ Semicolon (;): Every SQL statement must end with a semicolon.
●​ SET SERVEROUTPUT ON;: This command is crucial for displaying output from PL/SQL
blocks (like DBMS_OUTPUT.PUT_LINE). You should run it once at the beginning of your
session if you want to see PL/SQL output.
●​ ROLLBACK; and COMMIT;: Oracle performs DML operations within a transaction.
Changes are not permanently saved until COMMIT; is issued. ROLLBACK; undoes
uncommitted changes.
●​ SPOOL command: Used to save the output of your SQL*Plus session to a file. Useful for
logging your work.
●​ -- for single-line comments.
●​ /* ... */ for multi-line comments.
Section 1: Schema Design and Data Insertion (DDL & DML)
Let's design a simple schema for a "Library Management System".
1.1 DDL: Create Tables
-- DDL: Create Tables​

-- Create Authors Table​
CREATE TABLE Authors (​
author_id NUMBER GENERATED BY DEFAULT AS IDENTITY PRIMARY KEY, --
Oracle 12c+ Identity Column​
first_name VARCHAR2(100) NOT NULL,​
last_name VARCHAR2(100) NOT NULL,​
birth_date DATE,​
nationality VARCHAR2(50)​
);​

-- Create Genres Table​
CREATE TABLE Genres (​
genre_id NUMBER GENERATED BY DEFAULT AS IDENTITY PRIMARY KEY,​
genre_name VARCHAR2(100) UNIQUE NOT NULL​
);​

-- Create Books Table​
CREATE TABLE Books (​
book_id NUMBER GENERATED BY DEFAULT AS IDENTITY PRIMARY KEY,​
title VARCHAR2(255) NOT NULL,​
publication_year NUMBER(4),​
isbn VARCHAR2(20) UNIQUE NOT NULL,​
price NUMBER(10, 2) DEFAULT 0.00,​
author_id NUMBER NOT NULL,​
genre_id NUMBER NOTBALL,​
CONSTRAINT fk_author FOREIGN KEY (author_id) REFERENCES
Authors(author_id) ON DELETE CASCADE,​
CONSTRAINT fk_genre FOREIGN KEY (genre_id) REFERENCES
Genres(genre_id) ON DELETE SET NULL,​
CONSTRAINT chk_publication_year CHECK (publication_year BETWEEN
1000 AND TO_CHAR(SYSDATE, 'YYYY')) -- Check constraint​
);​

-- Create Members Table​
CREATE TABLE Members (​
member_id NUMBER GENERATED BY DEFAULT AS IDENTITY PRIMARY KEY,​
first_name VARCHAR2(100) NOT NULL,​
last_name VARCHAR2(100) NOT NULL,​
email VARCHAR2(150) UNIQUE NOT NULL,​
phone_number VARCHAR2(20),​
registration_date DATE DEFAULT SYSDATE​
);​

-- Create Loans Table​
CREATE TABLE Loans (​
loan_id NUMBER GENERATED BY DEFAULT AS IDENTITY PRIMARY KEY,​
book_id NUMBER NOT NULL,​
member_id NUMBER NOT NULL,​
loan_date DATE DEFAULT SYSDATE,​
return_date DATE,​
due_date DATE GENERATED ALWAYS AS (loan_date + 14) VIRTUAL, --
Virtual column (Oracle 11gR2+)​
CONSTRAINT fk_loan_book FOREIGN KEY (book_id) REFERENCES
Books(book_id) ON DELETE CASCADE,​
CONSTRAINT fk_loan_member FOREIGN KEY (member_id) REFERENCES
Members(member_id) ON DELETE CASCADE,​
CONSTRAINT chk_return_date CHECK (return_date >= loan_date)​
);​

-- End of DDL: Create Tables​

1.2 DML: Insert Data


-- DML: Insert Data​

-- Insert into Authors​
INSERT INTO Authors (first_name, last_name, birth_date, nationality)
VALUES ('Gabriel', 'García Márquez', TO_DATE('06-MAR-1927',
'DD-MON-YYYY'), 'Colombian');​
INSERT INTO Authors (first_name, last_name, birth_date, nationality)
VALUES ('Jane', 'Austen', TO_DATE('16-DEC-1775', 'DD-MON-YYYY'),
'British');​
INSERT INTO Authors (first_name, last_name, birth_date, nationality)
VALUES ('George', 'Orwell', TO_DATE('25-JUN-1903', 'DD-MON-YYYY'),
'British');​
INSERT INTO Authors (first_name, last_name, birth_date, nationality)
VALUES ('J.K.', 'Rowling', TO_DATE('31-JUL-1965', 'DD-MON-YYYY'),
'British');​
INSERT INTO Authors (first_name, last_name, birth_date, nationality)
VALUES ('Haruki', 'Murakami', TO_DATE('12-JAN-1949', 'DD-MON-YYYY'),
'Japanese');​

-- Insert into Genres​
INSERT INTO Genres (genre_name) VALUES ('Magical Realism');​
INSERT INTO Genres (genre_name) VALUES ('Classics');​
INSERT INTO Genres (genre_name) VALUES ('Dystopian');​
INSERT INTO Genres (genre_name) VALUES ('Fantasy');​
INSERT INTO Genres (genre_name) VALUES ('Contemporary');​
INSERT INTO Genres (genre_name) VALUES ('Science Fiction');​

-- Insert into Books (Note: Author_id and Genre_id correspond to the
order of insertion above)​
INSERT INTO Books (title, publication_year, isbn, price, author_id,
genre_id) VALUES ('One Hundred Years of Solitude', 1967,
'9780060883281', 15.99, 1, 1);​
INSERT INTO Books (title, publication_year, isbn, price, author_id,
genre_id) VALUES ('Pride and Prejudice', 1813, '9780141439518', 10.50,
2, 2);​
INSERT INTO Books (title, publication_year, isbn, price, author_id,
genre_id) VALUES ('1984', 1949, '9780451524935', 12.75, 3, 3);​
INSERT INTO Books (title, publication_year, isbn, price, author_id,
genre_id) VALUES ('Animal Farm', 1945, '9780451526342', 9.99, 3, 3);​
INSERT INTO Books (title, publication_year, isbn, price, author_id,
genre_id) VALUES ('Harry Potter and the Sorcerer''s Stone', 1997,
'9780590353427', 18.25, 4, 4);​
INSERT INTO Books (title, publication_year, isbn, price, author_id,
genre_id) VALUES ('Kafka on the Shore', 2002, '9780099458022', 14.00,
5, 5);​
INSERT INTO Books (title, publication_year, isbn, price, author_id,
genre_id) VALUES ('Norwegian Wood', 1987, '9780099448832', 13.50, 5,
5);​
INSERT INTO Books (title, publication_year, isbn, price, author_id,
genre_id) VALUES ('Love in the Time of Cholera', 1985,
'9780140119904', 16.50, 1, 1);​
INSERT INTO Books (title, publication_year, isbn, price, author_id,
genre_id) VALUES ('Emma', 1815, '9780141439587', 11.00, 2, 2);​


-- Insert into Members​
INSERT INTO Members (first_name, last_name, email, phone_number,
registration_date) VALUES ('Alice', 'Smith', 'alice.s@example.com',
'111-222-3333', TO_DATE('01-JAN-2023', 'DD-MON-YYYY'));​
INSERT INTO Members (first_name, last_name, email, phone_number,
registration_date) VALUES ('Bob', 'Johnson', 'bob.j@example.com',
'444-555-6666', TO_DATE('15-FEB-2023', 'DD-MON-YYYY'));​
INSERT INTO Members (first_name, last_name, email, phone_number,
registration_date) VALUES ('Charlie', 'Brown',
'charlie.b@example.com', '777-888-9999', TO_DATE('10-MAR-2024',
'DD-MON-YYYY'));​
INSERT INTO Members (first_name, last_name, email, phone_number,
registration_date) VALUES ('Diana', 'Prince', 'diana.p@example.com',
'000-111-2222', TO_DATE('05-APR-2024', 'DD-MON-YYYY'));​
INSERT INTO Members (first_name, last_name, email, phone_number,
registration_date) VALUES ('Eve', 'Adams', 'eve.a@example.com',
'999-888-7777', TO_DATE('20-MAY-2024', 'DD-MON-YYYY'));​


-- Insert into Loans (some books currently loaned, some returned)​
INSERT INTO Loans (book_id, member_id, loan_date, return_date) VALUES
(1, 1, TO_DATE('01-JUL-2024', 'DD-MON-YYYY'), TO_DATE('14-JUL-2024',
'DD-MON-YYYY')); -- Returned​
INSERT INTO Loans (book_id, member_id, loan_date) VALUES (3, 2,
TO_DATE('05-JUL-2024', 'DD-MON-YYYY')); -- Currently Loaned​
INSERT INTO Loans (book_id, member_id, loan_date, return_date) VALUES
(5, 3, TO_DATE('10-JUL-2024', 'DD-MON-YYYY'), TO_DATE('20-JUL-2024',
'DD-MON-YYYY')); -- Returned​
INSERT INTO Loans (book_id, member_id, loan_date) VALUES (2, 4,
TO_DATE('15-JUL-2024', 'DD-MON-YYYY')); -- Currently Loaned​
INSERT INTO Loans (book_id, member_id, loan_date) VALUES (7, 1,
TO_DATE('20-JUL-2024', 'DD-MON-YYYY')); -- Currently Loaned​
INSERT INTO Loans (book_id, member_id, loan_date, return_date) VALUES
(4, 5, TO_DATE('25-JUN-2024', 'DD-MON-YYYY'), TO_DATE('08-JUL-2024',
'DD-MON-YYYY')); -- Returned​

-- Commit the DML operations​
COMMIT;​

-- End of DML: Insert Data​

Section 2: Data Constraints - Alter Table Commands


Demonstrating adding and removing constraints using ALTER TABLE.
2.1 Add and Remove Integrity Constraints
-- DDL: Alter Table - Constraints​

-- Add a new column to Authors​
ALTER TABLE Authors​
ADD (email VARCHAR2(150));​

-- Add a UNIQUE constraint to the new email column in Authors​
ALTER TABLE Authors​
ADD CONSTRAINT uk_author_email UNIQUE (email);​

-- Add a NOT NULL constraint to the new email column (if it contains
NULLs, it will fail)​
-- First, update existing rows to have non-null emails for this to
work​
UPDATE Authors SET email = LOWER(first_name || '.' || last_name ||
'@author.com') WHERE email IS NULL;​
COMMIT;​

ALTER TABLE Authors​
MODIFY (email VARCHAR2(150) NOT NULL);​

-- Add a new CHECK constraint to Books: price must be greater than 5​
ALTER TABLE Books​
ADD CONSTRAINT chk_book_price_min CHECK (price > 5.00);​

-- Remove the unique constraint on author email​
ALTER TABLE Authors​
DROP CONSTRAINT uk_author_email;​

-- Remove the check constraint on book price​
ALTER TABLE Books​
DROP CONSTRAINT chk_book_price_min;​

-- Drop the email column we added​
ALTER TABLE Authors​
DROP COLUMN email;​

-- End of Data Constraints​

Section 3: Value-Matching and Pattern-Matching Conditions


3.1 Value Matching (=, IN, >, <, BETWEEN)
-- Value-matching conditions​

-- Retrieve books published in 1949​
SELECT title, publication_year​
FROM Books​
WHERE publication_year = 1949;​

-- Retrieve members registered in 2024​
SELECT first_name, last_name, registration_date​
FROM Members​
WHERE TO_CHAR(registration_date, 'YYYY') = '2024';​

-- Retrieve books with a price between 10.00 and 15.00 (inclusive)​
SELECT title, price​
FROM Books​
WHERE price BETWEEN 10.00 AND 15.00;​

-- Retrieve books by authors with author_id 1 or 3​
SELECT title, author_id​
FROM Books​
WHERE author_id IN (1, 3);​

-- Retrieve loans made before July 10, 2024​
SELECT loan_id, loan_date​
FROM Loans​
WHERE loan_date < TO_DATE('10-JUL-2024', 'DD-MON-YYYY');​

3.2 Pattern Matching (LIKE)


-- Pattern-matching conditions using LIKE​

-- Retrieve books whose title starts with 'H'​
SELECT title​
FROM Books​
WHERE title LIKE 'H%';​

-- Retrieve authors whose last name contains 'en'​
SELECT first_name, last_name​
FROM Authors​
WHERE last_name LIKE '%en%';​

-- Retrieve members whose email address ends with '.com'​
SELECT first_name, last_name, email​
FROM Members​
WHERE email LIKE '%.com';​

-- Retrieve books with 'the' in the title (case-insensitive for better
results, if NLS_COMP is LINGUISTIC and NLS_SORT is BINARY_CI)​
-- For a truly case-insensitive search regardless of session settings:​
SELECT title​
FROM Books​
WHERE LOWER(title) LIKE '%the%';​

-- Retrieve books with a two-letter word beginning with 'a' (e.g., 'a
') or ending with 'a' (e.g., ' a')​
SELECT title​
FROM Books​
WHERE title LIKE '% a %' OR title LIKE 'a %' OR title LIKE '% a';​

Section 4: Oracle Functions


4.1 Aggregate Functions
-- Aggregate Functions​

-- Total number of books​
SELECT COUNT(*) AS total_books FROM Books;​

-- Number of distinct authors​
SELECT COUNT(DISTINCT author_id) AS distinct_authors FROM Books;​

-- Average price of books​
SELECT AVG(price) AS average_book_price FROM Books;​

-- Sum of all book prices​
SELECT SUM(price) AS total_book_value FROM Books;​

-- Maximum price of a book​
SELECT MAX(price) AS highest_book_price FROM Books;​

-- Minimum price of a book​
SELECT MIN(price) AS lowest_book_price FROM Books;​

4.2 Numeric Functions


-- Numeric Functions​

-- ABS (Absolute value) - not directly applicable here, but for
example:​
SELECT ABS(-100) AS abs_value FROM DUAL;​

-- CEIL (Ceiling) and FLOOR (Floor)​
SELECT CEIL(15.99) AS ceil_price, FLOOR(15.99) AS floor_price FROM
DUAL;​

-- ROUND (Rounding to specified decimal places)​
SELECT ROUND(15.993, 2) AS rounded_price FROM DUAL;​
SELECT ROUND(15.997, 2) AS rounded_price FROM DUAL;​

-- TRUNC (Truncate to specified decimal places)​
SELECT TRUNC(15.997, 2) AS truncated_price FROM DUAL;​

-- MOD (Modulo)​
SELECT MOD(10, 3) AS remainder FROM DUAL; -- Returns 1​

4.3 String Functions


-- String Functions​

-- CONCAT (Concatenate strings - || is more common in Oracle)​
SELECT CONCAT(first_name, last_name) AS full_name_concat FROM Authors
WHERE author_id = 1;​
SELECT first_name || ' ' || last_name AS full_name_concatenated FROM
Authors WHERE author_id = 1;​

-- LENGTH (Length of a string)​
SELECT title, LENGTH(title) AS title_length FROM Books WHERE book_id =
1;​

-- SUBSTR (Substring)​
SELECT SUBSTR('One Hundred Years of Solitude', 1, 10) AS short_title
FROM DUAL;​

-- INSTR (Position of substring)​
SELECT INSTR('Harry Potter and the Sorcerer''s Stone', 'Potter') AS
potter_position FROM DUAL;​

-- LPAD / RPAD (Pad string with characters)​
SELECT LPAD(book_id, 5, '0') AS padded_id FROM Books WHERE book_id =
1;​

-- TRIM (Remove leading/trailing/both characters)​
SELECT TRIM(' Hello World ') AS trimmed_string FROM DUAL;​
SELECT TRIM(LEADING 'x' FROM 'xxxOraclexxx') AS trimmed_leading FROM
DUAL;​
SELECT TRIM(TRAILING 'x' FROM 'xxxOraclexxx') AS trimmed_trailing FROM
DUAL;​
SELECT TRIM(BOTH 'x' FROM 'xxxOraclexxx') AS trimmed_both FROM DUAL;​

-- REPLACE (Replace all occurrences of a substring)​
SELECT REPLACE('Old World', 'Old', 'New') AS new_string FROM DUAL;​

-- UPPER / LOWER / INITCAP (Case conversion)​
SELECT UPPER('oracle'), LOWER('ORACLE'), INITCAP('oracle database')
FROM DUAL;​
SELECT first_name, last_name, INITCAP(first_name || ' ' || last_name)
AS ProperCaseName FROM Members WHERE member_id = 1;​

4.4 Conversion Functions (TO_CHAR, TO_NUMBER, TO_DATE)


-- Conversion Functions​

-- TO_CHAR (Date to Character)​
SELECT loan_date, TO_CHAR(loan_date, 'DD-MON-YYYY') AS formatted_date
FROM Loans WHERE loan_id = 2;​
SELECT SYSDATE, TO_CHAR(SYSDATE, 'HH24:MI:SS') AS current_time FROM
DUAL;​
SELECT SYSDATE, TO_CHAR(SYSDATE, 'YYYYMMDD_HH24MISS') AS
timestamp_string FROM DUAL;​

-- TO_CHAR (Number to Character)​
SELECT price, TO_CHAR(price, '$9,999.00') AS formatted_price FROM
Books WHERE book_id = 1;​

-- TO_NUMBER (Character to Number)​
SELECT TO_NUMBER('123.45') + 10 AS calculated_number FROM DUAL;​

-- TO_DATE (Character to Date) - already used in INSERT statements​
SELECT TO_DATE('25-DEC-2024', 'DD-MON-YYYY') AS christmas_date FROM
DUAL;​

4.5 Date Functions


-- Date Functions​

-- SYSDATE (Current system date and time)​
SELECT SYSDATE AS current_system_date FROM DUAL;​

-- ADD_MONTHS (Add months to a date)​
SELECT SYSDATE AS today, ADD_MONTHS(SYSDATE, 3) AS date_plus_3_months
FROM DUAL;​

-- MONTHS_BETWEEN (Months between two dates)​
SELECT​
registration_date,​
SYSDATE,​
MONTHS_BETWEEN(SYSDATE, registration_date) AS
months_since_registration​
FROM Members​
WHERE member_id = 1;​

-- NEXT_DAY (Next occurrence of a weekday)​
SELECT NEXT_DAY(SYSDATE, 'SUNDAY') AS next_sunday FROM DUAL;​

-- LAST_DAY (Last day of the month)​
SELECT SYSDATE AS today, LAST_DAY(SYSDATE) AS last_day_of_month FROM
DUAL;​

-- TRUNC (Truncate date to a unit)​
SELECT TRUNC(SYSDATE, 'MM') AS first_day_of_month FROM DUAL; -- First
day of current month​
SELECT TRUNC(SYSDATE, 'YYYY') AS first_day_of_year FROM DUAL; -- First
day of current year​

-- EXTRACT (Extract specific date parts)​
SELECT​
EXTRACT(YEAR FROM SYSDATE) AS current_year,​
EXTRACT(MONTH FROM SYSDATE) AS current_month,​
EXTRACT(DAY FROM SYSDATE) AS current_day​
FROM DUAL;​

-- Date arithmetic (adding/subtracting days)​
SELECT loan_date, loan_date + 7 AS one_week_later, loan_date - 3 AS
three_days_earlier FROM Loans WHERE loan_id = 2;​

-- Days between two dates​
SELECT return_date - loan_date AS days_on_loan FROM Loans WHERE
loan_id = 1;​

4.6 Set Operations (UNION, UNION ALL, INTERSECT, MINUS)


-- Set Operations​

-- UNION (Combines results, removes duplicates, sorts)​
-- Members whose first name is 'Alice' or 'Bob'​
SELECT first_name, last_name FROM Members WHERE first_name = 'Alice'​
UNION​
SELECT first_name, last_name FROM Members WHERE first_name = 'Bob';​

-- UNION ALL (Combines results, includes duplicates, no sorting by
default)​
-- All first names from Authors and Members (including duplicates if
any)​
SELECT first_name FROM Authors​
UNION ALL​
SELECT first_name FROM Members;​

-- INTERSECT (Returns common rows)​
-- Authors who also appear as members (assuming same name structure) -
likely empty for our data​
SELECT first_name, last_name FROM Authors​
INTERSECT​
SELECT first_name, last_name FROM Members;​

-- MINUS (Returns rows from first query not found in second)​
-- Books whose publication year is 1949 but are NOT priced at 12.75​
SELECT title, publication_year, price FROM Books WHERE
publication_year = 1949​
MINUS​
SELECT title, publication_year, price FROM Books WHERE
publication_year = 1949 AND price = 12.75;​

Section 5: Group By and Having Clauses


-- Study of GROUP BY and HAVING Clauses​

-- Group books by publication year and count how many books were
published in each year​
SELECT​
publication_year,​
COUNT(book_id) AS number_of_books​
FROM​
Books​
GROUP BY​
publication_year​
ORDER BY​
publication_year DESC;​

-- Group books by author and calculate the average price of their
books​
SELECT​
a.first_name,​
a.last_name,​
AVG(b.price) AS average_book_price​
FROM​
Authors a​
JOIN​
Books b ON a.author_id = b.author_id​
GROUP BY​
a.first_name, a.last_name​
ORDER BY​
average_book_price DESC;​

-- Group members by registration year and count members, but only for
years with more than 1 registration​
SELECT​
TO_CHAR(registration_date, 'YYYY') AS registration_year,​
COUNT(member_id) AS number_of_members​
FROM​
Members​
GROUP BY​
TO_CHAR(registration_date, 'YYYY')​
HAVING​
COUNT(member_id) > 1​
ORDER BY​
registration_year;​

-- Find genres where the sum of book prices is greater than $30​
SELECT​
g.genre_name,​
SUM(b.price) AS total_genre_value​
FROM​
Genres g​
JOIN​
Books b ON g.genre_id = b.genre_id​
GROUP BY​
g.genre_name​
HAVING​
SUM(b.price) > 30​
ORDER BY​
total_genre_value DESC;​

-- Count currently loaned books per member, only showing members with
more than one active loan​
SELECT​
m.first_name,​
m.last_name,​
COUNT(l.loan_id) AS active_loans​
FROM​
Members m​
JOIN​
Loans l ON m.member_id = l.member_id​
WHERE​
l.return_date IS NULL​
GROUP BY​
m.first_name, m.last_name​
HAVING​
COUNT(l.loan_id) > 1;​

Section 6: Subqueries
-- Solve queries using the concept of Subquery​

-- 6.1 Scalar Subquery (returns a single value)​
-- Find books whose price is greater than the average book price​
SELECT​
title,​
price​
FROM​
Books​
WHERE​
price > (SELECT AVG(price) FROM Books);​

-- 6.2 Multi-row Subquery (returns multiple rows, single column)​
-- Find authors who have written books published after 2000​
SELECT​
first_name,​
last_name​
FROM​
Authors​
WHERE​
author_id IN (SELECT author_id FROM Books WHERE publication_year >
2000);​

-- Find members who have active loans (return_date IS NULL)​
SELECT​
first_name,​
last_name​
FROM​
Members​
WHERE​
member_id IN (SELECT DISTINCT member_id FROM Loans WHERE
return_date IS NULL);​

-- 6.3 Multi-column Subquery (returns multiple columns and rows) -
less common but possible​
-- Find books that share both the author and genre with a specific
book (e.g., Book ID 1)​
SELECT​
title,​
author_id,​
genre_id​
FROM​
Books​
WHERE​
(author_id, genre_id) = (SELECT author_id, genre_id FROM Books
WHERE book_id = 1)​
AND book_id != 1; -- Exclude the original book​

-- 6.4 Correlated Subquery (subquery depends on outer query)​
-- Find books that have a price higher than the average price of books
by the same author​
SELECT​
b1.title,​
b1.price,​
a.first_name,​
a.last_name​
FROM​
Books b1​
JOIN​
Authors a ON b1.author_id = a.author_id​
WHERE​
b1.price > (SELECT AVG(b2.price) FROM Books b2 WHERE b2.author_id
= b1.author_id);​

-- Find genres that have at least one book published after 2000​
SELECT​
genre_name​
FROM​
Genres g​
WHERE​
EXISTS (SELECT 1 FROM Books b WHERE b.genre_id = g.genre_id AND
b.publication_year > 2000);​

-- Find members who have not loaned any book (NOT EXISTS)​
SELECT​
first_name,​
last_name​
FROM​
Members m​
WHERE​
NOT EXISTS (SELECT 1 FROM Loans l WHERE l.member_id =
m.member_id);​

Section 7: Displaying Data from Multiple Tables Using Join Operations


-- Displaying data from Multiple Tables using Join Operations​

-- 7.1 INNER JOIN (Returns rows when there is a match in both tables)​
-- List all books with their author's full name and genre name​
SELECT​
b.title,​
a.first_name AS author_first_name,​
a.last_name AS author_last_name,​
g.genre_name​
FROM​
Books b​
INNER JOIN​
Authors a ON b.author_id = a.author_id​
INNER JOIN​
Genres g ON b.genre_id = g.genre_id​
ORDER BY​
b.title;​

-- List all current loans showing book title, member name, and loan
date​
SELECT​
m.first_name AS member_first_name,​
m.last_name AS member_last_name,​
b.title AS book_title,​
l.loan_date,​
l.due_date -- Virtual column​
FROM​
Loans l​
INNER JOIN​
Members m ON l.member_id = m.member_id​
INNER JOIN​
Books b ON l.book_id = b.book_id​
WHERE​
l.return_date IS NULL​
ORDER BY​
l.loan_date DESC;​

-- 7.2 LEFT JOIN (or LEFT OUTER JOIN)​
-- List all authors and their books. If an author has no books, still
show the author.​
SELECT​
a.first_name,​
a.last_name,​
b.title​
FROM​
Authors a​
LEFT JOIN​
Books b ON a.author_id = b.author_id​
ORDER BY​
a.last_name, b.title;​

-- List all genres and the books in them. Show genres even if they
have no books.​
INSERT INTO Genres (genre_name) VALUES ('Poetry'); -- Add a genre with
no books​
COMMIT;​

SELECT​
g.genre_name,​
b.title​
FROM​
Genres g​
LEFT JOIN​
Books b ON g.genre_id = b.genre_id​
ORDER BY​
g.genre_name, b.title;​

-- Clean up the 'Poetry' genre for other examples​
DELETE FROM Genres WHERE genre_name = 'Poetry';​
COMMIT;​


-- 7.3 RIGHT JOIN (or RIGHT OUTER JOIN)​
-- Less common as it can usually be rewritten as a LEFT JOIN by
swapping tables.​
-- Example: List all books and their genres. Show books even if their
genre_id became NULL​
-- (e.g., if ON DELETE SET NULL was triggered or if genre_id was
initially NULL).​
-- Note: Our schema has genre_id NOT NULL for Books, so this won't
show Books without Genres initially.​
-- If we drop a genre, books might have NULL genre_id if the FK was ON
DELETE SET NULL.​
-- Let's simulate a book with a NULL genre_id for this example if it
wasn't already handled.​
-- For this to show something, you'd need a book where genre_id is
NULL.​
-- Let's consider a LEFT JOIN on Books to Genres, which is more
intuitive for this data.​
SELECT​
b.title,​
g.genre_name​
FROM​
Books b​
RIGHT JOIN​
Genres g ON b.genre_id = g.genre_id​
ORDER BY​
g.genre_name, b.title;​


-- 7.4 FULL OUTER JOIN (Returns rows when there is a match in either
left or right table)​
-- List all authors and all genres, and match them with books where
possible.​
-- Oracle doesn't have a direct 'Books' entry for authors/genres
without books with a simple FULL JOIN.​
-- This shows all authors, all genres, and the books that connect
them.​
SELECT​
a.first_name,​
a.last_name,​
b.title,​
g.genre_name​
FROM​
Authors a​
FULL OUTER JOIN​
Books b ON a.author_id = b.author_id​
FULL OUTER JOIN​
Genres g ON b.genre_id = g.genre_id​
ORDER BY​
a.last_name, g.genre_name, b.title;​

-- 7.5 SELF JOIN (Joining a table to itself)​
-- Find pairs of books written by the same author​
SELECT​
b1.title AS book1_title,​
b2.title AS book2_title,​
a.first_name,​
a.last_name​
FROM​
Books b1​
JOIN​
Books b2 ON b1.author_id = b2.author_id AND b1.book_id <
b2.book_id​
JOIN​
Authors a ON b1.author_id = a.author_id​
ORDER BY​
a.last_name, b1.title, b2.title;​

Section 8: Database Backup & Recovery Commands (Conceptual/Logical Backup)


Note: Oracle's actual backup and recovery commands (RMAN, Data Pump) are not SQL
command-line statements that you execute within SQL*Plus directly like SELECT or CREATE
TABLE. They are separate utilities executed from the operating system command line.
Here, I'll demonstrate the logical backup/export using Data Pump utility which is run from OS
command line, but the SQL commands to view Data Pump jobs are useful. Also, Flashback
Query and Flashback Table are SQL features for point-in-time recovery of data/tables without a
full database restore.
8.1 Logical Backup (Data Pump Export/Import - OS Level)
Conceptual explanation (not SQL*Plus code to run directly):
To export (backup) schema data and structure: From your OS command line (e.g., Command
Prompt on Windows, Bash on Linux), logged in as a user with EXP_FULL_DATABASE role:
-- Export entire schema (e.g., LIBRARY_ADMIN schema)​
expdp library_admin/password@ORCLPDB1 SCHEMAS=LIBRARY_ADMIN
DIRECTORY=DATA_PUMP_DIR DUMPFILE=library_schema.dmp
LOGFILE=library_schema_export.log​

-- To import (recover) this schema data:​
impdp library_admin/password@ORCLPDB1 SCHEMAS=LIBRARY_ADMIN
DIRECTORY=DATA_PUMP_DIR DUMPFILE=library_schema.dmp
LOGFILE=library_schema_import.log​

-- If you want to import into a different user:​
impdp system/password@ORCLPDB1 REMAP_SCHEMA=LIBRARY_ADMIN:NEW_USER
DIRECTORY=DATA_PUMP_DIR DUMPFILE=library_schema.dmp
LOGFILE=library_schema_remap.log​

SQL*Plus commands to prepare/monitor Data Pump:


-- SQL*Plus: To see available Data Pump directories (must be created
by DBA)​
SELECT * FROM DBA_DIRECTORIES WHERE DIRECTORY_NAME = 'DATA_PUMP_DIR';​

-- If DATA_PUMP_DIR doesn't exist, as SYSDBA or privileged user:​
-- CREATE DIRECTORY DATA_PUMP_DIR AS '/path/to/your/backup/directory';​
-- GRANT READ, WRITE ON DIRECTORY DATA_PUMP_DIR TO your_user;​

-- SQL*Plus: To monitor Data Pump jobs (if you run expdp/impdp in
background)​
SELECT owner_name, job_name, operation, job_mode, state,
attached_sessions, degree​
FROM DBA_DATAPUMP_JOBS;​

8.2 Flashback Query (SQL Feature for Data Recovery)


This allows you to query data as it existed at a specific point in time in the past. Requires
UNDO_RETENTION to be configured sufficiently.
-- Flashback Query: See data as it was 5 minutes ago​
-- First, make some changes to demonstrate​
UPDATE Books SET price = 999.99 WHERE book_id = 1;​
COMMIT; -- Simulate a mistake​

-- Now, query the table as it was before the update (assuming it
happened recently)​
SELECT title, price FROM Books AS OF TIMESTAMP (SYSTIMESTAMP -
INTERVAL '5' MINUTE) WHERE book_id = 1;​

-- Or by SCN (System Change Number - advanced)​
-- SELECT CURRENT_SCN FROM V$DATABASE; -- Get current SCN​
-- SELECT title, price FROM Books AS OF SCN 1234567 WHERE book_id = 1;​

-- Revert the change (if it was a mistake and committed) by
re-updating or using flashback table.​
-- Let's revert the change for book_id 1​
UPDATE Books SET price = 15.99 WHERE book_id = 1;​
COMMIT;​

8.3 Flashback Table (SQL Feature for Table Recovery)


This can restore a table to a previous state. Requires UNDO_RETENTION and ROW
MOVEMENT enabled on the table.
-- Flashback Table: Recover a table to a past state​

-- Enable ROW MOVEMENT for the table (required for flashback table)​
ALTER TABLE Books ENABLE ROW MOVEMENT;​

-- Simulate a accidental delete​
DELETE FROM Books WHERE author_id = 3;​
COMMIT; -- Oh no, Orwell's books are gone!​

-- Check current state​
SELECT * FROM Books WHERE author_id = 3; -- Should show no rows​

-- Now, flashback the table to a time before the delete (adjust
timestamp as needed)​
-- Make sure the timestamp is before the DELETE operation.​
FLASHBACK TABLE Books TO TIMESTAMP (SYSTIMESTAMP - INTERVAL '1'
MINUTE);​

-- Verify recovery​
SELECT * FROM Books WHERE author_id = 3; -- Should show Orwell's books
again!​

-- Disable ROW MOVEMENT if not needed anymore​
ALTER TABLE Books DISABLE ROW MOVEMENT;​

COMMIT; -- Commit the flashback operation​

Important: The INTERVAL value must be adjusted based on how long ago the action you want
to revert occurred. Oracle's undo retention needs to be sufficient.
Section 9: Rollback, Commit, Savepoint
These commands manage transactions in Oracle.
-- Study & Implementation of Rollback, Commit, Savepoint​

-- Start a transaction implicitly (any DML starts one) or explicitly
with SET TRANSACTION​
-- SET TRANSACTION READ WRITE; -- Optional, DML implies this.​

-- 1. Insert a new genre​
INSERT INTO Genres (genre_name) VALUES ('Biography');​
SELECT * FROM Genres WHERE genre_name = 'Biography'; -- Visible in
current session​

-- 2. Create a savepoint​
SAVEPOINT sp_before_book_update;​

-- 3. Update a book's price​
UPDATE Books SET price = 20.00 WHERE book_id = 2;​
SELECT title, price FROM Books WHERE book_id = 2;​

-- 4. Delete a member​
DELETE FROM Members WHERE member_id = 5;​
SELECT * FROM Members WHERE member_id = 5; -- Should show no rows​

-- 5. Rollback to the savepoint (undoes update and delete, keeps the
insert)​
ROLLBACK TO sp_before_book_update;​

SELECT * FROM Genres WHERE genre_name = 'Biography'; -- Still there
(inserted before savepoint)​
SELECT title, price FROM Books WHERE book_id = 2; -- Price should be
back to original (10.50)​
SELECT * FROM Members WHERE member_id = 5; -- Member 5 should be back​

-- 6. Now, let's make a new change and commit it​
UPDATE Books SET price = 25.00 WHERE book_id = 6;​
SELECT title, price FROM Books WHERE book_id = 6;​

-- 7. Commit the transaction (makes all changes permanent)​
COMMIT;​

SELECT title, price FROM Books WHERE book_id = 6; -- Price is now
25.00 permanently​

-- 8. Let's try to rollback after a commit​
UPDATE Books SET price = 5.00 WHERE book_id = 6; -- New change​
ROLLBACK; -- This will only undo the last update, not the previous
commit​

SELECT title, price FROM Books WHERE book_id = 6; -- Price should
revert to 25.00, not 5.00​

-- Clean up the 'Biography' genre if you don't want it​
DELETE FROM Genres WHERE genre_name = 'Biography';​
COMMIT;​

Section 10: PL/SQL using Cursor and Triggers


10.1 PL/SQL - Cursor
-- PL/SQL using Cursor​

SET SERVEROUTPUT ON; -- Enable output from DBMS_OUTPUT​

-- Anonymous PL/SQL Block with an Explicit Cursor​
DECLARE​
CURSOR c_authors_books IS​
SELECT​
a.first_name,​
a.last_name,​
b.title,​
b.publication_year​
FROM​
Authors a​
JOIN​
Books b ON a.author_id = b.author_id​
ORDER BY​
a.last_name, b.publication_year;​

v_author_fname Authors.first_name%TYPE;​
v_author_lname Authors.last_name%TYPE;​
v_book_title Books.title%TYPE;​
v_pub_year Books.publication_year%TYPE;​
BEGIN​
DBMS_OUTPUT.PUT_LINE('--- List of Authors and their Books ---');​
DBMS_OUTPUT.PUT_LINE('-------------------------------------');​

OPEN c_authors_books; -- Open the cursor​

LOOP​
FETCH c_authors_books INTO v_author_fname, v_author_lname,
v_book_title, v_pub_year;​
EXIT WHEN c_authors_books%NOTFOUND; -- Exit loop when no more
rows​

DBMS_OUTPUT.PUT_LINE('Author: ' || v_author_fname || ' ' ||
v_author_lname ||​
', Book: ' || v_book_title || ' (' ||
v_pub_year || ')');​
END LOOP;​

CLOSE c_authors_books; -- Close the cursor​

DBMS_OUTPUT.PUT_LINE('-------------------------------------');​
DBMS_OUTPUT.PUT_LINE('Cursor processing complete.');​
EXCEPTION​
WHEN OTHERS THEN​
DBMS_OUTPUT.PUT_LINE('An error occurred: ' || SQLERRM);​
IF c_authors_books%ISOPEN THEN​
CLOSE c_authors_books;​
END IF;​
END;​
/​

10.2 PL/SQL - Trigger


Let's create a trigger that logs changes to the Loans table into a new Loan_Audit table.
-- PL/SQL - Trigger​

-- First, create an audit table​
CREATE TABLE Loan_Audit (​
audit_id NUMBER GENERATED BY DEFAULT AS IDENTITY PRIMARY KEY,​
loan_id NUMBER,​
book_id NUMBER,​
member_id NUMBER,​
action_type VARCHAR2(10) NOT NULL, -- 'INSERT', 'UPDATE', 'DELETE'​
old_loan_date DATE,​
new_loan_date DATE,​
old_return_date DATE,​
new_return_date DATE,​
change_by VARCHAR2(100) DEFAULT USER,​
change_date TIMESTAMP DEFAULT SYSTIMESTAMP​
);​

-- Create a BEFORE/AFTER Row-Level Trigger on Loans table​
CREATE OR REPLACE TRIGGER trg_loans_audit​
AFTER INSERT OR UPDATE OR DELETE ON Loans​
FOR EACH ROW​
DECLARE​
v_action VARCHAR2(10);​
BEGIN​
IF INSERTING THEN​
v_action := 'INSERT';​
INSERT INTO Loan_Audit (loan_id, book_id, member_id,
action_type, new_loan_date, new_return_date)​
VALUES (:NEW.loan_id, :NEW.book_id, :NEW.member_id, v_action,
:NEW.loan_date, :NEW.return_date);​
ELSIF UPDATING THEN​
v_action := 'UPDATE';​
INSERT INTO Loan_Audit (loan_id, book_id, member_id,
action_type, old_loan_date, new_loan_date, old_return_date,
new_return_date)​
VALUES (:OLD.loan_id, :OLD.book_id, :OLD.member_id, v_action,
:OLD.loan_date, :NEW.loan_date, :OLD.return_date, :NEW.return_date);​
ELSIF DELETING THEN​
v_action := 'DELETE';​
INSERT INTO Loan_Audit (loan_id, book_id, member_id,
action_type, old_loan_date, old_return_date)​
VALUES (:OLD.loan_id, :OLD.book_id, :OLD.member_id, v_action,
:OLD.loan_date, :OLD.return_date);​
END IF;​
END;​
/​

-- Verify trigger compilation​
SHOW ERRORS TRIGGER trg_loans_audit;​

-- Test the trigger:​
-- 1. Insert a new loan​
INSERT INTO Loans (book_id, member_id, loan_date) VALUES (6, 2,
TO_DATE('01-AUG-2024', 'DD-MON-YYYY'));​
COMMIT;​
SELECT * FROM Loans WHERE book_id = 6;​
SELECT * FROM Loan_Audit WHERE action_type = 'INSERT' AND book_id = 6;​

-- 2. Update a loan (return a book)​
UPDATE Loans SET return_date = TO_DATE('15-AUG-2024', 'DD-MON-YYYY')
WHERE book_id = 6 AND member_id = 2;​
COMMIT;​
SELECT * FROM Loans WHERE book_id = 6;​
SELECT * FROM Loan_Audit WHERE action_type = 'UPDATE' AND book_id = 6;​

-- 3. Delete a loan​
DELETE FROM Loans WHERE book_id = 6 AND member_id = 2;​
COMMIT;​
SELECT * FROM Loans WHERE book_id = 6;​
SELECT * FROM Loan_Audit WHERE action_type = 'DELETE' AND book_id = 6;​

-- Clean up the trigger and audit table if desired​
-- DROP TRIGGER trg_loans_audit;​
-- DROP TABLE Loan_Audit;​
-- COMMIT;​

This comprehensive script covers all aspects requested for an Oracle SQL command-line
environment. Remember to execute these commands sequentially and commit your DML
changes as needed.

You might also like