Please start any new threads on our new
site at https://forums.sqlteam.com. We've got lots of great SQL Server
experts to answer whatever question you can come up with.
| Author |
Topic |
|
label
Posting Yak Master
197 Posts |
Posted - 2003-04-10 : 15:02:48
|
The following query takes over 10 minutes to process in the Query Analyzer. quote: CREATE PROCEDURE ap_get_activity_by_country@fromdate datetimeASBegin set nocount onCreate table #temp (country varchar(50), country_count int, country_count_recent int, percent_active float, percent_active_recent float, download_count int, avg_download int) Declare Cursid Cursor for select distinct(country) from smc_new_products.dbo.usr_smc where country<>''Open cursid Declare @country varchar(50), @country_count int, @country_count_recent int, @active_users int, @percent_active float, @percent_active_recent float, @download_count int, @avg_download intFetch next from Cursid into @country while (@@fetch_status<>-1) Begin --Grab how many users are registered in each countryselect @country_count=count([user_id]) from smc_new_products.dbo.usr_smc where country=@country--Grab how many users are registered in each countryselect @country_count_recent=count([user_id]) from smc_new_products.dbo.usr_smc where country=@countryand [user_id] in (select [user_id] from smc_new_products.dbo.usr where user_create_time between @fromdate and getdate())--Grab how many users are active within the timeperiod providedselect @active_users=count(distinct(username)) from smc_new_products.dbo.etechmodelrequests where requestdatetime between @fromdate and getdate()and interfacename like '%download%'and username in (select convert(varchar(50), [user_id]) from smc_new_products.dbo.usr_smc where country=@country)--Calculate Active users versus all registered usersif @active_users>0 Begin Select @percent_active=(@active_users/@country_count) end else Begin select @percent_active=0 end --Calculate Active users versus only users registered in specified time frameif @active_users>0 Begin Select @percent_active_recent=(@active_users/@country_count_recent) end else Begin select @percent_active=0 end --Grab how many downloadsselect @download_count=count(username) from smc_new_products.dbo.etechmodelrequests where requestdatetime between @fromdate and getdate()and interfacename like '%download%'and username in (select convert(varchar(50), [user_id]) from smc_new_products.dbo.usr_smc where country=@country)--Calculate Average download per userif @active_users>0 and @download_count>0 Begin select @avg_download=(@download_count/@active_users) endelse Begin select @avg_download=0 end--Insert valuesinsert into #temp values (@country, @country_count, @country_count_recent, @percent_active, @percent_active_recent, @download_count, @avg_download)Fetch next from Cursid into @country end --Clean updeallocate cursidselect * from #temp order by country_count descendGO
Does anyone have any suggestions on how to make it run quicker or is this about all I can do for the type of data I want to return? |
|
|
jsmith8858
Dr. Cross Join
7423 Posts |
Posted - 2003-04-10 : 15:26:41
|
| Try this. read it carefully, it may not be right, I am only guessing -- it is hard to know what you are trying to do w/o your table structure or any information.but the key is to remove the cursor and learn more about select statements, CASE statements, GROUP By's , and JOINS.I hope this will help:CREATE PROCEDURE ap_get_activity_by_country @fromdate datetime AS select Countries.country, country_count, country_count_recent, active_users/ country_count as percent_Active, active_users, download_count, active_users_recent / country_count as percent_Active_recent case when active_users <> 0 then download_count / active_users else 0 end as avg_downloadsfrom(select country, count(*) as country_count, sum(case when user_create_time between @fromDate and getdate() then 1 else 0 end) as country_count_recent fromsmc_new_products.dbo.usr_smcwhere country <> ''group by country) Countriesinner join(select country, count(distinct username) as active_users, count(username) as download_count from smc_new_products.dbo.etechmodelrequests ainner joinsmc_new_products.dbo.usr_smc bon a.username = convert(varchar(50), b.user_id)where requestdatetime between @fromdate and getdate() and interfacename like '%download%' group by country)boncountries.country = b.countryAlso note: comparisions such as LIKE '%blah blah%' and JOINing based on converted fields (your username to varchar() conversion) can really slow things down because SQL can rarely use indexes for those sorts of things. If the interface name always starts with 'Download' then remove the first % and it will be much faster. better yet, add another field for that particular filter instead of relying on what is stored in a text field. That is, a field called "Download" that is 1 if it is a download or 0 otherwise.Good luck. Please try to learn from my code; if it doens't work exactly, play with it and really try to get it to work yourself and you'll be much better off.- JeffEdited by - jsmith8858 on 04/10/2003 15:29:44 |
 |
|
|
X002548
Not Just a Number
15586 Posts |
Posted - 2003-04-10 : 15:29:48
|
| How About the following:SELECT a.Country, Count(*) As Country_Count , Sum(Case When b.user_create_time between @fromdate and getdate() Then 1 Else 0 End) As Country_Count_Recent, Sum(Case When c.requestdatetime between @fromdate and getdate() and interfacename like '%download%' Then 1 Else 0 End) As Active_UsersFrom smc_new_products.dbo.usr_smc aLeft Join smc_new_products.dbo.usr bOn a.[user_id] = b.[user_id]Left Join smc_new_products.dbo.etechmodelrequests cOn c.Username = convert(varchar(50), a.[user_id]Brett8-) |
 |
|
|
MichaelP
Jedi Yak
2489 Posts |
Posted - 2003-04-10 : 15:30:12
|
Get rid of the cursor, I'm sure that would help tremendously.What you could do is do an INSERT INTO...SELECT on the distinct country query, and then JOIN those other queries to that table.Something like this: CREATE TABLE #DistinctCountries(Country VARCHAR(50))INSERT INTO #DistinctCountries(Country)SELECT distinct(country) FROM smc_new_products.dbo.usr_smc WHERE country <>'' I'm not 100% sure on how to get those other queries to put the data into the table like you want, but it will probably require more INSERT INTO...SELECT stuff. I started to work it out, but it's not easy to do without your tables and some data in them.Here was my work in progress: CREATE TABLE #DistinctCountries(Country VARCHAR(50))INSERT INTO #DistinctCountries(Country) VALUES('USA')INSERT INTO #DistinctCountries(Country) VALUES('Canada')INSERT INTO #DistinctCountries(Country) VALUES('England')CREATE TABLE #Users(UserID INT IDENTITY(1,1), Country VARCHAR(50))INSERT INTO #Users(Country) VALUES('USA')INSERT INTO #Users(Country) VALUES('USA')INSERT INTO #Users(Country) VALUES('USA')INSERT INTO #Users(Country) VALUES('USA')INSERT INTO #Users(Country) VALUES('Canada')INSERT INTO #Users(Country) VALUES('Canada')INSERT INTO #Users(Country) VALUES('Canada')INSERT INTO #Users(Country) VALUES('England')INSERT INTO #Users(Country) VALUES('England')SELECT u.Country, count(*) AS DistinctUsersPerCountryFROM #Users uINNER JOIN #DistinctCountries dc ON dc.Country = u.CountryGROUP BY u.CountryDROP TABLE #UsersDROP TABLE #DistinctCountries Michael<Yoda>Use the Search page you must. Find the answer you will.</Yoda> |
 |
|
|
jsmith8858
Dr. Cross Join
7423 Posts |
Posted - 2003-04-10 : 15:32:03
|
Oh, one more thing:quote: if @active_users>0 Begin Select @percent_active=(@active_users/@country_count) end else Begin select @percent_active=0 end
there is no need for the IF in your code; 0 divided by anything is 0. Normally in these cases you check the denominator to make sure it's not zero, NOT the numerator as you are doing. That is because anything divided BY 0 is an ERROR and needs to be handled.- Jeff |
 |
|
|
label
Posting Yak Master
197 Posts |
Posted - 2003-04-10 : 15:57:12
|
| Thanks for all your replies. I'm currently running through your suggestions and trying to implement the fastest solution. |
 |
|
|
|
|
|
|
|