T-SQL:获取多级产品类别时减少查询负载

T-SQL: Reduce query load when getting multilevel product categories

我正在存储 [products] 中的产品分配给 [products_category_mapping] 的类别。 可以将产品分配到级别 0、1 和 2 中的类别。因此,根据产品分配到的级别,产品将在 [products_category_mapping] 中出现 1、2 或 3 次。

然后我想通过查询检索有关这些产品所属类别的数据。 这些查询非常昂贵,因为它们包含大量连接,我们需要检查每个类别级别的产品是否出现在该级别。还有大约 200.000 种产品,每种都分配到 1、2 或 3 个类别,因此 运行 下面的查询需要很长时间。

我的问题:如何优化这些查询的性能?

ps。请不要建议 table 重新设计,看看我们在这个过程中所处的阶段,现在是不可行的。

当前查询:

SELECT label_nl+';'+slug_nl as labelslug_nl_0,label_en+';'+slug_en as labelslug_en_0,label_nl as label_nl_0,label_en as label_en_0,slug_nl as slug_nl_0,slug_en as slug_en_0
,pagetitle_nl as pagetitle_nl_0,pagetitle_en as pagetitle_en_0,image_nl as image_nl_0,image_en as image_en_0
,description_nl as description_nl_0,description_en as description_en_0
,metadescription_nl as metadescription_nl_0,metadescription_en as metadescription_en_0 
FROM articlegroups ga WITH (NOLOCK)
INNER JOIN products_category_mapping pcm on pcm.articlegroup_id=ga.id
INNER JOIN products gp on gp.id=pcm.artikelid
WHERE gp.id=3216743 AND ga.catlevel=0

SELECT label_nl+';'+slug_nl as labelslug_nl_0,label_en+';'+slug_en as labelslug_en_0,label_nl as label_nl_0,label_en as label_en_0,slug_nl as slug_nl_0,slug_en as slug_en_0
,pagetitle_nl as pagetitle_nl_0,pagetitle_en as pagetitle_en_0,image_nl as image_nl_0,image_en as image_en_0
,description_nl as description_nl_0,description_en as description_en_0
,metadescription_nl as metadescription_nl_0,metadescription_en as metadescription_en_0 
FROM articlegroups ga WITH (NOLOCK)
INNER JOIN products_category_mapping pcm on pcm.articlegroup_id=ga.id
INNER JOIN products gp on gp.id=pcm.artikelid
WHERE gp.id=3216743 AND ga.catlevel=1   

SELECT label_nl+';'+slug_nl as labelslug_nl_2,label_en+';'+slug_en as labelslug_en_2,label_nl as label_nl_2,label_en as label_en_2,slug_nl as slug_nl_2,slug_en as slug_en_2
,pagetitle_nl as pagetitle_nl_2,pagetitle_en as pagetitle_en_2,image_nl as image_nl_2,image_en as image_en_2
,description_nl as description_nl_2,description_en as description_en_2
,metadescription_nl as metadescription_nl_2,metadescription_en as metadescription_en_2 
FROM articlegroups ga WITH (NOLOCK)
INNER JOIN products_category_mapping pcm on pcm.articlegroup_id=ga.id
INNER JOIN products gp on gp.id=pcm.artikelid
WHERE gp.id=3216743 AND ga.catlevel=2

DDL

USE [mydb]
GO
/****** Object:  Table [dbo].[articlegroups]    Script Date: 29-04-2017 18:34:13 ******/
SET ANSI_NULLS ON
GO
SET QUOTED_IDENTIFIER ON
GO
CREATE TABLE [dbo].[articlegroups](
    [id] [int] IDENTITY(1,1) NOT NULL,
    [parentid] [int] NOT NULL,
    [catlevel] [tinyint] NOT NULL,
    [label_nl] [nvarchar](50) NOT NULL,
    [label_en] [nvarchar](50) NOT NULL,
    [slug_nl] [nvarchar](50) NOT NULL,
    [slug_en] [nvarchar](50) NOT NULL,
    [pagetitle_nl] [nvarchar](100) NULL,
    [pagetitle_en] [nvarchar](100) NULL,
    [image_nl] [nvarchar](50) NULL,
    [image_en] [nvarchar](50) NULL,
    [description_nl] [nvarchar](500) NOT NULL,
    [description_en] [nvarchar](500) NULL,
    [metadescription_nl] [nvarchar](200) NULL,
    [metadescription_en] [nvarchar](200) NULL,
    [createdate] [datetime] NOT NULL,
    [canonicalurl_nl] [nvarchar](150) NULL,
    [canonicalurl_en] [nvarchar](150) NULL,
 CONSTRAINT [PK_articlegroups] PRIMARY KEY CLUSTERED 
(
    [id] ASC
)WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON) ON [PRIMARY]
) ON [PRIMARY]
GO
ALTER TABLE [dbo].[articlegroups] ADD  CONSTRAINT [DF_articlegroups_lvl0_catlevel]  DEFAULT ((0)) FOR [catlevel]
GO
ALTER TABLE [dbo].[articlegroups] ADD  CONSTRAINT [DF_articlegroups_createdate]  DEFAULT (getdate()) FOR [createdate]
GO





USE [mydb]
GO
/****** Object:  Table [dbo].[products_category_mapping]    Script Date: 29-04-2017 18:33:59 ******/
SET ANSI_NULLS ON
GO
SET QUOTED_IDENTIFIER ON
GO
CREATE TABLE [dbo].[products_category_mapping](
    [artikelid] [int] NOT NULL,
    [articlegroup_id] [int] NOT NULL,
    [createdate] [datetime] NOT NULL
) ON [PRIMARY]
GO
ALTER TABLE [dbo].[products_category_mapping] ADD  CONSTRAINT [DF_products_category_mapping_createdate]  DEFAULT (getdate()) FOR [createdate]
GO
ALTER TABLE [dbo].[products_category_mapping]  WITH CHECK ADD  CONSTRAINT [FK_articlegroups_lvl1_mapping_products] FOREIGN KEY([artikelid])
REFERENCES [dbo].[products] ([id])
ON DELETE CASCADE
GO
ALTER TABLE [dbo].[products_category_mapping] CHECK CONSTRAINT [FK_articlegroups_lvl1_mapping_products]
GO
ALTER TABLE [dbo].[products_category_mapping]  WITH CHECK ADD  CONSTRAINT [FK_products_category_mapping_articlegroups] FOREIGN KEY([articlegroup_id])
REFERENCES [dbo].[articlegroups] ([id])
ON DELETE CASCADE
GO
ALTER TABLE [dbo].[products_category_mapping] CHECK CONSTRAINT [FK_products_category_mapping_articlegroups]
GO





USE [mydb]
GO
/****** Object:  Table [dbo].[products]    Script Date: 29-04-2017 18:33:12 ******/
SET ANSI_NULLS ON
GO
SET QUOTED_IDENTIFIER ON
GO
CREATE TABLE [dbo].[products](
    [id] [int] IDENTITY(1,1) NOT NULL,
    [friendlyurl] [nvarchar](200) NULL,
 CONSTRAINT [PK_products] PRIMARY KEY CLUSTERED 
(
    [id] ASC
)WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON) ON [PRIMARY]
) ON [PRIMARY]

GO






USE [mydb]
GO
SET IDENTITY_INSERT [dbo].[articlegroups] ON 
GO
INSERT [dbo].[articlegroups] ([id], [parentid], [catlevel], [label_nl], [label_en], [slug_nl], [slug_en], [pagetitle_nl], [pagetitle_en], [image_nl], [image_en], [description_nl], [description_en], [metadescription_nl], [metadescription_en], [createdate], [canonicalurl_nl], [canonicalurl_en]) VALUES (129, 0, 0, N'Baby / Geboorte', N'Baby and birth', N'baby-en-geboorte', N'baby-and-birth', N'Baby- en geboorte producten online kopen', N'Baby and birth', N'', N'', N'Alle baby en geboorte artikelen in 1 overzicht. Van kinderwagens tot baby-monitors tot commodes tot de inrichting van de kraamkamer.', N'', N'Online baby en geboorte producten kopen kan hier. Goedkope producten die jou helpen wanneer je net een  baby hebt!', N'', CAST(N'2016-04-12T23:31:43.003' AS DateTime), NULL, NULL)
GO
INSERT [dbo].[articlegroups] ([id], [parentid], [catlevel], [label_nl], [label_en], [slug_nl], [slug_en], [pagetitle_nl], [pagetitle_en], [image_nl], [image_en], [description_nl], [description_en], [metadescription_nl], [metadescription_en], [createdate], [canonicalurl_nl], [canonicalurl_en]) VALUES (680, 129, 1, N'Geboortekaartjes', N'Birth announcement', N'geboorte-kaartjes', N'birth-announcement-cards', N'Baby & Geboortekaartjes bestellen, drukken en versturen', N'Birth Announcements & Baby Birth Announcement Cards', N'', N'', N'Baby & geboortekaartjes bestel je in onze webshop. Wij hebben een flink aanbod in mooie en leuke kaartjes voor baby en geboorte. Schattige kaartjes bestellen.', N'', N'Leuke kaartjes voor baby & geboorte kun je hier eenvoudig bestellen. Originele en leuke kaarten speciaal voor de kleintjes.', N'', CAST(N'2016-04-12T23:31:43.310' AS DateTime), NULL, NULL)
GO
SET IDENTITY_INSERT [dbo].[articlegroups] OFF
GO

SET IDENTITY_INSERT [dbo].[products] ON 
GO
INSERT [dbo].[products] ([id],[friendlyurl]) VALUES (3216743, N'birth-with-flowers')




INSERT [dbo].[products_category_mapping] ([artikelid], [articlegroup_id], [createdate]) VALUES (3216743, 129, CAST(N'2017-04-24T20:05:58.463' AS DateTime))
GO
INSERT [dbo].[products_category_mapping] ([artikelid], [articlegroup_id], [createdate]) VALUES (3216743, 680, CAST(N'2017-04-24T20:05:58.463' AS DateTime))
GO

** 更新 1 **

根据下面评论中@HABO 的建议,我尝试使用 case 语句组合 3 个查询:

SELECT
CASE 
    when ga.catlevel=0 THEN
         label_nl+';'+slug_nl as labelslug_nl_0,label_en+';'+slug_en as labelslug_en_0,label_nl as label_nl_0,label_en as label_en_0,slug_nl as slug_nl_0,slug_en as slug_en_0
        ,pagetitle_nl as pagetitle_nl_0,pagetitle_en as pagetitle_en_0,image_nl as image_nl_0,image_en as image_en_0
        ,description_nl as description_nl_0,description_en as description_en_0
        ,metadescription_nl as metadescription_nl_0,metadescription_en as metadescription_en_0 
    when ga.catlevel=1 THEN
         label_nl+';'+slug_nl as labelslug_nl_1,label_en+';'+slug_en as labelslug_en_1,label_nl as label_nl_1,label_en as label_en_1,slug_nl as slug_nl_1,slug_en as slug_en_1
        ,pagetitle_nl as pagetitle_nl_1,pagetitle_en as pagetitle_en_1,image_nl as image_nl_1,image_en as image_en_1
        ,description_nl as description_nl_1,description_en as description_en_1
        ,metadescription_nl as metadescription_nl_1,metadescription_en as metadescription_en_1 
    else null
END
FROM globos_articlegroups ga WITH (NOLOCK)
INNER JOIN globos_products_category_mapping pcm on pcm.articlegroup_id=ga.id
INNER JOIN globos_products gp on gp.id=pcm.artikelid
WHERE gp.id=3216743 AND ga.catlevel in (0,1,2) 

但是我还不清楚,我如何确定如何根据猫级别区分列:0,1 或 2

更新 2

我得到了想要的结果集,但它有多行我想合并成一行。当有多行时,每列的最大值始终为 1,其他值为 NULL。我想将多行合并为一行,其中每列的最大值(即不是 NULL)将保留。

当前结果集

  <table>
    <tbody>
      <tr>
        <th>labelslug_nl_0
        </th>
        <th>labelslug_nl_1
        </th>
        <th>labelslug_nl_2
        </th>
      </tr>
      <tr>
        <td>Baby / Geboorte;baby-en-geboorte
        </td>
        <td>NULL
        </td>
        <td>NULL
        </td>
      </tr>
      <tr>
        <td>NULL
        </td>
        <td>Geboortekaartjes;geboorte-kaartjes
        </td>
        <td>NULL
        </td>
      </tr>
    </tbody>
  </table>

想要的结果集

  <table>
    <tbody>
      <tr>
        <th>labelslug_nl_0
        </th>
        <th>labelslug_nl_1
        </th>
        <th>labelslug_nl_2
        </th>
      </tr>
      <tr>
        <td>Baby / Geboorte;baby-en-geboorte
        </td>
        <td>Geboortekaartjes;geboorte-kaartjes
        </td>
        <td>NULL
        </td>
      </tr>
    </tbody>
  </table>

这个单一查询将检索您的三个查询返回的所有行。 CatLevel 01 行将填充第一组列,而第二组列填充空值。 CatLevel 2 行将执行相反的操作。

select
  case when AG.CatLevel in ( 0, 1 ) then label_nl + ';' + slug_nl end as labelslug_nl_0,
  case when AG.CatLevel in ( 0, 1 ) then label_en + ';' + slug_en as labelslug_en_0,
  case when AG.CatLevel in ( 0, 1 ) then label_nl end as label_nl_0,
  case when AG.CatLevel in ( 0, 1 ) then label_en end as label_en_0,
  case when AG.CatLevel in ( 0, 1 ) then slug_nl end as slug_nl_0,
  case when AG.CatLevel in ( 0, 1 ) then slug_en end as slug_en_0,
  case when AG.CatLevel in ( 0, 1 ) then pagetitle_nl end as pagetitle_nl_0,
  case when AG.CatLevel in ( 0, 1 ) then pagetitle_en end as pagetitle_en_0,
  case when AG.CatLevel in ( 0, 1 ) then image_nl end as image_nl_0,
  case when AG.CatLevel in ( 0, 1 ) then image_en end as image_en_0,
  case when AG.CatLevel in ( 0, 1 ) then description_nl end as description_nl_0,
  case when AG.CatLevel in ( 0, 1 ) then description_en end as description_en_0,
  case when AG.CatLevel in ( 0, 1 ) then metadescription_nl end as metadescription_nl_0,
  case when AG.CatLevel in ( 0, 1 ) then metadescription_en end as metadescription_en_0,
  case when AG.CatLevel = 2 then label_nl + ';' + slug_nl end as labelslug_nl_2,
  case when AG.CatLevel = 2 then label_en + ';' + slug_en as labelslug_en_2,
  case when AG.CatLevel = 2 then label_nl end as label_nl_2,
  case when AG.CatLevel = 2 then label_en end as label_en_2,
  case when AG.CatLevel = 2 then slug_nl end as slug_nl_2,
  case when AG.CatLevel = 2 then slug_en end as slug_en_2,
  case when AG.CatLevel = 2 then pagetitle_nl end as pagetitle_nl_2,
  case when AG.CatLevel = 2 then pagetitle_en end as pagetitle_en_2,
  case when AG.CatLevel = 2 then image_nl end as image_nl_2,
  case when AG.CatLevel = 2 then image_en end as image_en_2,
  case when AG.CatLevel = 2 then description_nl end as description_nl_2,
  case when AG.CatLevel = 2 then description_en end as description_en_2,
  case when AG.CatLevel = 2 then metadescription_nl end as metadescription_nl_2,
  case when AG.CatLevel = 2 then metadescription_en end as metadescription_en_2
  from ArticleGroups as AG inner join
    Products_Category_Mapping as PCM on PCM.ArticleGroup_Id = AG.Id inner join
    Products as P on P.Id = PCM.ArtikelId
  where P.Id = 3216743 and AG.CatLevel in ( 0, 1, 2 )

请注意,所有引用的列都应包含别名,例如AG.Label_NL。这留给 OP 作为练习。