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 / 101444
Accepted
Metaphor
Metaphor
Asked: 2015-05-14 10:48:13 +0800 CST2015-05-14 10:48:13 +0800 CST 2015-05-14 10:48:13 +0800 CST

Encontrar todas as uniões necessárias para ingressar programaticamente em uma tabela

  • 772

Dada uma SourceTable e uma TargetTable, gostaria de criar programaticamente uma string com todas as junções necessárias.

Resumindo, estou tentando encontrar uma maneira de criar uma string como esta:

FROM SourceTable t
JOIN IntermediateTable t1 on t1.keycolumn = t.keycolumn
JOIN TargetTable t2 on t2.keycolumn = t1.keycolumn

Eu tenho uma consulta que retorna todas as chaves estrangeiras para uma determinada tabela, mas estou enfrentando limitações ao tentar executar tudo isso recursivamente para encontrar o caminho de junção ideal e criar a string.

SELECT 
    p.name AS ParentTable
    ,pc.name AS ParentColumn
    ,r.name AS ChildTable
    ,rc.name AS ChildColumn
FROM sys.foreign_key_columns fk
JOIN sys.columns pc ON pc.object_id = fk.parent_object_id AND pc.column_id = fk.parent_column_id 
JOIN sys.columns rc ON rc.object_id = fk.referenced_object_id AND rc.column_id = fk.referenced_column_id
JOIN sys.tables p ON p.object_id = fk.parent_object_id
JOIN sys.tables r ON r.object_id = fk.referenced_object_id
WHERE fk.parent_object_id = OBJECT_ID('aTable')
ORDER BY ChildTable, fk.referenced_column_id

Tenho certeza de que isso já foi feito antes, mas não consigo encontrar um exemplo.

sql-server sql-server-2014
  • 2 2 respostas
  • 1768 Views

2 respostas

  • Voted
  1. Best Answer
    Geoff Patterson
    2015-05-15T12:38:28+08:002015-05-15T12:38:28+08:00

    Eu tinha um script que faz uma versão rudimentar da travessia de chave estrangeira. Adaptei-o rapidamente (veja abaixo) e talvez você possa usá-lo como ponto de partida.

    Dada uma tabela de destino, o script tenta imprimir a string de junção para o caminho mais curto (ou um deles no caso de empates) para todas as tabelas de origem possíveis, de modo que as chaves estrangeiras de coluna única possam ser percorridas para alcançar a tabela de destino. O script parece estar funcionando bem no banco de dados com alguns milhares de tabelas e muitas conexões FK nas quais experimentei.

    Como outros mencionaram nos comentários, você precisa tornar isso mais complexo se precisar lidar com chaves estrangeiras de várias colunas. Além disso, esteja ciente de que este não é, de forma alguma, um código totalmente testado e pronto para produção. Espero que seja um ponto de partida útil se você decidir criar essa funcionalidade!

    -- Drop temp tables that will be used below
    IF OBJECT_ID('tempdb..#paths') IS NOT NULL
        DROP TABLE #paths
    GO
    IF OBJECT_ID('tempdb..#shortestPaths') IS NOT NULL
        DROP TABLE #shortestPaths
    GO
    
    -- The table (e.g. "TargetTable") to start from (or end at, depending on your point of view)
    DECLARE @targetObjectName SYSNAME = 'TargetTable'
    
    -- Identify all paths from TargetTable to any other table on the database,
    -- counting all single-column foreign keys as a valid connection from one table to the next
    ;WITH singleColumnFkColumns AS (
        -- We limit the scope of this exercise to single column foreign keys
        -- We explicitly filter out any multi-column foreign keys to ensure that they aren't misinterpreted below
        SELECT fk1.*
        FROM sys.foreign_key_columns fk1
        LEFT JOIN sys.foreign_key_columns fk2 ON fk2.constraint_object_id = fk1.constraint_object_id AND fk2.constraint_column_id = 2
        WHERE fk1.constraint_column_id = 1
            AND fk2.constraint_object_id IS NULL
    )
    , parentCTE AS (
        -- Base case: Find all outgoing (pointing into another table) foreign keys for the specified table
        SELECT 
            p.object_id AS ParentId
            ,OBJECT_SCHEMA_NAME(p.object_id) + '.' + p.name AS ParentTable
            ,pc.column_id AS ParentColumnId
            ,pc.name AS ParentColumn
            ,r.object_id AS ChildId
            ,OBJECT_SCHEMA_NAME(r.object_id) + '.' + r.name AS ChildTable
            ,rc.column_id AS ChildColumnId
            ,rc.name AS ChildColumn
            ,1 AS depth
            -- Maintain the full traversal path that has been taken thus far
            -- We use "," to delimit each table, and each entry then has a
            -- "<object_id>_<parent_column_id>_<child_column_id>" format
            ,   ',' + CONVERT(VARCHAR(MAX), p.object_id) + '_NULL_' + CONVERT(VARCHAR(MAX), pc.column_id) +
                ',' + CONVERT(VARCHAR(MAX), r.object_id) + '_' + CONVERT(VARCHAR(MAX), pc.column_id) + '_' + CONVERT(VARCHAR(MAX), rc.column_id) AS TraversalPath
        FROM sys.foreign_key_columns fk
        JOIN sys.columns pc ON pc.object_id = fk.parent_object_id AND pc.column_id = fk.parent_column_id 
        JOIN sys.columns rc ON rc.object_id = fk.referenced_object_id AND rc.column_id = fk.referenced_column_id
        JOIN sys.tables p ON p.object_id = fk.parent_object_id
        JOIN sys.tables r ON r.object_id = fk.referenced_object_id
        WHERE fk.parent_object_id = OBJECT_ID(@targetObjectName)
            AND p.object_id <> r.object_id -- Ignore FKs from one column in the table to another
    
        UNION ALL
    
        -- Recursive case: Find all outgoing foreign keys for all tables
        -- on the current fringe of the recursion
        SELECT 
            p.object_id AS ParentId
            ,OBJECT_SCHEMA_NAME(p.object_id) + '.' + p.name AS ParentTable
            ,pc.column_id AS ParentColumnId
            ,pc.name AS ParentColumn
            ,r.object_id AS ChildId
            ,OBJECT_SCHEMA_NAME(r.object_id) + '.' + r.name AS ChildTable
            ,rc.column_id AS ChildColumnId
            ,rc.name AS ChildColumn
            ,cte.depth + 1 AS depth
            ,cte.TraversalPath + ',' + CONVERT(VARCHAR(MAX), r.object_id) + '_' + CONVERT(VARCHAR(MAX), pc.column_id) + '_' + CONVERT(VARCHAR(MAX), rc.column_id) AS TraversalPath
        FROM parentCTE cte
        JOIN singleColumnFkColumns fk
            ON fk.parent_object_id = cte.ChildId
            -- Optionally consider only a traversal of the same foreign key
            -- With this commented out, we can reach table A via column A1
            -- and leave table A via column A2.  If uncommented, we can only
            -- enter and leave a table via the same column
            --AND fk.parent_column_id = cte.ChildColumnId
        JOIN sys.columns pc ON pc.object_id = fk.parent_object_id AND pc.column_id = fk.parent_column_id 
        JOIN sys.columns rc ON rc.object_id = fk.referenced_object_id AND rc.column_id = fk.referenced_column_id
        JOIN sys.tables p ON p.object_id = fk.parent_object_id
        JOIN sys.tables r ON r.object_id = fk.referenced_object_id
        WHERE p.object_id <> r.object_id -- Ignore FKs from one column in the table to another
            -- If our path has already taken us to this table, avoid the cycle that would be created by returning to the same table
            AND cte.TraversalPath NOT LIKE ('%_' + CONVERT(VARCHAR(MAX), r.object_id) + '%')
    )
    SELECT *
    INTO #paths
    FROM parentCTE
    ORDER BY depth, ParentTable, ChildTable
    GO
    
    -- For each distinct table that can be reached by traversing foreign keys,
    -- record the shortest path to that table (or one of the shortest paths in
    -- case there are multiple paths of the same length)
    SELECT *
    INTO #shortestPaths
    FROM (
        SELECT *, ROW_NUMBER() OVER (PARTITION BY ChildTable ORDER BY depth ASC) AS rankToThisChild
        FROM #paths
    ) x
    WHERE rankToThisChild = 1
    ORDER BY ChildTable
    GO
    
    -- Traverse the shortest path, starting from the source the full path and working backwards,
    -- building up the desired join string as we go
    WITH joinCTE AS (
        -- Base case: Start with the from clause to the child table at the end of the traversal
        -- Note that the first step of the recursion will re-process this same row, but adding
        -- the ParentTable => ChildTable join
        SELECT p.ChildTable
            , p.TraversalPath AS ParentTraversalPath
            , NULL AS depth
            , CONVERT(VARCHAR(MAX), 'FROM ' + p.ChildTable + ' t' + CONVERT(VARCHAR(MAX), p.depth+1)) AS JoinString
        FROM #shortestPaths p
    
        UNION ALL
    
        -- Recursive case: Process the ParentTable => ChildTable join, then recurse to the
        -- previous table in the full traversal.  We'll end once we reach the root and the
        -- "ParentTraversalPath" is the empty string
        SELECT cte.ChildTable
            , REPLACE(p.TraversalPath, ',' + CONVERT(VARCHAR, p.ChildId) + '_' + CONVERT(VARCHAR, p.ParentColumnId)+ '_' + CONVERT(VARCHAR, p.ChildColumnId), '') AS TraversalPath
            , p.depth
            , cte.JoinString + '
    ' + CONVERT(VARCHAR(MAX), 'JOIN ' + p.ParentTable + ' t' + CONVERT(VARCHAR(MAX), p.depth) + ' ON t' + CONVERT(VARCHAR(MAX), p.depth) + '.' + p.ParentColumn + ' = t' + CONVERT(VARCHAR(MAX), p.depth+1) + '.' + p.ChildColumn) AS JoinString
        FROM joinCTE cte
        JOIN #paths p
            ON p.TraversalPath = cte.ParentTraversalPath
    )
    -- Select only the fully built strings that end at the root of the traversal
    -- (which should always be the specific table name, e.g. "TargetTable")
    SELECT ChildTable, 'SELECT TOP 100 * 
    ' +JoinString
    FROM joinCTE
    WHERE depth = 1
    ORDER BY ChildTable
    GO
    
    • 4
  2. Muthukrishnan Senthilkumaran
    2018-08-22T06:46:57+08:002018-08-22T06:46:57+08:00

    Você pode colocar a lista de chaves de uma tabela com dois campos TAB_NAME,KEY_NAME para todas as tabelas que deseja conectar.

    Exemplo, para mesaCity

    • Cidade|Cidade_nome
    • Cidade|Country_name
    • Cidade|Província_nome
    • Cidade|Cidade_Código

    da mesma forma Provincee Country.

    Colete os dados para as tabelas e coloque em uma única tabela (por exemplo, tabela de metadados)

    Agora elabore a consulta como abaixo

    select * from
    (Select Table_name,Key_name from Meta_Data 
    where Table_name in ('City','Province','Country')) A,
    (Select Table_name,Key_name from Meta_Data 
    where Table_name in ('City','Province','Country')) B,
    (Select Table_name,Key_name from Meta_Data 
    where Table_name in ('City','Province','Country')) C
    
    where
    
    A.Table_Name <> B.Table_name and
    B.Table_name <> C.Table_name and
    C.Table_name <> A.Table_name and
    A.Column_name = B.Column_name and
    B.Column_name = C.Column_name
    

    Isso mostrará como você pode vincular as tabelas com base nas chaves correspondentes (mesmos nomes de chave)

    Se você acha que o nome das chaves pode não corresponder, pode incluir um campo de chave alternativo e tentar usá-lo na condição where.

    • 0

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