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 / 163238
Accepted
M.Ali
M.Ali
Asked: 2017-02-05 15:24:54 +0800 CST2017-02-05 15:24:54 +0800 CST 2017-02-05 15:24:54 +0800 CST

Maneira mais eficiente de unir duas tabelas com valor MAX

  • 772

Estou otimizando uma consulta, onde tenho que juntar duas tabelas, digamos Producte TransactionHistoryuma tabela, e retornar várias colunas de ambas as tabelas para a última TransactionDateda TransactionHistorytabela para cada Produto na Producttabela.

TransactionHistorytable tem cerca de 13 milhões de linhas e a Producttabela tem quase 2 milhões de linhas.

Parece muito fácil, não é? mas para diferentes cenários, a consulta diferente tem um desempenho diferente.

Não estou usando meus nomes de tabela reais aqui, para mostrar a sintaxe de consulta diferente que tentei, estou usando as tabelas BigProduct e BigSalesHistory de Adam Machanic.


A consulta foi originalmente escrita como ....

SELECT p.ProductID
     , p.Name
     , h.Quantity
     , h.TransactionDate
FROM [dbo].[bigProduct] p
INNER JOIN [dbo].[bigTransactionHistory] h ON p.ProductID = h.ProductID
WHERE h.TransactionDate = ( SELECT MAX(TransactionDate)
                            FROM [dbo].[bigTransactionHistory]
                            WHERE ProductID = h.ProductID )
AND p.ProductID > 1317
AND p.ProductID < 1416
GO

O que resultou em cerca de 30 contagens de varredura e quase 300.000 leituras lógicas.


Eu sendo um grande fã de funções de janelas, escrevi as seguintes consultas usando ROW_NUMBER(), ambas saíram pela culatra:

SELECT p.ProductID
     , p.Name
     , c.*
FROM [dbo].[bigProduct] p
INNER JOIN ( SELECT  h.ProductID
                   , h.Quantity
                   , h.TransactionDate
                   , ROW_NUMBER() OVER (PARTITION BY h.ProductID 
                                            ORDER BY h.TransactionDate DESC) rn
              FROM [dbo].[bigTransactionHistory] h
            ) c
            ON p.ProductID = c.ProductID AND rn = 1
WHERE p.ProductID > 1317
  AND p.ProductID < 1416
GO

E

SELECT p.ProductID
     , p.Name
     , c.*
FROM [dbo].[bigProduct] p
CROSS APPLY ( SELECT  h.ProductID
                   , h.Quantity
                   , h.TransactionDate
                   , ROW_NUMBER() OVER (PARTITION BY h.ProductID 
                                            ORDER BY h.TransactionDate DESC) rn
              FROM [dbo].[bigTransactionHistory] h
              WHERE p.ProductID = ProductID
            ) c
WHERE p.ProductID > 1317
  AND p.ProductID < 1416
  AND rn = 1
GO

A contagem de varreduras foi para 100s e as leituras lógicas saltaram para 700.000s.


Ficando bastante desapontado com o desempenho da função de janela, reescrevi a consulta usandoCROSS APPLY

SELECT p.ProductID
     , p.Name
     , c.*
FROM [dbo].[bigProduct] p
CROSS APPLY ( SELECT TOP 1  h.ProductID
                          , h.Quantity
                          , h.TransactionDate
              FROM [dbo].[bigTransactionHistory] h
              WHERE p.ProductID = h.ProductID
              ORDER BY h.TransactionDate 
             ) c
WHERE p.ProductID > 1317
  AND p.ProductID < 1416
GO

Agora, essa consulta parece ter o melhor desempenho de todas as mencionadas acima, mas assim que o intervalo da consulta externa para ProductIDaumenta, essa consulta dá terrivelmente errado e começa a ter um desempenho pior do que os dois acima.

Além disso, embora tenha reduzido as leituras lógicas para 27.000, mas o tempo de CPU e o tempo decorrido aumentaram 8 vezes.


Existe uma maneira mais eficiente de escrever uma consulta como essa, tendo em mente que o intervalo de consulta externo para ProductID varia muito?.

Qualquer sugestão ou indicações na direção certa são muito apreciadas. Obrigada.

sql-server sql-server-2014
  • 1 1 respostas
  • 5795 Views

1 respostas

  • Voted
  1. Best Answer
    Rob Farley
    2017-02-05T17:50:16+08:002017-02-05T17:50:16+08:00

    Você precisa pensar em como gostaria de resolver isso se tivesse um sistema baseado em papel.

    Como - você deseja usar cada ProductID um por um e encontrar o mais novo? Então seu loop aninhado que chama um Seek usando TOP 1 em um índice em (ProductID, Transaction Date DESC) será o melhor.

    Deseja pesquisar todas as suas transações e agrupá-las por ProductID e obter o máximo? Então essa abordagem pode ser a melhor.

    Mas eu sugiro a você que o que você faria na vida real é fazer a segunda abordagem em um subconjunto de seus dados (como o mês mais recente) e, em seguida, fazer uma pesquisa produto por produto naqueles isso não funcionou. Tente uma junção externa em uma subconsulta que agrupe por ProductID e filtre por TransactionDate para "bastante recente". Em seguida, faça um Outer Apply para fazer um Top 1, mas inclua um Predicate lá como where m.ProductID IS NULL, para que ele faça apenas aqueles Seeks para produtos que foram perdidos na primeira rodada. Você deve ver um filtro com um predicado de expressão de inicialização e observar que a busca de índice é executada muito menos vezes.

    Mostro esse tipo de coisa em http://blogs.lobsterpot.com.au/2014/07/08/ssis-lookup-transformation-in-t-sql/ - procure o plano que mostro, no exemplo com dois Bits de aplicação externa.

    • 2

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