MAX and SUM function with JOINING tables - sql

I have two tables which is CustomerOrder and Customers
CustomerOrder
CusOrderID CusID OrderTotalPrice
S01 C01 180
S02 C04 50
S03 C05 70
S04 C07 46
S05 C09 78
S05 C09 164
Customers
CusID CusName
C01 James
C02 Jabrial
C03 Maxi
C04 Lim
C05 Tan
C06 Rem
C07 Subaru
C08 Jay
C09 Felix
I would need an output which is the customer who spent most on buying books where the display would look like this
CusID Name OrderTotalPrice
C09 Felix 242
I need to use max and sum function and join those two table together and display only one Customer which spend the most. How can I write the query ?
this below is my sample query which doesn't really work
it's Microsoft SQL and TOP function is also not encouraged.
SELECT MAX(s.Sum_OrdTotalPrice) AS max
From (SELECT CO.CusID,C.CusName,SUM(CO.OrderTotalPrice) AS Sum_OrdTotalPrice
FROM CustomerOrder CO
INNER JOIN Customers C On CO.CusID = C.CusID
Group by CO.CusID,C.CusName
)s

Order the results and take just the first row?
SELECT TOP (1) WITH TIES
CO.CusID,C.CusName,SUM(CO.OrderTotalPrice) AS Sum_OrdTotalPrice
FROM CustomerOrder CO
INNER JOIN Customers C On CO.CusID = C.CusID
GROUP BY CO.CusID,C.CusName
ORDER BY SUM(CO.OrderTotalPrice) DESC
EDIT 1: Changed to SQL Server Syntax, following edit to question
EDIT 2: Added WITH TIES so that multiple rows are returned if multiple customers are tied for highest spend

Should pretty much yield the same results a MatBailie, including ties, but avoids using TOP if that is an absolute necessity
SELECT
Results.CusID, Results.Name, Results.OrderTotalPrice
FROM (
SELECT
CustomerOrder.CusID, Customers.CusName as Name
, SUM(CustomerOrder.OrderTotalPrice) as OrderTotalPrice
, RANK() OVER (ORDER BY SUM(CustomerOrder.OrderTotalPrice) DESC) as SalesRank
FROM CustomerOrder
JOIN Customers ON Customers.CusID = CustomerOrder.CusID
GROUP BY CustomerOrder.CusID, CusName
) Results
WHERE Results.SalesRank = 1

Related

Eliminate database rows based on mod dates

I have an issue grabbing data from two tables base on a single field in one of the tables The field does not exist in both tables.
Example:
Table 1 (call it invoices):
invoice email customer_name original_bill invoice_status
1 a#g.com bob 5.00 P
2 a#g.com harry 23.00 P
3 a#g.com sally 4.00 P
4 b#g.com loretta 14.00 P
5 b#g.com hamish 74.00 P
Table 2 (customer invoice edits):
invoice email date_last_modified_timestamp mod_status mod_amount
1 a#g.com 2019-05-01 A 3.00
1 a#g.com 2019-04-01 D
3 b#g.com 2019-10-25 A
What I want
a list of all invioces and their mods but I only wish each invoice to appear once in the list. based on the latest date in Table two.
example:
invoice email customer_name original_bill invoice_status date_last_modified_timestamp mod_status mod_amount
1 a#g.com bob 5.00 P 2019-05-01 A 3.00
2 a#g.com harry 23.00 P
3 a#g.com sally 4.00 P 2019-10-25 A
4 b#g.com loretta 14.00 P
5 b#g.com hamish 74.00 P
How im pulling it now:
select invoice, email, customer_name, original bill, invoice status, max(date_last_modified_timestamp), mod status, mod_amount
from table one
left join on table1.invoice = table 2.invoice
group by
invoice, email, customer_name, original bill, invoice status, max(date_last_modified_timestamp), mod status, mod_amount
I've tried a gazzillion variation to no avail.
What i get
i do get accurate results, but those results inclues a duplicate row containing each invoice that has been modified. I only want one invoice per row. I want the one that's been modified last. Is this even possible? What am I doing wrong?
You can join and use a correlated subquery for filtering:
select i.*, e.*
from invoices i
inner join customer_invoice_edits e
on e.invoice = i.invoice
and e.date_last_modified_timestamp = (
select max(date_last_modified_timestamp)
from customer_invoice_edits e1
where e1.invoice = e.invoice
)
This will give you one record per row in the invoices table that has a match in customer_invoice_edits, along with the latest corresponding record in customer_invoice_edits.
If some invoices have no corresponding record in the dependant table, they will not appear in the resultset. If you do want to see them, then you can use a left join instead.
Is this what you want?
select
invoice,
email
from (
select
invoice,
email,
mod_status,
mod_amount,
date_last_modified_timestamp,
row_number() over (
partition by invoice, email
order by date_last_modified_timestamp desc
) rn
from table2
)
where rn=1
I'm pretty sure you want this field-by-field. Here is an example:
select i.invoice,
nz( (select top (1)
from invoice_edits as ie
where ie.invoice = i.invoice and ie.email is not null
), i.email
) as email,
nz( (select top (1)
from invoice_edits as ie
where ie.invoice = i.invoice and ie.mod_amount is not null
), i.original_bill
) as original_bill,
. . .
from invoices as i;

Access Cross tab Query

I have two tables.
I need to subtract the the number of items ordered from the quantity currently
recorded.
I can get the count() of the sales of each individual item like so:
SELECT SALES.PRODUCT_ID AS ORDERED_ID
,COUNT(SALES.PRODUCT_ID) AS ORDERED
FROM SALES
GROUP BY SALES.PRODUCT_ID
Which gives me:
ORDERED_ID ORDERED
1201 2
1202 2
1204 2
1205 3
1206 1
1207 2
1208 1
1209 1
1210 3
Getting the quantity is just a matter of
SELECT PRODUCT.PRODUCT_ID AS INVEN_ID
,PRODUCT.QUANTITY AS INVEN
FROM PRODUCT
Which gives me:
INVEN_ID INVEN
1199 5
1200 2
1201 33
1202 44
1203 55
1204 66
1205 77
1206 88
1207 99
1208 110
1209 121
1210 132
I've spent hours on this problem and gave up at what I thought should be the solution:
SELECT SUB1.INVEN - SUB2.ORDERED
FROM
(SELECT PRODUCT.PRODUCT_ID AS INVEN_ID
,PRODUCT.QUANTITY AS INVEN
FROM PRODUCT
)AS SUB1
,(SELECT SALES.PRODUCT_ID AS ORDERED_ID
,COUNT(SALES.PRODUCT_ID) AS ORDERED
FROM SALES
GROUP BY SALES.PRODUCT_ID
)AS SUB2
INNER JOIN SUB1 ON SUB1.INVEN_ID = SUB2.ORDERED_ID
However, access does not recognize that last join as a valid join and without it I just get a Cartesian product. If I try to retrieve quantity without a sub query and just try to SELECT product.quantity - SUB2.ORDERED access demands that I put product.quantity - SUB2.ORDERED in an aggregate function. When I do what it says it then tells me that product.quantity - SUB2.ORDERED can't be in an aggregate function. I'm at a loss.
EDIT:
Final Solution:
SELECT SUB1.INVEN_ID AS PRODUCT_ID
,SUB1.PRODUCT_NAME AS PRODUCT_NAME
,SUB1.PRICE AS PRICE
,SUB1.INVEN - NZ(SUB2.ORDERED,0) AS AVAILABLE
FROM
(SELECT PRODUCT.PRODUCT_ID AS INVEN_ID
,PRODUCT.PRODUCT_NAME AS PRODUCT_NAME
,PRODUCT.PRICE AS PRICE
,PRODUCT.QUANTITY AS INVEN
FROM PRODUCT
)AS SUB1
LEFT JOIN
(SELECT SALES.PRODUCT_ID AS ORDERED_ID
,COUNT(SALES.PRODUCT_ID) AS ORDERED
FROM SALES
GROUP BY SALES.PRODUCT_ID
)AS SUB2 ON SUB1.INVEN_ID = SUB2.ORDERED_ID
Your INNER JOIN should put after first subquery.
I think you are looking for LEFT JOIN,because PRODUCT table should be the master table.
if you use LEFT JOIN SUB2.ORDERED column might be NULL so use NZ function
Or IIF(ISNULL(SUB2.ORDERED),0,SUB2.ORDERED) to check.
You can try this.
SELECT SUB1.INVEN - NZ(SUB2.ORDERED,0)
FROM
(SELECT PRODUCT.PRODUCT_ID AS INVEN_ID
,PRODUCT.QUANTITY AS INVEN
FROM PRODUCT
)AS SUB1
LEFT JOIN
(SELECT SALES.PRODUCT_ID AS ORDERED_ID
,COUNT(SALES.PRODUCT_ID) AS ORDERED
FROM SALES
GROUP BY SALES.PRODUCT_ID
)AS SUB2 ON SUB1.INVEN_ID = SUB2.ORDERED_ID

Use table with multiple rows with the same value

I'm trying to do a INNER JOIN and count values with information from two tables. The problem is that the product category table have multiple rows with the same or similar value and my COUNT() is to high as a result.
My two tables
Sales table
Date prod_id
2016-01-01 81
2016-01-01 82
2016-01-01 81
2016-10-01 80
2016-01-01 80
2016-01-02 80
2016-01-02 80
2016-01-02 81
2016-01-02 81
.... ....
Product table
prodid Name
80 Banana
81 Apple
82 Orange
83 Ice Cream
80 BANANAS
81 APPLE
82
83 Ice Cream
.... ....
When I do an INNER JOIN and count the number of occurrences of e.g. prod_id I get an unreasonable high number, and my guess is that it's because there are more than one occurrence of prod_id 80 for example.
Do you have any idea for a solution? My first reaction was to redo the Procuct table, but there are many other systems depending on that table so I can't change it a foreseeable future.
My query so far:
SELECT
pt.Date AS "Date",
ft.Name AS "Product",
COUNT(ft.Name) Number
FROM SALES as pt
INNER JOIN PROD_TABLE AS ft ON pt.prod_id=ft.prodid
WHERE pt.Date BETWEEN '2016-01-01' AND '2016-01-30'
GROUP BY pt.Date, ft.Name
ORDER BY pt.Date DESC
Expected result:
Date Product Number
2016-01-01 Banana 2
2016-01-01 Apple 2
2016-01-01 Orange 1
First, you should fix the data. Having a product table with duplicates seems non-sensical. You shouldn't try to get around such issues by writing more complex queries.
That said, this is pretty easy to do in SQL Server. I think outer apply is appropriate:
select p.name, count(*)
from sales s outer apply (
(select top 1 p.*
from product p
where p.name is not null and
p.prodid = s.prod_id -- note: the columns should have the same name
) p;
I guess this simple query solves your requirement:
select
date,
name,
count(name)
from product p inner join sales s
on s.prod_id=p.prodid group by date,name

Cross-multiplying table

I have this SQL code and I want to show the sum of each item on its charge slip and on their receipt:
select item_description, sum(receipt_qty) as Samp1, sum(chargeSlip_qty) as Samp2
from Items inner join Receipt_Detail on (Receipt_Detail.item_number =
Items.item_number)
inner join ChargeSlip_Detail on (ChargeSlip_Detail.item_number =
Items.item_number)
group by item_description
It produces this output:
Acetazolamide 2681 1730
Ascorbic Acid 1512 651
Paracetamol 1370 742
Silk 576 952
But it should be:
Acetazolamide 383 173
Ascorbic Acid 216 93
Paracetamol 274 106
Silk 96 238
What's wrong with my code?
Since you are joining tables, you might have a one-to-many relationship that is causing the problem when you then get the sum(). So you can use subqueries to get the result. This will get the sum() for the receipt and chargeslip for each item_number and then you join that back to your items table to get the final result:
select i.item_description,
r.Samp1,
c.Samp2
from Items i
inner join
(
select sum(receipt_qty) Samp1,
item_number
from Receipt_Detail
group by item_number
) r
on r.item_number = i.item_number
inner join
(
select sum(chargeSlip_qty) Samp2,
item_number
from ChargeSlip_Detail
group by item_number
) c
on c.item_number = i.item_number
Do the GROUP BYs first, per Item_Number, so you don't multiply out rows from Receipt_Detail and ChargeSlip_Detail. That is, you generate the SUM values per Item_Number before JOINing back to Items
select
I.item_description,
R.Samp1,
C.Samp2
from
Items I
inner join
(SELECT item_number, sum(receipt_qty) as Samp1
FROM Receipt_Detail
GROUP BY item_number
) R
on (R.item_number = I.item_number)
inner join
(SELECT item_number, sum(chargeSlip_qty) as Samp2
FROM ChargeSlip_Detail
GROUP BY item_number
) C
on (C.item_number = I.item_number)
A left join returns rows from the left table, and for each row in the left table, all matching rows in the right table.
So for example:
create table Customers (name varchar(50));
insert Customers values
('Tim'),
('John'),
('Spike');
create table Orders (customer_name varchar(50), product varchar(50));
insert Orders values (
('Tim', 'Guitar'),
('John', 'Drums'),
('John', 'Trumpet');
create table Addresses (customer_name varchar(50), address varchar(50));
insert Addresses values (
('Tim', 'Penny Lane 1'),
('John', 'Abbey Road 1'),
('John', 'Abbey Road 2');
Then if you run:
select c.name
, count(o.product) as Products
, count(a.address) as Addresses
from Customers c
left join Orders o on o.customer_name = c.name
left join Addresses a on a.customer_name = c.name
group by name
You get:
name Products Addresses
Tim 1 1
John 4 4
Spike 0 0
But John doesn't have 4 products!
If you run without the group by, you can see why the counts are off:
select *
from Customers c
left join Orders o on o.customer_name = c.name
left join Addresses a on a.customer_name = c.name
You get:
name customer_name product customer_name address
Tim Tim Guitar Tim Penny Lane 1
John John Drums John Abbey Road 1
John John Drums John Abbey Road 2
John John Trumpet John Abbey Road 1
John John Trumpet John Abbey Road 2
Spike NULL NULL NULL NULL
As you can see, the joins end up repeating each other. For each product, the list of addresses is repeated. That gives you the wrong counts. To solve this problem, use one of the excellent other answers:
select c.name
, o.order_count
, a.address_count
from Customers c
left join
(
select customer_name
, count(*) as order_count
from Orders
group by
customer_name
) o
on o.customer_name = c.name
left join
(
select customer_name
, count(*) as address_count
from Addresses
group by
customer_name
) a
on a.customer_name = c.name
The subqueries ensure only one row is joined per customer. The result is much better:
name order_count address_count
Tim 1 1
John 2 2
Spike NULL NULL

Full Join and sum 2 queries in access

I am very new to this type of complicated joins and summing functions.
I had 2 table queries which have same fields (i.e: ProdID, ProdName, NetQty, Unit)
The 1st one query contains OpStock and the 2nd one contains Purchase.
I want to add this 2 table query to make a single table so that I can able to see current stock.
The data looks like this for 1 st table is:
ProdID ProdName Qty
100 Rose 700
101 Lilly 550
103 Jasmine 600
105 Lavender 400
The data looks like this for 2nd table is:
ProdID ProdName Qty
100 Rose 400
101 Lilly 250
104 Lotus 1000
106 MariGold 400
The final data looks like this for 3rd table is:
ProdID ProdName Qty
100 Rose 1100
101 Lilly 800
103 Jasmine 600
104 Lotus 1000
105 Lavender 400
106 MariGold 400
How can i achieve this one using sql for access2003.
Thanks.
I am very sorry Ciaran,
This is purely access used for vb.net
Here Is my access query1
SELECT sp_OpenIandP_Prod.ProdID,
sp_OpenIandP_Prod.ProdName,
Sum(([sp_OpenIandP_Prod.SumOfNetQty]-[sp_OpenSales_Prod.SumOfNetQty])) AS NetQty,
sp_OpenIandP_Prod.UnitSName
FROM sp_OpenIandP_Prod
INNER JOIN sp_OpenSales_Prod ON sp_OpenIandP_Prod.ProdID=sp_OpenSales_Prod.ProdID
GROUP BY sp_OpenIandP_Prod.ProdID,
sp_OpenIandP_Prod.ProdName,
sp_OpenIandP_Prod.UnitSName;
The 1st query result would be like this:
ProdID ProdName NetQty UnitSName
1 Rose 0 Kgs
2 Lilly 7125 Mts
3 Lotus 12374 Nos
The second query is:
SELECT Products.ProdID, Products.ProdName,
Sum(OPDDetails.NetQty) AS SumOfNetQty, Units.UnitSName
FROM Units
INNER JOIN (Products
INNER JOIN (Brands
INNER JOIN OPDDetails ON Brands.BrID=OPDDetails.BrandID)
ON Products.ProdID=Brands.ProdID)
ON Units.UnitID=Products.UnitID
WHERE (((OPDDetails.PurID)>0)
AND ((OPDDetails.OPDDate)>=[StartDate] And (OPDDetails.OPDDate)<=[EndDate]))
GROUP BY Products.ProdID, Products.ProdName, Units.UnitSName;
and the result set would be like this:
ProdID ProdName SumOfNetQty UnitSName
1 Rose 1800 Kgs
2 Lilly 21000 Mts
I got the above result easily.
But it is not as much easy to get like this:
ProdID ProdName SumOfNetQty UnitSName
1 Rose 1800 Kgs
2 Lilly 28125 Mts
3 Lotus 12374 Nos
That's all. Thanks again.
This is not a vb.net question, however you need to use a UNION
Select ProdId, ProdName, Sum(Qty) As QtySum
From (Select ProdId, ProdName, Qty From TableA
Union All
Select ProdId, ProdName, Qty From TableB) DerrivedView
Group By ProdId, ProdName
You can create a UNION between your 2 queries then group by product:
SELECT ProdID, ProdName, Sum(NetQty) As NetQty, UnitSName
FROM
(
SELECT sp_OpenIandP_Prod.ProdID,
sp_OpenIandP_Prod.ProdName,
Sum(([sp_OpenIandP_Prod.SumOfNetQty]-[sp_OpenSales_Prod.SumOfNetQty])) AS NetQty,
sp_OpenIandP_Prod.UnitSName
FROM sp_OpenIandP_Prod
INNER JOIN sp_OpenSales_Prod ON sp_OpenIandP_Prod.ProdID=sp_OpenSales_Prod.ProdID
GROUP BY sp_OpenIandP_Prod.ProdID,
sp_OpenIandP_Prod.ProdName,
sp_OpenIandP_Prod.UnitSName
UNION ALL
SELECT Products.ProdID, Products.ProdName,
Sum(OPDDetails.NetQty) AS NetQty, Units.UnitSName
FROM Units
INNER JOIN (Products
INNER JOIN (Brands
INNER JOIN OPDDetails ON Brands.BrID=OPDDetails.BrandID)
ON Products.ProdID=Brands.ProdID)
ON Units.UnitID=Products.UnitID
WHERE (((OPDDetails.PurID)>0)
AND ((OPDDetails.OPDDate)>=[StartDate] And (OPDDetails.OPDDate)<=[EndDate]))
GROUP BY Products.ProdID, Products.ProdName, Units.UnitSName
)
GROUP BY ProdID, ProdName, UnitSName