SQL View медленно фильтруется. Есть ли чистый способ улучшить производительность? - PullRequest
0 голосов
/ 02 апреля 2019

Позвольте мне открыть с помощью:

Отказано в разрешении SHOWPLAN в базе данных 'MyDatabase'.

После этого я разметлю свою ситуацию.

Итак, база данных, с которой я работаю, имеет представление, которое выполняется довольно быстро.

SELECT * FROM MyView

возвращает 32 строки в 1 секунду и содержит неиндексированный столбец значений (идентификаторов), который мне нужендля фильтрации.

Если я отфильтрую эти идентификаторы непосредственно в представлении:

SELECT * FROM MyView WHERE MyView.SomeId = 18

Все очень замедляется, и для возврата 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_ID для их PROC_CODE, поэтому необходимо преобразовать диапазоны PROC_CODE CMP в набор 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

1 Ответ

0 голосов
/ 04 апреля 2019

На основании быстрых голосов, ответ на мой вопрос:

'Нет, существует , а не чистое решение на основе синтаксиса для улучшения производительности, и запроситьодин из них не знает о декларативной природе 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  
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...