SQL 服务器:如何提高 WHERE 子句中具有多个 CTE 和子查询的查询的性能

SQL Server: How to improve performance for queries with multiple CTEs and subqueries in the WHERE clause

我有以下两个table:

    CREATE TABLE Portfolio.DailyPortfolio
    (
    BbgID varchar(30) NOT NULL,
    Ticker varchar(22) NULL,
    Cusip char(9) NULL,
    SecurityDescription varchar(50) NOT NULL,
    AssetCategory varchar(25) NOT NULL,
    LSPosition char(3) NULL,
    Ccy varchar(25) NOT NULL,
    Quantity int NULL,
    AvgCost decimal(7,3) NULL,
    PriceLocal decimal(7,3) NULL,
    Cost int NULL,
    MktValNet int NULL,
    GLPeriod int NULL,
    Beta decimal(4,2) NULL,
    BetaExpNet int NULL,
    BetaExpGross int NULL,
    Delta decimal(4,2) NULL,
    DeltaExpNet int NULL,
    DeltaExpGross int NULL,
    Issuer varchar(48) NOT NULL,
    Country varchar(30) NOT NULL,
    Region varchar(20) NOT NULL,
    Sector varchar(30) NOT NULL,
    Industry varchar(48) NOT NULL,
    MktCapCategory varchar(24) NULL,
    MktCapEnd int NULL,
    Date date NOT NULL,
    PortfolioID  AS BbgID+LSPosition+ Convert(varchar(8),Date,112) Persisted Primary Key
    )
    GO

这是第二个table:

    CREATE TABLE Portfolio.DailyStats
    (
    Date date NOT NULL Primary Key,
    NAV int NOT NULL,
    SP500 decimal(8,4) NULL,
    R2K decimal(8,4) NULL,
    NetExp decimal(8,4) NULL,
    GrossExp decimal(8,4) NULL,
    )
    GO
    ALTER TABLE Portfolio.DailyStats
    ADD [YrMn] as  CONVERT(varchar(7), Date)
    GO

每个工作日将 80-100 行添加到 DailyPortfolio table(table 目前大约有 32,000 行)。每个工作日向 DailyStats table 添加 1 行(目前大约有 500 行)。 Daily Portfolio table 中的 Date 列与 DailyStats table.

中的 Date 列有外键关系

我必须创建一个视图,其中包含来自两个 table 的几列,并使用上一季度作为日期范围。此视图的最后一列在其计算中使用 NAV 列的平均值,其中平均值是通过使用本季度 3 个月中每个月的第一个日期的 NAV 计算的。这是我的视图 DDL:

    CREATE VIEW Portfolio.PNLLastQTD
    AS
    WITH CTE1
    AS
    (
    Select Date, NAV,YrMn, ROW_NUMBER() OVER (PARTITION BY YrMn ORDER BY Date) AS Row
    FROM Portfolio.DailyStats

    WHERE DATE BETWEEN 
        (SELECT Convert(date, DATEADD(q, DATEDIFF(q,0,GETDATE()) -1 ,0)))
    AND
        (SELECT Convert(date, DATEADD(s,-1,DATEADD(q, DATEDIFF(q,0,GETDATE()),0))))
    ),
    CTE2
    AS
    ( 
    SELECT  AvG (NAV) As AvgNAV
    FROM CTE1
    WHERE Row=1
    )
    SELECT IssuerLS, Issuer, Ticker,  SUM (GLPeriod) As [PNL],
        CAST(SUM(GLPeriod)As Decimal (13,2)) /  CAST(CTE2.[AvgNAV] As Decimal (13,2)) as [%ofNAV]
    FROM Portfolio.DailyPortfolioIssuerLS ls
    JOIN cte2 on 1=1
    WHERE ReportDate 
    BETWEEN 
        (SELECT Convert(date, DATEADD(q, DATEDIFF(q,0,GETDATE()) -1 ,0)))
    AND
        (SELECT Convert(date, DATEADD(s,-1,DATEADD(q, DATEDIFF(q,0,GETDATE()),0))))
    GROUP BY 
        Issuer, Ticker, IssuerLS, CTE2.[AvgNAV]
    GO

视图工作正常,但执行需要将近 20 秒!我在这里有几个问题:

  1. 是否应该对视图的 DDL 进行一些更改?
  2. 在 DailyPortfolio table 的日期列(如果可能的话)上创建非聚集索引是个好主意吗?
  3. 我还应该考虑其他什么来提高这个特定问题的查询性能吗?

非常感谢您的帮助。由于我是 SQL.

的新手,请原谅我的明显错误

我想结束这个问题的循环。我在这里需要做的是创建两个非聚集索引。我使用了以下步骤:

  1. 将我的查询放在查询 window 上。
  2. 单击我的工具箱上的 "Display Estimated Execution Plan" 按钮,它立即通知我缺少非集群索引。
  3. 创建了第一个非聚集索引:

    USE [OurDB]
    GO
    CREATE NONCLUSTERED INDEX NCI_DailyPort_Issuer_Date
    ON [Portfolio].[DailyPortfolio] ([Issuer],[Date])
    GO   
    
  4. 重复步骤 2 并按照建议创建第二个非聚集索引:

    USE [OurDB]
    GO
    CREATE NONCLUSTERED INDEX NCI_DailyPort_Date_INC_DexpN_Issuer
    ON [Portfolio].[DailyPortfolio] ([Date])
    INCLUDE ([DeltaExpNet],[Issuer])
    GO    
    

查询现在的执行时间不到 3 秒,明显优于之前的 24 秒。

注意:如果右键单击通知您缺少索引的行,您可以选择一个选项来查看索引的代码,这样可以节省您的时间。