SQL Retrieval question - sql

I am trying to retrieve the following information:
For each customer whose average order amount is greater than $1,800, list the customer name, cust# and total number of orders.
my code is currently.
SELECT c.cname, c.`cust#`, COUNT(oi.`order#`)
FROM CUSTOMER c, `ORDER` o, `ORDER_ITEM` oi
WHERE c.`cust#` = o.`cust#`
AND o.`order#` = oi.`order#`
AND AVG(o.`ord_amt`) > 1800
GROUP BY c.cname, c .`cust#`
THE TABLES AND FIELDS TO MY DATABASE
customer(cust#:char(3), cname:varchar(30), city:varchar(20))
order (order# :char(4), odate, cust#:char(3), ord_amt:decimal(10.2))
order_item( order# :char(4), item#: char(4), qty:int(11))
item(item# :char(4), unit_price:decimal(10.2))
shipment(order# :char(4), warehouse# :char(4), ship_date:date)
warehouse (warehouse#: char(4), city:varchar(20))

You should use JOIN notation and a HAVING clause to compare the aggregate; you don't need the order items table:
SELECT c.cname, c.`cust#`, COUNT(oi.`order#`)
FROM CUSTOMER c JOIN `ORDER` o ON c.`cust#` = o.`cust#`
GROUP BY c.cname, c .`cust#`
HAVING AVG(o.`ord_amt`) > 1800
(Order of GROUP BY and HAVING fixed per comment by GolezTrol - thanks; my excuse is that it was late at night.)

Related

How to get sum of repeated rows?

I need to get weight of order, so I need to sum my results
This table looks like this
SalesOrderID SalesOrderDetailID SubTotal CompanyName Weight
------------ ------------------ --------------------- -------------------------------------------------------------------------------------------------------------------------------- ---------------------------------------
71774 110562 880,3484 Good Toys 1061.40
71774 110563 880,3484 Good Toys 988.83
71776 110567 78,81 West Side Mart 317.00
71780 110616 38418,6895 Nearby Cycle Shop 5098.36
71780 110617 38418,6895 Nearby Cycle Shop 24874.88
71780 110618 38418,6895 Nearby Cycle Shop 78053.76
71780 110619 38418,6895 Nearby Cycle Shop 2431.24
71780 110620 38418,6895 Nearby Cycle Shop 12596.19
The query:
SELECT a.SalesOrderID, c.SalesOrderDetailID, a.SubTotal,b.CompanyName,
(SELECT c.OrderQty*d.Weight WHERE c.SalesOrderID=c.SalesOrderID) AS Weight
FROM SalesLT.SalesOrderHeader as a
INNER JOIN SalesLT.Customer AS b
ON a.CustomerID=b.CustomerID
INNER JOIN SalesLT.SalesOrderDetail AS c
ON c.SalesOrderID=a.SalesOrderID
INNER JOIN SalesLT.Product as d
ON d.ProductID=c.ProductID
I've tried to make sum as sum(case when) but this gets me an error
Is there any other method?
Expected output:
71774 | 880,3484 | Good Toys | 2050,23
2050,23 is a sum of two rows of weight
You can use
WITH TMP_TABLE AS
(
SELECT
a.SalesOrderID,
c.SalesOrderDetailID,
a.SubTotal,
b.CompanyName,
(c.OrderQty * d.Weight) AS Weight
FROM SalesLT.SalesOrderHeader as a
INNER JOIN SalesLT.Customer AS b ON a.CustomerID=b.CustomerID
INNER JOIN SalesLT.SalesOrderDetail AS c ON c.SalesOrderID=a.SalesOrderID
INNER JOIN SalesLT.Product as d ON d.ProductID=c.ProductID
)
SELECT SalesOrderId,
SubTotal,
CompanyName,
SUM(Weight)
FROM TMP_TABLE
GROUP BY SalesOrderId,
SubTotal,
CompanyName
SELECT SalesOrderId,SUM(Weight) SumOfOrderWeights
FROM SalesLT.SalesOrderDetail
GROUP BY SalesOrderId
ORDER BY SalesOrderId
your data
declare #a table(
SalesOrderID INTEGER NOT NULL
,SalesOrderDetailID INTEGER NOT NULL
,SubTotal VARCHAR(60) NOT NULL
,CompanyName VARCHAR(60) NOT NULL
,Weight float NOT NULL
);
INSERT INTO #a
(SalesOrderID,SalesOrderDetailID,SubTotal,CompanyName,Weight) VALUES
(71774,110562,'880,3484','Good Toys',1061.40),
(71774,110563,'880,3484','Good Toys',988.83),
(71776,110567,'78,81','West Side Mart',317.00),
(71780,110616,'38418,6895','Nearby Cycle Shop',5098.36),
(71780,110617,'38418,6895','Nearby Cycle Shop',24874.88),
(71780,110618,'38418,6895','Nearby Cycle Shop',78053.76),
(71780,110619,'38418,6895','Nearby Cycle Shop',2431.24),
(71780,110620,'38418,6895','Nearby Cycle Shop',12596.19);
your query
select SalesOrderID,SubTotal,CompanyName,sum(Weight) Weight from #a
where CompanyName='Good Toys' --removing filter
group by SalesOrderID,SubTotal,CompanyName

Postgres Question: Aren't both a and b correct?

For questions below, use the following schema definition.
restaurant(rid, name, phone, street, city, state, zip)
customer(cid, fname, lname, phone, street, city, state, zip)
carrier(crid, fname, lname, lp)
delivery(did, rid, cid, tim, size, weight)
pickup(did, tim, crid)
dropoff(did, tim, crid)
It's a schema for a food delivery business that employs food carriers (carrier table).
Customers (customer table) order food from restaurants (restaurant table).
The restaurants order a delivery (delivery table); to deliver food from restaurant to customer.
The pickup table records when carrier picks up food at restaurant.
The dropoff table records when carrier drops off food at customer.
1.Find customers who have less than 5 deliveries.
a. select cid,count()
from delivery
group by cid
having count() < 5;
b. select a.cid,count()
from customer a
inner join delivery b
using(cid)
group by a.cid
having count() < 5;
c. select a.cid,count()
from customer a
left outer join delivery b
on a.cid=b.cid
group by a.cid
having count() < 5;
d. select cid,sum(case when b.cid is not null then 1 else 0 end)
from customer a
left outer join delivery b
using (cid)
group by cid
having sum(case when b.cid is not null then 1 else 0 end) < 5;
e. (write your own answer)
No, they are not correct. They miss customers who have had no deliveries.
The last is the best of a bunch of not so good queries. A better version would be:
select c.cid, count(d.cid)
from customer c left outer join
delivery d
on c.cid = d.cid
group by c.cid
having count(d.cid) < 5;
The sum(case) is over kill. And Postgres even offers a better solution than that!
count(*) filter (where d.cid is not null)
But count(d.cid) is still more concise.
Also note the use of meaningful table aliases. Don't get into the habit of using arbitrary letters for tables. That just makes queries hard to understand.

SELECT * FROM table in addition of aggregation function

Short context:
I would like to show a list of all companies except if they are in the sector 'defense' or 'government' and their individual total spent on training classes. Only the companies that have this total amount above 1000 must be shown.
So I wrote the following query:
SELECT NAME, ADDRESS, ZIP_CODE, CITY, SUM(FEE-PROMOTION) AS "Total spent on training at REX"
FROM COMPANY INNER JOIN PERSON ON (COMPANY_NUMBER = EMPLOYER) INNER JOIN ENROLLMENT ON (PERSON_ID = STUDENT)
WHERE SECTOR_CODE NOT IN (SELECT CODE
FROM SECTOR
WHERE DESCRIPTION = 'Government' OR DESCRIPTION = 'Defense')
GROUP BY NAME, ADDRESS, ZIP_CODE, CITY
HAVING SUM(FEE-PROMOTION) > 1000
ORDER BY SUM(FEE-PROMOTION) DESC
Now what I actually need is, instead of defining every single column in the COMPANY table, I would like to show ALL columns of the COMPANY table using *.
SELECT * (all tables from COMPANY here), SUM(FEE-PROMOTION) AS "Total spent on training at REX"
FROM COMPANY INNER JOIN PERSON ON (COMPANY_NUMBER = EMPLOYER) INNER JOIN ENROLLMENT ON (PERSON_ID = STUDENT)
WHERE SECTOR_CODE NOT IN (SELECT CODE
FROM SECTOR
WHERE DESCRIPTION = 'Government' OR DESCRIPTION = 'Defense')
GROUP BY * (How to fix it here?)
HAVING SUM(FEE-PROMOTION) > 1000
ORDER BY SUM(FEE-PROMOTION) DESC
I could define every single column from COMPANY in the SELECT and that solution will do the job (as in the first example), but how can I make the query shorter using "SELECT * from the table COMPANY"?
The key idea is to summarize in the subquery to get the total spend for the company. This allows you to remove the aggregation from the outer query:
select c.*, pe.total_spend
from company c join
sector s
on c.sector_code = s.code left join
(select p.employer, sum(e.fee - e.promotion) as training_spend
from person p join
enrollment e
on p.person_id = e.student
group by p.employer
) pe
on pe.employer = c.company_number
where s.sector not in ('Government', 'Defense') and
pe.total_spend > 1000

Joining and aggregating data from multiple tables

In customer table there is a SupportID which is the WorkerId in the worker Table, each WorkerId shows which will handle that customer.
Working Name | No. of accounts | total revenue
----------------------------------------------
John McCoy 20 10,000
Worker table contains - Firstname, Lastname, EmployeeId
Receipt table contains - receipt Id, CustomerId,
ReceiptLine Table contains - receiptlineId, receipt Id, Unitprice, quantity
At the moment I have this code / idea
SELECT FirstName, LastName
FROM Employee A, Invoice B
WHERE A.EmployeeId = B.CustomerId
In this question, you have not mention dependency among worker and receipt table. However, let the dependency column is workerId on table receipt. Now try this, hope you will get your desired result.
select a.firstName, sum(count(b.customerId)) as no_accounts, sum(c.unitPrice *c.quantity) as total_revenue
from (( worker a join receipt b on a.workerId = b.SupportId)
join receiptLine c on b.receiptId = c.receiptId) group by a.firstName order by a.firstName;
use GROUP BY to resolve your problem

calculating sum from another table using SQL

I have a table (District) have columns (Id - District name) . And another table (delivery) have columns (quantity - district Id). can i have a result like a table with every district name in a column and sum of quantity in other column using sql?
I understand that this question will be closed as this site is not about doing homework
select a.district_name, b.total
from District as a
inner join
(
select district_id, sum(quantity) as total
from delivery
group by district_id
) as b
on a.id = b.district_id
Try below code
SELECT dis.district_name,SUM(del.quantity) as quantity
FROM district as dis
INNER JOIN delivery as del
ON dis.id = del.district_id
GROUP BY del.district_id
you can use this code:
SELECT ID, Name, SUM(Quantity) AS SumOfQuantity
FROM
(
SELECT District.ID, District.Name, Delivery.Quantity
FROM District, Delivery
WHERE District.Id = Delivery.DistrictID
) AS T1
GROUP BY ID, Name