简体   繁体   English

过滤后的SQL View速度很慢。 有没有一种干净的方法来提高性能?

[英]SQL View slow when filtered. Is there a clean way to improve performance?

Let me open with: 让我开始:

SHOWPLAN permission denied in database 'MyDatabase'. SHOWPLAN权限在数据库“ MyDatabase”中被拒绝。

With that out of the way, I'll layout my situation. 有了这些,我将安排我的情况。

So, The database I work with has a view that executes fairly quickly. 因此,我使用的数据库具有一个可以快速执行的视图。

SELECT * FROM MyView

returns 32 rows in 1 second and includes a non-indexed column of values (IDs) I need to filter on. 在1秒钟内返回32行,并包含我需要进行过滤的值的非索引列(ID)。

If I filter on these IDs directly in the view: 如果我直接在视图中过滤这些ID:

SELECT * FROM MyView WHERE MyView.SomeId = 18

Things slow immensely and it takes 21 seconds to return the 20 rows with that ID. 事情非常缓慢,返回具有该ID的20行需要21秒。

As an experiment I pushed the unfiltered results into a temporary table and executed the filtered query on the the temporary table: 作为实验,我将未过滤的结果推入临时表,并在临时表上执行过滤后的查询:

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

And found that it returns the filtered results far faster (roughly 1 second) 并发现它返回过滤结果的速度要快得多(大约1秒)

Is there a cleaner syntax or pattern that can be implemented to achieve the same performance? 是否可以使用更简洁的语法或模式来实现相同的性能?


UPDATE: View Definition and Description 更新:视图定义和说明
Manually obfuscated, but I was careful so hopefully there aren't many errors. 手动混淆,但我很小心,因此希望不会有太多错误。 Still waiting on SHOWPLAN permissions, so Execution Plan is still pending. 仍在等待SHOWPLAN权限,因此执行计划仍处于待处理状态。

The view's purpose is to provide a count of all the records that belong to a specific component (CMP.COMPONENT_ID = '100') grouped by location. 该视图的目的是提供按位置分组的属于特定组件(CMP.COMPONENT_ID ='100')的所有记录的计数。

"Belonging" is determined by the record's PROC_CODE (mapped through PROC_ID) being within the CMP's inclusion range (CMP_INCs) and not in the CMP's exclusion range (CMP_EXCs). “属于”取决于记录的PROC_CODE(通过PROC_ID映射)在CMP的包含范围(CMP_INC)之内,而不在CMP的排除范围(CMP_EXC)中。

In practice, exclusion ranges are created for individual codes (the bounds are always equal) making it sufficient to check that the code is not equal a bound. 实际上,将为单个代码创建排除范围(边界始终相等),这足以检查代码是否不等于边界。

PROC_CODES can (and don't always) have an alphabetic prefix or suffix, which makes the ISNUMERIC() comparison necessary. PROC_CODES可以(但不总是)具有字母前缀或后缀,因此必须进行ISNUMERIC()比较。

Records store PROC_IDs for their PROC_CODEs, so it's necessary to convert the CMP's PROC_CODE ranges into a set of PROC_IDs for identifying which records belong to that component 记录存储其PROC_CODE的PROC_ID,因此有必要将CMP的PROC_CODE范围转换为一组PROC_ID,以识别哪些记录属于该组件

The performance issue occurs when trying to filter by DEPARTMENT_ID or LOCATION_ID 尝试按DEPARTMENT_ID或LOCATION_ID进行过滤时,会出现性能问题

[CO_RECORDS] is also a view, but if it's that deep I'm going turf this to someone with less red tape to fight through. [CO_RECORDS]也是一个视图,但是如果深度太深,我会把它草稿给繁文tape节较少的人。

    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

Based on the swift down votes, the answer to my question is 基于迅速的否决,我的问题的答案是

'No, there is not a clean syntax based solution for the improved performance, and asking for one is ignorant of the declarative nature of SQL you simple dirty plebeian'. “不, 没有一种基于语法的干净解决方案来提高性能,并且要求您忽略简单的卑鄙平民的SQL的声明性”。

From the requests for the view's definition, it's clear that performance issues in simple queries should be addressed by fixing the structure of the objects being queried ('MyView' in this case) rather than syntactical gymnastics. 从对视图定义的请求中可以明显看出,应通过固定所查询对象的结构(在这种情况下为“ MyView”)而不是句法体操来解决简单查询中的性能问题。

For interested parties the issue was resolved by adding a Row_Number() column to the final select in the view definition, wrapping it in a CTE, and using the new column in an always true filter while selecting the original columns. 对于感兴趣的各方,通过在视图定义的最终选择中添加Row_Number()列,将其包装在CTE中,并在选择原始列时在始终为true的过滤器中使用新列,从而解决了该问题。

I have no idea if this is the optimal solution. 我不知道这是否是最佳解决方案。 It doesn't feel good to me, but it appears to be working. 对我来说感觉不太好,但似乎正在运行。

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  

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM