简体   繁体   中英

Is there a way to calculate the correct average in SQL using the specified constraints?

I need SQL to determine the correct average for all customers who opened or closed an account during the prior month. This calculation is part of a much, much larger monthly extract. Furthermore, I need the output to not alter the average balance of all pre-existing accounts that did not open or close during the prior month (through a case statement?? uncertain).

Currently, our system calculates the month-end average by taking a snapshot of whatever the average balance was on the day the account was closed, or opened. But this is imprecise, because it's assigning a full month's 'credit' when an account may have only been open for a single day, or 15 days (etc) within a month.

Basic math tells me I need to compute the averages thusly:

average_balance * days_open_in_month_closed/days_in_month (for closed accounts) 

average_balance * days_open_in_month_new/days_in_month (for new accounts) 

I've written 3 subqueries within my larger query to get the data for all the day/date components; however I am bumping up against the boundaries of my SQL knowledge. Essentially, I'm not sure how to pull everything together: I don't know how to insert the average calculation, how to make everything fit into a single sub-query (if it can/should), etc.

,(SELECT DAY(DATEADD(DD,-1,DATEADD(MM,DATEDIFF(MM,0,  GETDATE()), 0)))) AS days_in)month
,(SELECT DATEDIFF(DAY,DATEADD(month, DATEDIFF(month, -1, getdate()) -2, 0),closed_date) AS days_opened_in_month_closed
,(SELECT DATEDIFF(DAY,DATEADD(month, DATEDIFF(month, -1, getdate()) -2,0,),open_date) AS days_open_in_month_new

I would expect a SQL calculated average of $660,000 for an account with a system-generated average balance of $1,800,000 that closed 11 days into a 30 day calendar month.

Similarly, I would expect a SQL calculated average of $45,000,000 for an account with a system-generated average balance of $750,000 that opened 2 days prior to the end of a 30 day calendar month.

I would expect SQL to not alter the system-generated average of 500,000 for an account that had been open the full 30 day month.

Thanks in advance.

Let's talk some best practices here:

Firstly, I would recommend that you store the date closed/date open, as supposed to the way you seem to be storing it which would imply that you store the "days_in_month_closed" as a parameter. To save yourself some conversion time in the future, be sure that you are using a standard timestamp string (unixtime integers should do the trick).

Next, you would be using the DATEDIFF() . If you want to see the number of days between the beginning of a month and the date of a client-closed account, you would write it

SELECT 
   DATEDIFF(day,
           `account_closed_date`
           CAST(CONCAT(YEAR(`account_closed_date),"-",MONTH(`account_closed_day`),"-01") AS date)  
FROM `accounts_table`

This query would return the number of days as an integer between the first and expected day of the month.

If you wanted to find the beginning of the next month, you would do something similar

SELECT 
   DATEDIFF(day,
           `account_open_date`
           DATEADD(MONTH, 1, CAST(CONCAT(YEAR(`account_open_date),"-",MONTH(`account_open_day`),"-01") AS date)))
FROM `accounts_table`

With these numbers calculated, you can then do a sum of the balance table and use this as the denominator.

SELECT 
   (Select AVG(balance) From balances)/
   DATEDIFF(day,
           `account_open_date`
           DATEADD(MONTH, 1, CAST(CONCAT(YEAR(`account_open_date),"-",MONTH(`account_open_day`),"-01") AS date)))
FROM `accounts_table` at
Join balances b on at.account_id = b.account_id;

This would be, of course, assuming you need to calculate the average balance per account within this query. Though the better practice would be to calculate the two numbers separately (ie one as the Total Balance in the month and one as the days account was active in the month per account and then divide them separately.

For the new account, you want to show that the balance was "Zero" every day of a month before that, then you would just divide by the total number of days in a month, which you can find by using the DATEADD(DAY, -1, DATEADD(MONTH, 1, Date_you_need)) or by using the function Day(EOMONTH(date_you_needed)) which will return an integer value between 28 and 31 appropriately. However this only is valid as of SQL Server 2012, which seems to be later than the version you have tagged in the question.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM