Calculating an AVG of COUNT by MONTH - sql

I'm trying to display the average number of counts/records for each month in 2016. The following code does not display each month, rather displays only the monthly average for 2016:
SELECT AVG(DISTINCT DayCnt) AS AvgCnt
FROM
(
SELECT COUNT(*) As DayCnt
FROM table
WHERE YEAR(Insert_Date) = '2016'
GROUP BY MONTH(Insert_Date)
)
AS AvgCnt

You first should be group your result per month and days and count daily inserted records, after that to get average of per month inserted records try this:
SELECT monthGroup, AVG(DayCnt) AS AvgCnt
FROM
(
SELECT MONTH(Insert_Date) monthGroup, DAY(Insert_Date) dayGroup, COUNT(*) As DayCnt
FROM table
WHERE YEAR(Insert_Date) = '2016'
GROUP BY MONTH(Insert_Date), DAY(Insert_Date)
)
AS AvgCnt
GROUP BY monthGroup

I'm not sure since you didn't post the data but if I understand correctly, this should work for you:
SELECT AVG(DayCnt) AS AvgCnt, mth
FROM
(
SELECT COUNT(*) As DayCnt, MONTH(Insert_Date) as mth
FROM table
WHERE YEAR(Insert_Date) = '2016'
GROUP BY MONTH(Insert_Date), DAY(Insert_Date) )
AS AvgCnt
GROUP BY mth

Related

How to get number of billable customers per month SQL

This is what my table looks like:
NOTE: Don't worry about the BMI field being empty in some rows. We assume that each row is a reading. I have omitted some columns for privacy reasons.
I want to get a count of the number of active customers per month. A customer is active if they have at least 18 readings in total (1 reading per day for 18 days in a given month). How do I write this SQL query? Assume the table name is 'cust'. I'm using SQL Server. Any help is appreciated.
Presumably a patient is a customer in your world. If so, you can use two levels of aggregation:
select yyyy, mm, count(*)
from (select year(createdat) as yyyy, month(createdat) as mm,
patient_id,
count(distinct convert(date, createdat)) as num_days
from t
group by year(createdat), month(createdat), patient_id
) ymp
where num_days >= 18
group by yyyy, mm;
You need to group by patient and the month, then group again by just the month
SELECT
mth,
COUNT(*) NumPatients
FROM (
SELECT
EOMONTH(c.createdat) mth
FROM cust c
GROUP BY EOMONTH(c.createdat), c.patient_id
HAVING COUNT(*) >= 18
-- for distinct days you could change it to:
-- HAVING COUNT(DISTINCT CAST(c.createdat AS date)) >= 18
) c
GROUP BY mth;

Calculate average days between orders The last three records tsql

I trying to take an average per customer, but you're not grouping by customer.
I would like to calculate the average days between several order dates from a table called invoice. For each BusinessPartnerID, what is the average days between orders i want average days last three records orders .
I got the average of all order for each user but need days last three records orders
The sample table is as below
;WITH temp (avg,invoiceid,carname,carid,fullname,mobail)
AS
(
SELECT AvgLag = AVG(Lag) , Lagged.idinvoice,
Lagged.carname ,
Lagged.carid ,Lagged.fullname,Lagged.mobail
FROM
(
SELECT
(car2.Name) as carname ,
(car2.id) as carid ,( busin.Name) as fullname, ( busin.Mobile) as mobail , INV.Id as idinvoice , Lag = CONVERT(int, DATEDIFF(DAY, LAG(Date,1)
OVER (PARTITION BY car2.Id ORDER BY Date ), Date))
FROM [dbo].[Invoice] AS INV
JOIN [dbo].[InvoiceItem] AS INITEM on INV.Id=INITEM.Invoiceid
JOIN [dbo].[BusinessPartner] as busin on busin.Id=INV.BuyerId and Type=5
JOIN [dbo].[Product] as pt on pt.Id=INITEM.ProductId and INITEM.ProductId is not null and pt.ProductTypeId=3
JOIN [dbo].[Car] as car2 on car2.id=INv.BusinessPartnerCarId
) AS Lagged
GROUP BY
Lagged.carname,
Lagged.carid,Lagged.fullname,Lagged.mobail, Lagged.idinvoice
-- order by Lagged.fullname
)
SELECT * FROM temp where avg is not null order by avg
I don't really see how your query relate to your question. Starting from a table called invoice that has columns businesspartnerid, and date, here is how you would take the average of the day difference between the last 3 invoices of each business partner:
select businesspartnerid,
avg(1.0 * datediff(
day,
lag(date) over(partition by businesspartnerid order by date),
date
) avg_diff_day
from (
select i.*,
row_number() over(partiton by businesspartnerid order by date desc) rn
from invoice i
) i
where rn <= 3
group by businesspartnerid
Note that 3 rows gives you 2 intervals only, that will be averaged.

Group By - select by a criteria that is met every month

The below query returns all USERS that have SUM(AMOUNT) > 10 in a given month. It includes Users in a month even if they don't meet the criteria in other months.
But I'd like to transform this query to return all USERS who must meet the criteria SUM(AMOUNT) > 10 every single month (i.e., from the first month in the table to the last one) across the entire data.
Put another way, exclude users who don't meet SUM(AMOUNT) > 10 every single month.
select USERS, to_char(transaction_date, 'YYYY-MM') as month
from Table
GROUP BY USERS, month
HAVING SUM(AMOUNT) > 10;
One approach uses a generated calendar table representing all months in your data set. We can left join this calendar table to your current query, and then aggregate over all months by user:
WITH months AS (
SELECT DISTINCT TO_CHAR(transaction_date, 'YYYY-MM') AS month
FROM yourTable
),
cte AS (
SELECT USERS, TO_CHAR(transaction_date, 'YYYY-MM') AS month
FROM yourTable
GROUP BY USERS, month
HAVING SUM(AMOUNT) > 10
)
SELECT
t.USERS
FROM months m
LEFT JOIN cte t
ON m.month = t.month
GROUP BY
t.USERS
HAVING
COUNT(t.USERS) = (SELECT COUNT(*) FROM months);
The HAVING clause above asserts that the number of months to which a user matches is in fact the total number of months. This would imply that the user meets the sum criteria for every month.
Perhaps you could use a correlated subquery, such as:
select t.*
from (select distinct table.users from table) t
where not exists
(
select to_char(u.transaction_date, 'YYYY-MM') as month
from table u
where u.users = t.users
group by month
having sum(u.amount) <= 10
)
One option would be using sign(amount-10) vs. sign(amount) logic as
SELECT q.users
FROM
(
with tab(users, transaction_date,amount) as
(
select 1,date'2018-11-24',8 union all
select 1,date'2018-11-24',18 union all
select 2,date'2018-10-24',13 union all
select 3,date'2018-11-24',18 union all
select 3,date'2018-10-24',28 union all
select 3,date'2018-09-24', 3 union all
select 4,date'2018-10-24',28
)
SELECT users, to_char(transaction_date, 'YYYY-MM') as month,
sum(sign(amount-10)) as cnt1,
sum(sign(amount)) as cnt2
FROM tab t
GROUP BY users, month
) q
GROUP BY q.users
HAVING sum(q.cnt1) = sum(q.cnt2)
GROUP BY q.users
users
-----
2
4
Rextester Demo
You need to compare the number of months > 10 to the number of months between the min and the max date:
SELECT users, Count(flag) AS months, Min(mth), Max(mth)
FROM
(
SELECT users, date_trunc('month',transaction_date) AS mth,
CASE WHEN Sum(amount) > 10 THEN 1 end AS flag
FROM tab t
GROUP BY users, mth
) AS dt
GROUP BY users
HAVING -- adding the number of months > 10 to the min date and compare to max
Min(mth) + (INTERVAL '1' MONTH * (Count(flag)-1)) = Max(mth)
If missing months don't count it would be a simple count(flag) = count(*)

refering to field out of subeselects scope

I'm working on a piece of SQL at the moment and i need to retrieve every row of a dataset with a median and an average aggregated in it.
Example
i have the following set
ID;month;value
and i would like to retrieve something like :
ID;month;value;average for this month;median for this month
without having to group by my result.
So it would be something like :
SELECT ID,month,value,
(SELECT AVG(value) FROM myTable) as "myAVG"
FROM myTable
but i would need that average to be the average for that month specifically. So, rows where the month="January" will have the average and median for "January" etc ...
Issue here is that i did not find a way to refer to the value of month in my subquery
(SELECT AVG(value) FROM myTable)
Does someone have a clue?
P.S: It's a redshift database i'm working on.
You would need to select all rows from the table, and do a left join with a select statement that does group by month. This way, you would get every row, and the group by results with them for that month.
Something like this:
SELECT * FROM myTable a
LEFT JOIN
(
SELECT Month, Sum(value being summed) as mySum
FROM myTable
GROUP BY Month
) b
ON a.Month = b.Month
Helpful?
with myavg as
(SELECT month, AVG(value) as avgval FROM myTable group by month)
, mymed as
(select month, median(value) as medval from myTable group by month)
select ID, month, value, ma.avgval, mm.medval
from mytable m left join myavg ma
on m.month = ma.month
left join mymed mm
on m.month = mm.month
You can use a cte to do this. However, you need a group by on month, as you are calculating an aggregate value.
In Redshift you can use Window Function.
select month,
avg(value) over
(PARTITION BY month rows unbounded preceding) as avg
from myTable
order by 1;

Group by select that returns only groups with updated rows

I have a Table "Postings"
CREATE TABLE POSTINGS(
Account_FK INT,
Department_FK INT,
Project_FK INT,
Company_FK INT,
Year INT,
Month INT,
Amount float,
Handled BIT
)
Im trying to make a select statement that will select the sum of Amounts for each company each month.
Like this:
SELECT Company_FK, Year, Month, Sum(Amount)
FROM Postings
GROUP BY Company_FK, Year, Month
But i will only need the rows that have not been handled. E.g. the rows with Handled = 0
SELECT Company_FK, Year, Month, Sum(Amount)
FROM Postings
WHERE Handled = 0
GROUP BY Company_FK, Year, Month
Now this query will sum up only the rows with Handled = 0 for the Company each year and month.
But i will also need the sums to include all the other rows for the company. I mean, if one row in the company is not handled. I will need to return the company sum of all company-rows.
So if Company_FK = 1 has three postings. All of which have handled = 1. Then this company could be ignored. But if Company_FK = 2 has three postings. And one of them has handled = 0, then i would need to return the sum of all three rows.
Do you understand what i mean?
Any suggestions?
Try this:
SELECT distinct c.Company_FK, c.Year, c.Month, b.Amount FROM Postings c
INNER JOIN
(SELECT Company_FK, Year, Month, Sum(Amount) as 'Amount'
FROM Postings
GROUP BY Company_FK, Year, Month) b ON c.Company_FK = b.Company_FK and c.Year = b.Year and c.Month = b.Month
WHERE c.Handled = 0
You can add a HAVING statement after your GROUP BY
HAVING COUNT(*) > SUM(CONVERT(INT, Handled))
You will need to remove the WHERE clause as well, because the HAVING will be the filter
Full Query:
SELECT Company_FK, Year, Month, Sum(Amount)
FROM Postings
GROUP BY Company_FK, Year, Month
HAVING COUNT(*) > SUM(CONVERT(INT, Handled))
SELECT Company_FK, Year, Month, Sum(Amount) as "Amount"
FROM Postings
GROUP BY Company_FK, Year, Month
HAVING COUNT(*) > SUM(CONVERT(INT, Handled))
SELECT Company_FK, Year, Month, Sum(Amount)
FROM Postings p
WHERE Exists (select top 1 1 from Postings po where p.company_fk=po.company_fk and Handled=0 )
GROUP BY Company_FK, Year, Month