0

While in the process of trying to optimize a query, I'm facing a bit odd situation. In a stored procedure I've had this query, which return a user rank:

SELECT @p_rank = COUNT(*) + 1
FROM leaderboard ur
WHERE ur.score > (SELECT ur2.score FROM leaderboard ur2 WHERE ur2.id = @p_id);

Based on this query I've written a second query in the SP, to get a rank by country. I've created a new leaderboard table with a country column, and tried the following query (based on the first query which performs very good)

SELECT @p_local_rank = COUNT(*) + 1
FROM leaderboard ur
WHERE country = @p_countryand and ur.score > 
(SELECT ur2.score FROM leaderboard ur2 WHERE ur2.id = @p_id);

This query didn't perform well. I've added a country index, which didn't help, but actually made things much slower. The Query Processor suggested adding a country index which will include also score, and I tried this suggestion. After adding it the query performed much better than all prior queries. I've changes the SP and tested it in SSMS, and it worked very well.

As soon as I tried the new SP in production the CPU reached 95+% almost immediately (usually it's around 45%). Only removing this query lowered the CPU back to it's normal values. Note that this SP is executed approximately 100-150 times per minute.

After reading a lot of questions on related issues, These are the things I've tried to resolve it:

  1. Changed the SP to use local variables to prevent Parameter sniffing
  2. Cleared execution plan cache. it's an azure DB so I've used ALTER DATABASE SCOPED CONFIGURATION CLEAR PROCEDURE_CACHE;
  3. SET ANSI_NULLS ON; as suggested here
  4. Updated statistics using sp_updatestats

Nothing seems to help and as soon as I move the SP to production the CPU reaches 95%+.

The table:

CREATE TABLE [dbo].[leaderboard](
[id] [int] NOT NULL,
[score] [int] NOT NULL CONSTRAINT [df_score_value]  DEFAULT ((100)),
[level] [int] NOT NULL CONSTRAINT [df_level_value]  DEFAULT ((1)),
[stage] [int] NOT NULL CONSTRAINT [df_stage_value]  DEFAULT ((1)),
[insert_date] [datetime] NULL,
[update_date] [datetime] NULL,
[daily_score] [int] NOT NULL CONSTRAINT [DF_leaderboard_daily_score]  DEFAULT ((0)),
[weekly_score] [int] NOT NULL CONSTRAINT [DF_leaderboard_weekly_score]  DEFAULT ((0)),
[country] [nchar](45) NULL,
CONSTRAINT [PK_leaderboard] PRIMARY KEY CLUSTERED 
(
[id] ASC
)WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON)
)

Indexes are on the following columns:

  1. score
  2. daily_Score
  3. weekly_score
  4. country INCLUDE score

The table contains about 3 Million records.

Execution plan can be viewed here

Any suggestion what else can be done? `

Hadi
  • 36,233
  • 13
  • 65
  • 124
SuperFrog
  • 7,631
  • 9
  • 51
  • 81
  • 1
    You have several things going on here. First is you are using antiquated join syntax. The ANSI-92 style joins have been available for more than 25 years now. https://sqlblog.org/2009/10/08/bad-habits-to-kick-using-old-style-joins You also have a subquery in your where clause that is hitting the same rows as the main query. Seems the subquery is not at all needed here. Count(distinct ur.PrimaryKey) instead. – Sean Lange Jan 16 '18 at 18:01
  • 1
    What would really help here is if you could post the table definition (including indexes) and approximate row counts. We are left speculating without those details. – Sean Lange Jan 16 '18 at 18:02
  • Upload the execution plan to https://www.brentozar.com/pastetheplan/. – Dan Guzman Jan 16 '18 at 19:26
  • Maybe you could post both tables? Kind of useless with only half of the tables involved. You have enough rep around here to know that what you have posted is not very good. – Sean Lange Jan 16 '18 at 19:33
  • Your subquery isn't the problem right? You said this query is too slow which has an outer query and a subquery. – Sean Lange Jan 16 '18 at 19:36
  • @DanGuzman - https://www.brentozar.com/pastetheplan/?id=SJjzVCoNG I've also added it to the question. – SuperFrog Jan 16 '18 at 19:37
  • OK I see what you are trying to do. Can you provide a dozen or so rows of sample data? – Sean Lange Jan 16 '18 at 19:50
  • 1
    Have you tried a common table expression (CTE) for SELECT ur2.score FROM leaderboard ur2 WHERE ur2.id = @p_id? On the other hand, could you please check the data types for country? I see in your execution plan that you have an implicit conversion. – Francisco Goldenstein Jan 16 '18 at 19:51
  • @SeanLange - sample data https://pastebin.com/H5yXdj23 please let me know if the format is not good. – SuperFrog Jan 16 '18 at 21:07
  • That works well thanks. Last but not least, what are the datatypes of your parameters? – Sean Lange Jan 16 '18 at 21:18
  • p_id INT, p_country nvarchar(40) I did try changing nvarchar to nchar with no effect. – SuperFrog Jan 16 '18 at 21:20
  • Did you try making p_country nchar(45) like in your table? I would much prefer to use the ANSI country code here instead of the name over and over. Then you have only a 2 or 3 character code and you can stick with char as you don't need any extended characters. – Sean Lange Jan 16 '18 at 21:35
  • Yes, I did try. As for the ANSI country code, it's an excellent idea. – SuperFrog Jan 16 '18 at 21:41
  • 1
    Seems that changing the datatype of the SP to nchar (as the country data type) and using local variables solved the problem, thanks! I would be happy to mark as answered if any of you will write his comment as the answer. – SuperFrog Jan 17 '18 at 12:44
  • @SeanLange why not writing your comment as answer if it solved the issue – Hadi Jan 17 '18 at 15:17

1 Answers1

2

It seems there were some datatype mismatches going on between parameters and columns. This can be very problematic on large datasets because the engine has to convert the data of every single row which can cause huge performance problems. Also there may be some parameter sniffing going but it seems you figured that part out already.

I would also suggest adding a table for countries and then having only the ANSI country code in your table. This would be better from a normalization perspective.

Sean Lange
  • 33,028
  • 3
  • 25
  • 40