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