SQL query optimization on cross production? - sql

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

Related

Customer product sale query incorrect result in sql server 2016

My DDL looks like below:
CREATE TABLE CUSTOMER
(
ID INT PRIMARY KEY,
CUSTOMER_NAME VARCHAR(50),
CITY_ID INT,
)
CREATE TABLE product
(
id int,
sku VARCHAR(50),
product_name varchar(100),
stock_qty int
)
create table Invoice
(
id int,
invoice_number varchar(100),
customer_id int
)
CREATE TABLE Invoice_item
(
id int,
invoice_id int,
product_id int,
quantity decimal(5,2),
price decimal(5,2),
line_total_price decimal(5,2)
)
I am trying to get sales details of all customer and products
output should return all customer even customer without invoices and
also all product even those product that were not sold.
I need to print customer even not have invoice and even those product
that were not sold
than customer and product as NA and quantity as o
Code i have written:
SELECT ISNULL(c.customer_name,'N/A')AS customer_name,ISNULL(p.product_name,'N/A') AS product_name,
sum(ISNULL(invitm.quantity,'0')) as quantity
FROM customer as c left outer join product as p
on c.id = p.id
left outer join invoice as inv on c.id = inv.id
left outer join invoice_item as invitm on c.id = invitm.id
group by c.customer_name,p.product_name
But this is giving incorrect result. am i doing any mistake with join. please share your suggestion
If you want all customer and product combinations, then I would suggest:
select c.customer_name, p.product_name,
coalesce(sum(ii.quantity), 0) as quantity
from customer c cross join
product p left join
invoice i
on c.id = i.customer_id left join
invoice_item ii
on ii.invoice_id = i.id and ii.product_id = p.id
group by c.customer_name, p.product_name;
If you want all customer/product combinations that exist and then extras for the customers and products that don't exist, I would suggest union all:
select c.customer_name, p.product_name,
coalesce(sum(ii.quantity), 0) as quantity
from invoice i join
customer c
on c.id = i.customer_id join
invoice_item ii
on ii.invoice_id = i.id join
product p
on ii.product_id = p.id
group by c.customer_name, p.product_name
union all
select c.customer_name, null, 0
from customer c
where not exists (select 1 from invoice i where i.customer_id = c.id)
union all
select null, p.product_name, 0
from product p
where not exists (select 1 from invoice_item ii where ii.product_id = p.id);
Maybe like this (I have not checked syntax) - all your joins were totally wrong; you need to join on corresponding fields (foreign keys), not on id-s:
SELECT ISNULL(c.customer_name,'N/A')AS customer_name,ISNULL(p.product_name,'N/A') AS product_name,
sum(ISNULL(invitm.quantity,'0')) as quantity
FROM customer as c full outer join product as p
on c.product_id = p.id
left outer join invoice as inv on c.id = inv.customer_id
left outer join invoice_item as invitm on invitm.invoice_id = inv.id and invitm.product_id = p.id
group by c.customer_name,p.product_name
This is actually a interview question. Below query might be correct:
SELECT c.customer_name,
p.product_name,
Coalesce((ii.quantity), 0) AS quantity
FROM customer c
LEFT JOIN invoice i
on c.id = i.customer_id
LEFT JOIN invoice_item ii
ON ii.invoice_id = i.id
LEFT JOIN product p
ON ii.product_id = p.id
ORDER BY c.customerid,
p.product_id,
ii.id
UNION
SELECT 'N/A',
p.product_name,
0
FROM products p ORDER p.id

SQL INNER JOIN entity

I want to execute this query :
-- The most expensive item sold ever
SELECT
c.itemID, c.itemName
FROM
item AS c
JOIN
(SELECT
b.itemID as 'itemid', MAX(b.item_initialPrice) AS 'MaxPrice'
FROM
buyeritem AS a
INNER JOIN
item AS b ON a.item_ID = b.itemID) AS d ON c.itemID = d.itemid
GROUP BY
c.itemID, c.itemName;
My item table looks like this:
create table item
(
itemID int IDENTITY(1000, 1) NOT NULL,
itemName varchar(15) NOT NULL,
Item_desc varchar(255),
Item_initialPrice MONEY,
ItemQty int,
ownerID int NOT NULL,
condition varchar(20) NOT NULL,
PRIMARY KEY (itemID),
FOREIGN KEY (ownerID) REFERENCES seller (sellerID)
);
The problem is that column item.itemID is invalid in the select list because it is not contained in either an aggregate function or the GROUP BY clause. I tried to add a group by clause at the end
group by c.itemID, c.itemName
but I still get the same error? I don't really know where the problem comes from.
I also have this query
-- The most active seller(the one who has offered the most number of items)
SELECT
a.ownerID, b.sellerName
FROM
item AS a
INNER JOIN
seller AS b ON a.ownerID = b.sellerID
GROUP BY
a.ownerID, b.sellerName
ORDER BY
COUNT(a.itemID) DESC;
I want to add itemQty along with the ownerID and sellerName from item table stated above, what would be the best way to achieve that?
Just write distinct instead of Group By as Group By will not work with out an aggregated function like sum,max etc. in select statement which is missing in your query.An example of this is second query which I have written
SELECT distinct c.itemID, c.itemName
FROM item AS c
JOIN (
SELECT b.itemID as itemid, MAX(b.item_initialPrice) AS MaxPrice FROM buyeritem AS a
INNER JOIN item AS b ON a.item_ID = b.itemID
GROUP BY b.itemID) as d
ON c.itemID = d.itemid ;
For second query
Select a.* from
(
SELECT a.ownerID, b.sellerName, count(distinct a.ITEM_ID) as item_qty
FROM item AS a
INNER JOIN seller AS b ON a.ownerID = b.sellerID
GROUP BY a.ownerID,b.sellerName
) a
order by item_qty DESC

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

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

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

How to use an aggregate function in SQL WITH JOINS?

The following is a query that I built to find the cheapest price for a particular movie along with the distributor who sells it, how do I manipulate it so that all of the movies are listed with their respective highest movie price along with the distributor who sells it?
/*finds the cheapest price for the movie "American Beauty" and the distributor who sells it*/
SELECT movies.title, movie_distributors.distributor_name, MIN(distributed_movie_list.unit_price) AS lowest_price
FROM distributed_movie_list
INNER JOIN movies ON distributed_movie_list.movie_id = movies.movie_id
INNER JOIN movie_distributors ON movie_distributors.distributor_id = distributed_movie_list.distributor_id
WHERE movies.title = 'American Beauty'
AND distributed_movie_list.unit_price =
(SELECT MIN(unit_price)
FROM distributed_movie_list
INNER JOIN movies ON distributed_movie_list.movie_id = movies.movie_id
WHERE movies.title = 'American Beauty')
GROUP BY movies.title, distributed_movie_list.distributor_id, movie_distributors.distributor_name;
The following tables are used for this query:
create table movies (
movie_id number(5),
title varchar2(30) not null,
category_code char(3) not null,
description varchar2(500),
released_by number(3) not null,
released_on date not null
);
create table movie_distributors(
distributor_id number(3),
distributor_name varchar2(30) not null,
location varchar2(40),
contact varchar2(40)
);
create table distributed_movie_list(
distribution_id number(8),
movie_id number(5) not null,
distributor_id number(3) not null,
distribute_type varchar2(10),
inventory_quantity number(3) default 0,
unit_price number(8,2)
);
The end result I'm looking for is a query that list distributors and highest prices for each movie. Any help would be greatly appreciated ; )
The end result I'm looking for is a query that list distributors and
highest prices for each movie
Then why not just GROUP BY title, distributor_name with MAX like so:
SELECT
m.movie_id,
m.title,
md.distributor_name,
MAX(d.unit_price) AS Highest_price
FROM distributed_movie_list AS d
INNER JOIN movies AS m ON d.movie_id = m.movie_id
INNER JOIN movie_distributors AS md ON md.distributor_id = d.distributor_id
GROUP BY m.movie_id,m.title,
md.distributor_name
HAVING MAX(d.unit_price) = (SELECT MAX(d2.unit_price)
FROM distributed_movie_list d2
WHERE m.movie_id = d2.movie_id)
The following should work on most RDBMSs:
SELECT DISTINCT m.title, md.distributor_name, dml.unit_price AS highest_price
FROM distributed_movie_list dml
INNER JOIN movies m ON dml.movie_id = m.movie_id
INNER JOIN movie_distributors md ON md.distributor_id = dml.distributor_id
INNER JOIN (SELECT movie_id, MAX(unit_price)
FROM distributed_movie_list
GROUP BY movie_id) dmlh
ON dml.unit_price = dmlH.unit_price AND dml.movie_id = dmlh.movie_id
A more efficient solution should be possible for RDBMSs (such as Oracle or SQLServer) that support ranking functions.
If you are trying to get the highest price for each movie -- and associated information -- then use the row_number() function. The following query returns all the information about the highest price for each movie:
select dml.*
from (select dml.*,
ROW_NUMBER() over (partition by movie_id order by unit_price desc) as seqnum
from distributed_movie_list dml
) dml
where seqnum = 1
You can join in other information that you want about movies and distributors.