SQL to deduct total sales from total purchase based on date - sql

I am trying to calculate total purchase units left after sales unit in time based. Below is my sample table and required output underneath.
fno pno vdate pdate units ttype
1234 1 1-Jan-2011 1-Jan-2011 100 p
12 1 1-Jan-2012 1-Jan-2011 100 p
1234 1 1-Jan-2012 1-Jan-2011 100 p
1234 1 1-Jan-2012 1-Jan-2011 100 p
12 1 1-Jan-2013 1-Jan-2011 100 s
1234 1 1-Jan-2013 1-Jan-2011 150 s
Output should be
fno pno vdate pdate units ttype
1234 1 1-Jan-2012 1-Jan-2011 50 p
1234 1 1-Jan-2012 1-Jan-2011 100 p
Here ttype is transaction type 's' means sales and 'p' means purchase. I would like to have SQL query which can calculate units based on order on which purchase was made.
Like if look at 1234 fno in 1-jan-2011 100 units were purchased and second purchase was on 1-jan-2012 of 100 units each. Same kind of transaction happend with fno = 12 and units are 100 (purchase) and 100 units have been sold on 1-jan-2013.
Here is SQL
select (t3.pUnits - t2.sUnits) as sb, t1.fno, t1.ttype, t1.vdate from
dbtrans as t1
left join (
select sum(units) as sUnits, fno, pno
from dbtrans
group by fno, pno, ttype having ttype = 's'
) as t2 on t1.fno = t2.fno
left join (
select sum(units) as pUnits, fno, pno
from dbtrans
group by fno, pno, ttype having ttype = 'p'
) as t3 on t1.fno = t3.fno
group by t1.vdate
So calculation should be made on vdate basis for transaction type.
If still I am missing anything please let me know.
Thanks in advance.

Related

Calculate total donations based on an attribute table

I am trying to get a list of donors who have cumulatively donated $5K+ between two different campaigns. My data is something like this
Attributes table
transactionid
attributevalue
123231
campaign 1
123456
campaing 2
123217
campaign 1
45623
campaing 2
65791
campaing 3
78931
campaign 4
11111
campaign 5
22222
campaing 6
Donations table
transactionid
donationamount
donorid
123231
2000
1233
123456
30000
1456
45623
8000
1233
78931
90
8521
11111
20
1233
22222
68
1456
Donor table
donorid
name
1233
John
1456
Mary
8521
Karl
This is what I tried, but the total I am getting is not right at all.
WITH test AS (
SELECT don.donorid,don.donationamount,a.attributevalue
FROM attributes table a
INNER JOIN donations don ON don.transactionid=a.transactionid
)
SELECT d.donorid,
SUM(CASE WHEN test.attributevalue='campaign 1' OR test.attributevalue='campaign 2'
THEN test.donationamount END) AS campaing_donation,
SUM(test.donationamount) AS total_donations
FROM donortable d
INNER JOIN test ON d.donorid = test.donorid
GROUP BY d.donorid
HAVING SUM(CASE WHEN test.attributevalue = 'campaign 1' OR test.attributevalue = 'campaign 2' THEN test.donationamount END) > 5000
but this is not working. My total donations sum is giving a value that is several times higher than the actual value.
Ideally, the final result would be something like this:
donorid
campaign_amount
totalamount
1233
10000
10020
1456
30000
30068
Select
sum (Donations.donationamount),
donor.donorid,
donor.name
from
Attributes
join Donations on
Donations.transactionid = attributes.transactionid
Join Donor on
donor.donorid = donations.donorid
Where
Attribute.attributevalue in ('campaign 1','campaign 2')
Group by
donor.donorid,
donor.name
create table #transection_tbl(tran_id int,attributevalue varchar(20))
create table #donation_tbl(tran_id int,donation_amount int ,donar_id int)
select donar_id,max(donation_amount) as 'campaing_amount',
sum(donation_amount) as 'totalamount'
from #transection_tbl as t1
inner join #donation_tbl as t2 on t1.tran_id=t2.tran_id
group by donar_id
having COUNT(attributevalue)=2

SQL Query to Return Total Number of Lines and Number of Items

I am running a query on two tables that needs to return the total number of lines in each sales order and a total of the items ordered.
A simplified version of the SalesOrder table is constructed like this:
SalesOrderID
Customer
SODate
102
Bob Smith
12/15/2021
101
Jane Doe
12/05/2021
100
Sarah Joy
12/01/2021
The second table, SalesOrderLine, contains the line items in the sales order:
SalesOrderID
LineNumber
Item
Quantity
100
1
Nuts
5
100
2
Bolts
10
100
3
Washers
3
101
1
Screws
15
102
1
Nails
25
102
2
Hooks
5
The result of the query would look like this:
SalesOrderID
SODate
Customer
TotalLines
TotalItems
102
12/15/2021
Bob Smith
2
30
101
12/05/2021
Jane Doe
1
15
100
12/01/2021
Sarah Joy
3
18
I am locking up on how to use the query to return the Total Number of Lines and Total Number of Items per SalesOrderID.
SELECT SalesOrder.SalesOrderID, SalesOrder.SODate, SalesOrder.SOCustomer
?? Total Number of Lines and Total Number of Items ??
FROM SalesOrder
INNER JOIN SalesOrders ON SalesOrder.SalesOrderID = SalesOrderLine.SalesOrderID
ORDER BY SalesOrderID
You can use apply :
select so.*, soo.*
from salesorder so cross apply
( select count(*) as Totallines, sum(soo.quantity) as TotalQty
from SalesOrders soo
where soo.SalesOrderID = so.SalesOrderID
) soo;
You are almost done, except the aggregation.
Query
select so.SalesOrderID, so.SODate, so.SOCustomer,
count(sol.LineNumber) as TotalLines, sum(sol.Quantity) as TotalItems
from SalesOrder as so
join SalesOrderLine as sol
on so.SalesOrderID = sol.SalesOrderID
group by so.SalesOrderID, so.SODate, so.SOCustomer
order by SalesOrderID;

Tracing original Value through Iteration SQL

Suppose there is a data collection system that, whenever a record is altered, it is then saved as a new record with a prefix (say M-[most recent number in que and is unique]).
Suppose I am given the following data set:
Customer | Original_Val
1 1020
2 1011
3 1001
I need to find the most recent value for each customer given the following table:
Customer | Most_Recent_Val | Pretained_To_Val | date
1 M-2000 M-1050 20170225
1 M-1050 M-1035 20170205
1 M-1035 1020 20170131
1 1020 NULL 20170101
2 M-1031 1011 20170105
2 1011 NULL 20161231
3 1001 NULL 20150101
My desired output would be:
Customer | Original_Val | Most_Recent_Val | date
1 1020 M-2000 20170225
2 1011 M-1031 20170105
3 1001 1001 20150101
For customer 1, there are 4 levels i.e (M-2000 <- M-1050 <- M-1035 <- 1020) Note that there would be no more than 10 levels of depth for each customer.
Much Appreciated! Thanks in advance.
Find the min and max of each customer and then join it together. Something like this:
Select
[min].Customer
,[min].Most_Recent_Val as Original_Val
,[max].Most_Recent_Val as Most_Recent_Val
,[max].date
From
(
Select
Customer
,Most_Recent_Val
,date
From
table t1
inner join (
Select
Customer
,MIN(date) as MIN_Date
From
table
Group By
Customer
) t2 ON t2.Customer = t1.Customer
and t2.MIN_Date = t1.Date
) [min]
inner join (
Select
Customer
,Most_Recent_Val
,date
From
table t1
inner join (
Select
Customer
,MAX(date) as MAX_Date
From
table
Group By
Customer
) t2 ON t2.Customer = t1.Customer
and t2.MAX_Date = t1.Date
) [max] ON [max].Customer = [min].Customer

Joining two tables on two criteria "cannot partition on repeated field"

I'm using BigQuery for this.
I have a subquery that pulls data from a table that has an account_id, product, date, and product_spend fields. This subquery calculates the total lifetime spend for each product for each 'account_id' by adding up each of the line items.
SELECT account_id,
product,
SUM(product_spend)/1000000 lifetime_product_spend
FROM usage
GROUP BY 1, 2
The result looks like this:
table: lifetime
account_id product lifetime_product_spend
===========================================================
A product1 50
A product2 20
B product2 100
B product3 150
C product3 500
I'm trying to preserve the values and join them with a larger query:
SELECT account_id,
product,
month,
SUM(spend)
FROM data_source
WHERE month >= DATE_ADD(today ,-5,"MONTH")
GROUP BY 1, 2, 3
This query has a table that looks like this:
table: monthly
account_id product month spend
=================================================================
A product1 1 10
A product1 2 20
A product1 3 30
A product2 1 5
A product2 2 15
B product2 2 100
B product3 2 100
B product3 3 50
C product3 1 100
C product3 2 400
I'm not using an aggregate to calculate lifetime_product_spend on the second table. Due to the sheer amount of data, I'm only able to include the last 6 months data. That's why I'm calculating the lifetime spend in a different table and joining them.
My current query is failing:
SELECT d.account_id,
d.product,
d.month,
sum(d.spend),
u.lifetime_product_spend
FROM data_source d
LEFT JOIN (SELECT account_id,
product,
SUM(product_spend)/1000000 lifetime_product_spend
FROM usage
GROUP BY account_id, product) u
ON d.account_id = u.account_id
WHERE d.month >= DATE_ADD(today ,-5,"MONTH")
GROUP BY d.account_id, d.product, d.month, u.lifetime_product_spend
because it doesn't seem to have assigned the lifetime figures to each product as in the Lifetime table. That's because I'm only joining on account_id. See below for the bad output. I've truncated this table because it basically added the # of outputs I have for lifetime_product_spend (5) and put one for each month, product, and company...because it's ignoring the 'product' assignment for these values:
table: monthly
account_id product month spend lifetime_product_spend
=====================================================================================
A product1 1 10 50
A product1 1 10 20
A product1 1 10 100
A product1 1 10 150
A product1 1 10 500
A product1 2 20 50
A product1 2 20 20
A product1 2 20 100
A product1 2 20 150
A product1 2 20 500
Is there a way for me to join on both of them? I've tried doing a JOIN ON x = x AND y = y:
SELECT d.account_id,
d.product,
d.month,
sum(d.spend),
u.lifetime_product_spend
FROM data_source d
LEFT JOIN (SELECT account_id,
product,
SUM(product_spend)/1000000 lifetime_product_spend
FROM usage
GROUP BY account_id, product) u
ON (d.account_id = u.account_id AND d.product = u.product)
WHERE d.month >= DATE_ADD(today ,-5,"MONTH")
GROUP BY d.account_id, d.product, d.month, u.lifetime_product_spend
but it gives me this error : "Execution Failed
Error: Cannot partition on repeated field d.product".
I want my final table to look like this:
table: monthly
account_id product month spend lifetime_product_spend
=====================================================================================
A product1 1 10 50
A product1 2 20 50
A product1 3 30 50
A product2 1 5 20
A product2 2 15 20
B product2 2 100 100
B product3 2 100 150
B product3 3 50 150
C product3 1 100 500
C product3 2 400 500
I think I need "FLATTEN" somewhere, but I can't seem to get it in the right place. Thanks for reading.
SELECT d.account_id,
d.product,
d.month,
sum(d.spend),
u.lifetime_product_spend
FROM FLATTEN(data_source, product) d
LEFT JOIN (SELECT account_id,
product,
SUM(product_spend)/1000000 lifetime_product_spend
FROM usage
GROUP BY account_id, product) u
ON (d.account_id = u.account_id AND d.product = u.product)
WHERE d.month >= DATE_ADD(today ,-5,"MONTH")
GROUP BY d.account_id, d.product, d.month, u.lifetime_product_spend
The above works with the original data source flattened around the repeated field d.product. Thanks for the comments and help.
Write "Select .... from usage" as a sub-query and apply an INNER JOIN or LEFT JOIN on data_source table.
SELECT d.account_id,
d.product,
d.month,
sum(d.spend),
u.lifetime_product_spend
from data_source d
left join (SELECT account_id,
product,
SUM(product_spend)/1000000 lifetime_product_spend
FROM usage
GROUP BY account_id, product) u
on(d.account_id=u.account_id and d.product=u.product)
WHERE d.month >= DATE_ADD(today ,-5,"MONTH")
GROUP BY d.account_id, d.product, d.month, u.lifetime_product_spend

sql server 2005 wrong output

I have 3 table stock,inward,issue
Stock table's columns and data :
part_no | part_name | totalqty
10100 ciol 30
112233 abc 20
123456 coper 50
inward table :
part_no | qty
123456 10
123456 20
10100 20
112233 15
10100 25
issue table :
part_no | qty
112233 20
112233 15
123456 10
112233 25
10100 40
10100 20
my desired output :
part_no | part_name |inwardQty |issueQty
10100 coil 45 60
112233 abc 15 60
123456 coper 30 10
following is the query i have written,but not giving my desired output
select s.part_no,s.part_name,sum(i.qty) as inwardQty,sum(p.qty)as issueQty
from stock s
left join inward i on s.part_no = i.part_no
left join issue p on s.part_no = p.part_no
group by
s.part_no,s.part_name
getting following output by this query :
part_no | part_name |inwardQty |issueQty
10100 coil 90 120
112233 abc 45 60
123456 coper 30 20
The problem is that you're matching every row for inward with every row for issue, for which they're dealing with the same part. I think subqueries would be best here:
select s.part_no,s.part_name,i.qty as inwardQty,p.qty as issueQty
from stock s
left join
(select part_no,sum(qty) as qty from inward group by part_no) i on s.part_no = i.part_no
left join
(select part_no,sum(qty) as qty from issue group by part_no) p on s.part_no = p.part_no
So now, there's only one (or zero) rows to join in each of the joins, and you don't get a cartesian product.
Try this query:
SELECT
s.part_no, s.part_name,
InwardQty = (SELECT SUM(qty) FROM #inward i WHERE i.part_no = s.part_no),
IssueQty = (SELECT SUM(qty) FROM #issue p WHERE p.part_no = s.part_no)
FROM
dbo.stock s
GROUP BY
s.part_no, s.part_name
Gives me exactly your desired output.