Efficient way of getting @@rowcount from a query using row_number
Check out the COUNT(*) aggregate when used with OVER(PARTITON BY..), like so:
SELECT
ROW_NUMBER() OVER(ORDER BY object_id, column_id) as RowNum
, COUNT(*) OVER(PARTITION BY 1) as TotalRows
, *
FROM master.sys.columns
This is IMHO the best way to do it without having to do two queries.
CTE, ROW_NUMBER and ROWCOUNT
In T-SQL it should be
;WITH Props AS
(
SELECT *,
ROW_NUMBER() OVER (ORDER BY PropertyID) AS RowNumber
FROM Property
WHERE PropertyType = @PropertyType AND ...
)
, Props2 AS
(
SELECT COUNT(*) CNT FROM Props
)
-- Now you can use even Props2.CNT
SELECT * FROM Props, Props2
WHERE RowNumber BETWEEN ((@PageNumber - 1) * @PageSize) + 1 AND (@PageNumber * @PageSize);
now you have CNT in every line... Or you wanted something different? You wanted a second resultset with only the count? Then do it!
-- This could be the second result-set of your query.
SELECT COUNT(*) CNT
FROM Property
WHERE PropertyType = @PropertyType AND ...
Note: reedited, the query 1 David was referencing now has been trashcanned, query 2 is now query 1.
Return total records from SQL Server when using ROW_NUMBER
I typically do it this way - never really checked whether it's very efficient from a performance point of view:
WITH YourCTE AS
(
SELECT
(list of columns),
ROW_NUMBER() OVER (ORDER BY ......) AS 'RowNum'
FROM dbo.YourBaseTable
)
SELECT
*,
(SELECT MAX(RowNum) FROM YourCTE) AS 'TotalRows'
FROM
YourCTE
WHERE
RowNum BETWEEN 101 AND 150
Basically, the RowNum
value will have values 1 through the total of rows (if you don't have a PARTITION BY
in your CTE) and thus selecting MAX(RowNum)
, you get the total number of rows.
Fastest way to count exact number of rows in a very large table?
Simple answer:
- Database vendor independent solution = use the standard =
COUNT(*)
- There are approximate SQL Server solutions but don't use COUNT(*) = out of scope
Notes:
COUNT(1) = COUNT(*) = COUNT(PrimaryKey) just in case
Edit:
SQL Server example (1.4 billion rows, 12 columns)
SELECT COUNT(*) FROM MyBigtable WITH (NOLOCK)
-- NOLOCK here is for me only to let me test for this answer: no more, no less
1 runs, 5:46 minutes, count = 1,401,659,700
--Note, sp_spaceused uses this DMV
SELECT
Total_Rows= SUM(st.row_count)
FROM
sys.dm_db_partition_stats st
WHERE
object_name(object_id) = 'MyBigtable' AND (index_id < 2)
2 runs, both under 1 second, count = 1,401,659,670
The second one has less rows = wrong. Would be the same or more depending on writes (deletes are done out of hours here)
sql row_number() vs select row_number() to get data
Writing a subquery without a from
clause is a waste of a subquery. It doesn't do anything desirable.
Just call the function directly.
Note: This advice applies to any expression in a subquery, not only row_number()
.
Why does your subquery return only "1"s? That reason is rather subtle to explain. But, imagine that the subquery were written as:
select *,
(select ROW_NUMBER() OVER (Partition by amount order by id) a
from dual)
This is, in fact, how the query could or would be written in several SQL databases. dual
is a table with exactly one row. That little fact emphasizes what is happening. The subquery is referring to one row at a time. Hence, the row_number()
that you are getting refers only to partitioning by that row. Voila! You only get "1".
I realize that it might help if you considered the subquery as:
select *,
(select ROW_NUMBER() OVER (Partition by amount order by id) a
from (select products.amount, products.id) p
)
This emphasizes that the row_number()
is being applied over the result set of the sub-select. That sub-select -- by definition -- has only one row, which results in the "1" being returned.
Getting total row count from OFFSET / FETCH NEXT
You can use COUNT(*) OVER()
... here is a quick example using sys.all_objects
:
DECLARE
@PageSize INT = 10,
@PageNum INT = 1;
SELECT
name, object_id,
overall_count = COUNT(*) OVER()
FROM sys.all_objects
ORDER BY name
OFFSET (@PageNum-1)*@PageSize ROWS
FETCH NEXT @PageSize ROWS ONLY;
However, this should be reserved for small data sets; on larger sets, the performance can be abysmal. See this Paul White article for better alternatives, including maintaining indexed views (which only works if the result is unfiltered or you know WHERE
clauses in advance) and using ROW_NUMBER()
tricks.
How to return total number of records with TOP * select
There are a lot of simple answers to this question, as other posters have pointed out. There is also a lot of subtlety depending on your scenario. There is a fairly in depth discussion of the issue @ Efficient way of getting @@rowcount from a query using row_number
How do I get rowcount of a cte in a separate dataset?
Without using a temp table first, I'd use a CROSS JOIN to reduce the risk of row by row evaluation on the COUNT
To get total row, this needs to happen separately to the WHERE
WITH cteCustomers
AS ( SELECT
id,
Row_Number( ) OVER(ORDER BY Age DESC) AS Row
FROM @Customer
WHERE employed = 1
/*Imagine I've joined to loads more tables with a really complex where clause*/
)
SELECT
name,
age,
Total
FROM cteCustomers
INNER JOIN @Customer cust
/*This is where I choose the columns I want to read, it returns really fast!*/
ON cust.id = cteCustomers.id
CROSS JOIN
(SELECT Count( *) AS Total FROM cteCustomers ) foo
WHERE row BETWEEN ( @PageSize * @PageNumber - 1 ) AND ( @PageSize * ( @PageNumber ) )
ORDER BY row ASC
However, this isn't guaranteed to give accurate results as demonstrated here:
can I get count() and rows from one sql query in sql server?
Edit: after a few comments.
How to avoid a CROSS JOIN
WITH cteCustomers
AS ( SELECT
id,
Row_Number( ) OVER(ORDER BY Age DESC) AS Row,
COUNT(*) OVER () AS Total --the magic for this edit
FROM @Customer
WHERE employed = 1
/*Imagine I've joined to loads more tables with a really complex where clause*/
)
SELECT
name,
age,
Total
FROM cteCustomers
INNER JOIN @Customer cust
/*This is where I choose the columns I want to read, it returns really fast!*/
ON cust.id = cteCustomers.id
WHERE row BETWEEN ( @PageSize * @PageNumber - 1 ) AND ( @PageSize * ( @PageNumber ) )
ORDER BY row ASC
Note: YMMV for performance depending on 2005 or 2008, Service pack etc
Edit 2:
SQL Server Central shows another technique where you have reverse ROW_NUMBER. Looks useful
Related Topics
SQL Server - Possible Pivot Solution
Date Comparison Returns Unusual Result - SQL Oracle
Returning Result Even for Elements in In List That Don't Exist in Table
Query to Order by the Number of Rows Returned from Another Select
Icalendar "Field" List (For Database Schema Based on Icalendar Standard)
Best Way to Store Working Hours and Query It Efficiently
How to See Cakephp's SQL Dump in the Controller
How to Add a Column That Doesn't Allow Nulls in a Postgresql Database
What Should I Name a Table That Maps Two Tables Together
Oracle SQL: Fill in Missing Dates
SQL Efficient Way to Join a Table Where All Values Exist
Select One Row Per Index Value with Max Column Value
Where Should I Start with My Opc-Ua Client
Order by with Inner Query, Giving Ora-00907 Missing Right Parenthesis
How to Return Two Columns with Function
How to Join Two Tables But Only Return Rows That Don't Match