AskOverflow.Dev

AskOverflow.Dev Logo AskOverflow.Dev Logo

AskOverflow.Dev Navigation

  • Início
  • system&network
  • Ubuntu
  • Unix
  • DBA
  • Computer
  • Coding
  • LangChain

Mobile menu

Close
  • Início
  • system&network
    • Recentes
    • Highest score
    • tags
  • Ubuntu
    • Recentes
    • Highest score
    • tags
  • Unix
    • Recentes
    • tags
  • DBA
    • Recentes
    • tags
  • Computer
    • Recentes
    • tags
  • Coding
    • Recentes
    • tags
Início / dba / Perguntas / 330663
Accepted
Charlieface
Charlieface
Asked: 2023-08-28 09:59:29 +0800 CST2023-08-28 09:59:29 +0800 CST 2023-08-28 09:59:29 +0800 CST

Consulta de agregação de visualização particionada não otimizada

  • 772

Dadas as duas tabelas a seguir:

CREATE TABLE SalesLedger (
  Id int PRIMARY KEY IDENTITY,
  Date date NOT NULL,
  Total decimal(38,18),
  INDEX IX (Date, Total)
);

CREATE TABLE Purchases (
  Id int PRIMARY KEY IDENTITY,
  Date date NOT NULL,
  Total decimal(38,18),
  INDEX IX (Date, Total)
);

E a seguinte visão

CREATE VIEW ViewMetrics
AS

Select
  Date,
  'Sale' as Metric,
  Total as Value
From SalesLedger

UNION ALL

Select
  Date,
  'Purchase' as Metric,
  Total as Value
From Purchases;

A consulta a seguir usa um Concatenation Sortpar:

Select SUM(Value) as Sales, Date
from ViewMetrics
Group By Date;

insira a descrição da imagem aqui

Colar o plano


Considerando que uma pequena reescrita dá obviamente mais desempenhoMerge Concatenation

SELECT SUM(Sales), Date
FROM (
  Select SUM(Value) as Sales, Date
  from ViewMetrics
  Group By Metric, Date
) t
GROUP BY Date;

insira a descrição da imagem aqui

Colar o plano


O compilador pode ver claramente que a visualização está particionada por Metric, como mostra esta consulta, não Sorté necessário:

Select SUM(Value) as Sales, Date
from ViewMetrics
where Metric = 'Sale'
Group By Date;

insira a descrição da imagem aqui


A questão é: por que a primeira consulta força a Sort, enquanto a segunda pode usar uma mais eficiente Merge Concatenation, visto que a Metriccoluna não possui WHEREpredicado em nenhum dos casos?

O compilador não deveria ser capaz de ver que a Mergefuncionaria, visto que os índices já estão classificados Datee o particionamento está ativado Metric? Ou se não consegue ver isso, por que GROUP BY Metric, Datede repente lhe dá essa habilidade?

banco de dados<> violino

Ainda mais estranho, como descobriu @MartinSmith, se não houver dados, o compilador usará o plano melhor, embora sem uma agregação intermediária em Metric, Date. db<>fiddle Por outro lado, a mesclagem sem agregação parcial é provavelmente mais lenta do que a classificação após a agregação parcial, porque há mais linhas para mesclar. A questão é por que não é possível fazer agregação parcial e mesclar ao mesmo tempo por padrão?

Suponho que haja alguma otimização específica para uma visualização particionada quando a agregação inclui o particionamento, porque nesse caso ela usa uma concatenação e quando uma ordem é necessária, ela usa a concatenação de mesclagem, consulte db<> fiddle . Isso ajuda quando você deseja agregar ainda mais, pois os dados agora já estão classificados na ordem correta. Mas se você não fizer a agregação intermediária não tem lógica que a aplique.

sql-server
  • 1 1 respostas
  • 128 Views

1 respostas

  • Voted
  1. Best Answer
    Paul White
    2023-08-30T04:33:11+08:002023-08-30T04:33:11+08:00

    O otimizador do SQL Server tem duas maneiras principais de empurrar um agregado para baixo de uma união.

    1. Empurrão Global

    A primeira regra é GbAggBelowUniAll. É uma transformação bastante simples que move a agregação para cada uma das entradas da união.

    Só pode fazer isso com segurança se a união for disjunta – isto é, se houver algo que torne cada entrada completamente independente, e esse fator aparecer na GROUP BYcláusula.

    Não precisa ser um valor literal como no seu caso, mas precisa ser algo que o otimizador possa reconhecer como uma separação completa dos conjuntos, como intervalos não sobrepostos. Como parte da transformação, a parte constante da cláusula de agrupamento é removida.

    Esta regra está envolvida na sua reescrita porque o atributo Metric é disjunto e está presente na especificação de agrupamento.

    Um exemplo usando o banco de dados de amostra Adventure Works:

    -- Helpful indexes
    CREATE INDEX i ON Production.TransactionHistory 
        (Quantity, TransactionDate);
    CREATE INDEX i ON Production.TransactionHistoryArchive 
        (Quantity, TransactionDate);
    
    SELECT 
        U.Quantity, 
        MAX(U.TransactionDate)
    FROM 
    (
        -- The predicates on quantity make the two union inputs disjoint
        SELECT 
            TH.*
        FROM Production.TransactionHistory AS TH
        WHERE 
            TH.Quantity BETWEEN 50 AND 60
    
        UNION ALL
    
        SELECT
            THA.*
        FROM Production.TransactionHistoryArchive AS THA
        WHERE 
            THA.Quantity BETWEEN 10 AND 20
    ) AS U
    -- Grouping by the disjoint element
    GROUP BY 
        U.Quantity
    ORDER BY 
        U.Quantity
    --OPTION (FORCE ORDER)
    ;
    

    Com a FORCE ORDERdica descomentada, o otimizador é impedido de mover o agregado:

    Planeje com dica

    Sem a dica, o agregado de nível superior pode ser movido e copiado para cada entrada de união:

    Agregado global empurrado para baixo após união todos

    2. Agregação Local

    A segunda transformação envolve algumas regras diferentes.

    Primeiro, GenLGAggdivide um agregado em duas partes, um agregado global e um agregado local . Por exemplo, um COUNTagregado seria dividido num COUNTagregado local e num SUMagregado global que soma todas as contribuições locais para chegar ao resultado correcto.

    A ideia geral é usada tanto em planos seriais quanto paralelos. Às vezes, o agregado local calcula um subtotal local para seu próprio thread, às vezes executa um subconjunto do trabalho abaixo de uma junção. Em qualquer caso, a ideia geral é a mesma: realizar alguma parte da tarefa global de agregação o mais cedo possível.

    Como muitas explorações de otimizadores, GenLGAggproduz uma ou mais alternativas que podem ser exploradas posteriormente por outras regras. Por exemplo, as novas agregações locais podem ser junções anteriores ou corresponder a uma visualização indexada.

    No seu caso, uma regra chamada LocalAggBelowUniAllé empregada para mover o agregado local abaixo do arquivo UNION ALL. Isso é o que estava acontecendo com a consulta original.

    É importante ressaltar que o agregado local não é exatamente um agregado normal. Ele está executando apenas parte do cálculo e pode acabar em um dos muitos threads em um plano paralelo. Uma agregação global sempre é executada em um único thread para garantir resultados corretos.

    Em um plano paralelo, um agregado local pode ser implementado fisicamente como um Hash Match Partial Aggregate. Este operador obtém apenas uma pequena concessão de memória fixa e nunca transborda para tempdb . Se ficar sem memória, ele simplesmente para de agregar. Os resultados ainda estarão corretos graças ao agregado global.

    As ressalvas não se limitam a esta operadora física ou planos paralelos. Em geral, você deve pensar em um agregado local como sendo um pouco diferente do tipo normal que você escreve em SQL.

    Isto é principalmente uma consequência dos detalhes de implementação e da necessidade de preservar a ligação entre os agregados locais e globais. Como muitas regras de exploração, LocalAggBelowUniAllrepresenta uma reescrita de consulta que você mesmo pode executar, mas você não deve esperar que ela se comporte exatamente da mesma forma em todos os aspectos que a representação T-SQL mais próxima. Deve-se dizer também que o otimizador tem a vantagem de poder decidir dinamicamente qual reescrita usar com base nas estatísticas e metadados atuais. Isso geralmente não é verdade para uma reescrita manual.

    De qualquer forma, uma das consequências de um agregado local ser um pouco diferente é que ele não vem com uma garantia de exclusividade associada às suas chaves de agrupamento. Isso é importante em muitos casos, mas especialmente com o operador Merge Concatenation desejado.

    Mesclar concatenação

    Como o nome do operador do plano sugere, Merge Concatenation é apenas um operador Merge Join normal executado em um modo especial. Requer entrada classificada nas 'chaves de junção', embora a ordem de classificação exata necessária possa ser afetada pela lista de colunas de projeção, pelo requisito de ordem global e por quaisquer garantias de exclusividade disponíveis (veja a referência abaixo).

    Um agregado normal que fornece garantias de exclusividade de chave de agrupamento pode permitir que a Merge Concatenation exija uma classificação menos onerosa do que é possível com a entrada de um agregado local.

    Para sua consulta original, o otimizador considerou a alternativa Merge Concatenation, mas a agregação local significava que as classificações eram necessárias para satisfazer as propriedades de entrada exigidas:

    Mesclar plano de concatenação com classificações

    As classificações extras e o custo mais alto da concatenação de mesclagem significaram que o otimizador escolheu a opção de plano mais barata com uma concatenação e uma única classificação. Como sempre, essas escolhas são baseadas no modelo de custo.

    Outras notas

    Com tabelas vazias, o otimizador custa mais barato a opção Merge Concatenation porque mescla apenas uma linha de cada entrada. O custo mais alto por linha de uma Concatenação de Mesclagem não compensa o custo da Classificação extra necessária no plano de Concatenação.

    Sua consulta deve rejeitar nulos ou as tabelas base devem ter um explícito NOT NULLno atributo Value . Isso simplificará o plano final (e tornará mais fácil qualquer correspondência futura de visualizações indexadas). Em particular, os Stream Aggregates não precisarão mais calcular um COUNT_BIG(Total)agregado e os Compute Scalars não serão mais necessários.

    Você também deve usar prefixos de esquema e evitar palavras-chave como nomes de atributos.

    SELECT 
        TotalSales = SUM(T.Sales), 
        T.[Date]
    FROM 
    (
        SELECT 
            Sales = ISNULL(SUM(VM.[Value]), 0.0), 
            VM.[Date]
        FROM dbo.ViewMetrics AS VM
        WHERE
            VM.[Value] IS NOT NULL
        GROUP BY
            VM.Metric, 
            VM.[Date]
    ) AS T
    GROUP BY 
        T.[Date];
    

    Plano final

    Leitura adicional

    • Bug do SQL Server Optimizer com JOIN e GROUP BY
      cobre agregação local, parcial e global
    • Evitando classificações com detalhes de concatenação de junção de mesclagem
      para classificações de concatenação de mesclagem

    Ambos escritos por mim.

    • 6

relate perguntas

  • SQL Server - Como as páginas de dados são armazenadas ao usar um índice clusterizado

  • Preciso de índices separados para cada tipo de consulta ou um índice de várias colunas funcionará?

  • Quando devo usar uma restrição exclusiva em vez de um índice exclusivo?

  • Quais são as principais causas de deadlocks e podem ser evitadas?

  • Como determinar se um Índice é necessário ou necessário

Sidebar

Stats

  • Perguntas 205573
  • respostas 270741
  • best respostas 135370
  • utilizador 68524
  • Highest score
  • respostas
  • Marko Smith

    conectar ao servidor PostgreSQL: FATAL: nenhuma entrada pg_hba.conf para o host

    • 12 respostas
  • Marko Smith

    Como fazer a saída do sqlplus aparecer em uma linha?

    • 3 respostas
  • Marko Smith

    Selecione qual tem data máxima ou data mais recente

    • 3 respostas
  • Marko Smith

    Como faço para listar todos os esquemas no PostgreSQL?

    • 4 respostas
  • Marko Smith

    Listar todas as colunas de uma tabela especificada

    • 5 respostas
  • Marko Smith

    Como usar o sqlplus para se conectar a um banco de dados Oracle localizado em outro host sem modificar meu próprio tnsnames.ora

    • 4 respostas
  • Marko Smith

    Como você mysqldump tabela (s) específica (s)?

    • 4 respostas
  • Marko Smith

    Listar os privilégios do banco de dados usando o psql

    • 10 respostas
  • Marko Smith

    Como inserir valores em uma tabela de uma consulta de seleção no PostgreSQL?

    • 4 respostas
  • Marko Smith

    Como faço para listar todos os bancos de dados e tabelas usando o psql?

    • 7 respostas
  • Martin Hope
    Jin conectar ao servidor PostgreSQL: FATAL: nenhuma entrada pg_hba.conf para o host 2014-12-02 02:54:58 +0800 CST
  • Martin Hope
    Stéphane Como faço para listar todos os esquemas no PostgreSQL? 2013-04-16 11:19:16 +0800 CST
  • Martin Hope
    Mike Walsh Por que o log de transações continua crescendo ou fica sem espaço? 2012-12-05 18:11:22 +0800 CST
  • Martin Hope
    Stephane Rolland Listar todas as colunas de uma tabela especificada 2012-08-14 04:44:44 +0800 CST
  • Martin Hope
    haxney O MySQL pode realizar consultas razoavelmente em bilhões de linhas? 2012-07-03 11:36:13 +0800 CST
  • Martin Hope
    qazwsx Como posso monitorar o andamento de uma importação de um arquivo .sql grande? 2012-05-03 08:54:41 +0800 CST
  • Martin Hope
    markdorison Como você mysqldump tabela (s) específica (s)? 2011-12-17 12:39:37 +0800 CST
  • Martin Hope
    Jonas Como posso cronometrar consultas SQL usando psql? 2011-06-04 02:22:54 +0800 CST
  • Martin Hope
    Jonas Como inserir valores em uma tabela de uma consulta de seleção no PostgreSQL? 2011-05-28 00:33:05 +0800 CST
  • Martin Hope
    Jonas Como faço para listar todos os bancos de dados e tabelas usando o psql? 2011-02-18 00:45:49 +0800 CST

Hot tag

sql-server mysql postgresql sql-server-2014 sql-server-2016 oracle sql-server-2008 database-design query-performance sql-server-2017

Explore

  • Início
  • Perguntas
    • Recentes
    • Highest score
  • tag
  • help

Footer

AskOverflow.Dev

About Us

  • About Us
  • Contact Us

Legal Stuff

  • Privacy Policy

Language

  • Pt
  • Server
  • Unix

© 2023 AskOverflow.DEV All Rights Reserve