SQL: Как добавить предложения GROUP BY / ORDER BY в запрос с вложенным оператором case - PullRequest
0 голосов
/ 22 января 2020
SELECT DISTINCT *
FROM 
    (SELECT dbtables.[name] as 'back_end_name', 
            CASE
                WHEN dbtables.[name] LIKE '_Result%' AND CAST(RIGHT(dbtables.[name], LEN(dbtables.[name]) - 7) AS int) = CalculationID
                THEN Calculation.Name
                WHEN dbtables.[name] LIKE '_History%' 
                THEN
                    CASE
                        WHEN LEN(dbtables.[name]) = 9 AND CAST(RIGHT(dbtables.[name], 1) AS int) = HistoryID
                        THEN HistoryMap.TableName
                        WHEN LEN(dbtables.[name]) = 10 AND CAST(RIGHT(dbtables.[name], 2) AS int) = HistoryID
                        THEN HistoryMap.TableName
                        WHEN LEN(dbtables.[name]) = 11 AND CAST(RIGHT(dbtables.[name], 3) AS int) = HistoryID
                        THEN HistoryMap.TableName
                        ELSE 'N/A'
                    END 
                ELSE 'N/A'
            END AS front_end_name,
            indexstats.avg_fragmentation_in_percent,
            indexstats.page_count,
            indexstats.record_count
        FROM Calculation, HistoryMap, sys.dm_db_index_physical_stats (DB_ID(), NULL, NULL, NULL, 'Sampled') AS indexstats
        INNER JOIN sys.tables dbtables on dbtables.[object_id] = indexstats.[object_id]
        INNER JOIN sys.schemas dbschemas on dbtables.[schema_id] = dbschemas.[schema_id]
        INNER JOIN sys.indexes AS dbindexes ON dbindexes.[object_id] = indexstats.[object_id]
        AND indexstats.index_id = dbindexes.index_id
        WHERE indexstats.database_id = DB_ID() AND indexstats.avg_fragmentation_in_percent >= 30) tmp
WHERE front_end_name NOT LIKE '%N/A%'

У меня есть следующий запрос, который работает, однако некоторые данные в 'front_end_name' дублируются, когда я хочу только 1 экземпляр, поэтому я добавляю 'GROUP BY front_end_name' в самом конце, но он возвращает ошибку "Столбец" tmp.back_end_name 'недопустимо в списке выбора, поскольку оно не содержится ни в статистической функции, ни в предложении GROUP BY ". Почему сейчас говорится о back_end_name?

Когда я добавляю GROUP BY после первого предложения WHERE, в конце вложенного запроса он говорит, что front_end_name является недопустимым именем столбца.

Также Я хочу ORDER BY indexstats.avg_fragmentation_in_percent. Очевидно, я не могу сделать это в самом конце запроса, потому что indexstats локально внутри вложенного запроса. Когда я пытаюсь сделать это в конце вложенного запроса, он говорит мне: «Предложение ORDER BY недопустимо в представлениях, встроенных функциях, производных таблицах, подзапросах и выражениях общих таблиц, если только TOP, OFFSET или FOR XML не являются также указано. "

Любые советы, как я могу добавить GROUP BY front_end_name и ORDER BY indexstats.avg_fragmentation_in_percent к моему существующему запросу? Все еще довольно плохо знаком с SQL, поэтому я учусь. Я стараюсь избегать написания описания дела более одного раза.

Спасибо!

1 Ответ

0 голосов
/ 22 января 2020

Не просто добавьте GROUP BY в любом месте, чтобы удалить дубликаты. Это используется в основном для агрегирования. Сделай шаг назад и подумай о своих потребностях.

  • Сначала спросите себя: когда front_end_name повторяется, другие столбцы (back_end_name, avg_fragmentation_in_percent, page_count, record_count) повторить, а? Скорее всего, они не повторяются, так как ваш внешний запрос использует DISTINCT.

  • Теперь подумайте о своих данных. Как обрабатывать неповторяющиеся столбцы для каждого повторяющегося front_end_name? Вероятно, это связано с гранулярностью объединенных таблиц Calculation, HistoryMap, sys.tables и др. c. которые могут иметь отношение «один ко многим» или «многие ко многим» в соответствии с вашим расчетным значением front_end_name .

Использование CTE для хранения вашего необработанного , base результаты, рассмотрите возможность объединения других столбцов, чтобы избежать повторения имен

WITH base AS (
     SELECT dbtables.[name] AS back_end_name, 
            CASE
                WHEN dbtables.[name] LIKE '_Result%' AND CAST(RIGHT(dbtables.[name], LEN(dbtables.[name]) - 7) AS int) = CalculationID
                THEN Calculation.Name
                WHEN dbtables.[name] LIKE '_History%' 
                THEN
                    CASE
                        WHEN LEN(dbtables.[name]) = 9 AND CAST(RIGHT(dbtables.[name], 1) AS int) = HistoryID
                        THEN HistoryMap.TableName
                        WHEN LEN(dbtables.[name]) = 10 AND CAST(RIGHT(dbtables.[name], 2) AS int) = HistoryID
                        THEN HistoryMap.TableName
                        WHEN LEN(dbtables.[name]) = 11 AND CAST(RIGHT(dbtables.[name], 3) AS int) = HistoryID
                        THEN HistoryMap.TableName
                        ELSE 'N/A'
                    END 
                ELSE 'N/A'
            END AS front_end_name,
            indexstats.avg_fragmentation_in_percent,
            indexstats.page_count,
            indexstats.record_count
        FROM Calculation, HistoryMap, sys.dm_db_index_physical_stats (DB_ID(), NULL, NULL, NULL, 'Sampled') AS indexstats
        INNER JOIN sys.tables dbtables on dbtables.[object_id] = indexstats.[object_id]
        INNER JOIN sys.schemas dbschemas on dbtables.[schema_id] = dbschemas.[schema_id]
        INNER JOIN sys.indexes AS dbindexes ON dbindexes.[object_id] = indexstats.[object_id]
        AND indexstats.index_id = dbindexes.index_id
        WHERE indexstats.database_id = DB_ID() AND indexstats.avg_fragmentation_in_percent >= 30
)

SELECT back_end_name,
       front_end_name,
       AVG(avg_fragmentation_in_percent) AS avg_page_count,   -- ADJUST AGG FUNCTION TO NEEDS
       SUM(page_count) AS sum_page_count,                     -- ADJUST AGG FUNCTION TO NEEDS
       SUM(record_count) AS sum_record_count                  -- ADJUST AGG FUNCTION TO NEEDS
FROM base
GROUP BY back_end_name,               -- GROUP BY REQUIRES ALL NON-AGG COLUMNS PRESENT IN CLAUSE
         front_end_name
ORDER BY AVG(avg_fragmentation_in_percent)

В качестве альтернативы, агрегируйте front_end_name, если вы знаете причину повторения:

WITH base AS (
    ...
)

SELECT back_end_name,
       MAX(front_end_name) AS front_end_name,
       AVG(avg_fragmentation_in_percent) AS sum_page_count,   -- ADJUST AGG FUNCTION TO NEEDS
       SUM(page_count) AS sum_page_count,                     -- ADJUST AGG FUNCTION TO NEEDS
       SUM(record_count) AS sum_record_count                  -- ADJUST AGG FUNCTION TO NEEDS
FROM base
GROUP BY back_end_name
ORDER BY AVG(avg_fragmentation_in_percent)
...