【问题标题】:SQL View slow when filtered. Is there a clean way to improve performance?过滤时 SQL 视图慢。有没有一种干净的方法来提高性能?
【发布时间】:2019-04-02 20:32:59
【问题描述】:

让我打开:

数据库“MyDatabase”中的 SHOWPLAN 权限被拒绝。

说完这些,我会布置一下我的情况。

所以,我使用的数据库有一个执行速度相当快的视图。

SELECT * FROM MyView

在 1 秒内返回 32 行,并包含我需要过滤的值 (ID) 的非索引列。

如果我直接在视图中过滤这些 ID:

SELECT * FROM MyView WHERE MyView.SomeId = 18

速度非常慢,返回具有该 ID 的 20 行需要 21 秒。

作为一个实验,我将未过滤的结果推送到临时表中,并在临时表上执行过滤后的查询:

IF OBJECT_ID('tempdb..#TEMP_TABLE') IS NOT NULL
BEGIN
    DROP TABLE #TEMP_TABLE
END    

SELECT * INTO #TEMP_TABLE
FROM MyView;

SELECT * 
FROM #TEMP_TABLE 
WHERE #TEMP_TABLE.SomeId = 18

DROP TABLE #TEMP_TABLE

发现它返回过滤结果的速度要快得多(大约 1 秒)

是否有更简洁的语法或模式可以实现相同的性能?


更新:查看定义和说明
手动混淆,但我很小心,所以希望没有太多错误。仍在等待 SHOWPLAN 权限,因此执行计划仍在等待中。

视图的目的是提供属于按位置分组的特定组件 (CMP.COMPONENT_ID = '100') 的所有记录的计数。

“归属”由记录的 ​​PROC_CODE(通过 PROC_ID 映射)在 CMP 的包含范围 (CMP_INCs) 内而不在 CMP 的排除范围 (CMP_EXCs) 内确定。

实际上,为各个代码创建排除范围(边界始终相等),足以检查代码是否等于边界。

PROC_CODES 可以(并不总是)有一个字母前缀或后缀,这使得 ISNUMERIC() 比较成为必要。

记录存储其 PROC_CODE 的 PROC_ID,因此有必要将 CMP 的 PROC_CODE 范围转换为一组 PROC_ID,以识别哪些记录属于该组件

尝试按 DEPARTMENT_ID 或 LOCATION_ID 过滤时出现性能问题

[CO_RECORDS] 也是一个视图,但如果它有那么深,我会把它交给没有繁文缛节的人来解决。

    CREATE VIEW [ViewsSchema].[MyView] AS 
    WITH 
    CMP_INCs AS (SELECT RNG.*, COALESCE(RNG.RANGE_END, RNG.RANGE_BEG) [SAFE_END] FROM DBEngine.DBO.DB_CMP_RANGE [RNG] WHERE [RNG].COMPONENT_ID = '100'),
    CMP_EXCs AS (SELECT CER.* FROM DBEngine.DBO.DB_CMP_EXC_RANGE CER WHERE CER.COMPONENT_ID = '100'),
    CMP_PROC_IDs AS (
        SELECT 
            DBEngine_ProcTable.PROC_ID          [CMP_PROC_ID],
            DBEngine_ProcTable.PROC_CODE        [CMP_PROC_CODE],
            DB_CmpTable.COMPONENT_ID            [CMP_ID],
            MAX(DB_CmpTable.COMPONENT_NAME)     [CMP_NAME]

        FROM        [DBEngine].DBO.DBEngine_ProcTable   DBEngine_ProcTable
        LEFT JOIN   CMP_INCs                            ON      ISNUMERIC(DBEngine_ProcTable.PROC_CODE) = ISNUMERIC(CMP_INCs.RANGE_BEG) 
                                                        AND(DBEngine_ProcTable.PROC_CODE = CMP_INCs.RANGE_BEG 
                                                         OR DBEngine_ProcTable.PROC_CODE BETWEEN CMP_INCs.RANGE_BEG AND CMP_INCs.SAFE_END)

        INNER JOIN  DBEngine.DBO.DB_CmpTable            ON CMP_INCs.COMPONENT_ID = DB_CmpTable.COMPONENT_ID
        LEFT JOIN   CMP_EXCs    EXCS                    ON EXCS.COMPONENT_ID = DB_CmpTable.COMPONENT_ID AND EXCS.EXCL_RANGE_END = DBEngine_ProcTable.PROC_CODE

        WHERE       EXCS.EXCL_RANGE_BEG IS NULL

        GROUP BY 
            DBEngine_ProcTable.PROC_ID,
            DBEngine_ProcTable.PROC_CODE,
            DBEngine_ProcTable.BILL_DESC,
            DBEngine_ProcTable.PROC_NAME,
            DB_CmpTable.COMPONENT_ID
    )

    SELECT 
         RECORD.LOCATION_NAME               [LOCATION_NAME]
       , RECORD.LOCATION_ID                 [LOCATION_ID]
       , MAX(RECORD.[Department])           [DEPARTMENT]
       , RECORD.[Department ID]             [DEPARTMENT_ID]
       , SUM(RECORD.PROCEDURE_QUANTITY)     [PROCEDURE_COUNT]

    FROM        DBEngineCUSTOMRPT.ViewsSchema.CO_RECORDS        [RECORDS]
    INNER JOIN  CMP_PROC_IDs                                    [CV]        ON [CV].CMP_PROC_ID = [RECORDS].PROC_ID
    CROSS JOIN  (SELECT DATEADD(M, DATEDIFF(M, 0,GETDATE()), 0) [FIRSTOFTHEMONTH])      VARS

    WHERE [RECORDS].TYPE = 1
    AND   ([RECORDS].VOID_DATE IS NULL OR [RECORDS].VOID_DATE >= VARS.[FIRSTOFTHEMONTH] )
    AND   [RECORDS].POST_DATE < VARS.[FIRSTOFTHEMONTH] 
    AND   [RECORDS].DOS_MONTHS_BACK = 2

    GROUP BY [RECORDS].LOCATION_NAME, [RECORDS].[Department ID]
    GO

【问题讨论】:

  • 看来问题出在MyView(您还没有向我们展示代码)。
  • 如果你可以访问sys表,你可以查询执行计划,一旦你点击xml结果,它将在新窗口中为你打开执行计划。
  • 这对我来说是新的。让我看看我能挖出什么。
  • 无权查看执行计划并不是“碍事”。如果您的公司确定您需要解决性能问题,那么他们需要让您访问允许您执行此操作的工具。另一种选择是将您的查询发送给高级和强大的 DBA,并告诉他们您需要他们来运行它并向您发送实际的执行计划,因为您无权访问。大约在您第二次这样做时,您将神奇地获得访问权限。解决性能问题的方法不是通过反复试验,而是通过分析执行。而这方面的工具就是执行计划。
  • 请包含MyView的定义。没有它,我们真的不可能提供帮助。当您添加过滤条件时,SQL 优化器很可能选择了错误的索引。表格统计信息是最新的吗?

标签: sql sql-server performance temp-tables sql-view


【解决方案1】:

根据 swift down 投票,我的问题的答案是

'不,没有没有一个基于干净语法的解决方案 性能,而要求一个是不了解声明性的 的 SQL 你简单肮脏的平民'。

从对视图定义的请求来看,很明显,简单查询中的性能问题应该通过修复被查询对象的结构(在本例中为“MyView”)而不是语法体操来解决。

对于感兴趣的各方,通过将 Row_Number() 列添加到视图定义中的最终选择,将其包装在 CTE 中,并在选择原始列时在始终为真的过滤器中使用新列,问题得到了解决。

我不知道这是否是最佳解决方案。这对我来说感觉不太好,但它似乎起作用了。

CREATE VIEW [ViewsSchema].[MyView] AS 
WITH 
CMP_INCs AS (SELECT RNG.*, COALESCE(RNG.RANGE_END, RNG.RANGE_BEG) [SAFE_END] FROM DBEngine.DBO.DB_CMP_RANGE [RNG] WHERE [RNG].COMPONENT_ID = '100'),
CMP_EXCs AS (SELECT CER.* FROM DBEngine.DBO.DB_CMP_EXC_RANGE CER WHERE CER.COMPONENT_ID = '100'),
CMP_PROC_IDs AS (
        SELECT 
            DBEngine_ProcTable.PROC_ID          [CMP_PROC_ID],
            DBEngine_ProcTable.PROC_CODE        [CMP_PROC_CODE],
            DB_CmpTable.COMPONENT_ID            [CMP_ID],
            MAX(DB_CmpTable.COMPONENT_NAME)     [CMP_NAME]

        FROM        [DBEngine].DBO.DBEngine_ProcTable   DBEngine_ProcTable
        LEFT JOIN   CMP_INCs                            ON      ISNUMERIC(DBEngine_ProcTable.PROC_CODE) = ISNUMERIC(CMP_INCs.RANGE_BEG) 
                                                        AND(DBEngine_ProcTable.PROC_CODE = CMP_INCs.RANGE_BEG 
                                                         OR DBEngine_ProcTable.PROC_CODE BETWEEN CMP_INCs.RANGE_BEG AND CMP_INCs.SAFE_END)

        INNER JOIN  DBEngine.DBO.DB_CmpTable            ON CMP_INCs.COMPONENT_ID = DB_CmpTable.COMPONENT_ID
        LEFT JOIN   CMP_EXCs    EXCS                    ON EXCS.COMPONENT_ID = DB_CmpTable.COMPONENT_ID AND EXCS.EXCL_RANGE_END = DBEngine_ProcTable.PROC_CODE

        WHERE       EXCS.EXCL_RANGE_BEG IS NULL

        GROUP BY 
            DBEngine_ProcTable.PROC_ID,
            DBEngine_ProcTable.PROC_CODE,
            DBEngine_ProcTable.BILL_DESC,
            DBEngine_ProcTable.PROC_NAME,
            DB_CmpTable.COMPONENT_ID
),

RESULTS as (
    SELECT 
      RECORD.LOCATION_NAME               [LOCATION_NAME]
    , RECORD.LOCATION_ID                 [LOCATION_ID]
    , MAX(RECORD.[Department])           [DEPARTMENT]
    , RECORD.[Department ID]             [DEPARTMENT_ID]
    , SUM(RECORD.PROCEDURE_QUANTITY)     [PROCEDURE_COUNT]
    , ROW_NUMBER() OVER (ORDER BY TDL.[Medical Department ID], TDL.[BILL_AREA_ID], TDL.JP_POS_NAME) [ROW]

    FROM        DBEngineCUSTOMRPT.ViewsSchema.CO_RECORDS        [RECORDS]
    INNER JOIN  CMP_PROC_IDs                                    [CV]        ON [CV].CMP_PROC_ID = [RECORDS].PROC_ID
    CROSS JOIN  (SELECT DATEADD(M, DATEDIFF(M, 0,GETDATE()), 0) [FIRSTOFTHEMONTH])      VARS

    WHERE [RECORDS].TYPE = 1
    AND   ([RECORDS].VOID_DATE IS NULL OR [RECORDS].VOID_DATE >= VARS.[FIRSTOFTHEMONTH] )
    AND   [RECORDS].POST_DATE < VARS.[FIRSTOFTHEMONTH] 
    AND   [RECORDS].DOS_MONTHS_BACK = 2

    GROUP BY [RECORDS].LOCATION_NAME, [RECORDS].[Department ID]
)
SELECT 
    [LOCATION_NAME]
  , [LOCATION_ID]
  , [DEPARTMENT]
  , [DEPARTMENT_ID]
  , [PROCEDURE_COUNT]
FROM RESULTS 
WHERE [ROW] > 0
GO  

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2022-08-11
    • 1970-01-01
    • 1970-01-01
    • 2011-06-12
    • 2012-10-14
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多