查询调优问题

Mar*_*aes 6 sql-server t-sql tuning

我在尝试调整以下查询时遇到了麻烦。我曾经 100% 专注于基础设施方面,但自今年 8 月以来,我在一家处理卡公司工作,该公司只要求我的“阴暗面”——查询调整技能。

如果您能为我提供提高查询性能的建议和技巧,我将不胜感激。以下是表格:

CREATE TABLE [dbo].[Delivery](
[Id] [int] IDENTITY(1,1) NOT NULL,
[Address_Name] [varchar](120) NULL,
[Address_Street] [varchar](120) NULL,
[Address_Complement] [varchar](120) NULL,
[Address_City] [varchar](120) NULL,
[Address_District] [varchar](120) NULL,
[Address_Number] [varchar](120) NULL,
[Address_State] [varchar](120) NULL,
[Address_ZipCode] [varchar](120) NULL,
[OsFk] [int] NOT NULL,
[WebstoreOrderId] [int] NOT NULL,
[CourierType] [int] NULL,
 CONSTRAINT [PK_Delivery] 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]


CREATE TABLE [dbo].[OrderItem](
[Id] [int] IDENTITY(1,1) NOT NULL,
[CardId] [bigint] NULL,
[WebstoreOrderId] [int] NULL,
[WebstoreEcommerceTypeId] [tinyint] NULL,
[WebstoreChannelId] [int] NULL,
[OS_Id] [int] NULL,
[Delivery_Id] [int] NULL,
[TrackingCode] [varchar](50) NULL,
[WebstoreOrderDate] [datetime] NULL,
[IsBipped] [bit] NULL,
[WebstoreCategoryId] [int] NULL,
[WebstoreOrderItemId] [int] NULL,
[Product] [int] NULL,
[IsPrePrinted] [bit] NULL,
CONSTRAINT [PK_OrderItem] 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]


CREATE TABLE [dbo].[Os](
[Id] [int] IDENTITY(1,1) NOT NULL,
[Status] [int] NULL,
[OSType_Id] [int] NULL,
[CreatedAt] [datetime] NULL,
[ParentOS_Id] [int] NULL,
[HasChip] [bit] NULL,
[HasProcessingError] [bit] NULL,
[Product] [int] NULL,
[IsPrePrinted] [bit] NULL,
CONSTRAINT [PK_ServiceOrder] 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]


CREATE TABLE [dbo].[OSType](
[Id] [int] IDENTITY(1,1) NOT NULL,
[Channels] [varchar](100) NULL,
[EcommerceTypes] [varchar](100) NULL,
[Name] [varchar](50) NULL,
[TypeCode] [int] NULL,
[DeliveryIsLabelled] [bit] NULL,
[IsActive] [bit] NULL,
[Modalities] [varchar](100) NULL,
[PasswordLetterType] [int] NULL,
[Categories] [varchar](100) NULL,
CONSTRAINT [PK_Channel] 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]



ALTER TABLE [dbo].[OrderItem]  WITH CHECK ADD  CONSTRAINT [FK_Delivery_X_OrderItem] FOREIGN KEY([Delivery_Id])
REFERENCES [dbo].[Delivery] ([Id])

ALTER TABLE [dbo].[OrderItem] CHECK CONSTRAINT [FK_Delivery_X_OrderItem]

ALTER TABLE [dbo].[OrderItem]  WITH CHECK ADD  CONSTRAINT [FK_Os_X_OrderItem] FOREIGN KEY([OS_Id])
REFERENCES [dbo].[Os] ([Id])

ALTER TABLE [dbo].[OrderItem] CHECK CONSTRAINT [FK_Os_X_OrderItem]
Run Code Online (Sandbox Code Playgroud)

这是查询:

SELECT ISNULL(ParentOS_Id,Id) Id, 
OSTypeId TypeId, 
OSType Type, 
SUM(Status) Status, 
SerializedOrderIds, 
SUM(StripeBlankStatus) StripeBlankStatus, 
SUM(ChipBlankStatus) ChipBlankStatus, 
SUM(StripePrePrintedStatus) StripePrePrintedStatus, 
SUM(ChipPrePrintedStatus) ChipPrePrintedStatus, 
SUM(StripeBlankId) StripeBlankId, 
SUM(ChipBlankId) ChipBlankId, 
SUM(StripePrePrintedId) StripePrePrintedId, 
SUM(ChipPrePrintedId) ChipPrePrintedId, 
CreatedAt Date, 
HasProcessingError HasProcessingError, 
SUM(BippedCardCount) BippedCardCount, 
SUM(DeliveryCount) DeliveryCount, 
SUM(StripeBlankCardCount) StripeBlankCardCount, 
SUM(ChipBlankCardCount) ChipBlankCardCount, 
SUM(StripePrePrintedCardCount) StripePrePrintedCardCount, 
SUM(ChipPrePrintedCardCount) ChipPrePrintedCardCount, 
SUM(CardCount) CardCount 
FROM ( 
     SELECT Id, 
     ParentOS_Id, 
     OSTypeId, 
     Name OSType, 
     CreatedAt, 
     Status, 
     StripeBlankStatus, 
     ChipBlankStatus, 
     StripePrePrintedStatus, 
     ChipPrePrintedStatus, 
     StripeBlankId, 
     ChipBlankId, 
     StripePrePrintedId, 
     ChipPrePrintedId, 
     HasProcessingError, 
     BippedCardCount, 
     DeliveryCount, 
     StripeBlankCardCount, 
     StripePrePrintedCardCount, 
     ChipBlankCardCount, 
     ChipPrePrintedCardCount, 
     (StripeBlankCardCount + ChipBlankCardCount + StripePrePrintedCardCount + ChipPrePrintedCardCount) AS CardCount, 
     SerializedOrderIds FROM ( 
                         SELECT OS.Id, 
                         OSType.Id OSTypeId, 
                         OSType.Name, 
                         OS.CreatedAt, 
                         OS.ParentOS_Id, 
                         (SELECT CASE WHEN OS.ParentOS_Id IS NULL THEN OS.Status ELSE NULL END FROM OS OS_STATUS WHERE OS_STATUS.Id = OS.ID) Status, 
                         (SELECT TOP 1 OS_ERROR.HasProcessingError FROM OS OS_ERROR WHERE OS_ERROR.Id = OS.Id OR OS_ERROR.ParentOS_Id = OS.Id 
                         ORDER BY OS_ERROR.HasProcessingError DESC) AS HasProcessingError, 
                         (SELECT COUNT(1) FROM OrderItem TB WHERE (OS.Id = TB.OS_Id) AND TB.IsBipped = 1) AS BippedCardCount, 

                         (SELECT COUNT( DISTINCT O.Delivery_Id) FROM dbo.OrderItem O JOIN dbo.Delivery D ON O.Delivery_Id = D.ID WHERE O.OS_Id = OS.Id) AS DeliveryCount, 
                         (SELECT CASE WHEN PRODUCT = 0 THEN OS_STATUS.STATUS ELSE NULL END FROM OS OS_STATUS WHERE OS.Id = OS_STATUS.Id ) AS StripeBlankStatus, 
                         (SELECT CASE WHEN PRODUCT = 1 THEN OS_STATUS.STATUS ELSE NULL END FROM OS OS_STATUS WHERE OS.Id = OS_STATUS.Id ) AS ChipBlankStatus, 
                         (SELECT CASE WHEN PRODUCT = 2 THEN OS_STATUS.STATUS ELSE NULL END FROM OS OS_STATUS WHERE OS.Id = OS_STATUS.Id ) AS StripePrePrintedStatus, 
                         (SELECT CASE WHEN PRODUCT = 4 THEN OS_STATUS.STATUS ELSE NULL END FROM OS OS_STATUS WHERE OS.Id = OS_STATUS.Id ) AS ChipPrePrintedStatus, 
                         (SELECT CASE WHEN PRODUCT = 0 THEN OS_IN.Id ELSE NULL END FROM OS OS_IN WHERE OS.Id = OS_IN.Id ) AS StripeBlankId, 
                         (SELECT CASE WHEN PRODUCT = 1 THEN OS_IN.Id ELSE NULL END FROM OS OS_IN WHERE OS.Id = OS_IN.Id ) AS ChipBlankId, 
                         (SELECT CASE WHEN PRODUCT = 2 THEN OS_IN.Id ELSE NULL END FROM OS OS_IN WHERE OS.Id = OS_IN.Id ) AS StripePrePrintedId, 
                         (SELECT CASE WHEN PRODUCT = 4 THEN OS_IN.Id ELSE NULL END FROM OS OS_IN WHERE OS.Id = OS_IN.Id ) AS ChipPrePrintedId, 
                         (SELECT COUNT(1) FROM OrderItem OI WHERE OS.Product = 0 AND OS.Id = OI.OS_Id) AS StripeBlankCardCount, 
                         (SELECT COUNT(1) FROM OrderItem OI WHERE OS.Product = 1 AND OS.Id = OI.OS_Id) AS ChipBlankCardCount, 
                         (SELECT COUNT(1) FROM OrderItem OI WHERE OS.Product = 2 AND OS.Id = OI.OS_Id) AS StripePrePrintedCardCount, 
                         (SELECT COUNT(1) FROM OrderItem OI WHERE OS.Product = 4 AND OS.Id = OI.OS_Id) AS ChipPrePrintedCardCount, 
                         (SUBSTRING((SELECT DISTINCT ',' + CONVERT(VARCHAR, WebstoreOrderId) AS [text()] FROM dbo.OrderItem OI WHERE ISNULL(OS.ParentOS_Id,OS.ID) = OI.OS_Id FOR XML PATH ('')), 2, 100000)) SerializedOrderIds 
                          FROM dbo.Os OS 
                          JOIN dbo.OSType OSType ON OSType.Id = OS.OSType_Id 
                          ) AS QUERY 
     ) RESULT 
GROUP BY ISNULL(ParentOS_Id, Id), OSType, OSTypeId, CreatedAt, HasProcessingError, SerializedOrderIds 
Run Code Online (Sandbox Code Playgroud)

Mik*_*sky 4

我将从名为“QUERY”的内部查询中删除所有子查询,并创建临时表或 CTE。

原因是, select 语句中的每个子查询都会针对外部查询生成的每条记录进行评估,从而降低查询的性能,并可能导致整个环境的性能问题。

下面是一个示例,用于将订单计数子查询替换为与包含订单计数数据集的 CTE 的联接。如果您的数据集很大,您可以将 CTE 更改为临时表,从而使您可以灵活地添加索引或创建自己的统计信息。

;WITH CTE_ORDER(OS_Id, OrderCnt)
AS
(
    SELECT 
        OI.OS_Id,
        COUNT(*)
    FROM OrderItem OI 
    GROUP BY OI.OS_Id
)

SELECT 
OS.Id, 
CASE WHEN OS.Product = 0 THEN ORDER.OrderCnt ELSE NULL END AS StripeBlankCardCount,
CASE WHEN OS.Product = 1 THEN ORDER.OrderCnt ELSE NULL END AS ChipBlankCardCount, 
CASE WHEN OS.Product = 2 THEN ORDER.OrderCnt ELSE NULL END AS StripePrePrintedCardCount, 
CASE WHEN OS.Product = 4 THEN ORDER.OrderCnt ELSE NULL END AS ChipPrePrintedCardCount 
--(SELECT COUNT(1) FROM OrderItem OI WHERE OS.Product = 0 AND OS.Id = OI.OS_Id) AS StripeBlankCardCount, 
--(SELECT COUNT(1) FROM OrderItem OI WHERE OS.Product = 1 AND OS.Id = OI.OS_Id) AS ChipBlankCardCount, 
--(SELECT COUNT(1) FROM OrderItem OI WHERE OS.Product = 2 AND OS.Id = OI.OS_Id) AS StripePrePrintedCardCount, 
--(SELECT COUNT(1) FROM OrderItem OI WHERE OS.Product = 4 AND OS.Id = OI.OS_Id) AS ChipPrePrintedCardCount, 
FROM dbo.Os OS 
    INNER JOIN dbo.OSType OSType 
        ON OSType.Id = OS.OSType_Id 
    INNER JOIN CTE_ORDER ORDER
        ON ORDER.OS_Id = OS.Id
Run Code Online (Sandbox Code Playgroud)

希望这可以帮助!

编辑:更正了 CTE 的名称和列名称。

  • 总体来说很好的建议,+1。创建一个 CTE(或派生表)而不是 4 个相关子查询可能会是一项(重大的)改进(并且可能根本不需要临时表)。如果查询也可以重写以避免“GROUP BY”(带有那个巨大的列表),那也很好。 (3认同)