SQL Summation with more than one Condition - sql

I have a table like this
Link PeriodiD Debit Credit Project
1 49 - 200 1
1 49 200 - 2
1 49 100 0
1 50 50 - 1
2 49 - 600 0
I want a script to sum the debit and credit per link per period disregarding project.
so the answer should look like
Link PeriodiD TotalDebit TotalCredit
1 49 300 200
1 50 50 -
2 49 - 600
i have more than 60 periodID and more than 100 link.
Please assist to make such a script

Use a Group by with aggregate functions.
SELECT Link,
PeriodID,
SUM(TotalDebit) AS TotalDebit,
SUM(TotalCredit) AS TotalCredit
FROM tablename
GROUP BY Link, PeriodId;
This query might not always give the expected result if you can have NULL values, depending on the DBMS that you use. You can modify it like this to account for this situation:
SELECT Link,
PeriodID,
SUM(COALESCE(TotalDebit,0)) AS TotalDebit,
SUM(COALESCE(TotalCredit,0)) AS TotalCredit
FROM tablename
GROUP BY Link, PeriodId;

Related

SQL update statement to sum column in one table, then add the total to a different column/table

Evening all, hoping for some pointers with an SQL Server query if possible.
I have two tables in a database, example as follows:
PostedTran
PostedTranID AccountID PeriodID Value TransactionDate
1 100 120 100 2019-01-01
2 100 120 200 2020-01-01
3 100 130 300 2021-01-01
4 101 120 400 2020-01-01
5 101 130 500 2021-01-01
PeriodValue
PeriodValueID AccountID PeriodID ActualValue
10 100 120 500
11 101 120 600
I have a mismatch in the two tables, and I'm failing miserably in my attempts. From the PostedTran table, I'm trying to select all transaction lines dated before 2021-01-01, then sum the Value for each AccountID from the results. I then need to add that value to the existing ActualValue in the PeriodValue table.
So, in the above example, the ActualValue on PeriodValueID 10 will update to 800, and 11 to 1000. The PeriodID in this example is constant and will always be 120.
Thanks in advance for any help.
Since RDMS not mentioned, pseudo-sql looks like:
with DataSum as
(
select AccountID, PeriodID, sum(Value) as TotalValue
from PostedTran
where TransactionDate<'1/1/2021'
group by AccountID, PeriodID
)
update PeriodValue set ActualValue = ActualValue + ds.TotalVaue
from PeriodValue pv inner join DataSum ds
on pv.accountid=ds.accountid and pv.periodid=ds.periodid
The following should do what you ask. I haven't included PeriodId in the correlation as you did not specify it in your description, however you can just include it if it's required.
update pv set pv.ActualValue=pv.ActualValue + t.Value
from PeriodValue pv
cross apply (
select Sum(value) value
from PostedTran pt
where pt.AccountId=pv.AccountId and pt.TransactionDate <'20210101'
)t

SQL How to calculate Average time between Order Purchases? (do sql calculations based on next and previous row)

I have a simple table that contains the customer email, their order count (so if this is their 1st order, 3rd, 5th, etc), the date that order was created, the value of that order, and the total order count for that customer.
Here is what my table looks like
Email Order Date Value Total
r2n1w#gmail.com 1 12/1/2016 85 5
r2n1w#gmail.com 2 2/6/2017 125 5
r2n1w#gmail.com 3 2/17/2017 75 5
r2n1w#gmail.com 4 3/2/2017 65 5
r2n1w#gmail.com 5 3/20/2017 130 5
ation#gmail.com 1 2/12/2018 150 1
ylove#gmail.com 1 6/15/2018 36 3
ylove#gmail.com 2 7/16/2018 41 3
ylove#gmail.com 3 1/21/2019 140 3
keria#gmail.com 1 8/10/2018 54 2
keria#gmail.com 2 11/16/2018 65 2
What I want to do is calculate the time average between purchase for each customer. So lets take customer ylove. First purchase is on 6/15/18. Next one is 7/16/18, so thats 31 days, and next purchase is on 1/21/2019, so that is 189 days. Average purchase time between orders would be 110 days.
But I have no idea how to make SQL look at the next row and calculate based on that, but then restart when it reaches a new customer.
Here is my query to get that table:
SELECT
F.CustomerEmail
,F.OrderCountBase
,F.Date_Created
,F.Total
,F.TotalOrdersBase
FROM #FullBase F
ORDER BY f.CustomerEmail
If anyone can give me some suggestions, that would be greatly appreciated.
And then maybe I can calculate value differences (in percentage). So for example, ylove spent $36 on their first order, $41 on their second which is a 13% increase. Then their second order was $140 which is a 341% increase. So on average, this customer increased their purchase order value by 177%. Unrelated to SQL, but is this the correct way of calculating a metric like this?
looking to your sample you clould try using the diff form min and max date divided by total
select email, datediff(day, min(Order_Date), max(Order_Date))/(total-1) as avg_days
from your_table
group by email
and for manage also the one order only
select email,
case when total-1 > 0 then
datediff(day, min(Order_Date), max(Order_Date))/(total-1)
else datediff(day, min(Order_Date), max(Order_Date)) end as avg_days
from your_table
group by email
The simplest formulation is:
select email,
datediff(day, min(Order_Date), max(Order_Date)) / nullif(total-1, 0) as avg_days
from t
group by email;
You can see this is the case. Consider three orders with od1, od2, and od3 as the order dates. The average is:
( (od2 - od1) + (od3 - od2) ) / 2
Check the arithmetic:
--> ( od2 - od1 + od3 - od2 ) / 2
--> ( od3 - od1 ) / 2
This pretty obviously generalizes to more orders.
Hence the max() minus min().

How to select the first row that met condition

I have the following View in PostgreSQL:
idshipment idorder quantity_order date quantity_in_shipment percent_sent
50 1 1020 1.1.16 432 42
51 1 1020 17.1.16 299 71
51 1 1020 20.1.16 144 85
51 1 1020 45.1.16 145 100
52 2 1 3.1.17 5 100
This View shows shipments per order.
For example:
idorder=1 was sent by 4 shipments:
quantity in first shipment is 432 which means 42% of order was sent
quantity in second shipment is 299 which means 71% of order was sent
quantity in third shipment is 144 which means 85% of order was sent
quantity in forth shipment is 145 which means 100% of order was sent
I need a query which will show me the first date where each order was sent above 75%. meaning each order shows only one row.
For the above data I should see:
idorder date
1 20.1.16 (cause its 85% first time above 75%)
2 3.1.17 (cause its 100% first time above 75%)
How can i do that?
You can use distinct on:
select distinct on (t.idshipment) t.*
from t
where t.percent_sent >= 75
order by t.idshipment, t.percent_sent asc;
Try something like this:
SELECT iorder, MIN("date") AS "date"
FROM your_view
WHERE percent_sent >= 75
GROUP BY iorder
use group by to get only one record per idorder and MIN() to aggregate date by selecting the earliest date
I created a table call shipment that has data like you provided:
and execute this query
SELECT s.idorder, MIN(s.date) as date
FROM shipment s
WHERE percent_sent >= 75
GROUP BY s.idorder
result:
idorder date
----------- ----------
1 2016-01-20
2 2017-03-01

Cumulative Compoud Interest Calculation(Oracle Database 11g Release 2)

I have a requirement to calculate rolling compound interest on several accounts in pl/sql. I was looking for help/advice on how to script calculate these calculations. The calculations I need are in the last two columns of the output below (INTERESTAMOUNT AND RUNNING TOTAL). I found similar examples of this on here, but nothing specifically fitting these requirements in pl/sql. I am also new to CTE/Recursive Techniques and the Model technique I found required a specific iteration which would be variable in this case. Please see my problem below:
Calculations:
INTERESTAMOUNT = (Previous Year RUNNING TOTAL+ Current Year AMOUNT) * INTEREST_RATE
RUNNINGTOTAL = (Previous Year RUNNING TOTAL+ Current Year AMOUNT) * (1 + INTEREST_RATE) - CURRENT YEAR EXPENSES
Input Table:
YEAR ACCT_ID AMOUNT INTEREST_RATE EXPENSES
2002 1 1000 0.05315 70
2003 1 1500 0.04213 80
2004 1 800 0.03215 75
2005 1 950 0.02563 78
2000 2 750 0.07532 79
2001 2 600 0.06251 75
2002 2 300 0.05315 70
Desired Output:
YEAR ACCT_ID AMOUNT INTEREST_RATE EXPENSES INTERESTAMOUNT RUNNINGTOTAL
2002 1 1000 0.05315 70 53.15 983.15
2003 1 1500 0.04213 80 104.62 2507.77
2004 1 800 0.03215 75 106.34 3339.11
2005 1 950 0.02563 78 109.93 4321.04
2000 2 750 0.07532 79 56.49 727.49
2001 2 600 0.06251 75 82.98 1335.47
2002 2 300 0.05315 70 86.93 1652.4
One way to do it is with a recursive cte.
with rownums as (select t.*
,row_number() over(partition by acct_id order by yr) as rn
from t) -- t is your tablename
,cte(rn,yr,acct_id,amount,interest_rate,expenses,running_total,interest_amount) as
(select rn,yr,acct_id,amount,interest_rate,expenses
,(amount*(1+interest_rate))-expenses
,amount*interest_rate
from rownums
where rn=1
union all
select t.rn,t.yr,t.acct_id,t.amount,t.interest_rate,t.expenses
,((c.running_total+t.amount)*(1+t.interest_rate))-t.expenses
,(c.running_total+t.amount)*t.interest_rate
from cte c
join rownums t on t.acct_id=c.acct_id and t.rn=c.rn+1
)
select * from cte
Sample Demo
Generate row numbers using row_number function
Calculate the interest and running total of the first row for each acct_id (anchor in the recursive cte).
Join every row to the next one (ordered by ascending order of year column) for each account_id and compute the running total and interest for the subsequent rows.

SQL Query to continuously bucket data

I have a table as follows:
Datetime | ID | Price | Quantity
2013-01-01 13:30:00 1 139 25
2013-01-01 13:30:15 2 140 25
2013-01-01 13:30:30 3 141 15
Supposing that I wish to end up with a table like this, which buckets the data into quantities of 50 as follows:
Bucket_ID | Max | Min | Avg |
1 140 139 139.5
2 141 141 141
Is there a simple query to do this? Data will constantly be added to the first table, it would be nice if it could somehow not recalculate the completed buckets of 50 and instead automatically start averaging the next incomplete bucket. Ideas appreciated! Thanks
You may try this solution. It should work even if "number" is bigger than 50 (but relying on fact that avg(number) < 50).
select
bucket_id,
max(price),
min(price),
avg(price)
from
(
select
price,
bucket_id,
(select sum(t2.number) from test t2 where t2.id <= t1.id ) as accumulated
from test t1
join
(select
rowid as bucket_id,
50 * rowid as bucket
from test) buckets on (buckets.bucket - 50) < accumulated
and buckets.bucket > (accumulated - number))
group by
bucket_id;
You can have a look at this fiddle http://sqlfiddle.com/#!7/4c63c/1 if it is what you want.