How to calculate average monthly number of some events in MS SQL Server? - sql

I have table in MS SQL Server like below:
ID_EVENT | PRODUCT| DATE
-------------------------------
123 | A | 2021-01-15
456 | A | 2021-01-22
789 | A | 2021-02-05
110 | B | 2021-01-18
124 | B | 2021-02-11
I need to calculate average monthly (for January and February) number of ID_EVENT per PRODUCT. So as a result I need something like below:
PRODUKT | AVG_PER_MNTH
-----------------
A | 1.5
B | 1
A = 1.5 because 3 / 2 = 1.5 --> (number of ID_EVENT for January + number of ID_EVENT for February) / number of months which we analyse (2 -> January and February)
B = 1 because 1 / 1 = 1 --> (number of ID_EVENT for January + number of ID_EVENT for February) / number of months which we analyse (2 -> January and February)
How can I do that in MS SQL Server ?

One option, aggregating first by month and year, then by product:
WITH cte AS (
SELECT PRODUCT, 1.0*COUNT(*) AS cnt
FROM yourTable
GROUP BY PRODUCT, FORMAT(DATE, 'MM.yyyy')
)
SELECT PRODUCT, AVG(cnt) AS AVG_PER_MNTH
FROM cte
GROUP BY PRODUCT;
Demo

WITH CTE AS
(SELECT PRODUCT
,COUNT(*) ac cnt,
,DATEADD(month, DATEDIFF(month, 0, Date), 0) AS StartOfMonth
FROM YourTable
GROUP BY PRODUCT
,DATEADD(month, DATEDIFF(month, 0, Date), 0)
)
SELECT
PRODUCT
,AVG(cnt) as MonthlyAverage
FROM CTE
GROUP BY PRODUCT

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.

SQL sum and previous row [duplicate]

This question already has answers here:
Calculate a Running Total in SQL Server
(15 answers)
Closed 3 years ago.
I have the following table:
________________________
date | amount
________________________
01-01-2019 | 10
01-01-2019 | 10
01-01-2019 | 10
01-01-2019 | 10
02-01-2019 | 5
02-01-2019 | 5
02-01-2019 | 5
02-01-2019 | 5
03-01-2019 | 20
03-01-2019 | 20
These are mutation values by date. I would like my query to return the summed amount by date. So for 02-01-2019 I need 40 ( 4 times 10) + 20 ( 4 times 5). For 03-01-2019 I would need ( 4 times 10) + 20 ( 4 times 5) + 40 ( 2 times 20) and so on. Is this possible in one query? How do I achieve this?
My current query to get the individual mutations:
Select s.date,
Sum(s.amount) As Sum_amount
From dbo.Financieel As s
Group By s.date
You can try below -
DEMO
select dateval,
SUM(amt) OVER(ORDER BY dateval ROWS BETWEEN UNBOUNDED PRECEDING AND CURRENT ROW) as amt
from
(
SELECT
dateval,
SUM(amount) amt
FROM t2 group by dateval
)A
OUTPUT:
dateval amt
01/01/2019 00:00:00 40
01/02/2019 00:00:00 60
01/03/2019 00:00:00 100
Try this below script to get your desired output-
SELECT A.date,
(SELECT SUM(amount) FROM <your_table> WHERE Date <= A.Date) C_Total
FROM <your_table> A
GROUP BY date
ORDER BY date
Output is-
date C_Total
01-01-2019 40
02-01-2019 60
03-01-2019 100
I suggest to use a window function, like this:
select date, sum(amount) over( order by date)
from table

Postgres count number or rows and group them by timestamp

Let's assume I have one table in postgres with just 2 columns:
ID which is PK for the table (bigint)
time which is type of timestamp
Is there any way how to get IDs grouped by time BY YEAR- when the time is date 18 February 2005 it would fit in 2005 group (so result would be)
year number of rows
1998 2
2005 5
AND if the number of result rows is smaller than some number (for example 3) SQL will return the result by month
Something like
month number of rows
(February 2018) 5
(March 2018) 2
Is that possible some nice way in postgres SQL?
You can do it using window functions (as always).
I use this table:
TABLE times;
id | t
----+-------------------------------
1 | 2018-03-14 20:04:39.81298+01
2 | 2018-03-14 20:04:42.92462+01
3 | 2018-03-14 20:04:45.774615+01
4 | 2018-03-14 20:04:48.877038+01
5 | 2017-03-14 20:05:08.94096+01
6 | 2017-03-14 20:05:16.123736+01
7 | 2017-03-14 20:05:19.91982+01
8 | 2017-01-14 20:05:32.249175+01
9 | 2017-01-14 20:05:35.793645+01
10 | 2017-01-14 20:05:39.991486+01
11 | 2016-11-14 20:05:47.951472+01
12 | 2016-11-14 20:05:52.941504+01
13 | 2016-10-14 21:05:52.941504+02
(13 rows)
First, group by month (subquery per_month).
Then add the sum per year with a window function (subquery with_year).
Finally, use CASE to decide which one you will output and remove duplicates with DISTINCT.
SELECT DISTINCT
CASE WHEN yc > 5
THEN mc
ELSE yc
END AS count,
CASE WHEN yc > 5
THEN to_char(t, 'YYYY-MM')
ELSE to_char(t, 'YYYY')
END AS period
FROM (SELECT
mc,
sum(mc) OVER (PARTITION BY date_trunc('year', t)) AS yc,
t
FROM (SELECT
count(*) AS mc,
date_trunc('month', t) AS t
FROM times
GROUP BY date_trunc('month', t)
) per_month
) with_year
ORDER BY 2;
count | period
-------+---------
3 | 2016
3 | 2017-01
3 | 2017-03
4 | 2018
(4 rows)
Just count years. If it's at least 3, then you group by years, else by months:
select
case (select count(distinct extract(year from time)) from mytable) >= 3 then
to_char(time, 'yyyy')
else
to_char(time, 'yyyy-mm')
end as season,
count(*)
from mytable
group by season
order by season;
(Unlike many other DBMS, PostgreSQL allows to use alias names in the GROUP BY clause.)

SQL Server - Get count for each pharmacy's outbound usage for each month

I am trying to write a query to select the total outbound usage of each pharmacy in my database table for each month.
Here is what I have so far, it outputs the correct data. But I want to eliminate the amount of rows selected
select pharmacyid, count(*) as usage, month(datecalled) as month
from outboundcalldata
where datepart(year, datecalled) = 2014
group by pharmacyid, YEAR(DateCalled), month(datecalled)
order by pharmacyid, month
example of output:
pharmacyid|usage| month
-----------------------
2220000006| 10 | 2
2220000006| 11 | 3
2220000006| 900 | 4
2220000006| 30 | 5
2220000007| 34 | 2
2220000007| 300 | 3
2220000007| 145 | 4
Instead I would like it to output 1 row per pharmacy and a column for each month.
;WITH CTE AS
(
select pharmacyid, count(*) as usage, month(datecalled) as [month]
from outboundcalldata
where datepart(year, datecalled) = 2014
group by pharmacyid, YEAR(DateCalled), month(datecalled)
)
SELECT *
FROM CTE C
PIVOT (SUM(usage)
FOR [month]
IN ([1],[2],[3],[4],[5],[6],[7],[8],[9],[10],[11],[12])
)p

SQL Rolling Total up to a certain date

I have two tables that I'm working with. Let's call them "Customers" and "Points".
The Points table looks like this:
Account Year M01 M02 M03 M04 M05 M06 M07 M08 M09 M10 M11 M12
123 2011 10 0 0 0 10 0 10 0 0 0 0 10
123 2012 0 0 0 0 10 0 0 10 10 10 10 20
123 2013 5 0 0 0 0 0 0 0 0 0 0 0
But these points work on a rolling 12 months. Calculating a current customer's points is simple enough, but the challenge is for customers who are no longer active. Say Customer 123 became inactive on Jan 2013, we would only want to calculate Feb'12-Jan'13. This is where the other table, Customers, comes in, let's simplify and say it looks just like this:
Account End Date
123 20130105
Now, what I want to do is create a query that calculates the amount of points that each customer has. (Current 12 months for active customers, last 12 months they were active for customers who are no longer active.)
Here's some more information:
I'm running SQL Server 2008.
These tables have been supplied to me like this, I can't modify them.
An active customer is one who has an end date of 99991231 (Dec 31 9999)
The points table only populates for years that the customer is an active customer. Aka, someone becomes an active customer Feb 2009, they have an entry for the year 2009, if they became inactive in July 2009, their points is only calculating Feb-July 2009, there is no row for 2008 because they weren't a customer back then. Jan & Aug-Dec 2009 will show 0's.
Additionally, the record is only created if the customer gains any points that year. If a customer gets 0 points in a year, there will be no record of it.
For border cases, if you get into the first day of a month, then that month is counted. Example, let's say today is April 1st, 2013, that means we sum up May'12-April'13.
This is a pretty complex question. If there's anything I can explain better please let me know. Thank you!
Unfortunately with your table structure of points you will have to unpivot the data. An unpivot takes the data from the multiple columns into rows. Once the data is in the rows, it will be much easier to join, filter the data and total the points for each account. The code to unpivot the data will be similar to this:
select account,
cast(cast(year as varchar(4))+'-'+replace(month_col, 'M', '')+'-01' as date) full_date,
pts
from points
unpivot
(
pts
for month_col in ([M01], [M02], [M03], [M04], [M05], [M06], [M07], [M08], [M09], [M10], [M11], [M12])
) unpiv
See SQL Fiddle with Demo. The query gives a result similar to this:
| ACCOUNT | FULL_DATE | PTS |
------------------------------
| 123 | 2011-01-01 | 10 |
| 123 | 2011-02-01 | 0 |
| 123 | 2011-03-01 | 0 |
| 123 | 2011-04-01 | 0 |
| 123 | 2011-05-01 | 10 |
Once the data is in this format, you can join the Customers table to get the total points for each account, so the code will be similar to the following:
select
c.account, sum(pts) TotalPoints
from customers c
inner join
(
select account,
cast(cast(year as varchar(4))+'-'+replace(month_col, 'M', '')+'-01' as date) full_date,
pts
from points
unpivot
(
pts
for month_col in ([M01], [M02], [M03], [M04], [M05], [M06], [M07], [M08], [M09], [M10], [M11], [M12])
) unpiv
) p
on c.account = p.account
where
(
c.enddate = '9999-12-31'
and full_date >= dateadd(year, -1, getdate())
and full_date <= getdate()
)
or
(
c.enddate <> '9999-12-31'
and dateadd(year, -1, [enddate]) <= full_date
and full_date <= [enddate]
)
group by c.account
See SQL Fiddle with Demo
Lousy data structure. The first thing to do is to unpivot it. Then you get a table with year-month-points as the columns.
From here, you can just select the most recent 12 months. In fact, you don't even have to worry about when a customer left, since presumably they have not collected points since then.
Here is an example in SQL:
with points as (
select 123 as account, 2012 as year,
10 as m01, 0 as m02, 0 as m03, 0 as m04, 10 as m05, 0 as m06,
10 as m07, 0 as m08, 0 as m09, 0 as m10, 0 as m11, 10 as m12
),
points_ym as (
select account, YEAR, mon, cast(right(mon, 2) as int) as monnum, points
from points
unpivot (points for mon in (m01, m02, m03, m04, m05, m06, m07, m08, m09, m10, m11, m12)
) as unpvt
)
select account, SUM(points)
from points_ym
where year*12+monnum >= year(getdate())*12+MONTH(getdate()) - 12
group by account