views:

72

answers:

4

I have a stored procedure that has this select statement in it:

select 
    (select Count(FaqId) from faq_read_stats where faqid = Faq.Id) as ReadCount,
    Faq.*
from Faq 
where Faq.Show = 1 and Faq.Active = 1

This gives me the results I want but it seems like having the ad-hoc select count in there might not be optimized for SQL Server.

I tried doing an inner join on the faq_read_stats table but this did not yield correct results (it left out faq records that did not have a record in faq_read_stats whereas the sql I typed above correctly shows a count of 0 for those).

Is there a more efficient way of getting this data?

+4  A: 

You'd need to try it out to compare, but an alternative is:

SELECT ISNULL(x.ReadCount, 0) AS ReadCount, f.*
FROM Faq f
    LEFT JOIN
    (
        SELECT faqid, COUNT(faqid) AS ReadCount
        FROM faq_read_stats
        GROUP BY faqid
    ) x ON f.id = x.faqid
WHERE f.Show = 1 AND f.Active = 1
AdaTheDev
+1 Nice query. I think SQL Server is smart enough to execute the query in the question and the query in the answer in the same way
Andomar
Nice. This is far more elegant that what I did. The results *seem* similar but my test records don't have enough data to really show differences. I did notice that the execution plan seems identical.
Sailing Judo
Yeah I would hope it would result in a v similar/same execution plan. You just never know really - I've learnt to not assume anything when it comes to the query optimiser!
AdaTheDev
A: 

Would something like this work or is this what you already tried?

SELECT SUM(CASE WHEN (faq_read_stats.FaqId = Faq.Id) THEN 1 ELSE 0 END) AS ReadCount, Faq.*
FROM Faq INNER JOIN faq_read_stats ON faq_read_stats.FaqId = Faq.Id
WHERE Faq.Show = 1 and Faq.Active = 1

James

James
That would error as you've got an aggregate function but no GROUP BY clause. If you add a GROUP BY, it'd then need to group by every returned field from Faq, which wouldn't be nice
AdaTheDev
A: 

Instead of using an INNER JOIN use a LEFT OUTER JOIN:

SELECT
     COUNT(*) AS read_count,
     FAQ.col_1,
     FAQ.col_2,
     FAQ.col_3,
     ...
FROM
     FAQ
LEFT OUTER JOIN Faq_Read_Stats FRS ON
     FRS.faq_id = FAQ.faq_id
WHERE
     FAQ.show = 1 AND
     FAQ.active = 1
GROUP BY
     FAQ.col_1,
     FAQ.col_2,
     FAQ.col_3,
     ...
Tom H.
A: 

When tuning queries, I always consider the 'divide to conquer' approach.

I would bet the code below yelds the best results:

select FaqID, count(*) as ReadCount
from #cnt
group by FaqID
go

select 
  #cnt.ReadCount,
  Faq.*
from
  Faq
    left outer join #cnt
    on cnt.FaqID = Faq.FaqID
where Faq.Show = 1 and Faq.Active = 1
go

drop table #cnt
go

This approach makes it more 'human readable' and does not introduces 'false optimal' plans.