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 / 165626
Accepted
Vojtěch Dohnal
Vojtěch Dohnal
Asked: 2017-02-28 05:17:36 +0800 CST2017-02-28 05:17:36 +0800 CST 2017-02-28 05:17:36 +0800 CST

Usar a variável de tabela em vez da tabela temporária torna a execução da consulta lenta

  • 772

Eu tenho uma tabela com dados históricos sobre carros AutoDatacom chave agrupada combinada Cas(DateTime) + GCom(Car ID). Um registro contém vários indicadores, como nível de combustível, estado do veículo etc.

Os intervalos entre os registros individuais de um carro na AutoDatatabela são irregulares, às vezes são 120 segundos, às vezes poucos segundos, às vezes horas etc. Preciso normalizar os registros para visualização, para que mostre um registro a cada 30 segundos.

Tenho o seguinte script:

DECLARE @GCom int = 2563,
    @Od DateTime2(0) = '20170210', 
    @Do DateTime2(0) = '20170224'    

--Create a table with intervals by 30 seconds
declare @temp Table ([cas] datetime2(0))
INSERT @temp([cas])
SELECT d
FROM
(
  SELECT
      d = DATEADD(SECOND, (rn - 1)*30, @Od)
  FROM 
  (
      SELECT TOP (DATEDIFF(MINUTE, @Od, @Do)*2) 
          rn = ROW_NUMBER() OVER (ORDER BY s1.[object_id])
      FROM
          sys.all_objects AS s1
      CROSS JOIN
          sys.all_objects AS s2
      ORDER BY
          s1.[object_id]
  ) AS x
) AS y;

--Create temp table
CREATE TABLE #AutoData (
    [Cas] [datetime2](0) NOT NULL PRIMARY KEY,
    [IDProvozniRezim] [tinyint] NOT NULL,
    [IDRidic] [smallint] NULL,
    [Stav] [tinyint] NOT NULL,
    [Klicek] [bit] NOT NULL,
    [Alarm] [bit] NOT NULL,
    [MAlarm] [tinyint] NOT NULL,
    [DAlarm] [bit] NOT NULL,
    [Bypass] [bit] NOT NULL,
    [Lat] [real] NULL,
    [Lon] [real] NULL,
    [ObjemAktualni] [real] NOT NULL,
    [RychlostMaxV1] [real] NOT NULL,
    [RychlostV2] [real] NOT NULL,
    [Otacky] [smallint] NOT NULL,
    [Nadspotreba] [real] NOT NULL,
    [Vzdalenost] [real] NOT NULL,
    [Motor] [smallint] NOT NULL
)

--Populate the temp table selecting only relevant AutoData records
INSERT INTO #AutoData
SELECT [Cas]
      ,[IDProvozniRezim]
      ,[IDRidic]
      ,[Stav]
      ,[Klicek]
      ,[Alarm]
      ,[MAlarm]
      ,[DAlarm]
      ,[Bypass]
      ,[Lat]
      ,[Lon]
      ,[ObjemAktualni]
      ,[RychlostMaxV1]
      ,[RychlostV2]
      ,[Otacky]
      ,[Nadspotreba]
      ,[Vzdalenost]
      ,[Motor]
FROM AutoData a 
WHERE a.GCom = @GCom AND a.cas BETWEEN @Od AND @do

--Select final data
SELECT t.cas, ad.malarm, ad.IDProvoznirezim, ad.Otacky, ad.motor, ad.objemAktualni, ad.Nadspotreba 
FROM @temp t
OUTER APPLY (
SELECT TOP 1 stav, malarm, otacky,motor, objemAktualni, Nadspotreba, IDProvoznirezim  FROM #AutoData a
                     WHERE DATEDIFF(SECOND, a.cas, t.cas)<=CASE WHEN Motor>120 THEN Motor ELSE 120 END 
                     AND DATEDIFF(SECOND,  a.cas, t.cas)>-30 
                     ORDER BY CASE WHEN DATEDIFF(SECOND, a.cas, t.cas)>0 THEN DATEDIFF(SECOND, a.cas, t.cas) ELSE (DATEDIFF(SECOND, a.cas, t.cas)*-1) +120 END
) ad

DROP TABLE #AutoData

A princípio tentei escrever o script com apenas uma variável de tabela @temp colocando a condição WHERE a.GCom = @GCom AND a.cas BETWEEN @Od AND @dona última seleção. O script levou 39 segundos para ser executado.

Quando eu usei #AutoDataa tabela temporária para pré-carregar o subconjunto de dados em uma tabela temporária como é mostrado no script acima, ele caiu para 5 segundos.

Então eu tentei usar uma variável de tabela @AutoDataem vez de #AutoData- mas levou muito mais tempo - 22 segundos.

@temptable tem 40320 registros e #AutoDatatable tem 1904 registros para este exemplo. Mas, surpreendentemente, apenas usar #temptabela em vez de @tempvariável tornou a execução lenta novamente.

Fiquei surpreso ao ver essas diferenças usando ou não a tabela/variável temporária. Aparentemente, o SQL Server não pôde por si só otimizar o interior da cláusula OUTER APPLY.

Mas por que há uma diferença tão grande usando variáveis ​​de tabela versus tabelas temporárias? Existe alguma outra maneira de saber, o que usar e não apenas tentar?


Plano de execução com tabela temporária #AutoData: TempTable

https://www.brentozar.com/pastetheplan/?id=B1y2x2Zcg

Plano de execução com a variável @AutoData: Variável

https://www.brentozar.com/pastetheplan/?id=r1rAZnbqx

sql-server sql-server-2008
  • 2 2 respostas
  • 4436 Views

2 respostas

  • Voted
  1. Best Answer
    Brent Ozar
    2017-02-28T05:47:42+08:002017-02-28T05:47:42+08:00

    A chave está nesta parte da sua pergunta:

    A tabela @temp tem 40320 registros

    No plano de execução, passe o mouse sobre a varredura da tabela @temp. Compare o número estimado de linhas com o número real de linhas. (Se você quiser postar o plano em http://PasteThePlan.com , podemos fornecer detalhes mais específicos. Isenção de responsabilidade: esse é o site da minha empresa.)

    Você verá que o número estimado de linhas é muito baixo.

    O SQL Server estima que 1-3 linhas retornarão de uma variável de tabela (dependendo da sua versão do SQL Server, estimador de cardinalidade, sinalizadores de rastreamento etc.) Isso, por sua vez, fornece um plano de execução muito ruim porque o SQL Server subestima a quantidade de trabalho precisará de outras tabelas, quanta memória reservar, etc.

    Aqui estão as duas maneiras mais populares de obter uma estimativa mais precisa:

    • Tente uma tabela temporária (e observe as linhas estimadas versus reais no plano)
    • Use OPTION (RECOMPILE) em sua consulta - o que fornecerá uma estimativa muito mais precisa, mas com algumas desvantagens muito grandes em relação à visibilidade do cache do plano e ao uso da CPU

    Para me ver fazendo isso ao vivo, assista ao Watch Brent Tune Queries de 1 hora (disclaimer: sou eu, linkando para um vídeo meu) onde pego uma consulta do Stack Overflow que usa uma variável de tabela e a ajusto ao vivo na frente de um audiência no SQL Rally Noruega.

    • 4
  2. paparazzo
    2017-02-28T05:32:58+08:002017-02-28T05:32:58+08:00

    O planejador de consultas é mais eficiente com #temp. Em uma variável de tabela, ela considera apenas as primeiras linhas.

    Sua variável de tabela (e #temp se você usar uma) provavelmente se beneficiaria de declarar uma chave primária.

    Coloque uma chave em #AutoData e preencha apenas as linhas necessárias.

    Classifique por chave à medida que adiciona linhas .

    Eu suspeito que abaixo pode ser otimizado com um row_number()

    SELECT t.cas
         , ad.malarm, ad.IDProvoznirezim, ad.Otacky
         , ad.motor, ad.objemAktualni, ad.Nadspotreba 
    FROM @temp t
    OUTER APPLY ( SELECT TOP 1 malarm, IDProvoznirezim, Otacky
                             , motor, objemAktualni, Nadspotreba   
                   FROM #AutoData a
                  WHERE DATEDIFF(SECOND, a.cas, t.cas) <= CASE WHEN Motor > 120 THEN Motor ELSE 120 END 
                    AND DATEDIFF(SECOND, a.cas, t.cas)  > -30 
                  ORDER BY CASE WHEN DATEDIFF(SECOND, a.cas, t.cas) > 0 THEN DATEDIFF(SECOND, a.cas, t.cas) 
                                ELSE DATEDIFF(SECOND, t.cas, a.cas) + 120 END
                ) ad  
    

    Esta é uma tentativa como row_number()

    select * from 
    ( SELECT t.cas
           , a.malarm, a.IDProvoznirezim, a.Otacky
           , a.motor, a.objemAktualni, a.Nadspotreba 
           , row_nunber() over (partition by t.cas 
                                ORDER BY CASE WHEN DATEDIFF(SECOND, a.cas, t.cas) > 0 THEN DATEDIFF(SECOND, a.cas, t.cas) 
                                ELSE DATEDIFF(SECOND, t.cas, a.cas) + 120 END) rn
        FROM @temp t  -- with primay key t.cas order by
        join AutoData a
          on a.GCom = @GCom 
         AND a.cas BETWEEN @Od AND @do
         AND DATEDIFF(SECOND, a.cas, t.cas) <= CASE WHEN Motor > 120 THEN Motor ELSE 120 END 
         AND DATEDIFF(SECOND, a.cas, t.cas)  > -30 
    ) ad 
    where ad.rn = 1  
    
    • 1

relate perguntas

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

  • Quanto "Padding" coloco em meus índices?

  • Existe um processo do tipo "práticas recomendadas" para os desenvolvedores seguirem para alterações no banco de dados?

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

  • Downgrade do SQL Server 2008 para 2005

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