3

I'm building an ASP.NET MVC5 application.

I have a DBML file representing my database (SQL Server 2008).

I have the following SQL function :

CREATE FUNCTION [dbo].[sp_GetResultsPerSubjects] (
    @program VARCHAR(MAX) = NULL,
    @pedagoPeriod VARCHAR(MAX) = NULL,
    @intake VARCHAR(MAX) = NULL,
    @acadYear VARCHAR(MAX) = NULL,
    @section VARCHAR(MAX) = NULL,
    @studentFilter VARCHAR(MAX) = NULL,
    @moduleFilter VARCHAR(MAX) = NULL
)
RETURNS TABLE
AS
RETURN (
    SELECT
        A.[id],
        A.[lastname],
        A.[firstname],
        D.[module_name],
        D.[id] as [module_id],
        H.[id] as [course_id],
        H.[course_name],
        G.[data_text]
    FROM
        [dbo].[Person] A
        INNER JOIN [dbo].[Inscription] B ON A.[id] = B.[person_Id]
        INNER JOIN [dbo].[Inscrsubject] C ON B.[id] = C.[inscription_Id] 
        INNER JOIN [dbo].[Module] D ON C.[id] = D.[inscrsubject_id]
        INNER JOIN [dbo].[Result] E ON C.[id] = E.[inscrsubject_id]
        INNER JOIN [dbo].[Note] F ON E.[id] = F.[result_id] 
        INNER JOIN [dbo].[Data] G ON F.[id] = G.[note_id]
        INNER JOIN [dbo].[Course] H ON H.[result_id] = E.[id]
        INNER JOIN [dbo].[GPS] I ON B.[gps_id] = I.[id]
    WHERE
        (@program IS NULL OR I.[program] IN (SELECT [Value] FROM [dbo].SplitString(@program, '|')))
        AND (@pedagoPeriod IS NULL OR I.[pedagoPeriod] IN (SELECT [Value] FROM [dbo].SplitString(@pedagoPeriod, '|')))
        AND (@intake IS NULL OR I.[intake] IN (SELECT [Value] FROM [dbo].SplitString(@intake, '|')))
        AND (@acadYear IS NULL OR I.[acadYear] IN (SELECT [Value] FROM [dbo].SplitString(@acadYear, '|')))
        AND (@section IS NULL OR I.[section] IN (SELECT [Value] FROM [dbo].SplitString(@section, '|')))
        AND (@studentFilter IS NULL OR A.[id] IN (SELECT CONVERT(DECIMAL, [Value]) FROM [dbo].SplitString(@studentFilter, '|')))
        AND (@moduleFilter IS NULL OR D.[id] IN (SELECT CONVERT(DECIMAL, [Value]) FROM [dbo].SplitString(@moduleFilter, '|')))
        AND G.[data] = 'AVERAGE'
        AND G.[data_Text] IS NOT NULL
        AND G.[data_Text] <> ''
)

I call the function that way from my controller (all parameters are string) :

resultsPerSubjects = dataContext.sp_GetResultsPerSubjects(programs, pedagogicPeriods, intakes, academicYears, sections, studentSelection, moduleSelection).ToList();

I get a timeout : The wait operation timed out

Using the SQL Profiler, I see that the following query is executed :

exec sp_executesql N'
    SELECT [t0].[id], [t0].[lastname], [t0].[firstname], [t0].[module_name], [t0].[module_id], [t0].[course_id], [t0].[course_name], [t0].[data_text]
    FROM [dbo].[sp_GetResultsPerSubjects](@p0, @p1, @p2, @p3, @p4, @p5, @p6) AS [t0]',
    N'@p0 varchar(8000), @p1 varchar(8000), @p2 varchar(8000), @p3 varchar(8000), @p4 varchar(8000), @p5 varchar(8000), @p6 varchar(8000)',
    @p0='AAAA', @p1='1', @p2='a', @p3='2013-2014', @p4='F', @p5='219894352', @p6='92955'

Indeed running this query takes 12 minutes, more than the datacontext timeout.

But the strange thing is that running the function directly from SQL takes arround 1 second ! With the same parameters in the WHERE clause :

('AAAA' IS NULL OR I.[program] IN (SELECT [Value] FROM [dbo].SplitString('AAAA', '|')))
    AND ('1' IS NULL OR I.[pedagoPeriod] IN (SELECT [Value] FROM [dbo].SplitString('1', '|')))
    AND ('a' IS NULL OR I.[intake] IN (SELECT [Value] FROM [dbo].SplitString('a', '|')))
    AND ('2013-2014' IS NULL OR I.[acadYear] IN (SELECT [Value] FROM [dbo].SplitString('2013-2014', '|')))
    AND ('f' IS NULL OR I.[section] IN (SELECT [Value] FROM [dbo].SplitString('f', '|')))
    AND ('219894352' IS NULL OR A.[id] IN (SELECT CONVERT(DECIMAL, [Value]) FROM [dbo].SplitString('2319894352', '|')))
    AND ('92955' IS NULL OR D.[id] IN (SELECT CONVERT(DECIMAL, [Value]) FROM [dbo].SplitString('902955', '|')))
    AND G.[data] = 'AVERAGE'
    AND G.[data_text] IS NOT NULL
    AND G.[data_text] <> ''

If I omit the 2 last parameters, the problem disappear...

Any idea ?

EDIT :

Turning Arithabort OFF or ON in SSMS does not change query execution time (still arround 1 second).

I'm not using ADO.NET or EF, but a simple LINQ to SQL DBML file.

I cannot use OPTION RECOMPILE or OPTIMIZE FOR as it is a function, not a stored procedure (yes I know I have named it sp_* by error).

EDIT2 :

I'm now not sure it is a parameter sniffing problem. I tried :

  1. Turning Arithabort OFF or ON in SSMS
  2. Executing EXEC sp_updatestats to update statistics
  3. Modifying the function to RETURNS @t TABLE(...) so I can declare local variables and assign parameters
  4. Using a stored procedure instead of a function so I can use the OPTION (RECOMPILE) option
  5. Using a stored procedure with parameters assignment to local variables

In all cases I still get a timeout when 2 last parameters are not null. All queries run under 2s in SSMS...

Anything else I can try ?

Yann39
  • 14,285
  • 11
  • 56
  • 84
  • 1
    Maybe because of [parameter sniffing](http://blogs.msdn.com/b/turgays/archive/2013/09/10/parameter-sniffing-problem-and-workarounds.aspx), like in [this](http://stackoverflow.com/questions/10933366/sp-executesql-is-slow-with-parameters) question? – molnarm May 07 '15 at 16:23
  • 1
    EF runs with Arithabort OFF by default, SSMS runs with it on. Try turning it off in SSMS to see if you get the same slow result. If so, you can turn it on in EF, but you will need to do it in the same transaction as your query for it to work. – Peter May 07 '15 at 17:19
  • 1
    I have edited my question. It is a function, not a stored procedure, so I don't think it is a duplicate. – Yann39 May 08 '15 at 07:27
  • Run the query with RECOMPILE for testing purposes. If it becomes fast it's sniffing. Post the good and the bad actual execution plans. – usr May 16 '15 at 09:25

0 Answers0