Cumulative Sum Query in SQL table with distinct elements - sql

I have a table like this, with column names as Date of Sale and insurance Salesman Names -
Date of Sale | Salesman Name | Sale Amount
2021-03-01 | Jack | 40
2021-03-02 | Mark | 60
2021-03-03 | Sam | 30
2021-03-03 | Mark | 70
2021-03-02 | Sam | 100
I want to do a group by, using the date of sale. The next column should display the cumulative count of the sellers who have made the sale till that date. But same sellers shouldn't be considered again.
For example,
The following table is incorrect,
Date of Sale | Count(Salesman Name) | Sum(Sale Amount)
2021-03-01 | 1 | 40
2021-03-02 | 3 | 200
2021-03-03 | 5 | 300
The following table is correct,
Date of Sale | Count(Salesman Name) | Sum(Sale Amount)
2021-03-01 | 1 | 40
2021-03-02 | 3 | 200
2021-03-03 | 3 | 300
I am not sure how to frame the SQL query, because there are two conditions involved here, cumulative count while ignoring the duplicates. I think the OVER clause along with the unbounded row preceding may be of some use here? Request your help
Edit - I have added the Sale Amount as a column. I need the cumulative sum for the Sales Amount also. But in this case , all the sale amounts should be considered unlike the salesman name case where only unique names were being considered.

One approach uses a self join and aggregation:
WITH cte AS (
SELECT t1.SaleDate,
COUNT(CASE WHEN t2.Salesman IS NULL THEN 1 END) AS cnt,
SUM(t1.SaleAmount) AS amt
FROM yourTable t1
LEFT JOIN yourTable t2
ON t2.Salesman = t1.Saleman AND
t2.SaleDate < t1.SaleDate
GROUP BY t1.SaleDate
)
SELECT
SaleDate,
SUM(cnt) OVER (ORDER BY SaleDate) AS NumSalesman,
SUM(amt) OVER (ORDER BY SaleDate) AS TotalAmount
FROM cte
ORDER BY SaleDate;
The logic in the CTE is that we try to find, for each salesman, an earlier record for the same salesman. If we can't find such a record, then we assume the record in question is the first appearance. Then we aggregate by date to get the counts per day, and finally take a rolling sum of counts in the outer query.

The best way to do this uses window functions to determine the first time a sales person appears. Then, you just want cumulative sums:
select saledate,
sum(case when seqnum = 1 then 1 else 0 end) over (order by saledate) as num_salespersons,
sum(sum(sales)) over (order by saledate) as running_sales
from (select t.*,
row_number() over (partition by salesperson order by saledate) as seqnum
from t
) t
group by saledate
order by saledate;
Note that this in addition to being more concise, this should have much, much better performance than a solution that uses a self-join.

Related

How to calculate average monthly number of some action in some perdion in Teradata SQL?

I have table in Teradata SQL like below:
ID trans_date
------------------------
123 | 2021-01-01
887 | 2021-01-15
123 | 2021-02-10
45 | 2021-03-11
789 | 2021-10-01
45 | 2021-09-02
And I need to calculate average monthly number of transactions made by customers in a period between 2021-01-01 and 2021-09-01, so client with "ID" = 789 will not be calculated because he made transaction later.
In the first month (01) were 2 transactions
In the second month was 1 transaction
In the third month was 1 transaction
In the nineth month was 1 transactions
So the result should be (2+1+1+1) / 4 = 1.25, isn't is ?
How can I calculate it in Teradata SQL? Of course I showed you sample of my data.
SELECT ID, AVG(txns) FROM
(SELECT ID, TRUNC(trans_date,'MON') as mth, COUNT(*) as txns
FROM mytable
-- WHERE condition matches the question but likely want to
-- use end date 2021-09-30 or use mth instead of trans_date
WHERE trans_date BETWEEN date'2021-01-01' and date'2021-09-01'
GROUP BY id, mth) mth_txn
GROUP BY id;
Your logic translated to SQL:
--(2+1+1+1) / 4
SELECT id, COUNT(*) / COUNT(DISTINCT TRUNC(trans_date,'MON')) AS avg_tx
FROM mytable
WHERE trans_date BETWEEN date'2021-01-01' and date'2021-09-01'
GROUP BY id;
You should compare to Fred's answer to see which is more efficent on your data.

Query for Offset Rolling Sum & Count - Oracle SQL

I've been working to build a distinct count and sum total of sales based on orders placed 4 to 180 days back for each day in the data table starting at Orders placed on day 181, then grouped by Month & Year, but have been unable to do it.
The end result would look something like the table below. Each order would show up multiple times, up to 176 times, but would be distinct for the given day (order 42999, placed on 10-01-2011 for example would be counted once on every day between 10-05-2011 and 2-01-2012 for example)
| OrdMonthYr | Grouped Order Count | Sum of Orders |
------------------------------------------------------
| 2011-06 | 140 | $450 |
| 2011-07 | 190 | $500 |
| 2011-08 | 250 | $600 |
------------------------------------------------------
The order count would take the total count of sales for a given day executed 4 to 180 days prior to that day (so March 1st, 2011 would have a distinct order count and order sum for orders placed between Nov 1st, 2010 and Feb 25th, 2011 as an example) followed by a function aggregating each of those totals up to month & year per the table above.
As I understand you want to get cumulative sum and count for the previous days from 4 to 180. But its not clear how it should be rolled up
If so you may use analytic functions. Next query will calculate it
select trunc(o.orderdate)
,count(*) over (order by trunc(o.orderdate) range between 180 PRECEDING AND 4 PRECEDING )
,sum(amount) over (order by trunc(o.orderdate) range between 180 PRECEDING AND 4 PRECEDING)
from orders o
What about rolling up orders to month. May be you need to take the first of every month and get sum and amount if so you may just take one row for each month from previous query:
select ord_date, cnt,sum_amount FRoM (
select trunc(o.orderdate) as ord_date
,count(*) over (order by trunc(o.orderdate) range between 180 PRECEDING AND 4 PRECEDING ) as cnt
,sum(amount) over (order by trunc(o.orderdate) range between 180 PRECEDING AND 4 PRECEDING) as sum_amount
,row_number() over (order by trunc(o.orderdate),rowid) as RN
from orders o)
WHERE rn = 1
and ord_date = trunc(ord_date,'MM')
Does this get at what you want?
select orderdate,
(select count(*)
from orders o
where o.orderdate between d.dte - 180 an d.dte - 4
) as cnt,
(select sum(amount)
from orders o
where o.orderdate between d.dte - 180 an d.dte - 4
) as amount
from (select distinct orderdate as dte from orders) d;

SQL Server: how to divide the result of sum of total for every customer id

I have 4 tables like this (you can ignore table B because this problem did not use that table)
I want to show the sum of 'total' for each 'sales_id' from table 'sales_detail'
What I want (the result) is like this:
sales_id | total
S01 | 3
S02 | 2
S03 | 4
S04 | 1
S05 | 2
S05 | 3
I have tried with this query:
select
sum(total)
from
sales_detail
where
sales_id = any (select sales_id
from sales
where customer_id = any (select customer_id
from customer)
)
but the query returns a value if 15 because they are the sum of those rows of data.
I have tried to use "distinct" before sum
and the result is [ 1, 2, 3 ] because those are distinct of those rows of data (not sum of each sales_id)
It's all about subquery
You are just so far off track that a simple comment won't help. Your query only concerns one table, sales_detail. It has nothing to do with the other two.
And, it is just an aggregation query:
select sd.sales_id, sum(sd.total)
from sales_detail sd
group by sd.sales_id;
This is actually pretty close to what the question itself is asking.

Compare 2 subsets of data from table?

I'm not sure if this is possible - I'm having real trouble getting my head around it.
This is for a product schedule, showing how much we are expecting to deliver on a given date. Data is imported into this schedule weekly which creates a new entry.
For example, if the schedule for the day currently totals 10, and you import 15, a new row is inserted with Qty 5, bringing the sum to 15.
The data I have is like so:
Product | Delivery Required Date | Qty
Prod1 | 1/1/13 | 10
Prod1 | 1/1/13 | -10
Prod1 | 1/1/13 | 10
Prod1 | 1/1/13 | -10
Prod1 | 1/1/13 | 25
I want to design a query which shows the variance between the previous schedule, and the current schedule.
For example, the query will sum all of the rows "Qty", excluding the last entry - and compare it to the last entry. In the data above, the variance is 25 (Existing total was 0, latest entry is 25, 0+25 =25).
Is this possible?
Thanks
I suspect there'a better answer using Common Table Expressions, but a quick & ugly solution might be
select sum(case when EntryNo <> MAX(EntryNo) then Qty else 0 end) as 'sumLessLast'
from MyTable
If MyTable has a million rows in it you'll want a better solution.
SqlServer 2005 and 2008:
;with r1 as (
select DeliveryReqDate, sum(Qty) as TotalQty
from TableName
group by DeliveryReqDate)
, r2 as (
select DeliveryReqDate, Qty
, row_number() over (partition by DeliveryReqDate order by EntryNo desc) rn
from TableName)
select r1.DeliveryReqDate, r1.TotalQty, r2.Qty as LastQty
, r1.TotalQty - r2.Qty as TotalButLastQty
from r1
join r2 on r2.DeliveryReqDate = r1.DeliveryReqDate and r2.rn = 1
SqlServer 2012
;with r1 as (
select DeliveryReqDate, Qty
, sum(Qty) over (partition by DeliveryReqDate) as TotalQty
, row_number() over (partition by DeliveryReqDate order by EntryNo desc) rn
from TableName)
select DeliveryReqDate, TotalQty, Qty as LastQty
, TotalQty - Qty as TotalButLastQty
from r1
where rn = 1
I'm not sure that I completely understand logic regarding the accounting of product and date, but I hope you can adapt above queries to your needs.

Displaying unique attributes of table and the total value in postgresql

I have one table with columns stamp_type and amount as follows
stamp_type | amount
--------------------------------------------------------------+--------
GENERAL STAMP | 11000
GENERAL STAMP | 25000
COURT FEE STAMP | 9800
SPECIAL ADHESIVE | 721000
GENERAL STAMP | 125000
COURT FEE STAMP | 21000
Now I want to display as follows:
stamp_type | amount
GENERAL STAMP 161000
COURT FEE STAMP 30800
SPECIAL ADHESIVE 721000
TOTAL:912800
I am unable to display the unique values. can any one give suggest me the query. I tried to use Distinct but dint work.
select * from
(
select 0 as srt, stamp_type, sum(amount) as SumAmount from t group by stamp_type
union
select 1 as srt, 'Total' as stamp_type, sum(amount) as SumAmount from t
) b order by srt
try this code::
select stamp_type, sum(amount) amount
from tbl
group by stamp_type
union
select 'Total:' stamp_type,sum(amount) amount
from tbl;