SQL: How can I retrieve total amount from joined tables? - sql

My tables
CREATE TABLE Customers (
id SERIAL PRIMARY KEY,
firstname VARCHAR(50),
lastname VARCHAR(50)
);
CREATE TABLE Payments (
id SERIAL PRIMARY KEY,
amount INT,
customer_id INT,
CONSTRAINT fk_CustomerPayment FOREIGN KEY (customer_id) REFERENCES Customers (id)
);
I want to get total payment amount for all customers. Here is my try:
SELECT SUM(p.amount)
FROM Customers c
JOIN Payments p
ON c.id = p.customer_id
GROUP BY p

select sum(p.amount) as total
from
customers c
inner join
payments p on c.id = p.customer_id
If there can be null values in payments.customer_id the join condition will exclude them.
Or cheaper without the join:
select sum(amount) as total
from payments
where customer_id is not null

remove group by from query..
SELECT SUM(p.amount)
FROM Customers c
JOIN Payments p
ON c.id = p.customer_id

Related

SQL query optimization on cross production?

I have the following schema
CUSTOMER (CID INTEGER NOT NULL, NAME VARCHAR(30), ADDRESS VARCHAR(50))
PRODUCT (PID INTEGER NOT NULL, NAME VARCHAR(50), PRICE DECIMAL(10,2))
SALE (SID BIGINT NOT NULL, STATUS VARCHAR(10), CID INTEGER, TOTALPRICE DECIMAL(30,2))
PRODUCTSALE (SID BIGINT NOT NULL, PID INTEGER NOT NULL, UNITS INTEGER, SUBTOTAL DECIMAL(30,2))
I am currently have a statement like this:
SELECT
P.NAME, COUNT(DISTINCT C.CID) AS NUM_CUSTOMERS
FROM
CUSTOMER AS C, PRODUCT AS P, PRODUCTSALE AS PS, SALE AS S
WHERE
C.CID = S.CID
AND S.SID = PS.SID
AND PS.PID = P.PID
GROUP BY
P.NAME
ORDER BY
NUM_CUSTOMERS DESC
I think it's creating a four table(P,S,PS,C) cross product? Can I optimize it by using nature join on four of them? What are the other way of optimize this statement?
Start with the biggest table and filter down.
SELECT p.NAME, COUNT(DISTINCT c.CID) AS NUM_CUSTOMERS
FROM ProductSale ps
INNER JOIN Product p ON ps.PID=p.PID
INNER JOIN Sale s ON ps.SID=s.SID
INNER JOIN Customer c ON s.CID=c.CID
GROUP BY p.Name
ORDER BY NUM_CUSTOMERS DESC

Oracle query for customers who buy popular products

I have three tables: customer, order and line items. They are set up as follows:
CREATE TABLE cust_account(
cust_id DECIMAL(10) NOT NULL,
first VARCHAR(30),
last VARCHAR(30),
address VARCHAR(50),
PRIMARY KEY (cust_id));
CREATE TABLE orders(
order_num DECIMAL(10) NOT NULL,
cust_id DECIMAL(10) NOT NULL,
order_date DATE,
PRIMARY KEY (order_num));
CREATE TABLE line_it(
order_id DECIMAL(10) NOT NULL,
line_id DECIMAL(10) NOT NULL,
item_num DECIMAL(10) NOT NULL,
PRIMARY KEY (order_id, line_id),
FOREIGN KEY (item_id) REFERENCES products);
I need to write a query that selects customers, their names and addresses who have purchased items that have been bought by 3 or more people. I have the following query:
SELECT cust_account.cust_id, cust_account.first, cust_account.last, cust_account.address
FROM cust_account
INNER JOIN orders ON cust_account.cust_id = orders.cust_id
INNER JOIN line_it ON orders.order_id = line_it.order_id
GROUP BY cust_account.cust_id, cust_account.last
HAVING COUNT(line_it.item_num) = (
SELECT COUNT (DISTINCT order_num > 3)
FROM line_it
);
Do I even need to make it a subquery? I am a bit lost. Appreciate any help, thanks.
Start with "items bought by 3 or more people". You can get these by doing:
select li.item_id
from line_item li join
order_info oi
on li.order_id = oi.order_id
group by li.item_id
having count(distinct oi.customer_id) >= 3;
Now you want customers in this set. Hmmmm:
select distinct ca.*
from customer_account ca join
orderinfo oi
on ca.customer_id = oi.customer_id join
line_item li
on li.order_id = oi.order_id
where li.item_id in (select li.item_id
from line_item li join
order_info oi
on li.order_id = oi.order_id
group by li.item_id
having count(distinct oi.customer_id) >= 3
);
You can also express this with window functions:
select distinct ca.*
from (select ca.*, count(distinct customer_id) over (partition by li.item_id) as num_customers_on_item
from customer_account ca join
orderinfo oi
on ca.customer_id = oi.customer_id join
line_item li
on li.order_id = oi.order_id
) ca
where num_customers_on_item >= 3;
You can use the following query
SELECT distinct customer_account.* FROM line_item, order_info ,customer_account where item_id in (
--Selecting only item purchased 3 or more
SELECT item_id FROM line_item group by item_id having count(1) >=3
)
and line_item.order_id = order_info.order_id
and customer_account.customer_id = order_info.customer_id
;

Sql join solution is possible for below case?

Can we solve below case with joins, I have solved with window functions
Relation: In the tables below, each order in the Orders table, is associated with a given Customer through the cust_id foreign key column that references the ID column in the Customer table.
Question: Find the largest order amount for each salesperson and the associated order number, along with the customer to whom that order belongs and sales person name.
Create Table Salesperson
(
ID int,
name varchar(100),
age float,
salary money
);
Create Table Orders
(
Number int,
order_date datetime,
cust_id int,
salesperson_id int,
Amount money
);
Create Table Customer
(
ID int,
name varchar(100),
city varchar(100),
IndustryType varchar(100)
);
insert into Salesperson values
( 1,'Rohit',25,50000),
( 2,'Pramod',25,50000),
( 3,'Atul',25,50000);
insert into Orders values
( 1,getdate(),101,1,50000),
( 2,getdate(),101,1,500000),
( 3,getdate(),102,1,10000),
( 4,getdate(),101,2,5000),
( 5,getdate(),102,2,700000),
( 6,getdate(),102,2,10000);
insert into Customer values
( 101,'Altu','bhopal','IT'),
( 102,'bltu','bhopal','ITES'),
( 103,'cltu','bhopal','NW');
Solution on with window function:
with CTE_MaxAmount
as
(
select max(amount) over (partition by salesperson_id ) as amount,
dense_rank() over (partition by salesperson_id order by amount) as rowid,
cust_id,
salesperson_id,number
from Orders with(nolock)
)
select ct.amount,
ct.cust_id,
c.name as customername,
s.name as salesman,
ct.salesperson_id,
number as OrderNumbner
from Customer c
join CTE_MaxAmount ct
on (c.id = ct.cust_id)
join Salesperson s
on (s.id = ct.salesperson_id)
where rowid = 1;
I'm breaking with my personal policy not to answer homework questions because the question is an opportunity to show how easily English is translated into SQL. The question is phrased exactly as the query can be built up.
find the largest order amount for each salesperson
select max(Amount) as Amount, salesperson_id from Orders group by salesperson_id
and the associated order number
select o.Number, M.salesperson_id, M.Amount
from Orders as o join (
select max(Amount) as amount, salesperson_id
from Orders group by salesperson_id
) as M
on o.salesperson_id = M.salesperson_id
and o.Amount = M.Amount
along with the customer
select c.name, o.Number, M.salesperson_id, M.Amount
from Orders as o join (
select max(Amount) as amount, salesperson_id
from Orders group by salesperson_id
) as M
on o.salesperson_id = M.salesperson_id
and o.Amount = M.Amount
join Customer as c
on o.cust_id = c.ID
and sales person name
select s.name as 'salesperson',
c.name as 'customer',
o.Number, M.salesperson_id, M.Amount
from Orders as o join (
select max(Amount) as amount, salesperson_id
from Orders group by salesperson_id
) as M
on o.salesperson_id = M.salesperson_id
and o.Amount = M.Amount
join Customer as c
on o.cust_id = c.ID
join Salesperson as s
on o.salesperson_id = s.ID

How to handle a attribute with just two possible values

In my web application i have a company which can be a buyer or a supplier or both.
So my database tables would be like this
Company( id_company, ..., is_buyer, is_supplier, ... )
Or :
Company( id_company, ... )
Type_company( id_type_company, type )
Extra_table(id_company, id_type_company )
Or :
Company( id_company, ... )
Type_company( id_company, id_type_company, type )
I want a explication (Pros and Cons) for every case if it's possible.
You can consider using the common supertype like this
CREATE TABLE companies
(
id int not null primary key,
name varchar(128)
-- other columns
);
CREATE TABLE buyers
(
company_id int not null primary key,
foreign key (company_id) references companies (id)
);
CREATE TABLE suppliers
(
company_id int not null primary key,
foreign key (company_id) references companies (id)
);
Here are some sample queries:
-- Select all buyers
SELECT c.id, c.name
FROM companies c JOIN buyers b
ON c.id = b.company_id;
-- Select all suppliers
SELECT c.id, c.name
FROM companies c JOIN suppliers s
ON c.id = s.company_id;
-- Select companies that are both buers and suppliers
SELECT c.id, c.name
FROM companies c JOIN buyers b
ON c.id = b.company_id JOIN suppliers s
ON c.id = s.company_id;
-- Select companies that are buers BUT NOT suppliers
SELECT c.id, c.name
FROM companies c JOIN buyers b
ON c.id = b.company_id LEFT JOIN suppliers s
ON c.id = s.company_id
WHERE s.company_id IS NULL;
Here is SQLFiddle demo
Recommended reading:
SQL Antipatterns by #BillKarwin

SQL query select client that has the most orders placed

I want to select the client that has the most orders placed:
I have 2 tables :
CREATE TABLE customers(
customerid INT(11) NOT NULL AUTO_INCREMENT PRIMARY KEY,
NAME CHAR(50) NOT NULL,
address CHAR(100) NOT NULL,
city CHAR(30)NOT NULL
);
CREATE TABLE orders(
orderid INT UNSIGNED NOT NULL,
customerid INT UNSIGNED NOT NULL,
);
This is what i am trying to do:
SELECT customers.customerid, customers.name,orders.orderid, COUNT(customerid)
AS CostumerCount
FROM customers
INNER JOIN orders
ON customers.customerid=orders.orderid;
How can i make this?
Since it is not necessarily for you what RDBMS you are using, then let me assume that you are using MySQL, and you are looking for the customer that has the highest orders count:
SELECT
customers.customerid,
customers.name,
COUNT(orders.orderid) AS Orderscount
FROM customers
INNER JOIN orders ON customers.customerid = orders.customerid
GROUP BY customers.customerid,
customers.name
ORDER BY Orderscount DESC
LIMIT 1;
your create table contain wrong syntax
try this
CREATE TABLE customers(
customerid INT(11) NOT NULL AUTO_INCREMENT PRIMARY KEY,
NAME CHAR(50) NOT NULL,
address CHAR(100) NOT NULL,
city CHAR(30)NOT NULL
);
CREATE TABLE orders(
orderid INT UNSIGNED NOT NULL,
customerid INT UNSIGNED NOT NULL
); ^----remove comma ',' from here
and your query works good just replace COUNT(customerid) by COUNT(customers.customerid)
try it here
SELECT customers.customerid, customers.name,orders.orderid, COUNT(customers.customerid)
AS CostumerCount
FROM customers
INNER JOIN orders
ON customers.customerid=orders.orderid;
SELECT customers.customerid, customers.name,
orders.orderid, COUNT( orders.customerid ) AS CostumerCount
FROM customers
INNER JOIN orders ON customers.customerid = orders.customerid
For SQL SERVER;
SELECT TOP (1) * FROM (
SELECT customers.customerid, customers.name, COUNT(*) AS CostumerCount
FROM customers INNER JOIN orders
ON customers.customerid=orders.customerid
GROUP BY customers.customerid, customers.name
) A
ORDER BY CostumerCount DESC
SELECT * FROM Customers WHERE cno = (
SELECT cno FROM (
SELECT count(*) as ordCount, cno
FROM ORDERS
GROUP BY cno HAVING ordCount >= (
SELECT max(ordCount)
FROM (
SELECT count(*) as ordCount, cno
FROM ORDERS
GROUP BY cno
)
)
)
)
I have edited the first answer because if you have more clients with the same number of orders you will only get the first one!
SELECT
customers.customerid,
customers.name,
COUNT(orders.orderid) AS Orderscount
FROM customers
INNER JOIN orders ON customers.customerid = orders.customerid
GROUP BY customers.customerid,
customers.name
HAVING Orderscount = (
SELECT COUNT(id) AS Orderscount
FROM orders
GROUP BY customerid
ORDER BY Orderscount DESC
LIMIT 1
)