6

Some years ago, I wrote a generic query to fetch posts in a given month.

WITH allData AS (
    SELECT
                ROW_NUMBER() OVER (ORDER BY P.Id) AS row
                , P.Id
                , P.PostTypeId
                , P.ParentId
                , P.AcceptedAnswerId
                , P.CreationDate
                , P.OwnerUserId
                , P.Tags
                , P.ClosedDate
    FROM        Posts    AS P
    WHERE       YEAR(P.CreationDate) = (##Year:INT?1##) AND MONTH(P.CreationDate) = (##Month:INT?1##)
)
SELECT      *
FROM        allData
WHERE       row    >= 1 + 50000*(##StartPage:INT?1## - 1)
ORDER BY    row

I have used this query numerous times over the years without issue.

Yesterday and today, however, the query gives me the error

Line 0: Execution Timeout Expired. The timeout period elapsed prior to completion of the operation or the server is not responding.

Note
The problem seems to lie with the first WHERE clause. (WHERE YEAR(P.CreationDate) = (##Year:INT?1##) AND MONTH(P.CreationDate) = (##Month:INT?1##)). Without this, the query runs fine.

2
  • Did you try smaller chunk sizes than 50,000 rows? What are you going to do with 50,000 rows of output in a web UI? Seems like a job for the API, not a web page.
    – user1502910
    Commented Aug 23 at 15:03
  • 2
    I found a solution. Since the post IDs increment by 1, I just filter the data WHERE P.Id >= X and P.Id < Y. I have to mess around to find the right values for X and Y, but it works. (I'm dumping the data into R where I'm studying trending tags.)
    – Ben
    Commented Aug 23 at 15:30

1 Answer 1

5

That query will do a full table scan on the view Posts (which is backed by the table PostsWithDeleted) because that table is lacking an index on the creationdate attribute, assuming you rewrite that query to make actual use of a potential index. That the query ran till now is just pure luck and beefy CPU and IO juice to spare.

I've gone ahead and made a few changes (slightly altering what you query) to at least make that query run. These changes were made:

  • Instead of using Year and Month on the left hand side of the expression, build the start date of the month and calculate the end of the month so we can do a range check
  • Split the CTE in Questions and Answers because PostTypeId has an index so a lower number of rows has to be to scanned, we hope (didn't happen)
  • Use OFFSET and FETCH for paging purposes

This is what the query looks like:

declare @month datetime = '##Year##-##Month##-01'
declare @eomonth datetime = dateadd(month,1,@month)
declare @offset int = (50000*(##StartPage:INT?1## - 1)) 

;WITH allQuestions AS (
    SELECT
                --  ROW_NUMBER() OVER (ORDER BY P.Id) AS row
                -- , 
                  P.Id
                , P.PostTypeId
                , P.ParentId
                , P.AcceptedAnswerId
                , P.CreationDate
                , P.OwnerUserId
                , P.Tags
                , P.ClosedDate
    FROM        Posts    AS P
    WHERE       PostTypeId = 1
    and         P.CreationDate >= @month 
    and         P.CreationDate < @eomonth
),
allAnswers AS (
    SELECT
                --  ROW_NUMBER() OVER (ORDER BY P.Id) AS row
                -- , 
                  P.Id
                , P.PostTypeId
                , P.ParentId
                , P.AcceptedAnswerId
                , P.CreationDate
                , P.OwnerUserId
                , P.Tags
                , P.ClosedDate
    FROM        Posts    AS P
    WHERE       PostTypeId = 2
    and         P.CreationDate >= @month 
    and         P.CreationDate < @eomonth
),
allData as (
  select *
  from allQuestions
  union
  select *
  from allAnswers
)


SELECT      ROW_NUMBER() OVER (ORDER BY Id ASC) [row]
          , *
FROM        allData
ORDER BY    Id ASC  OFFSET @offset ROWS 
FETCH NEXT 50000 ROWS ONLY;

Today that query runs on Stack Overflow for 68376 ms which is just 50% over the max allowed 120,000 ms. That explains the Timeout error you got.

Keep in mind SEDE is updated once a week on Sunday.
Use the very useful SEDE Tutorial written by the awesome Monica Cellio.
Say "Hi" in SEDE chat.

2
  • Doesn’t CreationDate have time? In which case EOMONTH will miss data from after midnight on the last day of the month. Should be < DATEADD(day,1,eomonth(@month)) or simply < dateadd(month,1,@month)
    – user1502910
    Commented Aug 24 at 12:28
  • @testing-for-ya yeah, that seems to be right.
    – rene Mod
    Commented Aug 24 at 14:15

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .