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 / 173550
Accepted
Metro Smurf
Metro Smurf
Asked: 2017-05-15 08:22:54 +0800 CST2017-05-15 08:22:54 +0800 CST 2017-05-15 08:22:54 +0800 CST

Subconsulta de baixo desempenho com comparações de data

  • 772

Ao usar uma subconsulta para encontrar a contagem total de todos os registros anteriores com um campo correspondente, o desempenho é terrível em uma tabela com apenas 50 mil registros. Sem a subconsulta, a consulta é executada em alguns milissegundos. Com a subconsulta, o tempo de execução é superior a um minuto.

Para esta consulta, o resultado deve:

  • Inclua apenas os registros dentro de um determinado intervalo de datas.
  • Inclua uma contagem de todos os registros anteriores, sem incluir o registro atual, independentemente do intervalo de datas.

Esquema de Tabela Básico

Activity
======================
Id int Identifier
Address varchar(25)
ActionDate datetime2
Process varchar(50)
-- 7 other columns

Dados de exemplo

Id  Address     ActionDate (Time part excluded for simplicity)
===========================
99  000         2017-05-30
98  111         2017-05-30
97  000         2017-05-29
96  000         2017-05-28
95  111         2017-05-19
94  222         2017-05-30

resultados esperados

Para o intervalo de datas de 2017-05-29a2017-05-30

Id  Address     ActionDate    PriorCount
=========================================
99  000         2017-05-30    2  (3 total, 2 prior to ActionDate)
98  111         2017-05-30    1  (2 total, 1 prior to ActionDate)
94  222         2017-05-30    0  (1 total, 0 prior to ActionDate)
97  000         2017-05-29    1  (3 total, 1 prior to ActionDate)

Os registros 96 e 95 são excluídos do resultado, mas são incluídos na PriorCountsubconsulta

Consulta atual

select 
    *.a
    , ( select count(*) 
        from Activity
        where 
            Activity.Address = a.Address
            and Activity.ActionDate < a.ActionDate
    ) as PriorCount
from Activity a
where a.ActionDate between '2017-05-29' and '2017-05-30'
order by a.ActionDate desc

Índice atual

CREATE NONCLUSTERED INDEX [IDX_my_nme] ON [dbo].[Activity]
(
    [ActionDate] ASC
)
INCLUDE ([Address]) WITH (
    PAD_INDEX = OFF, 
    STATISTICS_NORECOMPUTE = OFF, 
    SORT_IN_TEMPDB = OFF, 
    DROP_EXISTING = OFF, 
    ONLINE = OFF, 
    ALLOW_ROW_LOCKS = ON, 
    ALLOW_PAGE_LOCKS = ON
)

Pergunta

  • Quais estratégias podem ser usadas para melhorar o desempenho dessa consulta?

Edit 1
Em resposta à pergunta do que posso modificar no banco de dados: posso modificar os índices, mas não a estrutura da tabela.

Edit 2
Agora adicionei um índice básico na Addresscoluna, mas isso não pareceu melhorar muito. Atualmente, estou encontrando um desempenho muito melhor ao criar uma tabela temporária e inserir os valores sem o PriorCounte atualizar cada linha com suas contagens específicas.

Editar 3
O Spool de Índice Joe Obbish (resposta aceita) encontrado foi o problema. Depois de adicionar um novo nonclustered index [xyz] on [Activity] (Address) include (ActionDate), os tempos de consulta caíram de mais de um minuto para menos de um segundo sem usar uma tabela temporária (consulte a edição 2).

performance subquery
  • 1 1 respostas
  • 444 Views

1 respostas

  • Voted
  1. Best Answer
    Joe Obbish
    2017-05-15T09:29:15+08:002017-05-15T09:29:15+08:00

    Com a definição de índice que você tem para IDX_my_nme, o SQL Server poderá buscar usando a ActionDatecoluna, mas não com a Addresscoluna. O índice contém todas as colunas necessárias para cobrir a subconsulta, mas provavelmente não é muito seletivo para essa subconsulta. Suponha que quase todos os dados na tabela tenham um ActionDatevalor anterior a '2017-05-30'. Uma busca de ActionDate < '2017-05-30'retornará quase todas as linhas do índice, que são filtradas ainda mais depois que a linha é buscada do índice. Se sua consulta retornar 200 linhas, você provavelmente faria quase 200 varreduras completas de índice em IDX_my_nme, o que significa que você lerá cerca de 50.000 * 200 = 10 milhões de linhas do índice.

    É provável que a busca Addressseja muito mais seletiva para sua subconsulta, embora você não tenha nos fornecido informações estatísticas completas sobre a consulta, então isso é uma suposição de minha parte. No entanto, suponha que você crie um índice em just Addresse sua tabela tenha 10k valores exclusivos para Address. Com o novo índice, o SQL Server precisará buscar apenas 5 linhas do índice para cada execução da subconsulta, então você lerá cerca de 200 * 5 = 1000 linhas do índice.

    Estou testando no SQL Server 2016, portanto, pode haver algumas pequenas diferenças de sintaxe. Abaixo estão alguns dados de exemplo nos quais fiz suposições semelhantes às acima para distribuição de dados:

    CREATE TABLE #Activity (
        Id int NOT NULL,
        [Address] varchar(25) NULL,
        ActionDate datetime2 NULL,
        FILLER varchar(100),
        PRIMARY KEY (Id)
    );
    
    INSERT INTO #Activity WITH (TABLOCK)
    SELECT TOP (50000) -- 50k total rows
    x.RN
    , x.RN % 10000 -- 10k unique addresses
    , DATEADD(DAY, x.RN / 100, '20160201') -- 100 rows per day
    , REPLICATE('Z', 100)
    FROM
    (
        SELECT ROW_NUMBER() OVER (ORDER BY (SELECT NULL)) RN
        FROM master..spt_values t1
        CROSS JOIN master..spt_values t2
    ) x;
    
    CREATE NONCLUSTERED INDEX [IDX_my_nme] ON #Activity
    ([ActionDate] ASC) INCLUDE ([Address]);
    

    Eu criei seu índice conforme descrito na pergunta. Estou testando essa consulta que retorna os mesmos dados da pergunta:

    select 
        a.*
        , ( select count(*) 
            from #Activity Activity
            where 
                Activity.[Address] = a.[Address]
                and Activity.ActionDate < a.ActionDate
        ) as PriorCount
    from #Activity a
    where a.ActionDate between '2017-05-29' and '2017-05-30'
    order by a.ActionDate desc;
    

    Eu recebo um carretel de índice. O que isso significa em um nível básico é que o otimizador de consulta cria um índice temporário dinamicamente porque nenhum dos índices existentes na tabela era adequado.

    carretel de índice

    A consulta ainda termina rapidamente para mim. Talvez você não esteja obtendo a otimização do spool de índice em seu sistema ou há algo diferente na definição da tabela ou na consulta. Para fins educacionais, posso usar um recurso não documentado OPTION (QUERYRULEOFF BuildSpool)para desativar o carretel de índice. Veja como é o plano:

    busca de índice ruim

    Não se deixe enganar pela aparência de uma simples busca de índice. O SQL Server lê quase 10 milhões de linhas do índice:

    10 milhões de linhas do índice

    Se eu for executar a consulta mais de uma vez, provavelmente não fará sentido para o otimizador de consulta criar um índice toda vez que for executado. Eu poderia criar um índice antecipado que seria mais seletivo para esta consulta:

    CREATE NONCLUSTERED INDEX [IDX_my_nme_2] ON #Activity
    ([Address] ASC) INCLUDE (ActionDate);
    

    O plano é semelhante ao anterior:

    procurar índice

    No entanto, com o novo índice, o SQL Server lê apenas 1.000 linhas do índice. 800 das linhas são retornadas para serem contadas. O índice pode ser definido para ser mais seletivo, mas isso pode ser bom o suficiente dependendo da sua distribuição de dados.

    boa procura

    Se você não conseguir definir nenhum índice adicional na tabela, eu consideraria usar funções de janela. O seguinte parece funcionar:

    SELECT t.*
    FROM
    (
        select 
            a.*
            , -1 + ROW_NUMBER() OVER (PARTITION BY [Address] ORDER BY ActionDate) PriorCount
        from #Activity a
    ) t
    where t.ActionDate between '2017-05-29' and '2017-05-30'
    order by t.ActionDate desc;
    

    Essa consulta faz uma única varredura dos dados, mas faz uma classificação cara e calcula a ROW_NUMBER()função para cada linha da tabela, então parece que há algum trabalho extra feito aqui:

    má sorte

    No entanto, se você realmente gosta desse padrão de código, pode definir um índice para torná-lo mais eficiente:

    CREATE NONCLUSTERED INDEX [IDX_my_nme] ON #Activity
    ([Address], [ActionDate]) INCLUDE (FILLER);
    

    Isso move o tipo para o final, que será muito menos caro:

    boa sorte

    Se nada disso ajudar, você precisará adicionar mais informações à pergunta, de preferência incluindo planos de execução reais.

    • 17

relate perguntas

  • Existe um ganho de desempenho ao manipular dados com procedimentos armazenados em vez de alimentá-los em funções após a recuperação?

  • Como você ajusta o MySQL para uma carga de trabalho pesada do InnoDB?

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

  • Onde posso encontrar o log lento do mysql?

  • Como posso otimizar um mysqldump de um banco de dados grande?

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