Oracle Sql 3 tables with count condition and equal condition - sql

CREATE TABLE CUSTOMER (
CUSID VARCHAR(25) NOT NULL,
CNAME VARCHAR(50),
CONSTRAINT CUSTOMER_PKEY PRIMARY KEY (CUSID),
);
CREATE TABLE SHOP (
SHOPID VARCHAR(10) NOT NULL,
ADDRESS VARCHAR(25),
CONSTRAINT SHOP_PKEY PRIMARY KEY (SHOPID),
);
CREATE TABLE VISIT (
CUSID VARCHAR(25) NOT NULL,
SHOPID VARCHAR(10) NOT NULL,
VDATE DATE NOT NULL,
CONSTRAINT VISIT_PKEY PRIMARY KEY (CUSID, SHOPID, VDATE),
CONSTRAINT VISIT_FKEY1 FOREIGN KEY (CUSID) REFERENCES CUSTOMER(CUSID),
CONSTRAINT VISIT_FKEY2 FOREIGN KEY (SHOPID) REFERENCES SHOP(SHOPID)
);
how to find the address of shops that have been visited at least 2 times by customer with name 'john'??
SELECT ADDRESS FROM SHOP NATURAL JOIN VISIT WHERE CUSID IN (SELECT CUSID FROM CUSTOMER WHERE CNAME = 'john' GROUP BY CUSID HAVING COUNT(CUSID) > 2);
i have tried many kinds of joins, it seems that after i put count and equal condition in together,my results will be 0 rows.

SELECT DISTINCT s.address
FROM shop s
JOIN visit v ON s.shopid = v.shopid
JOIN customer c ON v.customerid = c.customerid
WHERE c.cname = 'John'
GROUP BY
s.address
, c.customerid
HAVING COUNT(*) > 1

Related

SQL join query to library

I have these tables,
CREATE TABLE Book
(
ISBN CHAR(05)NOT NULL,
BKName VARCHAR(20)NOT NULL,
Author VARCHAR(20)NOT NULL,
Price NUMERIC(02)NOT NULL,
CONSTRAINT Book_PK PRIMARY KEY (ISBN)
);
CREATE TABLE Location
(
LoID CHAR(05)NOT NULL,
CityName VARCHAR(15)NOT NULL,
Stoke CHAR(05)NOT NULL,
CONSTRAINT Location_PK PRIMARY KEY (LoID)
);
CREATE TABLE Customer
(
CuID CHAR(05)NOT NULL,
CusName VARCHAR(20)NOT NULL,
RegiDate DATE NOT NULL,
Gender VARCHAR(06)NOT NULL,
TeleNum CHAR(12)NOT NULL,
Address VARCHAR(30)NOT NULL,
CONSTRAINT Customer_PK PRIMARY KEY (CuID)
);
CREATE TABLE BookCopy
(
CopyID CHAR(05)NOT NULL,
ISBN CHAR(05)NOT NULL,
LoID CHAR(05)NOT NULL,
CONSTRAINT pk_BookCopy PRIMARY KEY (CopyID),
CONSTRAINT fk_BookCopy_ISBN_FK FOREIGN KEY (ISBN) REFERENCES Book(ISBN),
CONSTRAINT fk_BookCopy_LoID_FK FOREIGN KEY (LoID) REFERENCES Location(LoID)
);
CREATE TABLE BorrowRecord
(
BrrDate DATE NOT NULL,
RetDate DATE NOT NULL,
BrFee NUMBER(05) NOT NULL,
Cus_Review NUMERIC(02)NOT NULL,
CopyID CHAR(05)NOT NULL,
CuID CHAR(05) NOT NULL,
CONSTRAINT pk_BorrowRecord PRIMARY KEY (CopyID, CuID),
CONSTRAINT fk_BorrowRecord_CopyID_FK FOREIGN KEY (CopyID) REFERENCES BookCopy(CopyID) ,
CONSTRAINT fk_BorrowRecord_CuID_FK FOREIGN KEY (CuID) REFERENCES Customer(CuID) );
This is the task: "write and test a query to list the customer ID and name of every Customer along with the books that they have hired within the past 200 days. Include starting date, ending date, and location name for those hirings. All customer details (ID and name) should be included in the output, whether or not they have actually borrowed any books."
I write a query to list the customer ID and name of every Customer along with the books that they have borrowed within the past 200 days. Include starting date, ending date, and location name for those hirings. This the query for it.
SELECT
BorrowRecord.CuID, Customer.CusName, Book.ISBN, Book.BkName,
BorrowRecord.BrrDate, BorrowRecord.RetDate, Location.CityName
FROM
Book
LEFT JOIN
BookCopy ON Book.ISBN = BookCopy.ISBN
LEFT JOIN
BorrowRecord ON BookCopy.CopyID = BorrowRecord.CopyID
LEFT JOIN
Customer ON BorrowRecord.CuID = Customer.CuID
LEFT JOIN
Location ON Location.LoID = BookCopy.LoID
WHERE
BorrowRecord.BrrDate >= sysdate - 200 ;
But I also need to get all customer details (ID and name) that should be included in the output, whether or not they have actually borrowed any books. How can I do it?
If You want to see all customers independently whether they have borrowed a book or not, it's important that the first LEFT JOIN is based on customers table. In Your script, You start with Book, then join to BorrowRecord and only then have The Customer on the right side of the join. Another point is the WHERE -Condition that excludes all entries of Customer that borrowed a book outside of the 200-days-range. This meand that all records of BorrowRecord are shown (that match to the previous join) but only the customers that are found in a link to BorrowRecords from the past 200 days.
Try the following:
SELECT BorrowRecord.CuID, Customer.CusName, Book.ISBN, Book.BkName,
BorrowRecord.BrrDate,BorrowRecord.RetDate, Location.CityName
FROM Customer
LEFT JOIN BorrowRecord ON BorrowRecord.CuID = Customer.CuID
LEFT JOIN BookCopy ON BorrowRecord.CopyID = BookCopy.CopyID
LEFT JOIN Book ON BookCopy.ISBN= Book.ISBN
LEFT JOIN Location ON Location.LoID = BookCopy.LoID
WHERE ISNULL(BorrowRecord.BrrDate,'') >=sysdate-200 ;

PostgreSQL query for Library Management System

Get the member ID and name of the members to whom no more books can be issued, because they have already got as many books issued as the number for which they are entitled
Following are the schemas:
Book_Records(accession_no,isbn_no)
Book(isbn_no, author, publisher, price)
Members(member_id, member_name,max_no_books,max_no_days)
Book_Issue(member_id,accession_no,issue_date,return_date)
CREATE TABLE BOOK (ISBN_NO VARCHAR(35) PRIMARY KEY,
AUTHOR VARCHAR(35) NOT NULL,
PUBLISHER VARCHAR(35) NOT NULL,
PRICE NUMERIC(10,3));
CREATE TABLE BOOK_RECORDS(ACCESSION_NO VARCHAR(35) PRIMARY KEY,
ISBN_NO VARCHAR(35) REFERENCES BOOK(ISBN_NO));
CREATE TABLE MEMBERS(MEMBER_ID VARCHAR(35) PRIMARY KEY,
MEMBER_NAME VARCHAR(35) NOT NULL,
MAX_NO_BOOKS INT,
MAX_NO_DAYS INT);
CREATE TABLE BOOK_ISSUE(MEMBER_ID VARCHAR(35) REFERENCES MEMBERS(MEMBER_ID),
ACCESSION_NO VARCHAR(35) REFERENCES
BOOK_RECORDS(ACCESSION_NO),
ISSUE_DATE DATE NOT NULL,
RETURN_DATE DATE,
PRIMARY KEY(MEMBER_ID,ACCESSION_NO));
I tried the following query but fails.
SELECT DISTINCT member_name
FROM members AS m
JOIN (
SELECT member_id, COUNT(*) AS no_books_issued
FROM book_issue
GROUP BY member_id,accesion_no
HAVING no_books_issued >= max_no_books
) AS b ON m.member_id = b.member_id;
Presumably, a query like this gets the number of books currently issued:
SELECT member_id, COUNT(*) AS num_books
FROM book_issue
WHERE return_date IS NULL
GROUP BY member_id;
My understanding of the maximum number of books would be concurrently -- that is, only count books that have not been returned. Perhaps you have a different definition.
Then, you can use this in a JOIN, doing the comparison on the maximum outside the subquery:
SELECT member_name
FROM members m JOIN
(SELECT member_id, COUNT(*) AS num_books
FROM book_issue
WHERE return_date IS NULL
GROUP BY member_id
) b
ON b.member_id = m.member_id AND
b.num_books >= m.max_no_books;
Notes:
In a JOIN, the comparison to the outer table needs to be outside the subqueries.
No SELECT DISTINCT is needed.
The GROUP BY for counting books should be only at the member level.

SQL Query with a count condition

Here is my table structure:
CREATE TABLE CITY(
CITY_ID NUMBER(3) CONSTRAINT CITY_ID_PK PRIMARY KEY,
CITY_NAME VARCHAR2(20) CONSTRAINT CITY_NAME_NN NOT NULL);
CREATE TABLE PILOT(
PILOT_ID NUMBER(3) CONSTRAINT PILOT_ID_PK PRIMARY KEY,
LAST_NAME VARCHAR2(20) CONSTRAINT LAST_NAME_NN NOT NULL,
FIRST_NAME VARCHAR2(20) CONSTRAINT FIRST_NAME_NN NOT NULL,
CITY_ID NUMBER(3) CONSTRAINT CITY_ID_FK REFERENCES CITY(CITY_ID),
SALARY NUMBER(7,2) CONSTRAINT SALARY_CK CHECK (SALARY >= 5000 AND SALARY <= 7000));
CREATE TABLE PLANE(
PLA_ID NUMBER(2) CONSTRAINT PLANE_ID_PK PRIMARY KEY,
PLA_DESC VARCHAR2(20) CONSTRAINT PLANE_DESC_NN NOT NULL,
MAX_PASSENGER NUMBER(3),
CITY_ID NUMBER(3) CONSTRAINT PLANE_CITY_ID_FK REFERENCES CITY(CITY_ID),
CONSTRAINT MAX_PASSENGER_CK CHECK (MAX_PASSENGER <= 500));
CREATE TABLE FLIGHT(
FLIGHT_ID NUMBER(3) CONSTRAINT FLIGHT_ID_PK PRIMARY KEY,
PILOT_ID NUMBER(3) CONSTRAINT FLIGHT_PILOT_ID_FK REFERENCES PILOT(PILOT_ID),
PLA_ID NUMBER(2) CONSTRAINT FLIGHT_PLA_ID_FK REFERENCES PLANE(PLA_ID),
CITY_DEP NUMBER(3) CONSTRAINT FLIGHT_CITY_DEP_FK REFERENCES CITY(CITY_ID),
CITY_ARR NUMBER(3) CONSTRAINT FLIGHT_CITY_ARR_FK REFERENCES CITY(CITY_ID),
DEP_DATE DATE,
DEP_TIME NUMBER(4),
ARR_TIME NUMBER(4),
CONSTRAINT ARR_TIME_CK CHECK (ARR_TIME > DEP_TIME));
The question I have in this lab is to display pilots (ID and Name) who perform two or more flights out of Montreal (It is required that I use the city name in the query and not the ID)
Here is what I have come up with so far:
SELECT PILOT_ID, LAST_NAME, FIRST_NAME
FROM PILOT
JOIN FLIGHT USING (PILOT_ID)
WHERE CITY_DEP=(SELECT CITY_ID
FROM CITY
WHERE CITY_NAME='MONTREAL')
Obviously this gets me part of the answer, but it is not displaying exactly the information I need which is simply the pilots who make this fight >= 2 times.
You can use FETCH ROWS
SELECT PILOT_ID, LAST_NAME, FIRST_NAME
FROM PILOT
JOIN FLIGHT USING (PILOT_ID)
WHERE CITY_DEP=(SELECT CITY_ID
FROM CITY
WHERE CITY_NAME='MONTREAL')
FETCH FIRST 2 ROWS ONLY
Edited with new information on data structure
Understanding your goal
I believe I understand your goal to be querying pilot level data for pilots who have departed from Montreal at least twice in one day.
Query Solution
If my assumptions are true, I believe you can meet your needs by doing something similar to this:
CREATE GLOBAL TEMPORARY TABLE flight_per_day ON COMMIT PRESERVE ROWS AS
SELECT
p.pilot_id,
f.dep_date,
COUNT(CASE WHEN c.city_name = 'MONTREAL' THEN 1 ELSE NULL END) as
montreal_cnt
FROM flights f
LEFT JOIN pilot p ON p.pilot_id = f.pilot_id
LEFT JOIN city c on f.city_dep = c.city_id
GROUP BY 1, 2;
SELECT
p.pilot_id,
p.first_name,
p.last_name
FROM flight_per_day fp
LEFT JOIN pilot p ON p.pilot_id = fp.pilot_id
WHERE fp.montreal_cnt>=2
or without a temp table you could do
SELECT
p.pilot_id,
p.first_name,
p.last_name
FROM
(SELECT
p.pilot_id,
f.dep_date,
-- Find the total number of flights (COUNT) where (CASE WHEN) a flight departs from Montreal (THEN) count it otherwise (ELSE) ignore it (NULL)
COUNT(CASE WHEN c.city_name = 'MONTREAL' THEN 1 ELSE NULL END) as
montreal_cnt
FROM flights f
-- Join in pilot table to get the counts by pilot_id
LEFT JOIN pilot p ON p.pilot_id = f.pilot_id
-- Join in city table to get city_name instead of city_id
LEFT JOIN city c on f.city_dep = c.city_id
GROUP BY 1, 2) fp
LEFT JOIN pilot p ON p.pilot_id = fp.pilot_id
-- Only give me the data for pilots who have flown out of Montreal at least twice in one day
WHERE fp.montreal_cnt>=2
For each pilot, you need to count how many flights that pilot has from Montreal, and then retrieve the pilots that have 2 or more flights. This is a job for GROUP BY and HAVING.
SELECT PILOT_ID, LAST_NAME, FIRST_NAME
FROM PILOT
JOIN FLIGHT USING (PILOT_ID)
JOIN CITY ON (CITY_DEP = CITY_ID)
WHERE CITY_NAME='MONTREAL'
GROUP BY PILOT_ID, LAST_NAME, FIRST_NAME
HAVING COUNT(*) >= 2;

JOIN ON SQL syntax

I am taking my first database class and need some info on what I need to change for my script to process. Currently I have 2 tables, an "orders" table and a "customers" table and this is how they are coded.
CREATE TABLE customers (
customer_id INT ,
customer_first_name VARCHAR(20),
customer_last_name VARCHAR(20) NOT NULL,
customer_address VARCHAR(50) NOT NULL,
customer_city VARCHAR(20) NOT NULL,
customer_state CHAR(2) NOT NULL,
customer_zip CHAR(5) NOT NULL,
customer_phone CHAR(10) NOT NULL,
customer_fax CHAR(10),
CONSTRAINT customers_pk
PRIMARY KEY (customer_id)
);
CREATE TABLE order (
order_id INT NOT NULL,
customer_id INT NOT NULL,
order_date DATE NOT NULL,
shipped_date DATE,
employee_id INT,
CONSTRAINT orders_pk
PRIMARY KEY (order_id),
CONSTRAINT orders_fk_customers
FOREIGN KEY (customer_id) REFERENCES customers (customer_id),
CONSTRAINT orders_fk_employees
FOREIGN KEY (employee_id) REFERENCES employees (employee_id)
);
My script to join these two tables together is as follows:
SELECT or.order_id, or.order_date, or.customer_city,
cu.customer_first_name, cu.customer_last_name
FROM orders or INNER JOIN customers cu
ON or.customer_first_name = cu.customer_first_name
AND or.customer_last_name = cu.customer_last_name;
Now obviously, I know it is incorrect and may have multiple errors, so be gentle. I would love to know what I can do to make it work. Please advise.
Two things:
Don't use or as an alias. That's a reserved word.
Join by the foreign key.
The query should look like:
SELECT o.order_id, o.order_date, cu.customer_city,
cu.customer_first_name, cu.customer_last_name
FROM orders o
INNER JOIN customers cu
on o.customer_id = cu.customer_id
You can join your tables on the ID numbers, and you won't have to worry about what happens when multiple people have the same name.
Use this instead:
FROM orders ord INNER JOIN customers cu
ON ord.order_id = cu.customer_id
Your select statement can remain as it is. I also re-aliased according to the comment from Error_2646.
Using a field that is a primary key in one of your tables is also better practice than using non-key fields, when possible.

NOT EXIST clause

I am trying to find Products that have never been ordered. My 2 tables look like this.
CREATE TABLE Orders
(OrderNum NUMBER(10) NOT NULL,
OrderDate DATE NOT NULL,
Cust NUMBER(10),
Rep NUMBER(10),
Mfr CHAR(3) NOT NULL,
Product CHAR(5) NOT NULL,
Qty NUMBER(5) NOT NULL,
Amount NUMBER(9,2) NOT NULL,
CONSTRAINT OrdersPK
PRIMARY KEY (OrderNum));
CREATE TABLE Products
(Mfr CHAR(3) NOT NULL,
Product CHAR(5) NOT NULL,
Description VARCHAR2(20) NOT NULL,
Price NUMBER(9,2) NOT NULL,
QtyOnHand NUMBER(5),
CONSTRAINT ProductsPK
PRIMARY KEY (Mfr, Product));
The code I currently have looks like this.
SELECT Mfr, Product
FROM Products
WHERE NOT EXISTS (SELECT Products.Mfr
FROM Orders, Products
WHERE Orders.Mfr = Products.Mfr);
Although I am not getting any errors there are also no results showing up.
**EDIT: There are 26 Products and 19 of them have been ordered. I am expecting to get 7 Results but I am getting 0.
You can use NOT EXISTS, but you need to compare both keys:
SELECT p.Mfr, p.Product
FROM Products p
WHERE NOT EXISTS (SELECT 1
FROM Orders o
WHERE o.Mfr = p.Mfr AND
o.Product = p.Product
);
This is a case where it makes lots of sense to have an auto generated primary key that can be used for foreign key relationships.
Try this one
SELECT Mfr, Product
FROM Products
WHERE NOT EXISTS (SELECT Orders.Mfr
FROM Orders
WHERE Orders.Mfr = Products.Mfr AND Orders.Product = Products.Product);
An alternative is to use the set operation operator EXCEPT - as you want "the set of Products that don't exist in Orders":
SELECT
Mfr,
Product
FROM
Products
EXCEPT
SELECT
DISTINCT
Mfr,
Product
FROM
Orders
You can then use this as a subquery to get full product information.
SELECT
*
FROM
Products
INNER JOIN (
SELECT
Mfr,
Product
FROM
Products
EXCEPT
SELECT
DISTINCT
Mfr,
Product
FROM
Orders
) AS ProductsWithNoOrders ON
Products.Mfr = ProductsWithNoOrders.Mfr AND
Products.Product = ProductsWithNoOrders.Product