Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Why does a query slow down drastically if in the WHERE clause a constant is replaced by a parameter (having the same value)?

I have a recursive query which executes very fast if the WHERE clause contains a constant but becomes very slow if I replace the constant with a parameter having the same value.

Query #1 - with constant

;WITH Hierarchy (Id, ParentId, Data, Depth)
AS
( SELECT Id, ParentId, NULL AS Data, 0 AS Depth
  FROM Test
  UNION ALL
  SELECT h.Id, t.ParentId, COALESCE(h.Data, t.Data), Depth + 1 AS Depth
  FROM Hierarchy h
       INNER JOIN Test t ON t.Id = h.ParentId
)
SELECT *
FROM Hierarchy
WHERE Id = 69

Query #2 - with parameter

DECLARE @Id INT
SELECT @Id = 69

;WITH Hierarchy (Id, ParentId, Data, Depth)
AS
( SELECT Id, ParentId, NULL AS Data, 0 AS Depth
  FROM Test
  UNION ALL
  SELECT h.Id, t.ParentId, COALESCE(h.Data, t.Data), Depth + 1 AS Depth
  FROM Hierarchy h
       INNER JOIN Test t ON t.Id = h.ParentId
)
SELECT *
FROM Hierarchy
WHERE Id = @Id

In case of a table with 50,000 rows the query with the constant runs for 10 milliseconds and the one with the parameter runs for 30 seconds (3,000 times slower).

It is not an option to move the last WHERE clause to the anchor definition of the recursion, as I would like to use the query to create a view (without the last WHERE). The select from the view would have the WHERE clause (WHERE Id = @Id) - I need this because of Entity Framework, but that is another story.

Can anybody suggest a way to force query #2 (with the parameter) to use the same query plan as query #1 (with the constant)?

I already tried playing with indexes but that did not help.

If somebody would like I can post the table definition and some sample data as well. I am using SQL 2008 R2.

Thank you for your help in advance!

Execution plan - Query #1 - with constant

alt text

Execution plan - Query #2 - with parameter

alt text

like image 758
Zoltan Avatar asked Nov 19 '10 14:11

Zoltan


People also ask

What are some potential reasons that the query is slow?

Queries can become slow for various reasons ranging from improper index usage to bugs in the storage engine itself. However, in most cases, queries become slow because developers or MySQL database administrators neglect to monitor them and keep an eye on their performance.

Does where clause slow down query?

Although the where clause has a huge impact on performance, it is often phrased carelessly so that the database has to scan a large part of the index. The result: a poorly written where clause is the first ingredient of a slow query.

Does where clause speed up query?

A where clause will generally increase the performance of the database. Generally, it is more expensive to return data and filter in the application. The database can optimize the query, using indexes and partitions. The database may be running in parallel, executing the query in parallel.

What impact does a where clause have on a select query?

Summary. The SQL WHERE clause is used to restrict the number of rows affected by a SELECT, UPDATE or DELETE query. The WHERE condition in SQL can be used in conjunction with logical operators such as AND and OR, comparison operators such as ,= etc.


2 Answers

As Martin suggested in a comment under the question, the problem is that SQL server does not push down properly the predicate from the WHERE clause - see the link in his comment.

I ended up with creating a user defined table-valued function and use it with the CROSS APPLY operator for creating the view.

Let's see the solution itself.

User Defined Table-valued Function

CREATE FUNCTION [dbo].[TestFunction] (@Id INT)
RETURNS TABLE 
AS
RETURN 
(
    WITH
    Hierarchy (Id,  ParentId, Data, Depth)
    AS(
    SELECT Id, ParentId, NULL AS Data, 0 AS Depth FROM Test Where Id = @Id
    UNION ALL
    SELECT h.Id, t.ParentId, COALESCE(h.Data, t.Data), Depth + 1 AS Depth
        FROM Hierarchy h
            INNER JOIN Test t ON t.Id = h.ParentId
    )
    SELECT * FROM Hierarchy
)

View

CREATE VIEW [dbo].[TestView]
AS
SELECT t.Id, t.ParentId, f.Data, f.Depth
FROM
    Test AS t
    CROSS APPLY TestFunction(Id) as f

Query with constant

SELECT * FROM TestView WHERE Id = 69

Query with parameter

DECLARE @Id INT
SELECT @Id = 69
SELECT * FROM TestView WHERE Id = @Id

The query with the parmater executes basically as fast as the query with the constant.

Thank You Martin and for the others as well!

like image 108
Zoltan Avatar answered Oct 04 '22 18:10

Zoltan


For your second Query try using the OPTIMIZE FOR or OPTION(RECOMPILE) query hint to see if that forces it to recomplile based on the provided parameter value.

like image 44
kevchadders Avatar answered Oct 04 '22 19:10

kevchadders