Cannot perform an aggregate function on a subquery - tsql

Can someone help me with this query?
SELECT p.OwnerName, SUM(ru.MonthlyRent) AS PotentinalRent, SUM(
(SELECT COUNT(t.ID) * ru.MonthlyRent FROM tblTenant t
WHERE t.UnitID = ru.ID)
) AS ExpectedRent
FROM tblRentalUnit ru
LEFT JOIN tblProperty p ON p.ID = ru.PropertyID
GROUP BY p.OwnerName
I'm having problems with the second sum, it won't let me do it. Evidently SUM won't work on subqueries, but I need to calculate the expected rent (MonthlyRent if there is a tenant assigned to the RentalUnit's id, 0 of they're not). How can I make this work?

SELECT p.OwnerName, SUM(ru.MonthlyRent) AS PotentialRent, SUM(cnt) AS ExpectedRent
FROM tblRentalUnit ru
LEFT JOIN
tblProperty p
ON p.ID = ru.PropertyID
OUTER APPLY
(
SELECT COUNT(t.id) * ru.MonthlyRent AS cnt
FROM tblTenant t
WHERE t.UnitID = ru.ID
) td
GROUP BY p.OwnerName
Here's a test script to check:
WITH tblRentalUnit AS
(
SELECT 1 AS id, 100 AS MonthlyRent, 1 AS PropertyID
UNION ALL
SELECT 2 AS id, 300 AS MonthlyRent, 2 AS PropertyID
),
tblProperty AS
(
SELECT 1 AS id, 'Owner 1' AS OwnerName
UNION ALL
SELECT 2 AS id, 'Owner 2' AS OwnerName
),
tblTenant AS
(
SELECT 1 AS id, 1 AS UnitID
UNION ALL
SELECT 2 AS id, 1 AS UnitID
)
SELECT p.OwnerName, SUM(ru.MonthlyRent) AS PotentialRent, SUM(cnt) AS ExpectedRent
FROM tblRentalUnit ru
LEFT JOIN
tblProperty p
ON p.ID = ru.PropertyID
OUTER APPLY
(
SELECT COUNT(t.id) * ru.MonthlyRent AS cnt
FROM tblTenant t
WHERE t.UnitID = ru.ID
) td
GROUP BY p.OwnerName

What is the meaning of the sum of the unitMonthlyRent times the number of tenants, for some partiicular rental unit (COUNT(t.ID) * ru.MonthlyRent )?
Is it the case that all you are trying to do is see the difference between the total potential rent from all untis versus the expected rent (From only occcupied units) ? If so, then try this
Select p.OwnerName,
Sum(r.MonthlyRent) AS PotentinalRent,
Sum(Case t.Id When Null Then 0
Else r.MonthlyRent End) ExpectedRent
From tblRentalUnit r
Left Join tblTenant t
On t.UnitID = r.ID
left Join tblProperty p
On p.ID = r.PropertyID)
Group By p.OwnerName

Related

Select specific lines in data according to last update [duplicate]

Name Value AnotherColumn
-----------
Pump 1 8000.0 Something1
Pump 1 10000.0 Something2
Pump 1 10000.0 Something3
Pump 2 3043 Something4
Pump 2 4594 Something5
Pump 2 6165 Something6
My table looks something like this. I would like to know how to select max value for each pump.
select a.name, value from out_pumptable as a,
(select name, max(value) as value from out_pumptable where group by posnumber)g where and g.value = value
this code does the job, but i get two entries of Pump 1 since it has two entries with same value.
select name, max(value)
from out_pumptable
group by name
select name, value
from( select name, value, ROW_NUMBER() OVER(PARTITION BY name ORDER BY value desc) as rn
from out_pumptable ) as a
where rn = 1
SELECT
b.name,
MAX(b.value) as MaxValue,
MAX(b.Anothercolumn) as AnotherColumn
FROM out_pumptabl
INNER JOIN (SELECT
name,
MAX(value) as MaxValue
FROM out_pumptabl
GROUP BY Name) a ON
a.name = b.name AND a.maxValue = b.value
GROUP BY b.Name
Note this would be far easier if you had a primary key. Here is an Example
SELECT * FROM out_pumptabl c
WHERE PK in
(SELECT
MAX(PK) as MaxPK
FROM out_pumptabl b
INNER JOIN (SELECT
name,
MAX(value) as MaxValue
FROM out_pumptabl
GROUP BY Name) a ON
a.name = b.name AND a.maxValue = b.value)
select Name, Value, AnotherColumn
from out_pumptable
where Value =
(
select Max(Value)
from out_pumptable as f where f.Name=out_pumptable.Name
)
group by Name, Value, AnotherColumn
Try like this, It works.
select * from (select * from table order by value desc limit 999999999) v group by v.name
Using analytic function is the easy way to find max value of every group.
Documentation : https://learn.microsoft.com/en-us/sql/t-sql/functions/row-number-transact-sql?view=sql-server-ver15
Select name,
value,
AnotherColumn
From(
SELECT Row_Number() over(partition by name order by value desc)as
row_number, *
FROM students
)
Where row_number = 1
SELECT t1.name, t1.Value, t1.AnotherColumn
FROM mytable t1
JOIN (SELECT name AS nameMax, MAX(Value) as valueMax
FROM mytable
GROUP BY name) AS t2
ON t2.nameMax = t1.name AND t2.valueMax = t1.Value
WHERE 1 OR <anything you would like>
GROUP BY t1.name;
SELECT DISTINCT (t1.ProdId), t1.Quantity FROM Dummy t1 INNER JOIN
(SELECT ProdId, MAX(Quantity) as MaxQuantity FROM Dummy GROUP BY ProdId) t2
ON t1.ProdId = t2.ProdId
AND t1.Quantity = t2.MaxQuantity
ORDER BY t1.ProdId
this will give you the idea.

SUM(CASE WHEN ...) returns a greater number than COUNT(DISTINCT..)

I have written a query in two models, but I can't figure out why the second query returns a greater number than the first one; while the number that the first one, COUNT(DISTINCT...) returns is correct:
WITH types(id) AS (VALUES('{1, 4, 5, 3}'::INTEGER[])),
date_gen64 AS
(
SELECT CAST (generate_series(date '10/1/2017', date '11/15/2017', interval
'1 day') AS date) as days ORDER BY days)
SELECT cl.class_date AS c_date,
count(DISTINCT (CASE WHEN co.id = 1 THEN p.id END)),
count(DISTINCT (CASE WHEN co.id = 2 THEN p.id END))
FROM person p
JOIN envelope e ON e.personID = p.id
JOIN "class" cl on cl.id = p.classID
JOIN course co ON co.id = cl.course_id AND co.id = 1
JOIN types ON cr.type_id = ANY (types.id)
RIGHT JOIN date_gen64 dg ON dg.days = cl.class_date
GROUP BY cl.class_date
ORDER BY cl.class_date
The above query returns 26 but following query returns 27!
The reason why I rewrote it with SUM is that the first query
was too slow. But my question is that why the second one counts more?
WITH types(id) AS (VALUES('{1, 4, 5, 3}'::INTEGER[]))
SELECT tmpcl.days,
SUM(CASE WHEN tmp80.course_id = 1 THEN 1
ELSE 0 END),
SUM(CASE WHEN tmp80.course_id = 2 THEN 1
ELSE 0 END)
FROM (
SELECT CAST (generate_series(date '10/1/2017', date '11/15/2017',
interval '1 day') AS date) as days ORDER BY days) tmpcl
LEFT JOIN (
SELECT DISTINCT p.id AS "person_id",
cl.class_date AS c_date,
co.id AS "course_id"
FROM person p
JOIN envelope e ON e.personID = p.id
JOIN "class" cl on cl.id = p.classID
JOIN course co ON co.id = cl.course_id
JOIN types ON cr.type_id = ANY (types.id)
WHERE co.id IN ( 1 , 2 )
) tmp80 ON tmpcl.days = tmp80.class_date
GROUP BY tmpcl.days
ORDER BY tmpcl.days
You can theoretically have multiple people enrolled in the same class on the same day. Indeed that would seem to be the main point of having classes. So each time there are multiple people assigned to the same class on the same day you can have a higher count than you would in your first query. Does that make sense?
You don't appear to be using p.id in that inner query so simply remove it and your counts should match.
WITH types(id) AS (VALUES('{1, 4, 5, 3}'::INTEGER[]))
SELECT tmpcl.days,
SUM(CASE WHEN tmp80.course_id = 1 THEN 1
ELSE 0 END),
SUM(CASE WHEN tmp80.course_id = 2 THEN 1
ELSE 0 END)
FROM (
SELECT CAST (generate_series(date '10/1/2017', date '11/15/2017',
interval '1 day') AS date) as days ORDER BY days) tmpcl
LEFT JOIN (
SELECT DISTINCT cl.class_date AS c_date,
co.id AS "course_id"
FROM person p
JOIN envelope e ON e.personID = p.id
JOIN "class" cl on cl.id = p.classID
JOIN course co ON co.id = cl.course_id
JOIN types ON cr.type_id = ANY (types.id)
WHERE co.id IN ( 1 , 2 )
) tmp80 ON tmpcl.days = tmp80.class_date
GROUP BY tmpcl.days
ORDER BY tmpcl.days

can I rank results got from different WHERE clause?

Say I want to select the posts that has certain tags or matches the keyword.
select t1.*
from (
select p.*, count(p.id) from plainto_tsquery('hElLo') AS q , post p
left join post_tag pt on pt.post_id = p.id
left join tag t on t.id = pt.tag_id
WHERE (tsv ## q) or t.id in (2,3)
group by p.id
) as t1
order by count desc, ts_rank_cd(t1.tsv, plainto_tsquery('hElLo')) desc
limit 5;
the above does select what I want. In tsv, I gave title A weight and description D weight. it now becomes pretty pointless when sorting by count because each entry has the same weight. Is it possible to do things like if this row is picked from t.id in (2,3), they get to sorted to the first, then sort by ts_rank_cd, or give each match tag 'A' weight, title become B weight and description is D?
Try CASE WHEN
select t1.*
from (
select p.*, count(p.id),
(CASE WHEN t.id in (2,3) THEN 1 ELSE 2 END) as ranking
from plainto_tsquery('hElLo') AS q , post p
left join post_tag pt on pt.post_id = p.id
left join tag t on t.id = pt.tag_id
WHERE (tsv ## q) or t.id in (2,3)
group by p.id
) as t1
order by count desc, ranking asc, ts_rank_cd(t1.tsv, plainto_tsquery('hElLo')) desc
limit 5;
Edited(Correct Answer):
select t1.*
from (
select p.*, count(p.id),
COUNT(1) filter(where t.id in (2,3)) ranking
from plainto_tsquery('hElLo') AS q , post p
left join post_tag pt on pt.post_id = p.id
left join tag t on t.id = pt.tag_id
WHERE (tsv ## q) or t.id in (2,3)
group by p.id
) as t1
order by count desc, ranking asc, ts_rank_cd(t1.tsv, plainto_tsquery('hElLo')) desc
limit 5;
I'm not sure why the count would be the same, but you can add more keys to the order by:
order by count desc,
(t.id in (2, 3)) desc,
ts_rank_cd(t1.tsv, plainto_tsquery('hElLo')) desc ;
The desc is because true > false, and you want the true values to be first.

Avoiding Order By in T-SQL

Below sample query is a part of my main query. I found SORT operator in below query is consuming 30% of the cost.
To avoid SORT, there is need of creation of Indexes. Is there any other way to optimize this code.
SELECT TOP 1 CONVERT( DATE, T_Date) AS T_Date
FROM TableA
WHERE ID = r.ID
AND Status = 3
AND TableA_ID >ISNULL((
SELECT TOP 1 TableA_ID
FROM TableA
WHERE ID = r.ID
AND Status <> 3
ORDER BY T_Date DESC
), 0)
ORDER BY T_Date ASC
Looks like you can use not exists rather than the sorts. I think you'll probably get a better performance boost by use a CTE or derived table instead of the a scalar subquery.
select *
from r ... left outer join
(
select ID, min(t_date) as min_date from TableA t1
where status = 3 and not exists (
select 1 from TableA t2
where t2.ID = t1.ID
and t2.status <> 3 and t2.t_date > t1.t_date
)
group by ID
) as md on md.ID = r.ID ...
or
select *
from r ... left outer join
(
select t1.ID, min(t1.t_date) as min_date
from TableA t1 left outer join TableA t2
on t2.ID = t1.ID and t2.status <> 3
where t1.status = 3 and t1.t_date < t2.t_date
group by t1.ID
having count(t2.ID) = 0
) as md on md.ID = r.ID ...
It also appears that you're relying on an identity column but it's not clear what those values mean. I'm basically ignoring it and using the date column instead.
Try this:
SELECT TOP 1 CONVERT( DATE, T_Date) AS T_Date
FROM TableA a1
LEFT JOIN (
SELECT ID, MAX(TableA_ID) AS MaxAID
FROM TableA
WHERE Status <> 3
GROUP BY ID
) a2 ON a2.ID = a1.ID AND a1.TableA_ID > coalesce(a2.MAXAID,0)
WHERE a1.ID = r.ID AND a1.Status = 3
ORDER BY T_Date ASC
The use of TOP 1 in combination with the unexplained r alias concern me. There's almost certainly a MUCH better way to get this data into your results that doesn't involve doing this in a sub query (unless this is for an APPLY operation).

How to PIVOT this query and display only TOP 10 records filtered by SUM(NetWrittenPremium) DESC

In this query I cant understand what would be the proper syntax to PIVOT it by month and also display just top 10 records based on SUM(NetWrittenPremium).
;with cte_TopClasses
AS (
select
b.YearNum,
b.MonthNum,
REPLACE(ClassCode,'+','') + ' - '+ QLL.Description as Description,
SUM( Premium) as NetWrittenPremium
FROM tblCalendar b
LEFT JOIN ProductionReportMetrics prm ON b.MonthNum=Month(prm.EffectiveDate) AND b.YearNum = YEAR(EffectiveDate)
AND prm.EffectiveDate >=DateAdd(yy, -1, DATEADD(d, 1, EOMONTH(GETDATE()))) AND prm.EffectiveDate <= EOMONTH(GETDATE()) AND CompanyLine = 'Ironshore Insurance Company'
LEFT JOIN NetRate_Quote_Insur_Quote Q ON prm.NetRate_QuoteID = Q.QuoteID
LEFT JOIN NetRate_Quote_Insur_Quote_Locat QL ON Q.QuoteID = QL.QuoteID
LEFT JOIN (SELECT * FROM NetRate_Quote_Insur_Quote_Locat_Liabi nqI
JOIN ( SELECT LocationID as LocID, MAX(ClassCode) as ClCode
FROM NetRate_Quote_Insur_Quote_Locat_Liabi GROUP BY LocationID ) nqA
ON nqA.LocID = nqI.LocationID AND nqA.ClCode = nqI.ClassCode ) QLL
ON QLL.LocationID = QL.LocationID
WHERE ( b.YearNum = YEAR(GETDATE())-1 and b.MonthNum >= MONTH(GETDATE())+1 ) OR
( b.YearNum = YEAR(GETDATE()) and b.MonthNum <= MONTH(GETDATE()) )
GROUP BY b.YearNum,b.MonthNum,ClassCode, QLL.Description
)
SELECT
--TOP 10
RANK() OVER (ORDER BY NetWrittenPremium DESC) AS Rank, *
FROM cte_TopClasses
WHERE Description IS NOT NULL
ORDER BY NetWrittenPremium DESC,YearNum,MonthNum
The result should look something like that:
If I use the query below and then using matrics in SSRS to PIVOT it - then after grouping by Description it only displays me 2 Description.
;with cte_TopClasses
AS (
select
b.YearNum,
b.MonthNum,
REPLACE(ClassCode,'+','') + ' - '+ QLL.Description as Description,
SUM( Premium) as NetWrittenPremium
FROM tblCalendar b
LEFT JOIN ProductionReportMetrics prm ON b.MonthNum=Month(prm.EffectiveDate) AND b.YearNum = YEAR(EffectiveDate)
AND prm.EffectiveDate >=DateAdd(yy, -1, DATEADD(d, 1, EOMONTH(GETDATE()))) AND prm.EffectiveDate <= EOMONTH(GETDATE()) AND CompanyLine = 'Ironshore Insurance Company'
LEFT JOIN NetRate_Quote_Insur_Quote Q ON prm.NetRate_QuoteID = Q.QuoteID
LEFT JOIN NetRate_Quote_Insur_Quote_Locat QL ON Q.QuoteID = QL.QuoteID
LEFT JOIN (SELECT * FROM NetRate_Quote_Insur_Quote_Locat_Liabi nqI
JOIN ( SELECT LocationID as LocID, MAX(ClassCode) as ClCode
FROM NetRate_Quote_Insur_Quote_Locat_Liabi GROUP BY LocationID ) nqA
ON nqA.LocID = nqI.LocationID AND nqA.ClCode = nqI.ClassCode ) QLL
ON QLL.LocationID = QL.LocationID
WHERE ( b.YearNum = YEAR(GETDATE())-1 and b.MonthNum >= MONTH(GETDATE())+1 ) OR
( b.YearNum = YEAR(GETDATE()) and b.MonthNum <= MONTH(GETDATE()) )
GROUP BY b.YearNum,b.MonthNum,ClassCode, QLL.Description
)
SELECT *
FROM (SELECT RANK() OVER (ORDER BY NetWrittenPremium DESC) AS Rank, *
FROM cte_TopClasses
WHERE Description IS NOT NULL) AA
WHERE AA.Rank <= 10
ORDER BY AA.NetWrittenPremium DESC, AA.YearNum, AA.MonthNum
And the result of it in SSRS matrics :
You could try something like this at the end of the query, rather than what is there now:
SELECT *
FROM (SELECT RANK() OVER (ORDER BY [Description] DESC) AS Rank, *
FROM cte_TopClasses
WHERE Description IN (SELECT [Description]
FROM (SELECT RANK() OVER (ORDER BY SUM(NetWrittenPremium) DESC) AS [Rank], [Description], SUM(NetWrittenPremium) AS total
FROM cte_TopClasses
WHERE [Description] IS NOT NULL
GROUP BY [Description]) BB
WHERE [Rank] <= 10)) AA
ORDER BY YearNum, MonthNum
This wraps the query in a SELECT, and filters the ranked results to the 10 you want.
Then use a matrix in the report to pivot the results.