Creating a trend line from data set SQL
linear-regression, math, sql, visual-studio
Solution
I figured it out. I divided the data into multiple derived tables and sub queries, essentially dividing the data in half. These are my formulas to get each value:
*(each row is a week)*
y1 = average of data first half
y2 = average of data second half
x1 = 1/4 of number of weeks
x2 = 3/4 of number of weeks
m = (y1-y2)/(x1-x2)
b = y2 - (m * x2)
trend = (m * row_number) + b
And here is my (very dirty) SQL code:
SELECT resolved_half1,resolved_half2,opened_half1,opened_half2, c.period,
((SUM (resolved_half1) OVER () + SUM(opened_half1) OVER ()) - (SUM(resolved_half2) OVER () + SUM(opened_half2) OVER ())) / ((COUNT(resolved_half1) OVER () + COUNT(opened_half1) OVER ()) / 2) as y1,
((SUM(resolved_half2) OVER () + SUM(opened_half2) OVER ()) / (COUNT(resolved_half2) OVER () + COUNT (opened_half2) OVER ())) as y2,
((COUNT(c.period) OVER ()) / 4) as x1,
(((COUNT(c.period) OVER ()) / 4) * 3) as x2,
((CAST(((SUM (resolved_half1) OVER () + SUM(opened_half1) OVER ()) - (SUM(resolved_half2) OVER () + SUM(opened_half2) OVER ())) / ((COUNT(resolved_half1) OVER () + COUNT(opened_half1) OVER ()) / 2) as float) - CAST(((SUM(resolved_half2) OVER () + SUM(opened_half2) OVER ()) / (COUNT(resolved_half2) OVER () + COUNT (opened_half2) OVER ())) as float)) / (CAST(((COUNT(c.period) OVER ()) / 4) as float) - CAST( (((COUNT(c.period) OVER ()) / 4) * 3) as float))) as m,
(CAST(((SUM(resolved_half2) OVER () + SUM(opened_half2) OVER ()) / (COUNT(resolved_half2) OVER () + COUNT (opened_half2) OVER ())) as float) - (((CAST(((SUM (resolved_half1) OVER () + SUM(opened_half1) OVER ()) - (SUM(resolved_half2) OVER () + SUM(opened_half2) OVER ())) / ((COUNT(resolved_half1) OVER () + COUNT(opened_half1) OVER ()) / 2) as float) - CAST(((SUM(resolved_half2) OVER () + SUM(opened_half2) OVER ()) / (COUNT(resolved_half2) OVER () + COUNT (opened_half2) OVER ())) as float)) / (CAST(((COUNT(c.period) OVER ()) / 4) as float) - CAST( (((COUNT(c.period) OVER ()) / 4) * 3) as float))) * (((COUNT(c.period) OVER ()) / 4) * 3))) as b,
((((CAST(((SUM (resolved_half1) OVER () + SUM(opened_half1) OVER ()) - (SUM(resolved_half2) OVER () + SUM(opened_half2) OVER ())) / ((COUNT(resolved_half1) OVER () + COUNT(opened_half1) OVER ()) / 2) as float) - CAST(((SUM(resolved_half2) OVER () + SUM(opened_half2) OVER ()) / (COUNT(resolved_half2) OVER () + COUNT (opened_half2) OVER ())) as float)) / (CAST(((COUNT(c.period) OVER ()) / 4) as float) - CAST( (((COUNT(c.period) OVER ()) / 4) * 3) as float))) * (ROW_NUMBER() OVER(ORDER BY c.yearClosed,c.weekClosed))) + (CAST(((SUM(resolved_half2) OVER () + SUM(opened_half2) OVER ()) / (COUNT(resolved_half2) OVER () + COUNT (opened_half2) OVER ())) as float) - (((CAST(((SUM (resolved_half1) OVER () + SUM(opened_half1) OVER ()) - (SUM(resolved_half2) OVER () + SUM(opened_half2) OVER ())) / ((COUNT(resolved_half1) OVER () + COUNT(opened_half1) OVER ()) / 2) as float) - CAST(((SUM(resolved_half2) OVER () + SUM(opened_half2) OVER ()) / (COUNT(resolved_half2) OVER () + COUNT (opened_half2) OVER ())) as float)) / (CAST(((COUNT(c.period) OVER ()) / 4) as float) - CAST( (((COUNT(c.period) OVER ()) / 4) * 3) as float))) * (((COUNT(c.period) OVER ()) / 4) * 3)))) as trend,
ROW_NUMBER() OVER(ORDER BY c.yearClosed,c.weekClosed) as row
FROM
(SELECT *, CAST(yearClosed as varchar(5)) + ', ' + CAST(weekClosed as varchar(5)) AS period
FROM (SELECT TOP (100) PERCENT COUNT(DISTINCT TicketNbr) AS resolved_half1, { fn WEEK(date_closed) } AS weekClosed, { fn YEAR(date_closed) } AS yearClosed
FROM v_rpt_Service
WHERE (date_closed >= DateAdd(Day, DateDiff(Day, 0, GetDate()) - (180), 0))
GROUP BY { fn WEEK(date_closed) }, { fn YEAR(date_closed) }) AS a
LEFT OUTER JOIN
(SELECT TOP (100) PERCENT COUNT(DISTINCT TicketNbr) AS opened_half1, { fn WEEK(date_entered) } AS weekEntered, { fn YEAR(date_entered)
FROM v_rpt_Service AS v_rpt_Service_1
WHERE (date_entered > = DateAdd(Day, DateDiff(Day, 0, GetDate()) - (180), 0))
GROUP BY { fn WEEK(date_entered) }, { fn YEAR(date_entered) }) AS b ON a.weekClosed = b.weekEntered AND a.yearClosed = b.yearEntered) as c
LEFT OUTER JOIN
(SELECT *, CAST(yearClosed as varchar(5)) + ', ' + CAST(weekClosed as varchar(5)) AS period
FROM (SELECT TOP (100) PERCENT COUNT(DISTINCT TicketNbr) AS resolved_half2, { fn WEEK(date_closed) } AS weekClosed, { fn YEAR(date_closed) } AS yearClosed
FROM v_rpt_Service
WHERE (date_closed >= DateAdd(Day, DateDiff(Day, 0, GetDate()) - (180 / 2), 0))
GROUP BY { fn WEEK(date_closed) }, { fn YEAR(date_closed) }) AS d
LEFT OUTER JOIN
(SELECT TOP (100) PERCENT COUNT(DISTINCT TicketNbr) AS opened_half2, { fn WEEK(date_entered) } AS weekEntered, { fn YEAR(date_entered)} AS yearEntered
FROM v_rpt_Service AS v_rpt_Service_1
WHERE (date_entered > = DateAdd(Day, DateDiff(Day, 0, GetDate()) - (180 / 2), 0))
GROUP BY { fn WEEK(date_entered) }, { fn YEAR(date_entered) }) AS e ON d.weekClosed = e.weekEntered AND d.yearClosed = e.yearEntered
) as f ON c.yearClosed = f.yearClosed AND c.weekClosed = f.weekClosed AND c.weekEntered = f.weekEntered AND c.yearEntered = f.yearEntered AND c.period = f.period
GROUP BY c.period, resolved_half1,resolved_half2,opened_half1,opened_half2,c.yearClosed,c.weekClosed
ORDER BY row
This code uses a hard coded value of 180 days. I still need to be able to use a varibale to select the number of days (without getting a divide by 0 error), and the code really needs to be cleaned up. If someone can do those two things for me (I'm not the best at SQL), the bounty is theirs.
Image:
Problem
The code below returns the number of resolved tickets and the number of opened tickets for a period (period is YYYY,WW) going back a certain number of days. For example if @NoOfDays is 7: resolved | opened | week | year | period 56 | 30 | 13 | 2012 | 2012, 13 237 | 222 | 14 | 2012 | 2012, 14 'resolved' and 'opened' are graphed on lines (y) over period (x). I would like to add another column 'trend' that would return a number that when graphed over period, will be a trend line (simple linear regression). I do want to use both sets of values as one data source for the trend. This is the code I have: ``` SELECT a.resolved, b.opened, a.weekClosed AS week, a.yearClosed AS year, CAST(a.yearClosed as varchar(5)) + ', ' + CAST(a.weekClosed as varchar(5)) AS period FROM (SELECT TOP (100) PERCENT COUNT(DISTINCT TicketNbr) AS resolved, { fn WEEK(date_closed) } AS weekClosed, { fn YEAR(date_closed) } AS yearClosed FROM v_rpt_Service WHERE (date_closed >= DateAdd(Day, DateDiff(Day, 0, GetDate()) - @NoOfDays, 0)) GROUP BY { fn WEEK(date_closed) }, { fn YEAR(date_closed) }) AS a LEFT OUTER JOIN (SELECT TOP (100) PERCENT COUNT(DISTINCT TicketNbr) AS opened, { fn WEEK(date_entered) } AS weekEntered, { fn YEAR(date_entered) } AS yearEntered FROM v_rpt_Service AS v_rpt_Service_1 WHERE (date_entered > = DateAdd(Day, DateDiff(Day, 0, GetDate()) - @NoOfDays, 0)) GROUP BY { fn WEEK(date_entered) }, { fn YEAR(date_entered) }) AS b ON a.weekClosed = b.weekEntered AND a.yearClosed = b.yearEntered ORDER BY year, week ``` Edit: According to serc.carleton.edu/files/mathyouneed/best_fit_line_dividing.pdf, it seems that I want to break the data in half, then calculate the average. Then I need to find the best fit line, and use the slope and the y-intercept to calculate the values needed to return in 'trend' using `y = mx + b`? I know this is very possible in SQL, however, the program I am inserting the SQL into has limitations on what I can do. The red and blue dots are the numbers I am returning now(opened and resolved). I need to return a value for every period in 'trend' in order to create the purple line. (this image is hypothetical)