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 / 345932
Accepted
Fabian Schmied
Fabian Schmied
Asked: 2025-03-27 18:19:32 +0800 CST2025-03-27 18:19:32 +0800 CST 2025-03-27 18:19:32 +0800 CST

Por que o SQL Server não usaria um operador Merge Join (Concatenação) em valores não exclusivos, mas pré-classificados?

  • 772
Esta pergunta foi migrada do Stack Overflow porque pode ser respondida no Database Administrators Stack Exchange. Migrada há 15 dias .

Tenho uma consulta muito simples em que uso um UNION ALL+ ORDER BYem duas consultas que retornam dados pré-ordenados de seus respectivos índices. Por algum motivo, o SQL não usa um Merge Join (Concatenation)para isso, mas um Concatenation, seguido por um Sort. Qual poderia ser o motivo?

Aqui está um exemplo completo de reprodução. (As INDEXdicas são necessárias para que o SQL Server use o índice, apesar do baixo número de linhas na tabela.)

CREATE TABLE T1(
    SequenceNumber bigint IDENTITY NOT NULL,
    TenantId uniqueidentifier NOT NULL,
    Object1Id uniqueidentifier NOT NULL,
    Payload nvarchar(max) NOT NULL,
    OtherNumber bigint NOT NULL,

  CONSTRAINT PK_T1 PRIMARY KEY CLUSTERED (TenantId,SequenceNumber ASC)
)

CREATE INDEX IX_TenantId_Object1Id_OtherNumber ON T1(TenantId, Object1Id, OtherNumber)

CREATE TABLE T2(
    SequenceNumber bigint IDENTITY NOT NULL,
    TenantId uniqueidentifier NOT NULL,
    Object2Id uniqueidentifier NOT NULL,
    Payload nvarchar(max) NOT NULL,
    OtherNumber bigint NOT NULL,

  CONSTRAINT PK_T2 PRIMARY KEY CLUSTERED (TenantId,SequenceNumber ASC)
)

CREATE INDEX IX_TenantId_Object2Id_OtherNumber ON T2(TenantId, Object2Id, OtherNumber)

DECLARE @tenantId UNIQUEIDENTIFIER = NEWID()
DECLARE @object1Id UNIQUEIDENTIFIER = NEWID()
DECLARE @object2Id UNIQUEIDENTIFIER = NEWID()

SELECT OtherNumber, Payload FROM T1 WITH (INDEX(IX_TenantId_Object1Id_OtherNumber)) WHERE TenantId = @tenantId AND Object1Id = @object1Id
UNION ALL
SELECT OtherNumber, Payload FROM T2 WITH (INDEX(IX_TenantId_Object2Id_OtherNumber)) WHERE TenantId = @tenantId AND Object2Id = @object2Id
ORDER BY OtherNumber

DROP TABLE T1
DROP TABLE T2

E esta é uma captura de tela do plano de execução: Plano de execução da consulta acima mencionada

Quando adiciono a MERGE UNIONopção, o SQL Server pré-classifica explicitamente os resultados individuais da consulta (em OtherNumbere Payload, por algum motivo).

Agora, uma reviravolta interessante: quando adiciono uma restrição UNIQUE às OtherNumbercolunas, o SQL Server repentinamente escolhe o Merge Join (Concatenation)operador. Por quê?

Testei isso localmente no SQL Server 2016 e no Azure SQL.

sql-server
  • 1 1 respostas
  • 77 Views

1 respostas

  • Voted
  1. Best Answer
    Paul White
    2025-04-11T15:15:57+08:002025-04-11T15:15:57+08:00

    Isso é essencialmente respondido por Ordem e natureza das colunas na lista de seleção da consulta UNION classificada afeta o desempenho , que se vincula à explicação completa e complicada no meu artigo, Evitando classificações com concatenação de junção de mesclagem .

    O principal ponto a ser considerado é que o otimizador de consultas do SQL Server não realiza uma busca exaustiva das estratégias disponíveis para evitar ordenações. Esta é uma decisão pragmática, pois o espaço de busca cresce rapidamente.

    Como resultado, às vezes você verá uma classificação evitável em um plano de execução. Existe um método para determinar como o SQL Server atualmente deriva os requisitos de classificação para Concatenação de Mesclagem e como ele pode, às vezes, tomar atalhos (dado um índice ou restrição exclusivos, por exemplo). Ainda assim, isso é observacional; não há garantias comportamentais publicadas.

    Nesse sentido, a resposta para esse tipo de pergunta é sempre a mesma e envolve seguir a lógica utilizada pelo otimizador.


    A resposta para o seu tipo específico da mesma questão subjacente é abordada na seção do meu artigo, Problemas com índices não exclusivos :

    A maneira como o otimizador raciocina sobre os requisitos de classificação para concatenação de mesclagem pode resultar em problemas de classificação desnecessários, como mostra o próximo exemplo:

    CREATE TABLE #T1 (c1 int, c2 int, c3 int, c4 int, c5 int);
    CREATE TABLE #T2 (c1 int, c2 int, c3 int, c4 int, c5 int);
    CREATE CLUSTERED INDEX cx ON #T1 (c1);
    CREATE CLUSTERED INDEX cx ON #T2 (c1);
    
    SELECT * FROM #T1 AS T1
    UNION ALL 
    SELECT * FROM #T2 AS T2
    ORDER BY c1
    OPTION (MERGE UNION);
    
    DROP TABLE #T1, #T2;
    

    Observando a consulta e os índices disponíveis, esperaríamos um plano de execução que realizasse uma varredura ordenada dos índices agrupados, usando concatenação de junção por mesclagem para evitar a necessidade de qualquer ordenação. Essa expectativa é totalmente justificada, pois os índices agrupados fornecem a ordenação especificada na ORDER BYcláusula. Infelizmente, o plano que obtemos inclui duas ordenações:

    Plano com dois tipos

    Não há uma boa razão para essas classificações; elas só aparecem porque a lógica do otimizador de consultas é imperfeita. A lista de colunas de saída da mesclagem (c1, c2, c3, c4, c5) é um superconjunto de ORDER BY, mas não há uma chave única para simplificar essa lista. Como resultado dessa lacuna no raciocínio do otimizador, ele conclui que a mesclagem requer que sua entrada seja classificada em (c1, c2, c3, c4, c5).


    Fornecendo mais uma ilustração para cobrir a importância da lista de projeções:

    DECLARE 
        @tenantId uniqueidentifier = NEWID(),
        @object1Id uniqueidentifier = NEWID(),
        @object2Id uniqueidentifier = NEWID();
    
    SELECT 
        SequenceNumber, -- added
        OtherNumber, 
        Payload 
    FROM T1 
        WITH (INDEX(IX_TenantId_Object1Id_OtherNumber)) 
    WHERE 
        TenantId = @tenantId 
        AND Object1Id = @object1Id
    
    UNION ALL
    
    SELECT 
        SequenceNumber, -- added
        OtherNumber, 
        Payload 
    FROM T2 
        WITH (INDEX(IX_TenantId_Object2Id_OtherNumber)) 
    WHERE 
        TenantId = @tenantId 
        AND Object2Id = @object2Id
    ORDER BY OtherNumber;
    

    Mesclar concatenação sem classificação

    Adicionar SequenceNumber ao início da lista de projeção resultou na simplificação do requisito de classificação devido às chaves (primárias) em ( TenantId,SequenceNumber ), com TenantId restrito a um único valor pelo predicado de igualdade em sua consulta.

    • 3

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