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 / 15596
Accepted
Quandary
Quandary
Asked: 2012-03-19 20:38:03 +0800 CST2012-03-19 20:38:03 +0800 CST 2012-03-19 20:38:03 +0800 CST

Desaceleração da consulta SQL de 1 segundo para 11 minutos - por quê?

  • 772

Descrição: Estou transferindo a consulta abaixo (listando tabelas por dependências de chaves estrangeiras) para PostGreSql.

WITH Fkeys AS (

    SELECT DISTINCT 
         OnTable       = OnTable.name
        ,AgainstTable  = AgainstTable.name 
    FROM sysforeignkeys fk 

        INNER JOIN sysobjects onTable 
            ON fk.fkeyid = onTable.id 

        INNER JOIN sysobjects againstTable  
            ON fk.rkeyid = againstTable.id 

    WHERE 1=1
        AND AgainstTable.TYPE = 'U'
        AND OnTable.TYPE = 'U'
        -- ignore self joins; they cause an infinite recursion
        AND OnTable.Name <> AgainstTable.Name
    )

,MyData AS (

    SELECT 
         OnTable = o.name 
        ,AgainstTable = FKeys.againstTable 
    FROM sys.objects o 

    LEFT JOIN FKeys
        ON o.name = FKeys.onTable 

    WHERE (1=1) 
        AND o.type = 'U' 
        AND o.name NOT LIKE 'sys%' 
    )

,MyRecursion AS (

    -- base case
    SELECT  
         TableName    = OnTable
        ,Lvl        = 1
    FROM MyData
    WHERE 1=1
        AND AgainstTable IS NULL 

    -- recursive case
    UNION ALL 

    SELECT 
         TableName = OnTable 
        ,Lvl       = r.Lvl + 1 
    FROM MyData d 
        INNER JOIN MyRecursion r 
            ON d.AgainstTable = r.TableName 
)
SELECT 
     Lvl = MAX(Lvl)
    ,TableName
    --,strSql = 'delete from [' + tablename + ']'
FROM 
    MyRecursion
GROUP BY
    TableName

ORDER BY lvl

/*
ORDER BY 

     2 ASC
    ,1 ASC

*/

Usando o information_schema, a consulta fica assim:

WITH Fkeys AS 
(
    SELECT DISTINCT 
         KCU1.TABLE_NAME AS OnTable 
        ,KCU2.TABLE_NAME AS AgainstTable 
    FROM INFORMATION_SCHEMA.REFERENTIAL_CONSTRAINTS RC 

    LEFT JOIN INFORMATION_SCHEMA.KEY_COLUMN_USAGE KCU1 
        ON KCU1.CONSTRAINT_CATALOG = RC.CONSTRAINT_CATALOG  
        AND KCU1.CONSTRAINT_SCHEMA = RC.CONSTRAINT_SCHEMA 
        AND KCU1.CONSTRAINT_NAME = RC.CONSTRAINT_NAME 

    LEFT JOIN INFORMATION_SCHEMA.KEY_COLUMN_USAGE KCU2 
        ON KCU2.CONSTRAINT_CATALOG =  RC.UNIQUE_CONSTRAINT_CATALOG  
        AND KCU2.CONSTRAINT_SCHEMA = RC.UNIQUE_CONSTRAINT_SCHEMA 
        AND KCU2.CONSTRAINT_NAME = RC.UNIQUE_CONSTRAINT_NAME 
        AND KCU2.ORDINAL_POSITION = KCU1.ORDINAL_POSITION 

    WHERE (1=1)
    AND KCU1.TABLE_NAME <> KCU2.TABLE_NAME 
)

,MyData AS 
( 
    SELECT 
         TABLE_NAME AS OnTable  
        ,FKeys.againstTable AS AgainstTable
    FROM INFORMATION_SCHEMA.TABLES 

    LEFT JOIN FKeys
        ON TABLE_NAME = FKeys.onTable  

    WHERE (1=1) 
        AND TABLE_TYPE = 'BASE TABLE'
        AND TABLE_NAME NOT IN ('sysdiagrams', 'dtproperties') 
)

,MyRecursion AS 
(
    -- base case
    SELECT  
         OnTable AS TableName 
        ,1 AS Lvl 
    FROM MyData
    WHERE 1=1
    AND AgainstTable IS NULL 

    -- recursive case
    UNION ALL 

    SELECT 
         OnTable AS TableName
        ,r.Lvl + 1 AS Lvl 
    FROM MyData d 

    INNER JOIN MyRecursion r 
        ON d.AgainstTable = r.TableName 
)

SELECT 
     MAX(Lvl) AS Lvl 
    ,TableName
    --,strSql = 'delete from [' + tablename + ']'
FROM 
    MyRecursion
GROUP BY
    TableName

ORDER BY lvl

/*
ORDER BY 

     2 ASC
    ,1 ASC

*/

Minha pergunta agora é:

No SQL Server (testado no 2008 R2): Por que a consulta salta de 1 segundo para 11 minutos quando substituo

SELECT DISTINCT 
     OnTable       = OnTable.name
    ,AgainstTable  = AgainstTable.name 
FROM sysforeignkeys fk 

    INNER JOIN sysobjects onTable 
        ON fk.fkeyid = onTable.id 

    INNER JOIN sysobjects againstTable  
        ON fk.rkeyid = againstTable.id 

WHERE 1=1
    AND AgainstTable.TYPE = 'U'
    AND OnTable.TYPE = 'U'
    -- ignore self joins; they cause an infinite recursion
    AND OnTable.Name <> AgainstTable.Name

com

SELECT DISTINCT 
     KCU1.TABLE_NAME AS OnTable 
    ,KCU2.TABLE_NAME AS AgainstTable 
FROM INFORMATION_SCHEMA.REFERENTIAL_CONSTRAINTS RC 

LEFT JOIN INFORMATION_SCHEMA.KEY_COLUMN_USAGE KCU1 
    ON KCU1.CONSTRAINT_CATALOG = RC.CONSTRAINT_CATALOG  
    AND KCU1.CONSTRAINT_SCHEMA = RC.CONSTRAINT_SCHEMA 
    AND KCU1.CONSTRAINT_NAME = RC.CONSTRAINT_NAME 

LEFT JOIN INFORMATION_SCHEMA.KEY_COLUMN_USAGE KCU2 
    ON KCU2.CONSTRAINT_CATALOG =  RC.UNIQUE_CONSTRAINT_CATALOG  
    AND KCU2.CONSTRAINT_SCHEMA = RC.UNIQUE_CONSTRAINT_SCHEMA 
    AND KCU2.CONSTRAINT_NAME = RC.UNIQUE_CONSTRAINT_NAME 
    AND KCU2.ORDINAL_POSITION = KCU1.ORDINAL_POSITION 

WHERE (1=1)
AND KCU1.TABLE_NAME <> KCU2.TABLE_NAME 

???

Tanto quanto eu posso dizer, realmente não há diferença de velocidade significativa ao executar apenas as consultas parciais separadamente. Além disso, o conjunto de resultados é totalmente o mesmo (verifiquei todas as linhas no Excel), embora a ordem seja diferente.

Abaixo da versão funcional do PostGreSQL (concluída em 35 ms exatamente no mesmo conteúdo do banco de dados [75 tabelas]...)
-- Sem garantia alguma --

WITH RECURSIVE Fkeys AS 
(
    SELECT DISTINCT 
         KCU1.TABLE_NAME AS OnTable 
        ,KCU2.TABLE_NAME AS AgainstTable 
    FROM INFORMATION_SCHEMA.REFERENTIAL_CONSTRAINTS RC 

    LEFT JOIN INFORMATION_SCHEMA.KEY_COLUMN_USAGE KCU1 
        ON KCU1.CONSTRAINT_CATALOG = RC.CONSTRAINT_CATALOG  
        AND KCU1.CONSTRAINT_SCHEMA = RC.CONSTRAINT_SCHEMA 
        AND KCU1.CONSTRAINT_NAME = RC.CONSTRAINT_NAME 

    LEFT JOIN INFORMATION_SCHEMA.KEY_COLUMN_USAGE KCU2 
        ON KCU2.CONSTRAINT_CATALOG =  RC.UNIQUE_CONSTRAINT_CATALOG  
        AND KCU2.CONSTRAINT_SCHEMA = RC.UNIQUE_CONSTRAINT_SCHEMA 
        AND KCU2.CONSTRAINT_NAME = RC.UNIQUE_CONSTRAINT_NAME 
        AND KCU2.ORDINAL_POSITION = KCU1.ORDINAL_POSITION 
)

,MyData AS 
( 
    SELECT 
         TABLE_NAME AS OnTable  
        ,FKeys.againstTable AS AgainstTable
    FROM INFORMATION_SCHEMA.TABLES 

    LEFT JOIN FKeys
        ON TABLE_NAME = FKeys.onTable  

    WHERE (1=1) 
        AND TABLE_TYPE = 'BASE TABLE'
        AND TABLE_SCHEMA = 'public'
        --AND TABLE_NAME NOT IN ('sysdiagrams', 'dtproperties') 
)


,MyRecursion AS 
(
    -- base case
    SELECT  
         OnTable AS TableName 
        ,1 AS Lvl 
    FROM MyData
    WHERE 1=1
    AND AgainstTable IS NULL 

    -- recursive case
    UNION ALL 

    SELECT 
         OnTable AS TableName
        ,r.Lvl + 1 AS Lvl 
    FROM MyData d 

    INNER JOIN MyRecursion r 
        ON d.AgainstTable = r.TableName 
)

SELECT 
     MAX(Lvl) AS Lvl 
    ,TableName
    --,strSql = 'delete from [' + tablename + ']'
FROM 
    MyRecursion
GROUP BY
    TableName

ORDER BY lvl


/*
ORDER BY 

     2 ASC
    ,1 ASC

*/

Também parece que

AND KCU1.TABLE_NAME <> KCU2.TABLE_NAME

é supérfluo ao usar information_schema, portanto, na verdade, deve ser mais rápido.

sql-server performance
  • 2 2 respostas
  • 12139 Views

2 respostas

  • Voted
  1. Best Answer
    Martin Smith
    2012-03-21T02:46:22+08:002012-03-21T02:46:22+08:00

    Eu provavelmente abandonaria as INFORMATION_SCHEMAexibições aqui e usaria as novas sys.exibições (em oposição às de compatibilidade com versões anteriores), ou pelo menos materializaria os resultados do JOINprimeiro em uma tabela indexada.

    CTEs recursivos sempre obtêm o mesmo plano básico no SQL Server, onde cada linha é adicionada a um spool de pilha e processada uma a uma. Isso significa que a junção REFERENTIAL_CONSTRAINTS RC, KEY_COLUMN_USAGE KCU1, KEY_COLUMN_USAGE KCU2ocorrerá tantas vezes quanto o resultado da seguinte consulta SELECT COUNT(*) FROM MyRecursion.

    O que eu suponho no seu caso (a partir dos 11 minutos de tempo de execução) é provavelmente muitos milhares de vezes, então você precisa que a parte recursiva seja o mais eficiente possível. Sua consulta executará o seguinte tipo de coisa milhares de vezes.

       SELECT  
               KCU1.TABLE_CATALOG,
               KCU1.TABLE_SCHEMA,
               KCU1.TABLE_NAME
        FROM INFORMATION_SCHEMA.REFERENTIAL_CONSTRAINTS RC 
        INNER JOIN INFORMATION_SCHEMA.KEY_COLUMN_USAGE KCU1 
            ON KCU1.CONSTRAINT_CATALOG = RC.CONSTRAINT_CATALOG  
            AND KCU1.CONSTRAINT_SCHEMA = RC.CONSTRAINT_SCHEMA 
            AND KCU1.CONSTRAINT_NAME = RC.CONSTRAINT_NAME 
        INNER JOIN INFORMATION_SCHEMA.KEY_COLUMN_USAGE KCU2 
            ON KCU2.CONSTRAINT_CATALOG =  RC.UNIQUE_CONSTRAINT_CATALOG  
            AND KCU2.CONSTRAINT_SCHEMA = RC.UNIQUE_CONSTRAINT_SCHEMA 
            AND KCU2.CONSTRAINT_NAME = RC.UNIQUE_CONSTRAINT_NAME 
            AND KCU2.ORDINAL_POSITION = KCU1.ORDINAL_POSITION 
        WHERE KCU2.TABLE_NAME = 'FOO' 
    

    (Observação: ambas as versões de sua consulta retornarão resultados incorretos se o mesmo nome de tabela em esquemas diferentes)

    Como você pode ver, o plano para isso é horrível.

    Plano

    Comparando isso com o plano da sua sysconsulta que é um pouco mais simples.

    SELECT OnTable = OnTable.name, 
           AgainstTable = AgainstTable.name 
    FROM   sysforeignkeys fk 
           INNER JOIN sysobjects OnTable 
             ON fk.fkeyid = OnTable.id 
           INNER JOIN sysobjects AgainstTable 
             ON fk.rkeyid = AgainstTable.id 
    WHERE  AgainstTable.name = 'FOO' 
    

    Plano 2

    você pode incentivar a materialização intermediária sem criar uma #temptabela explicitamente, alterando a definição de MyDatapara

    MyData AS 
    ( 
        SELECT TOP 99.999999 PERCENT
             TABLE_NAME AS OnTable  
            ,Fkeys.AgainstTable AS AgainstTable
        FROM INFORMATION_SCHEMA.TABLES 
    
        LEFT JOIN Fkeys
            ON TABLE_NAME = Fkeys.OnTable  
    
        WHERE (1=1) 
            AND TABLE_TYPE = 'BASE TABLE'
            AND TABLE_NAME NOT IN ('sysdiagrams', 'dtproperties') 
            ORDER BY TABLE_NAME
    )
    

    Testando Adventureworks2008em minha máquina, isso reduziu o tempo de execução de cerca de 10 segundos para 250 ms (depois que a primeira execução foi concluída, pois o plano levou 2 segundos para ser compilado). Ele adiciona um spool ansioso ao plano que materializa o resultado do Join na primeira chamada recursiva e o reproduz nas chamadas subsequentes. No entanto, este comportamento não é garantido e você pode querer votar a favor da solicitação do item Conectar Forneça uma dica para forçar a materialização intermediária de CTEs ou tabelas derivadas

    Eu me sentiria mais seguro criando a #temptabela explicitamente como abaixo, em vez de confiar nesse comportamento.

    CREATE TABLE #MyData
    (
    OnTable SYSNAME,
    AgainstTable NVARCHAR(128) NULL,
    UNIQUE CLUSTERED (AgainstTable, OnTable)
    );
    
    WITH Fkeys AS 
    (
        SELECT DISTINCT 
             KCU1.TABLE_NAME AS OnTable 
            ,KCU2.TABLE_NAME AS AgainstTable 
        FROM INFORMATION_SCHEMA.REFERENTIAL_CONSTRAINTS RC 
    
        LEFT JOIN INFORMATION_SCHEMA.KEY_COLUMN_USAGE KCU1 
            ON KCU1.CONSTRAINT_CATALOG = RC.CONSTRAINT_CATALOG  
            AND KCU1.CONSTRAINT_SCHEMA = RC.CONSTRAINT_SCHEMA 
            AND KCU1.CONSTRAINT_NAME = RC.CONSTRAINT_NAME 
    
        LEFT JOIN INFORMATION_SCHEMA.KEY_COLUMN_USAGE KCU2 
            ON KCU2.CONSTRAINT_CATALOG =  RC.UNIQUE_CONSTRAINT_CATALOG  
            AND KCU2.CONSTRAINT_SCHEMA = RC.UNIQUE_CONSTRAINT_SCHEMA 
            AND KCU2.CONSTRAINT_NAME = RC.UNIQUE_CONSTRAINT_NAME 
            AND KCU2.ORDINAL_POSITION = KCU1.ORDINAL_POSITION 
    
        WHERE (1=1)
        AND KCU1.TABLE_NAME <> KCU2.TABLE_NAME 
    )
    
    ,MyData AS 
    ( 
        SELECT 
             TABLE_NAME AS OnTable  
            ,Fkeys.AgainstTable AS AgainstTable
        FROM INFORMATION_SCHEMA.TABLES 
    
        LEFT JOIN Fkeys
            ON TABLE_NAME = Fkeys.OnTable  
    
        WHERE (1=1) 
            AND TABLE_TYPE = 'BASE TABLE'
            AND TABLE_NAME NOT IN ('sysdiagrams', 'dtproperties') 
    )
    INSERT INTO #MyData
    SELECT *
    FROM MyData;
    
    
    WITH MyRecursion AS 
    (
        -- base case
        SELECT  
             OnTable AS TableName 
            ,1 AS Lvl 
        FROM #MyData
        WHERE 1=1
        AND AgainstTable IS NULL 
    
        -- recursive case
        UNION ALL 
    
        SELECT 
             OnTable AS TableName
            ,r.Lvl + 1 AS Lvl 
        FROM #MyData d 
    
        INNER JOIN MyRecursion r 
            ON d.AgainstTable = r.TableName 
    )
    
    SELECT 
         MAX(Lvl) AS Lvl 
        ,TableName
        --,strSql = 'delete from [' + tablename + ']'
    FROM 
        MyRecursion
    GROUP BY
        TableName
    
    ORDER BY Lvl
    
    DROP TABLE #MyData
    

    ou alternativamente

    • 12
  2. Jānis
    2012-03-20T03:09:56+08:002012-03-20T03:09:56+08:00

    Em ambos os casos, você está consultando exibições, que são deixadas para compatibilidade: Exibições de compatibilidade e Exibições de esquema de informações .

    Em vez disso, use exibições de catálogo para obter o melhor desempenho (msdn: "Recomendamos que você use exibições de catálogo porque elas são a interface mais geral para os metadados do catálogo e fornecem a maneira mais eficiente de obter, transformar e apresentar formas personalizadas dessas informações") ..

    • 2

relate perguntas

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

  • 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

    Como ver a lista de bancos de dados no Oracle?

    • 8 respostas
  • Marko Smith

    Quão grande deve ser o mysql innodb_buffer_pool_size?

    • 4 respostas
  • Marko Smith

    Listar todas as colunas de uma tabela especificada

    • 5 respostas
  • Marko Smith

    restaurar a tabela do arquivo .frm e .ibd?

    • 10 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

    Como selecionar a primeira linha de cada grupo?

    • 6 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
    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
    pedrosanta Listar os privilégios do banco de dados usando o psql 2011-08-04 11:01:21 +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
  • Martin Hope
    bernd_k Quando devo usar uma restrição exclusiva em vez de um índice exclusivo? 2011-01-05 02:32:27 +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