Reputation: 111
I have a select like this:
SELECT
FORMAT(AVG([DC].[ContractedAmount]) , '$###,###,###,###.##') AS [AverageContractedAmount]
FROM
[DesignCustomer] AS [DC]
INNER JOIN
[Design] AS [D] ON [DC].[DesignKey] = [D].[DesignKey]
INNER JOIN
[Task] AS [T] ON [D].[DesignKey] = [t].[DesignKey]
INNER JOIN
[ProjectDesign] AS [PD] ON [D].[DesignKey] = [PD].[DesignKey]
INNER JOIN
[Project] AS [P] ON [PD].[ProjectKey] = [P].[ProjectKey]
INNER JOIN
[Address] AS [A] ON [A].[AddressGuid] = [P].[ProjectGuid]
As you can see I get the Average
of Contracted Amount. I get something like this:
+---------------------------+
| [AverageContractedAmount] |
+---------------------------+
| $1,000.00 |
+---------------------------+
Now I want to get project who is more closest to that value
For example if I have 5 projects in project table like:
+----------------+
| ProjectName |
+----------------+
| First Project |
| Second Project |
| Third Project |
| Four Project |
| Five Project |
+----------------+
Relation of [DC]
with project is something like this:
+----------------+------------------+
| ProjectName | ContractedAmount |
+----------------+------------------+
| First Project | 500 |
| Second Project | 700 |
| Third Project | 300 |
| Four Project | 950 |
| Five Project | 800 |
+----------------+------------------+
I want the query to return the Four Project Name
because its ContractedAmount
value is the closest to the AVG
value. How can I achieve this? Regards
Upvotes: 0
Views: 156
Reputation: 190
It seems that you've already figured out getting your average, so taking your sample tables, but fudged to get an average of $1000, I wrote a single select statement using the average to get your answer. For this answer, I've just calculated the average from the table rather than your code in order to get the variable @AVERAGE.
CREATE TABLE #Amts (Project VARCHAR(20), Amount INT);
INSERT INTO #Amts
VALUES
('One Project', 500),
('Two Project', 1500),
('Three Project', 300),
('Four Project', 1700),
('Five Project', 1100),
('Six Project', 900) ;
DECLARE @AVERAGE INT = (SELECT AVG(Amount) FROM #Amts) -- $1000
SELECT TOP 1 Project -- Since you said whichever project is suitable, this should be fine.
FROM #Amts AS A
WHERE ABS(A.Amount - @AVERAGE) = (SELECT MIN(ABS(Amin.Amount - @AVERAGE)) FROM #Amts AS Amin)
ORDER BY Project
DROP TABLE #Amts
This gives you the answer of "Five Project".
Upvotes: 1
Reputation: 50200
Without putting much thought into this, you can just dump that into a subquery and subtract, sort by the difference, and keep the top result:
SELECT TOP 1
project_name,
FROM Project
ORDER BY Abs(ContractedAmount -
(
SELECT
AVG([DC].[ContractedAmount]) AS [AverageContractedAmount]
FROM [DesignCustomer] AS [DC]
INNER JOIN [Design] AS [D] ON [DC].[DesignKey] = [D].[DesignKey]
INNER JOIN [Task] AS [T] ON [D].[DesignKey] = [t].[DesignKey]
INNER JOIN [ProjectDesign] AS [PD] ON [D].[DesignKey] = [PD].[DesignKey]
INNER JOIN [Project] AS [P] ON [PD].[ProjectKey] = [P].[ProjectKey]
INNER JOIN [Address] AS [A] ON [A].[AddressGuid] = [P].[ProjectGuid]
)) ASC
Upvotes: 1
Reputation: 1169
Strictly closest would be:
; WITH AvgAmt AS (
SELECT AVG([DC].[ContractedAmount]) AS [AverageContractedAmount]
FROM [DesignCustomer] AS [DC]
INNER JOIN [Design] AS [D] ON [DC].[DesignKey] = [D].[DesignKey]
INNER JOIN [Task] AS [T] ON [D].[DesignKey] = [t].[DesignKey]
INNER JOIN [ProjectDesign] AS [PD] ON [D].[DesignKey] = [PD].[DesignKey]
INNER JOIN [Project] AS [P] ON [PD].[ProjectKey] = [P].[ProjectKey]
INNER JOIN [Address] AS [A] ON [A].[AddressGuid] = [P].[ProjectGuid]
)
SELECT TOP(1) P.ProjectName, DC.ContractedAmount
FROM [DesignCustomer] AS [DC]
INNER JOIN [Design] AS [D] ON [DC].[DesignKey] = [D].[DesignKey]
INNER JOIN [ProjectDesign] AS [PD] ON [D].[DesignKey] = [PD].[DesignKey]
INNER JOIN [Project] AS [P] ON [PD].[ProjectKey] = [P].[ProjectKey]
ORDER BY ABS(AvgAmt.[AverageContractedAmount] - ContractedAmount);
Depending on your data, you might want to exclude a few INNER JOIN
's from the CTE itself simply to improve performance if the data doesn't require it.
The solution can also be easily parametrized to select closest @n
contracted amounts instead of the closest one (converting SELECT TOP(1)
to SELECT TOP(@n)
).
As an addition, if there are multiple projects with the same difference to the average contracted amount, you might want to add some other columns into ORDER BY
to break the tie. This is something you can decide on as the one with the knowledge of what data represents and what you expect from it.
Upvotes: 0
Reputation: 7665
using DENSE_RANK:
WITH getavg AS (
SELECT AVG([DC].[ContractedAmount]) OVER() AS [AverageContractedAmount]
, p.projectname, p.contractedamount
FROM [DesignCustomer] AS [DC]
INNER JOIN [Design] AS [D] ON [DC].[DesignKey] = [D].[DesignKey]
INNER JOIN [Task] AS [T] ON [D].[DesignKey] = [t].[DesignKey]
INNER JOIN [ProjectDesign] AS [PD] ON [D].[DesignKey] = [PD].[DesignKey]
INNER JOIN [Project] AS [P] ON [PD].[ProjectKey] = [P].[ProjectKey]
INNER JOIN [Address] AS [A] ON [A].[AddressGuid] = [P].[ProjectGuid]
),
ranked as (
SELECT projectname, contractedamount
DENSE_RANK() OVER(ORDER BY ABS([AverageContractedAmount]-contractedamount)) AS dr
FROM getavg)
SELECT * FROM ranked
WHERE dr <= 5
Upvotes: 0