0

I have to admit I am a computer programmer, and not a DBA so I lack a few prerequisites to do this analysis work properly.

That said, I have optimized an ORM generated query, and now the performance is quite acceptable, except sometimes (with some parameter) performance drops dramatically and I don't know how to solve the issue.

Looking around, I discovered this thing called 'parameter sniffing' and a quick tutorial on how to analyze the situation, but need some help to validate my assumption, and suggestions on how to resolve the issue.

I have an articles list filtered characteristics this generate the following query (in all the subsequent queries, all parameters are replaced by their actual value):

SELECT [t9].[ID], [t9].[ID_BRAND], [t9].[CODE], [t9].[CODFOR], [t9].[COD_ALT01], [t9].[COD_ALT02], [t9].[COD_ALT03], [t9].[ID_UOM], [t9].[IS_ACTIVE], [t9].[_ATTRIBUTES] AS [_ATTRIBUTES], [t9].[_DOCUMENTS] AS [_DOCUMENTS], [t9].[_SEO] AS [_SEO], [t9].[_TRANSLATIONS] AS [_TRANSLATIONS], [t9].[_TAGS] AS [_TAGS], [t9].[_NOTES] AS [_NOTES], [t9].[_METADATA] AS [_METADATA], [t9].[IS_B2B], [t9].[IS_B2C], [t9].[IS_PROMO], [t9].[IS_NEWS], [t9].[CAN_BE_RETURNED], [t9].[IS_SHIPPABLE], [t9].[HAS_SHIPPING_COSTS], [t9].[IS_PURCHEASABLE], [t9].[test], [t9].[ID2], [t9].[ID_LINE], [t9].[ID_GROUP], [t9].[ID_CLASS], [t9].[ID_FAM], [t9].[ID_ARTICLE], [t9].[LINE_CODE], [t9].[GROUP_CODE], [t9].[CLASS_CODE], [t9].[FAMILY_CODE], [t9].[ARTICLE_CODE], [t9].[test2], [t9].[ID3], [t9].[CODE2], [t9].[BUSINESS_NAME], [t9].[NAME], [t9].[PHONE_01], [t9].[PHONE_02], [t9].[PHONE_03], [t9].[FAX_01], [t9].[FAX_02], [t9].[COUNTRY_01], [t9].[CITY_01], [t9].[ADDRESS_01], [t9].[COUNTRY_02], [t9].[CITY_02], [t9].[ADDRESS_02], [t9].[EMAIL_01], [t9].[EMAIL_02], [t9].[PEC], [t9].[SITE_01], [t9].[SITE_02], [t9].[SITE_03], [t9].[SITE_04], [t9].[VAT_NUMBER], [t9].[SORT], [t9].[GROUPID_01], [t9].[IS_GROUPLEADER_01], [t9].[GROUPID_02], [t9].[IS_GROUPLEADER_02], [t9].[IS_ACTIVE2], [t9].[[_DOCUMENTS]]2] AS [_DOCUMENTS2], [t9].[[_SEO]]2] AS [_SEO2], [t9].[[_METADATA]]2] AS [_METADATA2], [t9].[test3], [t9].[ID4], [t9].[CODE3], [t9].[[_TRANSLATIONS]]2] AS [_TRANSLATIONS2], [t9].[[_METADATA]]3] AS [_METADATA3], [t10].[ID_CHARACTERISTIC], [t10].[ID_ARTICLE] AS [ID_ARTICLE2], [t10].[ID_FILTER], (
    SELECT COUNT(*)
    FROM (
        SELECT NULL AS [EMPTY]
        FROM [dbo].[tbl_src_ArticlesCharacteristics] AS [t11]
        WHERE [t11].[ID_ARTICLE] = [t9].[ID]
        ) AS [t12]
    ) AS [value]
FROM (
    SELECT ROW_NUMBER() OVER (ORDER BY [t2].[LINE_CODE], [t2].[GROUP_CODE], [t2].[CLASS_CODE], [t2].[FAMILY_CODE], [t0].[COD_ALT01], [t0].[ID], [t2].[ID], [t4].[ID], [t6].[ID]) AS [ROW_NUMBER], [t0].[ID], [t0].[ID_BRAND], [t0].[CODE], [t0].[CODFOR], [t0].[COD_ALT01], [t0].[COD_ALT02], [t0].[COD_ALT03], [t0].[ID_UOM], [t0].[IS_ACTIVE], [t0].[_ATTRIBUTES], [t0].[_DOCUMENTS], [t0].[_SEO], [t0].[_TRANSLATIONS], [t0].[_TAGS], [t0].[_NOTES], [t0].[_METADATA], [t0].[IS_B2B], [t0].[IS_B2C], [t0].[IS_PROMO], [t0].[IS_NEWS], [t0].[CAN_BE_RETURNED], [t0].[IS_SHIPPABLE], [t0].[HAS_SHIPPING_COSTS], [t0].[IS_PURCHEASABLE], [t2].[test], [t2].[ID] AS [ID2], [t2].[ID_LINE], [t2].[ID_GROUP], [t2].[ID_CLASS], [t2].[ID_FAM], [t2].[ID_ARTICLE], [t2].[LINE_CODE], [t2].[GROUP_CODE], [t2].[CLASS_CODE], [t2].[FAMILY_CODE], [t2].[ARTICLE_CODE], [t4].[test] AS [test2], [t4].[ID] AS [ID3], [t4].[CODE] AS [CODE2], [t4].[BUSINESS_NAME], [t4].[NAME], [t4].[PHONE_01], [t4].[PHONE_02], [t4].[PHONE_03], [t4].[FAX_01], [t4].[FAX_02], [t4].[COUNTRY_01], [t4].[CITY_01], [t4].[ADDRESS_01], [t4].[COUNTRY_02], [t4].[CITY_02], [t4].[ADDRESS_02], [t4].[EMAIL_01], [t4].[EMAIL_02], [t4].[PEC], [t4].[SITE_01], [t4].[SITE_02], [t4].[SITE_03], [t4].[SITE_04], [t4].[VAT_NUMBER], [t4].[SORT], [t4].[GROUPID_01], [t4].[IS_GROUPLEADER_01], [t4].[GROUPID_02], [t4].[IS_GROUPLEADER_02], [t4].[IS_ACTIVE] AS [IS_ACTIVE2], [t4].[_DOCUMENTS] AS [[_DOCUMENTS]]2], [t4].[_SEO] AS [[_SEO]]2], [t4].[_METADATA] AS [[_METADATA]]2], [t6].[test] AS [test3], [t6].[ID] AS [ID4], [t6].[CODE] AS [CODE3], [t6].[_TRANSLATIONS] AS [[_TRANSLATIONS]]2], [t6].[_METADATA] AS [[_METADATA]]3]
    FROM [dbo].[tbl_ana_Articles] AS [t0]
    LEFT OUTER JOIN (
        SELECT 1 AS [test], [t1].[ID], [t1].[ID_LINE], [t1].[ID_GROUP], [t1].[ID_CLASS], [t1].[ID_FAM], [t1].[ID_ARTICLE], [t1].[LINE_CODE], [t1].[GROUP_CODE], [t1].[CLASS_CODE], [t1].[FAMILY_CODE], [t1].[ARTICLE_CODE]
        FROM [dbo].[tbl_src_ArticlesCategories] AS [t1]
        ) AS [t2] ON [t2].[ID_ARTICLE] = [t0].[ID]
    LEFT OUTER JOIN (
        SELECT 1 AS [test], [t3].[ID], [t3].[CODE], [t3].[BUSINESS_NAME], [t3].[NAME], [t3].[PHONE_01], [t3].[PHONE_02], [t3].[PHONE_03], [t3].[FAX_01], [t3].[FAX_02], [t3].[COUNTRY_01], [t3].[CITY_01], [t3].[ADDRESS_01], [t3].[COUNTRY_02], [t3].[CITY_02], [t3].[ADDRESS_02], [t3].[EMAIL_01], [t3].[EMAIL_02], [t3].[PEC], [t3].[SITE_01], [t3].[SITE_02], [t3].[SITE_03], [t3].[SITE_04], [t3].[VAT_NUMBER], [t3].[SORT], [t3].[GROUPID_01], [t3].[IS_GROUPLEADER_01], [t3].[GROUPID_02], [t3].[IS_GROUPLEADER_02], [t3].[IS_ACTIVE], [t3].[_DOCUMENTS], [t3].[_SEO], [t3].[_METADATA]
        FROM [dbo].[tbl_ana_Brands] AS [t3]
        ) AS [t4] ON [t4].[ID] = [t0].[ID_BRAND]
    LEFT OUTER JOIN (
        SELECT 1 AS [test], [t5].[ID], [t5].[CODE], [t5].[_TRANSLATIONS], [t5].[_METADATA]
        FROM [dbo].[tbl_ana_UoMs] AS [t5]
        ) AS [t6] ON [t6].[ID] = [t0].[ID_UOM]
    WHERE (((
        SELECT COUNT(*)
        FROM (
            SELECT NULL AS [EMPTY]
            FROM [dbo].[tbl_src_ArticlesCharacteristics] AS [t7]
            WHERE ([t7].[ID_CHARACTERISTIC] IN (121, 122, 92, 93, 94, 95, 96)) AND ([t7].[ID_ARTICLE] = [t0].[ID])
            GROUP BY [t7].[ID_FILTER]
            ) AS [t8]
        )) = 2) AND ([t0].[IS_ACTIVE] = 1)
    ) AS [t9]
LEFT OUTER JOIN [dbo].[tbl_src_ArticlesCharacteristics] AS [t10] ON [t10].[ID_ARTICLE] = [t9].[ID]
WHERE [t9].[ROW_NUMBER] BETWEEN  1 AND 36
ORDER BY [t9].[ROW_NUMBER], [t10].[ID_CHARACTERISTIC], [t10].[ID_ARTICLE]

The execution plan is: https://www.brentozar.com/pastetheplan/?id=HJ-HfRfi9

So far so good execution takes less than a second. But if I filter for one more characteristic the query becomes:

    SELECT [t9].[ID], [t9].[ID_BRAND], [t9].[CODE], [t9].[CODFOR], [t9].[COD_ALT01], [t9].[COD_ALT02], [t9].[COD_ALT03], [t9].[ID_UOM], [t9].[IS_ACTIVE], [t9].[_ATTRIBUTES] AS [_ATTRIBUTES], [t9].[_DOCUMENTS] AS [_DOCUMENTS], [t9].[_SEO] AS [_SEO], [t9].[_TRANSLATIONS] AS [_TRANSLATIONS], [t9].[_TAGS] AS [_TAGS], [t9].[_NOTES] AS [_NOTES], [t9].[_METADATA] AS [_METADATA], [t9].[IS_B2B], [t9].[IS_B2C], [t9].[IS_PROMO], [t9].[IS_NEWS], [t9].[CAN_BE_RETURNED], [t9].[IS_SHIPPABLE], [t9].[HAS_SHIPPING_COSTS], [t9].[IS_PURCHEASABLE], [t9].[test], [t9].[ID2], [t9].[ID_LINE], [t9].[ID_GROUP], [t9].[ID_CLASS], [t9].[ID_FAM], [t9].[ID_ARTICLE], [t9].[LINE_CODE], [t9].[GROUP_CODE], [t9].[CLASS_CODE], [t9].[FAMILY_CODE], [t9].[ARTICLE_CODE], [t9].[test2], [t9].[ID3], [t9].[CODE2], [t9].[BUSINESS_NAME], [t9].[NAME], [t9].[PHONE_01], [t9].[PHONE_02], [t9].[PHONE_03], [t9].[FAX_01], [t9].[FAX_02], [t9].[COUNTRY_01], [t9].[CITY_01], [t9].[ADDRESS_01], [t9].[COUNTRY_02], [t9].[CITY_02], [t9].[ADDRESS_02], [t9].[EMAIL_01], [t9].[EMAIL_02], [t9].[PEC], [t9].[SITE_01], [t9].[SITE_02], [t9].[SITE_03], [t9].[SITE_04], [t9].[VAT_NUMBER], [t9].[SORT], [t9].[GROUPID_01], [t9].[IS_GROUPLEADER_01], [t9].[GROUPID_02], [t9].[IS_GROUPLEADER_02], [t9].[IS_ACTIVE2], [t9].[[_DOCUMENTS]]2] AS [_DOCUMENTS2], [t9].[[_SEO]]2] AS [_SEO2], [t9].[[_METADATA]]2] AS [_METADATA2], [t9].[test3], [t9].[ID4], [t9].[CODE3], [t9].[[_TRANSLATIONS]]2] AS [_TRANSLATIONS2], [t9].[[_METADATA]]3] AS [_METADATA3], [t10].[ID_CHARACTERISTIC], [t10].[ID_ARTICLE] AS [ID_ARTICLE2], [t10].[ID_FILTER], (
        SELECT COUNT(*)
        FROM (
            SELECT NULL AS [EMPTY]
            FROM [dbo].[tbl_src_ArticlesCharacteristics] AS [t11]
            WHERE [t11].[ID_ARTICLE] = [t9].[ID]
            ) AS [t12]
        ) AS [value]
    FROM (
        SELECT ROW_NUMBER() OVER (ORDER BY [t2].[LINE_CODE], [t2].[GROUP_CODE], [t2].[CLASS_CODE], [t2].[FAMILY_CODE], [t0].[COD_ALT01], [t0].[ID], [t2].[ID], [t4].[ID], [t6].[ID]) AS [ROW_NUMBER], [t0].[ID], [t0].[ID_BRAND], [t0].[CODE], [t0].[CODFOR], [t0].[COD_ALT01], [t0].[COD_ALT02], [t0].[COD_ALT03], [t0].[ID_UOM], [t0].[IS_ACTIVE], [t0].[_ATTRIBUTES], [t0].[_DOCUMENTS], [t0].[_SEO], [t0].[_TRANSLATIONS], [t0].[_TAGS], [t0].[_NOTES], [t0].[_METADATA], [t0].[IS_B2B], [t0].[IS_B2C], [t0].[IS_PROMO], [t0].[IS_NEWS], [t0].[CAN_BE_RETURNED], [t0].[IS_SHIPPABLE], [t0].[HAS_SHIPPING_COSTS], [t0].[IS_PURCHEASABLE], [t2].[test], [t2].[ID] AS [ID2], [t2].[ID_LINE], [t2].[ID_GROUP], [t2].[ID_CLASS], [t2].[ID_FAM], [t2].[ID_ARTICLE], [t2].[LINE_CODE], [t2].[GROUP_CODE], [t2].[CLASS_CODE], [t2].[FAMILY_CODE], [t2].[ARTICLE_CODE], [t4].[test] AS [test2], [t4].[ID] AS [ID3], [t4].[CODE] AS [CODE2], [t4].[BUSINESS_NAME], [t4].[NAME], [t4].[PHONE_01], [t4].[PHONE_02], [t4].[PHONE_03], [t4].[FAX_01], [t4].[FAX_02], [t4].[COUNTRY_01], [t4].[CITY_01], [t4].[ADDRESS_01], [t4].[COUNTRY_02], [t4].[CITY_02], [t4].[ADDRESS_02], [t4].[EMAIL_01], [t4].[EMAIL_02], [t4].[PEC], [t4].[SITE_01], [t4].[SITE_02], [t4].[SITE_03], [t4].[SITE_04], [t4].[VAT_NUMBER], [t4].[SORT], [t4].[GROUPID_01], [t4].[IS_GROUPLEADER_01], [t4].[GROUPID_02], [t4].[IS_GROUPLEADER_02], [t4].[IS_ACTIVE] AS [IS_ACTIVE2], [t4].[_DOCUMENTS] AS [[_DOCUMENTS]]2], [t4].[_SEO] AS [[_SEO]]2], [t4].[_METADATA] AS [[_METADATA]]2], [t6].[test] AS [test3], [t6].[ID] AS [ID4], [t6].[CODE] AS [CODE3], [t6].[_TRANSLATIONS] AS [[_TRANSLATIONS]]2], [t6].[_METADATA] AS [[_METADATA]]3]
        FROM [dbo].[tbl_ana_Articles] AS [t0]
        LEFT OUTER JOIN (
            SELECT 1 AS [test], [t1].[ID], [t1].[ID_LINE], [t1].[ID_GROUP], [t1].[ID_CLASS], [t1].[ID_FAM], [t1].[ID_ARTICLE], [t1].[LINE_CODE], [t1].[GROUP_CODE], [t1].[CLASS_CODE], [t1].[FAMILY_CODE], [t1].[ARTICLE_CODE]
            FROM [dbo].[tbl_src_ArticlesCategories] AS [t1]
            ) AS [t2] ON [t2].[ID_ARTICLE] = [t0].[ID]
        LEFT OUTER JOIN (
            SELECT 1 AS [test], [t3].[ID], [t3].[CODE], [t3].[BUSINESS_NAME], [t3].[NAME], [t3].[PHONE_01], [t3].[PHONE_02], [t3].[PHONE_03], [t3].[FAX_01], [t3].[FAX_02], [t3].[COUNTRY_01], [t3].[CITY_01], [t3].[ADDRESS_01], [t3].[COUNTRY_02], [t3].[CITY_02], [t3].[ADDRESS_02], [t3].[EMAIL_01], [t3].[EMAIL_02], [t3].[PEC], [t3].[SITE_01], [t3].[SITE_02], [t3].[SITE_03], [t3].[SITE_04], [t3].[VAT_NUMBER], [t3].[SORT], [t3].[GROUPID_01], [t3].[IS_GROUPLEADER_01], [t3].[GROUPID_02], [t3].[IS_GROUPLEADER_02], [t3].[IS_ACTIVE], [t3].[_DOCUMENTS], [t3].[_SEO], [t3].[_METADATA]
            FROM [dbo].[tbl_ana_Brands] AS [t3]
            ) AS [t4] ON [t4].[ID] = [t0].[ID_BRAND]
        LEFT OUTER JOIN (
            SELECT 1 AS [test], [t5].[ID], [t5].[CODE], [t5].[_TRANSLATIONS], [t5].[_METADATA]
            FROM [dbo].[tbl_ana_UoMs] AS [t5]
            ) AS [t6] ON [t6].[ID] = [t0].[ID_UOM]
        WHERE (((
            SELECT COUNT(*)
            FROM (
                SELECT NULL AS [EMPTY]
                FROM [dbo].[tbl_src_ArticlesCharacteristics] AS [t7]
                WHERE ([t7].[ID_CHARACTERISTIC] IN (121, 122, 92, 93, 94, 95, 96,97)) AND ([t7].[ID_ARTICLE] = [t0].[ID])
                GROUP BY [t7].[ID_FILTER]
                ) AS [t8]
            )) = 2) AND ([t0].[IS_ACTIVE] = 1)
        ) AS [t9]
    LEFT OUTER JOIN [dbo].[tbl_src_ArticlesCharacteristics] AS [t10] ON [t10].[ID_ARTICLE] = [t9].[ID]
    WHERE [t9].[ROW_NUMBER] BETWEEN  1 AND 36
    ORDER BY [t9].[ROW_NUMBER], [t10].[ID_CHARACTERISTIC], [t10].[ID_ARTICLE]

The changed execution plan is: https://www.brentozar.com/pastetheplan/?id=Byf6M0Giq

The execution now takes almost 13s.

What I have noticed comparing the two execution plans is that even if they have the same shape, (structure and nodes type) they hugely change in the time taken by Hash Match (right outer join) node. But I have no idea on how to improve that.

Here I report the structure of involved tables so you have an idea of the indexes involved:

CREATE TABLE [dbo].[tbl_ana_Articles](
    [ID] [int] IDENTITY(1,1) NOT NULL,
    [ID_BRAND] [int] NOT NULL,
    [CODE] [nvarchar](40) NOT NULL,
    [CODFOR] [nvarchar](40) NOT NULL,
    [COD_ALT01] [nvarchar](50) NOT NULL,
    [COD_ALT02] [nvarchar](50) NOT NULL,
    [COD_ALT03] [nvarchar](50) NOT NULL,
    [ID_UOM] [int] NOT NULL,
    [IS_ACTIVE] [bit] NOT NULL,
    [_ATTRIBUTES] [nvarchar](max) NOT NULL,
    [_DOCUMENTS] [nvarchar](max) NOT NULL,
    [_SEO] [nvarchar](max) NOT NULL,
    [_TRANSLATIONS] [nvarchar](max) NOT NULL,
    [_TAGS] [nvarchar](max) NOT NULL,
    [_NOTES] [nvarchar](max) NOT NULL,
    [_METADATA] [nvarchar](max) NOT NULL,
    [IS_B2B] [bit] NOT NULL,
    [IS_B2C] [bit] NOT NULL,
    [IS_PROMO] [bit] NOT NULL,
    [IS_NEWS] [bit] NOT NULL,
    [CAN_BE_RETURNED] [bit] NOT NULL,
    [IS_SHIPPABLE] [bit] NOT NULL,
    [HAS_SHIPPING_COSTS] [bit] NOT NULL,
    [IS_PURCHEASABLE] [bit] NOT NULL,
 CONSTRAINT [PK_tbl_ana_articles] PRIMARY KEY CLUSTERED 
(
    [ID] ASC
)WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON, OPTIMIZE_FOR_SEQUENTIAL_KEY = OFF) ON [PRIMARY]
) ON [PRIMARY] TEXTIMAGE_ON [PRIMARY]
GO
/****** Object:  Table [dbo].[tbl_ana_Brands]    Script Date: 06/07/2022 11:21:05 ******/
SET ANSI_NULLS ON
GO
SET QUOTED_IDENTIFIER ON
GO
CREATE TABLE [dbo].[tbl_ana_Brands](
    [ID] [int] IDENTITY(1,1) NOT NULL,
    [CODE] [nvarchar](10) NOT NULL,
    [BUSINESS_NAME] [nvarchar](50) NOT NULL,
    [NAME] [nvarchar](50) NOT NULL,
    [PHONE_01] [nvarchar](20) NOT NULL,
    [PHONE_02] [nvarchar](20) NOT NULL,
    [PHONE_03] [nvarchar](20) NOT NULL,
    [FAX_01] [nvarchar](20) NOT NULL,
    [FAX_02] [nvarchar](20) NOT NULL,
    [COUNTRY_01] [nvarchar](30) NOT NULL,
    [CITY_01] [nvarchar](50) NOT NULL,
    [ADDRESS_01] [nvarchar](100) NOT NULL,
    [COUNTRY_02] [nvarchar](30) NOT NULL,
    [CITY_02] [nvarchar](50) NOT NULL,
    [ADDRESS_02] [nvarchar](100) NOT NULL,
    [EMAIL_01] [nvarchar](100) NOT NULL,
    [EMAIL_02] [nvarchar](100) NOT NULL,
    [PEC] [nvarchar](100) NOT NULL,
    [SITE_01] [nvarchar](400) NOT NULL,
    [SITE_02] [nvarchar](400) NOT NULL,
    [SITE_03] [nvarchar](400) NOT NULL,
    [SITE_04] [nvarchar](400) NOT NULL,
    [VAT_NUMBER] [nvarchar](50) NOT NULL,
    [SORT] [int] NOT NULL,
    [GROUPID_01] [int] NOT NULL,
    [IS_GROUPLEADER_01] [bit] NOT NULL,
    [GROUPID_02] [int] NOT NULL,
    [IS_GROUPLEADER_02] [bit] NOT NULL,
    [IS_ACTIVE] [bit] NOT NULL,
    [_DOCUMENTS] [nvarchar](max) NOT NULL,
    [_SEO] [nvarchar](max) NOT NULL,
    [_METADATA] [nvarchar](max) NOT NULL,
 CONSTRAINT [PK_tbl_ana_Brands] PRIMARY KEY CLUSTERED 
(
    [ID] ASC
)WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON, OPTIMIZE_FOR_SEQUENTIAL_KEY = OFF) ON [PRIMARY]
) ON [PRIMARY] TEXTIMAGE_ON [PRIMARY]
GO
/****** Object:  Table [dbo].[tbl_ana_UoMs]    Script Date: 06/07/2022 11:21:05 ******/
SET ANSI_NULLS ON
GO
SET QUOTED_IDENTIFIER ON
GO
CREATE TABLE [dbo].[tbl_ana_UoMs](
    [ID] [int] IDENTITY(1,1) NOT NULL,
    [CODE] [nvarchar](30) NOT NULL,
    [_TRANSLATIONS] [nvarchar](max) NOT NULL,
    [_METADATA] [nvarchar](max) NOT NULL,
 CONSTRAINT [PK_tbl_ana_UoM] PRIMARY KEY CLUSTERED 
(
    [ID] ASC
)WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON, OPTIMIZE_FOR_SEQUENTIAL_KEY = OFF) ON [PRIMARY]
) ON [PRIMARY] TEXTIMAGE_ON [PRIMARY]
GO
/****** Object:  Table [dbo].[tbl_src_ArticlesCharacteristics]    Script Date: 06/07/2022 11:21:05 ******/
SET ANSI_NULLS ON
GO
SET QUOTED_IDENTIFIER ON
GO
CREATE TABLE [dbo].[tbl_src_ArticlesCharacteristics](
    [ID_CHARACTERISTIC] [int] NOT NULL,
    [ID_ARTICLE] [int] NOT NULL,
    [ID_FILTER] [int] NOT NULL,
 CONSTRAINT [PK_tbl_src_ArticlesCharacteristics] PRIMARY KEY CLUSTERED 
(
    [ID_CHARACTERISTIC] ASC,
    [ID_ARTICLE] ASC
)WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON, OPTIMIZE_FOR_SEQUENTIAL_KEY = OFF) ON [PRIMARY]
) ON [PRIMARY]
GO
/****** Object:  Index [IX_ACTIVES_WITH_CODE]    Script Date: 06/07/2022 11:21:05 ******/
CREATE NONCLUSTERED INDEX [IX_ACTIVES_WITH_CODE] ON [dbo].[tbl_ana_Articles]
(
    [IS_ACTIVE] ASC
)
INCLUDE([CODE]) WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, SORT_IN_TEMPDB = OFF, DROP_EXISTING = OFF, ONLINE = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON, OPTIMIZE_FOR_SEQUENTIAL_KEY = OFF) ON [PRIMARY]
GO
/****** Object:  Index [IX_BRANDS]    Script Date: 06/07/2022 11:21:05 ******/
CREATE NONCLUSTERED INDEX [IX_BRANDS] ON [dbo].[tbl_ana_Articles]
(
    [ID_BRAND] ASC
)WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, SORT_IN_TEMPDB = OFF, DROP_EXISTING = OFF, ONLINE = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON, OPTIMIZE_FOR_SEQUENTIAL_KEY = OFF) ON [PRIMARY]
GO
SET ANSI_PADDING ON
GO
/****** Object:  Index [IX_COD_ALT01]    Script Date: 06/07/2022 11:21:05 ******/
CREATE NONCLUSTERED INDEX [IX_COD_ALT01] ON [dbo].[tbl_ana_Articles]
(
    [COD_ALT01] ASC
)WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, SORT_IN_TEMPDB = OFF, DROP_EXISTING = OFF, ONLINE = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON, OPTIMIZE_FOR_SEQUENTIAL_KEY = OFF) ON [PRIMARY]
GO
SET ANSI_PADDING ON
GO
/****** Object:  Index [IX_COD_ALT02]    Script Date: 06/07/2022 11:21:05 ******/
CREATE NONCLUSTERED INDEX [IX_COD_ALT02] ON [dbo].[tbl_ana_Articles]
(
    [COD_ALT02] ASC
)WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, SORT_IN_TEMPDB = OFF, DROP_EXISTING = OFF, ONLINE = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON, OPTIMIZE_FOR_SEQUENTIAL_KEY = OFF) ON [PRIMARY]
GO
SET ANSI_PADDING ON
GO
/****** Object:  Index [IX_COD_ALT03]    Script Date: 06/07/2022 11:21:05 ******/
CREATE NONCLUSTERED INDEX [IX_COD_ALT03] ON [dbo].[tbl_ana_Articles]
(
    [COD_ALT03] ASC
)WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, SORT_IN_TEMPDB = OFF, DROP_EXISTING = OFF, ONLINE = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON, OPTIMIZE_FOR_SEQUENTIAL_KEY = OFF) ON [PRIMARY]
GO
SET ANSI_PADDING ON
GO
/****** Object:  Index [IX_CODE]    Script Date: 06/07/2022 11:21:05 ******/
CREATE NONCLUSTERED INDEX [IX_CODE] ON [dbo].[tbl_ana_Articles]
(
    [CODE] ASC
)WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, SORT_IN_TEMPDB = OFF, DROP_EXISTING = OFF, ONLINE = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON, OPTIMIZE_FOR_SEQUENTIAL_KEY = OFF) ON [PRIMARY]
GO
SET ANSI_PADDING ON
GO
/****** Object:  Index [IX_CODFOR]    Script Date: 06/07/2022 11:21:05 ******/
CREATE NONCLUSTERED INDEX [IX_CODFOR] ON [dbo].[tbl_ana_Articles]
(
    [CODFOR] ASC
)WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, SORT_IN_TEMPDB = OFF, DROP_EXISTING = OFF, ONLINE = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON, OPTIMIZE_FOR_SEQUENTIAL_KEY = OFF) ON [PRIMARY]
GO
/****** Object:  Index [IX_UOM]    Script Date: 06/07/2022 11:21:05 ******/
CREATE NONCLUSTERED INDEX [IX_UOM] ON [dbo].[tbl_ana_Articles]
(
    [ID_UOM] ASC
)WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, SORT_IN_TEMPDB = OFF, DROP_EXISTING = OFF, ONLINE = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON, OPTIMIZE_FOR_SEQUENTIAL_KEY = OFF) ON [PRIMARY]
GO
/****** Object:  Index [MISSING_CHARACTERISTING_INDEX]    Script Date: 06/07/2022 11:21:05 ******/
CREATE NONCLUSTERED INDEX [MISSING_CHARACTERISTING_INDEX] ON [dbo].[tbl_ana_Articles]
(
    [IS_ACTIVE] ASC
)WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, SORT_IN_TEMPDB = OFF, DROP_EXISTING = OFF, ONLINE = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON, OPTIMIZE_FOR_SEQUENTIAL_KEY = OFF) ON [PRIMARY]
GO
SET ANSI_PADDING ON
GO
/****** Object:  Index [IX_CODE]    Script Date: 06/07/2022 11:21:05 ******/
CREATE NONCLUSTERED INDEX [IX_CODE] ON [dbo].[tbl_ana_Brands]
(
    [CODE] ASC
)WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, SORT_IN_TEMPDB = OFF, DROP_EXISTING = OFF, ONLINE = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON, OPTIMIZE_FOR_SEQUENTIAL_KEY = OFF) ON [PRIMARY]
GO
/****** Object:  Index [IX_ID_FILTER]    Script Date: 06/07/2022 11:21:05 ******/
CREATE NONCLUSTERED INDEX [IX_ID_FILTER] ON [dbo].[tbl_src_ArticlesCharacteristics]
(
    [ID_FILTER] ASC
)WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, SORT_IN_TEMPDB = OFF, DROP_EXISTING = OFF, ONLINE = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON, OPTIMIZE_FOR_SEQUENTIAL_KEY = OFF) ON [PRIMARY]
GO
/****** Object:  Index [IX_tbl_src_ArticlesCharacteristics]    Script Date: 06/07/2022 11:21:05 ******/
CREATE NONCLUSTERED INDEX [IX_tbl_src_ArticlesCharacteristics] ON [dbo].[tbl_src_ArticlesCharacteristics]
(
    [ID_ARTICLE] ASC
)WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, SORT_IN_TEMPDB = OFF, DROP_EXISTING = OFF, ONLINE = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON, OPTIMIZE_FOR_SEQUENTIAL_KEY = OFF) ON [PRIMARY]
GO
ALTER TABLE [dbo].[tbl_ana_Articles] ADD  CONSTRAINT [DF_tbl_ana_Articles_ID_BRAND]  DEFAULT ((0)) FOR [ID_BRAND]
GO
ALTER TABLE [dbo].[tbl_ana_Articles] ADD  CONSTRAINT [DF_tbl_ana_Articles_COD_ALT03]  DEFAULT (' ') FOR [COD_ALT03]
GO
ALTER TABLE [dbo].[tbl_ana_Articles] ADD  CONSTRAINT [DF_tbl_ana_Articles__ATTRIBUTES]  DEFAULT ('') FOR [_ATTRIBUTES]
GO
ALTER TABLE [dbo].[tbl_ana_Articles] ADD  CONSTRAINT [DF_tbl_ana_Articles_TEST]  DEFAULT ((0)) FOR [IS_B2B]
GO
ALTER TABLE [dbo].[tbl_ana_Articles] ADD  CONSTRAINT [DF_tbl_ana_Articles_IS_B2C]  DEFAULT ((0)) FOR [IS_B2C]
GO
ALTER TABLE [dbo].[tbl_ana_Articles] ADD  CONSTRAINT [DF_tbl_ana_Articles_IS_PROMO]  DEFAULT ((0)) FOR [IS_PROMO]
GO
ALTER TABLE [dbo].[tbl_ana_Articles] ADD  CONSTRAINT [DF_tbl_ana_Articles_IS_NEWS]  DEFAULT ((0)) FOR [IS_NEWS]
GO
ALTER TABLE [dbo].[tbl_ana_Articles] ADD  CONSTRAINT [DF_tbl_ana_Articles_CAN_BE_RETURNED]  DEFAULT ((0)) FOR [CAN_BE_RETURNED]
GO
ALTER TABLE [dbo].[tbl_ana_Articles] ADD  CONSTRAINT [DF_tbl_ana_Articles_IS_SHIPPABLE]  DEFAULT ((0)) FOR [IS_SHIPPABLE]
GO
ALTER TABLE [dbo].[tbl_ana_Articles] ADD  CONSTRAINT [DF_tbl_ana_Articles_HAS_SHIPPING_COSTS]  DEFAULT ((0)) FOR [HAS_SHIPPING_COSTS]
GO
ALTER TABLE [dbo].[tbl_ana_Articles] ADD  CONSTRAINT [DF_tbl_ana_Articles_IS_PURCHEASABLE]  DEFAULT ((0)) FOR [IS_PURCHEASABLE]
GO
ALTER TABLE [dbo].[tbl_src_ArticlesCharacteristics] ADD  CONSTRAINT [DF_tbl_src_ArticlesCharacteristics_ID_FILTER]  DEFAULT ((0)) FOR [ID_FILTER]
GO

It seems if I omit to select these fields:

[_ATTRIBUTES] [nvarchar](max) NOT NULL,
[_DOCUMENTS] [nvarchar](max) NOT NULL,
[_SEO] [nvarchar](max) NOT NULL,
[_TRANSLATIONS] [nvarchar](max) NOT NULL,
[_TAGS] [nvarchar](max) NOT NULL,
[_NOTES] [nvarchar](max) NOT NULL,
[_METADATA] [nvarchar](max) NOT NULL,

performance improves a lot.

Those fields are meant to be extension point into data structure allowing the program to place few KB of XML inside each rows.

Still not clear if this is just a coincidence or may be a problem for the Hash Match operator.

I am working with SQL Server 2019 Standard Edition. The program is a C# application written for Net Framework 4.8 using the Linq To SQL ORM.

A lot of this query should be rewritten, unfortunately I have to deal with ORM query generation. Seems like that a few nvarchar(MAX) fields slow down the query, even if the result is paged. If I omit them I get a big performance boost.

4
  • How is this parameter sniffing? I don't see any parameters in your queries, and the problem is that your plan changes, not that it stays the same. Jul 6, 2022 at 13:35
  • @ErikDarling thanks for your help. As stated in the question i have replaced the parameters with value so you can see that a tiny change (only one parameter is added) have a big impact. That said i have discovered this kind of issues today, and thought (accordingly with what i have read) that the name is parameter sniffng, maybe i made mistake. Comparing the two plan i start having the feeling that the additional parameter cause a spill to tempdb. I have tried adding query hint and problem appear to be solved, but i'm not sure that this "solution" apply to cases where in DB there are more rows
    – Skary
    Jul 6, 2022 at 14:46
  • 1
    @Skary Yeah, but that's still not parameter sniffing, because you're getting two different plans. Parameter sniffing is when the same plan doesn't play well with the same parameters, when different values are supplied. You're right about the outcome of adding an additional parameter value, though. It doesn't make a lot of sense that spilling ~6000 pages each for two Sorts adds 6-7 seconds each time, though. What kind of storage are you working with under there? Jul 6, 2022 at 17:18
  • The machine is a Windows Server 2019 (v1809) and have a HDD (not a solid state disk). The machine is also virtualized into datacenter. Maybe i have to better parametrize SQL Server to handle HDD? (i start thinking this because it's not the first time that i get poor perfromance in reading/writing data to disk with SQL Server)
    – Skary
    Jul 7, 2022 at 12:14

0

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy