March 2, 2005 at 9:02 am
Is there a way that I can use, say, "select top 10 * from Table1", or "set rowcount 10 select * from Table1", and yet STILL get the actual number of records that would be returned if I didn't set rowcount to 10 or use "top 10", without running the query twice? @@rowcount is always equal to 10 when I do this, but there are hundreds of records in my real table. I'm using this in a reporting system where the user can limit (preview) the number of records to whatever they want, yet we still want to show the number of records that would be returned if there was no limit without running the report query twice. Thx -Vic
March 2, 2005 at 9:21 am
I'm curious to see what other people are gonna say but I think you'll have to run a second query to get the actual count. Or run the query once and send it to a temp table, giving you the actual row count... then select top 10 from that table. But you still do 2 selects and an insert on top of that (which may not be that bad if the table is huge or the query very complex).
March 2, 2005 at 11:56 am
As Remi said, this is still running 2 queries, just one inside another....
use pubs
go
select top 10 au_lname, au_fname,
(select count(*) from authors) as num
from authors
Steve
March 2, 2005 at 12:23 pm
Won't this query be run multiple times
(select count(*) from authors) since it's a sub query
?
if I run this
use northwind
go
SET STATISTICS IO ON
select top 10 *, (select count(*) from Orders) as num from Orders
select top 10 * from Orders
SET STATISTICS IO OFF
I get an 87% / 13% load... and 85% of the load of the first query goes for the count(*). The strange thing is that when I do top 500 it's more balanced (69%/31% and only 52% for count(*))... can't explain that one though except that it may be considered as a deterministic query and not reran after a few executions.
It think I would test both my method of the temp table to get the count without rerunning the query and 2 separate queries that don't use a temp table to see which is the fastest. I assume that the more complexe the search is, the more efficient the temp table becomes (read @table variable here instead of temp table).
March 2, 2005 at 12:34 pm
agreed with the combo approach of @table and multiple queries to getting the data you want. Something similiar to this.
Declare @table table ( MyColumn varchar(50), iCount int, iTotal int )
Insert into @table
( MyColumn, iCount )
select C.ContactTitle, Count(*)
from dbo.Customers C with (nolock)
Group By C.ContactTitle
Update @table set iTotal = ( select Count(*) From dbo.Customers with (nolock) )
select * From @table
The better part of the query load here appears to be the: Update @table set iTotal = ( select Count(*) From dbo.Customers with (nolock) )
March 2, 2005 at 12:55 pm
Actually what he wants to accomplish is :
insert into @Table
Select * from ?? where .....
set @MyRowCount = @@RowCount
Select top 10 * from @Table
March 2, 2005 at 1:02 pm
understood what he wants to accomplish. Seems to me that there is less load adding only the records he needs to the temp table, then updating the table with the count of all records, rather than add all records to the temp table, then derive from that.
Lets take a look and see
March 2, 2005 at 1:07 pm
lol .. well, I guess that is why you are an addict and I am a mere grasshopper.
When run as a batch.
Declare @table table ( MyColumn varchar(50), iCount int, iTotal int )
Insert into @table
( MyColumn, iCount )
select C.ContactTitle, Count(*)
from dbo.Customers C with (nolock)
Group By C.ContactTitle
Update @table set iTotal = ( select Count(*) From dbo.Customers with (nolock) )
select * From @table
Declare @table2 table ( MyColumn varchar(50), iCustID varchar(10) )
Declare @MyRowCount int
Insert into @table2
( MyColumn, iCustID )
select C.ContactTitle, C.CustomerID
from Customers C with (nolock)
Set @MyRowCount = @@RowCount
Select top 10 @MyRowCount, *
From @table2
Your solution uses less resources. Better way to measure this than ExecutionPlan?
March 2, 2005 at 1:07 pm
Looks like we're saying the same thing.
which of these is the closer to what you are thinking?
execute search into temp table without top 10
gives @@rowcount
select top 10 from temp table
or
Execute search with top 10 and send directly to user
rerun query without top 10 and only count(*)
March 2, 2005 at 1:19 pm
Thanks for the responses guys. Actually what I was looking for was a magic bullet, something like, just check this system var and it gives you the total! it's unfortunate that there is no way to get that value easily. These queries I am running for the reports are quite complex with up to 5 or 6 inner & outer joins in them & returning potentially 10's of thousands of records, so running the query twice is not really an option we want to go with at this point. However, I'm thinking that Remi's option makes the most sense here. At least inserting into a table var once with the full query & where clause, then getting that @@rowcount & saving it to a var, then selecting the top X from the table var out without a where clause, appears to be the cheapest idea yet. Thanks! -Vic
March 2, 2005 at 1:20 pm
Execution plan + profiler with DBCC DROPCLEANBUFFERS between batches is usually what I use.
However a good rule of thumb is to move as little data as possibble and also to avoid rerunning very long queries... The only way to see which weighs more in this case is to test with his data to see what yields best performance. Anything more said from here is merely a guess and unhelpfull to him. So I'll stop typing .
March 2, 2005 at 1:31 pm
I've run in a situation like this before. Is the data from the report static or can it be changed in the execution of the report?
if the data is static, you can create a permanent report table and requery this table.
pseudo code
create permanent table that holds the result of the search
add column Search_id
create permanent table Searches
SearchDate
SearchCriteria1
SearchCriteria2
SearchCriteria3
TotalLines
sp that makes the search :
check if the search has already been run and that the results are still valid : if yes then select from the report table
if not
create the search in the search table
make the full 6 tables inner join and send the data in the report table and link back to the search
in all cases
select the first 10 lines in the reports table joined to the search table for the total row count (or use @@rowcount if you just inserted the data)
you could even add an identity column to the report table and make it the clustered index (combined with the search_id) so that you could select the next 10 ids that are greater than the last presented id for the current search... those selects would be based on a single clustered index so it would be much faster than a complex 6 table joins ran 2 times for each page.
March 2, 2005 at 1:54 pm
Great idea Remi, but alas the report data is very dynamic. In fact, a single proc is used to execute different queries that are already stored in a table for each report. It's all data dictionary driven, and our customers essentially "change the where clause" by picking criteria in dropdowns for each column in the output of the report. It's actually quite slick and performs rather well. My mgr wanted to see if this was possible without rerunning the query and I couldn't make anything with "top" or "set rowcount" work. We will be moving this off our OLTP system and starting a data mart that the customers will use for the reports, so I might implement an idea of your type above down the road! -Vic
March 2, 2005 at 2:07 pm
HTH... too bad my last idea couldn't work in this case. I guess checking if a search like the current one has been cached, and adding where conditions to the cache from there is out of the question... but it would be a nice project to try to make happen someday .
March 2, 2005 at 6:59 pm
I would greatly caution you against using the temp table. Check out the two scripts below; the 1st set of statements costs 30% of the total cost. This will scale to larger recordsets as well (and those with where clauses), even more so when you are putting a large number of records into the temp table.
If you need to count on one row, plus the 10 rows as a sample, you definitely need to run your query twice. I've run into this specific problem (same as you, using dynamic SQL to create the where clause and needing a sample and a total count), tested it heavily, and came to the conclusion that two queries against the underlying table are better than using an intermediate table. Of course this assumes that the underlying table is heavily indexed (which it sure better be!).
If you decide you MUST use a temp table, only store the primary key value in this table, then join back to the original table to get the full top 10 records (see below for example).
---------------------------------------------------------------------------------------
use pubs
go
---1st Version
select count(*) from authors
select top 10 *
from authors
----2nd Version
Declare @table table ( Au_id varchar(50))
Insert into @table
(Au_id)
select Au_id
from authors with (nolock)
select Count(*) From @table
select a.*
From @table t
JOIN authors a on t.au_id = a.au_id
Signature is NULL
Viewing 15 posts - 1 through 15 (of 19 total)
You must be logged in to reply to this topic. Login to reply