views:

68

answers:

2

In the following:
(running a query on the Stack Exchange Data Explorer, which uses an SQL Azure implementation of OData. The FAQ says it supports most of the TSQL commands):

DECLARE @MinPosts int = ##MinNumberOfPosts##
SELECT
    Id AS [User Link],
    Reputation,
    (SELECT COUNT(*)
    FROM posts
    WHERE posts.OwnerUserId = Users.Id
    ) AS [# Posts],
    Reputation /
    (SELECT COUNT(*)
    FROM posts
    WHERE posts.OwnerUserId = Users.Id
    ) AS [Rep Per Post]
FROM Users
WHERE (SELECT COUNT(*)
    FROM posts
    WHERE posts.OwnerUserId = Users.Id
    ) > @MinPosts
ORDER BY [Rep Per Post] DESC

I write out:

(SELECT COUNT(*)
FROM posts
WHERE posts.OwnerUserId = Users.Id)

three times. How can I create a variable or a function for the above snippet of code?

I tried using the column alias [# Posts], but that didn't work.

I found these posts on reusing a calculated filed name for further calculation, which seems like what I want, but I can't figure out how to apply it.

+1  A: 

You can use CROSS APPLY,

DECLARE @MinPosts int = ##MinNumberOfPosts##

SELECT
    Id AS [User Link],
    Reputation,
    counts.[# Posts],
    Reputation / counts.[# Posts] AS [Rep Per Post]
FROM Users
  CROSS APPLY (
    SELECT COUNT(*)
    FROM posts
    WHERE posts.OwnerUserId = Users.Id
  ) counts([# Posts])
WHERE counts.[# Posts] > @MinPosts
ORDER BY [Rep Per Post] DESC​
jasonpenny
Thanks. This is exactly what I was looking
Peter Ajtai
I don't think CROSS APPLY is called for here.
Emtucifor
+3  A: 

Since you need the count for every user in the whole database and thus every post, just do it the straightforward way:

SELECT
    Id AS [User Link],
    Reputation,
    Reputation * 1.0 / C.Cnt AS [Rep Per Post]
FROM
   Users U
   INNER JOIN (
      SELECT OwnerUserId, Cnt = Count(*)
      FROM posts
      GROUP BY OwnerUserID
      HAVING Count(*) >= ##MinNumberOfPosts##
   ) C ON U.Id = C.OwnerUserId
ORDER BY [Rep Per Post] DESC

You may find this gives the same execution plan as the CROSS APPLY solution, but that would only be because the engine is clever enough to avoid the correlated subquery and switch it to a simple aggregate as this query explicitly denotes. If CROSS APPLY really performs better I'm quite interested to know why.

P.S. I added * 1.0 because I'm guessing (not sure of the engine here) that integer division yields integers and it seems like you'd probably want fractions. You'll have to experiment.

Emtucifor
Thanks. I did have it showing integers, just to have a cleaner look. Does `Reputation*1.0` do the same as `CAST(Reputation AS float)`? It is quicker to write.
Peter Ajtai
I think 1.0 is the same as casting to decimal. There's a possibility it's very slightly slower than an explicit cast, but I doubt it would have any performance implication. Decimal vs. float wouldn't be a concern for these purposes.
Emtucifor