SQLProvider icon indicating copy to clipboard operation
SQLProvider copied to clipboard

Count doesn't work on sorted queries (MSSQL)

Open ScRyX opened this issue 7 years ago • 3 comments
trafficstars

Description

Trying to get a count from a query that has already been sorted throws an exception.

Repro steps

Let's have a sorted query:

let q = query {
    for x in ctx.myEntity do
    sortBy x.sortField
    select x
}

Now let's try to get the length of the result set:

let n = query {
    for _ in q do
    count
}

this throws an SqlException with the message:

Column "dbo.myEntity.sortField" is invalid in the ORDER BY clause because it is not contained in either an aggregate function or the GROUP BY clause.

this is because the resulting SQL query is: SELECT COUNT(1) FROM [dbo].[MyEntity] as [x] ORDER BY [x].[sortField] DESC

this has nothing to do with us using an anonymous variable in the count query above, because the same SQL is generated for the following:

let n = query {
    for x in q do
    select x
    count
}

Expected behavior

Count works also for queries containing an orderBy clause

Actual behavior

SqlException is thrown:

Column "dbo.myEntity.sortField" is invalid in the ORDER BY clause because it is not contained in either an aggregate function or the GROUP BY clause.

Known workarounds

N/A

Related information

  • Used database: MSSQL
  • Operating system: Windows
  • .NET Runtime, CoreCLR or Mono Version: .NET Core 2

ScRyX avatar Jan 22 '18 08:01 ScRyX

What you are trying to do here? I think this is a "problem" (or a feature) of Microsoft SQL Server. So that you wouldn't execute the query multiple times by accident.

When you have executed the query, you can get the count/length from the normal data structure without a query syntax:

let myQuery = query {
    for x in ctx.myEntity do
    sortBy x.sortField
    select x
} |> Seq.toArray

let executed =  myQuery  |> Seq.toArray

let mycount = executed |> Array.length

Thorium avatar Jan 22 '18 15:01 Thorium

I don't want to evaluate the query, I want the DB to do the counting. I want to obtain the count for paging purposes, and paging needs a sorted query, so the paging function receives a sorted query, which crashes the first step of paging - getting the total length of the data.

Wouldn't it be possible to remove the orderBy clause from the query when generating the query for count? Sorting won't change the number of results....

ScRyX avatar Jan 23 '18 08:01 ScRyX

So something like this?

let getItems pageIdx =
  let total = query {
    for x in q do
    select x
    count
  }

  if totalcount = 0 then Array.empty, 0
  else

  let pagesize = 10
  let pgitms = pagesize*pageIdx

  let page = query {
    for x in q do
    sortByDescending (x.Created)
    skip (pgitms)
    take (pagesize)
    select x
  } |> Seq.toArray
  page, totalcount

I think the reason SQL Server not supporting this as one query is that sorting the query will significantly slower the query execution. And you have to do the separate query for skip and take anyways, right?

Thorium avatar Jan 23 '18 13:01 Thorium