通过键查找从一个表到另一个表的路径

时间:2019-02-28 11:05:02

标签: sql tsql

例如;

我在数据库中有500个表。一个表名为“ Table1”,另一个表名为“ Table234”。我需要加入“ Table1”和“ Table234”,但不能直接加入它们,我需要使用其他表。是否可以查询在哪里输入起始表(在此情况下为Table1)和结束表(在此情况下为Table234),并找到从起始表到结束表的所有键路径。

假设我有两种可能的路径:

  • 表1->表23->表456->表234
  • 表1->表34->表23-> Zable 456->表234

    从表1中选择*

    在XX = XX上加入table23

    在XX = XX上加入表456

    在XX = XX上加入table234

也许像BFS这样的东西可以找到最短的路径,而不是将其保存在某个地方并找到没有该路径的BFS?

解决方案(@Geoff Patterson):

IF OBJECT_ID('tempdb..#paths') IS NOT NULL
    DROP TABLE #paths

IF OBJECT_ID('tempdb..#shortestPaths') IS NOT NULL
    DROP TABLE #shortestPaths
GO

DECLARE @targetObjectName SYSNAME = 'YourTableNameHere'

;WITH singleColumnFkColumns AS (

    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 (
    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

        ,   ',' + 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 

    UNION ALL

    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
    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 
        AND cte.TraversalPath NOT LIKE ('%_' + CONVERT(VARCHAR(MAX), r.object_id) + '%')
)
SELECT *
INTO #paths
FROM parentCTE
ORDER BY depth, ParentTable, ChildTable
GO

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

WITH joinCTE AS (

    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

    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 ChildTable, 'SELECT TOP 100 * 
' +JoinString
FROM joinCTE
WHERE depth = 1
ORDER BY ChildTable
GO

您可以在脚本中替换“ YourTableNameHere”,它将找到您从该表到该表的所有链接。

在我的JAVA软件中,外观如下:

RS = CALIzb.main(Conn, "IF OBJECT_ID('tempdb..#paths') IS NOT NULL DROP TABLE #paths IF OBJECT_ID('tempdb..#shortestPaths') IS NOT NULL DROP TABLE #shortestPaths  DECLARE @targetObjectName SYSNAME = '"+txtTablica.getText().trim()+"' ;WITH singleColumnFkColumns AS ( 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 ( 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,   ',' + 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  UNION ALL 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 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  AND cte.TraversalPath NOT LIKE ('%_' + CONVERT(VARCHAR(MAX), r.object_id) + '%')) SELECT * INTO #paths FROM parentCTE ORDER BY depth, ParentTable, ChildTable 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");
                RS = CALIzb.main(Conn, "WITH joinCTE AS ( 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 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 ChildTable, 'SELECT TOP 100 * ' +JoinString FROM joinCTE WHERE depth = 1 ORDER BY ChildTable");
                vezaKljuceva.setModel(DbUtils.resultSetToTableModel(RS));

0 个答案:

没有答案