TSQL - 从记录集中动态生成查询
TSQL - Dynamically generate queries from a record set
示例数据:
DECLARE @Tbl_List TABLE
(
[PSchemaName] sysname
, [PTableName] sysname
, [PColumnName] sysname
, [FSchemaName] sysname
, [FTableName] sysname
, [FColumnName] sysname
, [ColumnOrder] TINYINT
) ;
INSERT INTO @Tbl_List
VALUES
( 'emp', 'emphdr1', 'id', 'emp', 'empdtl1', 'hdrid', 1 )
, ( 'emp', 'emphdr2', 'id', 'emp', 'empdtl2', 'hdrid', 1 )
, ( 'emp', 'emphdr2', 'key', 'emp', 'empdtl2', 'hdrkey', 2 )
, ( 'emp', 'emphdr3', 'id', 'emp', 'empdtl3', 'hdrid', 1 )
, ( 'emp', 'emphdr3', 'key1', 'emp', 'empdtl3', 'hdrkey1', 2 )
, ( 'emp', 'emphdr3', 'key2', 'emp', 'empdtl3', 'hdrkey2', 3 )
, ( 'emp', 'emphdr3', 'id', 'emp', 'empdtl4', 'hdrid', 1 )
, ( 'emp', 'emphdr3', 'key1', 'emp', 'empdtl4', 'hdrkey1', 2 ) ;
PSchemaName PTableName PColumnName FSchemaName FTableName FColumnName ColumnOrder
emp emphdr1 id emp empdtl1 hdrid 1
emp emphdr2 id emp empdtl2 hdrid 1
emp emphdr2 key emp empdtl2 hdrkey 2
emp emphdr3 id emp empdtl3 hdrid 1
emp emphdr3 key1 emp empdtl3 hdrkey1 2
emp emphdr3 key2 emp empdtl3 hdrkey2 3
emp emphdr3 id emp empdtl4 hdrid 1
emp emphdr3 key1 emp empdtl4 hdrkey1 2
目标:
动态地 create/output 一个 SELECT 语句 - JOINs FSchema/FTable 与 PSchema/PTable on (F/S)ColumnName 按照 ColumnOrder 中指定的顺序 - 在新专栏。
查询将派生为...
"SELECT [F].[{FColumnName}], [P].[{PColumnName}] FROM [{FSchemaName}].[{FTableName}] AS [F] JOIN [{PSchemaName}].[{ PTableName}] AS [P] ON [P].[{PColumnName}] = [F].[{FColumnName}] ;" (同样,ON 子句是按照 [ColumnOrder] 字段中指定的顺序派生的)
预期输出:
PSchemaName PTableName FSchemaName FTableName CMD
emp emphdr1 emp empdtl1 SELECT [F].[hdrid], [P].[id] FROM [emp].[empdtl1] AS [F] JOIN [emp].[emphdr1] AS [P] ON [P].[id] = [F].[hdrid] ;
emp emphdr2 emp empdtl2 SELECT [F].[hdrid], [F].[hdrkey], [P].[id], [P].[key] FROM [emp].[empdtl2] AS [F] JOIN [emp].[emphdr2] AS [P] ON [P].[id] = [F].[hdrid] AND [P].[key] = [F].[hdrkey] ;
emp emphdr3 emp empdtl3 SELECT [F].[hdrid], [F].[hdrkey1], [F].[hdrkey2], [P].[id], [P].[key1], [P].[key2] FROM [emp].[empdtl3] AS [F] JOIN [emp].[emphdr3] AS [P] ON [P].[id] = [F].[hdrid] AND [P].[key1] = [F].[hdrkey1] AND [P].[key2] = [F].[hdrkey2] ;
emp emphdr3 emp empdtl4 SELECT [F].[hdrid], [F].[hdrkey1], [P].[id], [P].[key1] FROM [emp].[empdtl3] AS [F] JOIN [emp].[emphdr4] AS [P] ON [P].[id] = [F].[hdrid] AND [P].[key1] = [F].[hdrkey1] ;
我的尝试:
我还在想办法。连接单个列可以通过 FOR XML 实现,不确定我将如何在两列之间连接“=”并为多列连接添加“AND”..
使用 Group By
和 String_Agg
函数,您可以生成所需的输出。
SELECT PSchemaName,PTableName,FSchemaName,FTableName,
CONCAT('SELECT ',fselectcol,',',pselectcol,' FROM ',FTableName ,' f JOIN ',PTableName,' p ON ',joincol) AS CMD
FROM
(SELECT
MAX(PSchemaName) AS PSchemaName,
MAX(PTableName) AS PTableName,
MAX(FSchemaName) AS FSchemaName,
FTableName,
STRING_AGG('p.' + PColumnName,',') pselectcol,
STRING_AGG('f.' + FColumnName,',') fselectcol,
STRING_AGG('p.' + PColumnName + ' = f.' + FColumnName,' AND ') joincol
FROM @Tbl_List
GROUP BY FTableName) t
编辑: SQL-SERVER 2016
不支持 String_Agg
,所以使用 XML PATH
SELECT
MAX(PSchemaName) AS PSchemaName,
MAX(PTableName) AS PTableName,
MAX(FSchemaName) AS FSchemaName,
FTableName,
CONCAT('SELECT ',MAX(fselectcol),',',MAX(pselectcol),' FROM ',FTableName ,' f JOIN ',MAX(PTableName),' p ON ', SUBSTRING(MAX(joincol),0,LEN(MAX(joincol)) - 3)) AS CMD
FROM
(SELECT t1.*,
STUFF(
(SELECT ',p.' + PColumnName
FROM @Tbl_List AS t2
WHERE t2.FTableName = t1.FTableName
FOR XML PATH('')), 1, 1, NULL) AS pselectcol,
STUFF(
(SELECT ',f.' + FColumnName
FROM @Tbl_List AS t2
WHERE t2.FTableName = t1.FTableName
FOR XML PATH('')), 1, 1, NULL) AS fselectcol,
STUFF(
(SELECT 'p.' + PColumnName + ' = f.' + FColumnName,' AND '
FROM @Tbl_List AS t2
WHERE t2.FTableName = t1.FTableName
FOR XML PATH('')), 1, 0, NULL) AS joincol
FROM @Tbl_List AS t1) T
GROUP BY FTableName
演示 db<>fiddle - sql server 2016
演示 db<>fiddle
混合使用 Cross Apply 和 For Xml 来生成查询。
SELECT
[PSchemaName], [PTableName], [FSchemaName], [FTableName]
, [Cmd] = CONCAT('SELECT ', [Fields], char(10),
'FROM ', [From], char(10),
'JOIN ', [Join], char(10), [On])
FROM
(
SELECT
[FSchemaName], [FTableName], [PSchemaName], [PTableName]
, [From] = CONCAT(QUOTENAME([FSchemaName]), '.', QUOTENAME([FTableName]), ' AS [F] ')
, [Join] = CONCAT(QUOTENAME([PSchemaName]), '.', QUOTENAME([PTableName]), ' AS [P] ')
FROM @Tbl_List
GROUP BY
[FSchemaName], [FTableName], [PSchemaName], [PTableName]
) t
CROSS APPLY (
SELECT
[Fields] = STUFF(
(SELECT ', [F].'+ QUOTENAME([FColumnName])
+', [P].'+ QUOTENAME([PColumnName])
FROM @Tbl_List t2
WHERE t2.FTableName = t.FTableName
FOR XML PATH('')), 1, 2, NULL)
, [On] = ' ON' + STUFF(
(SELECT ' AND [P].'+ QUOTENAME([PColumnName])
+' = [F].'+ QUOTENAME([FColumnName]) +char(10)
FROM @Tbl_List t2
WHERE t2.FTableName = t.FTableName
FOR XML PATH('')), 1, 4, NULL)
from @Tbl_List t2
where t2.FSchemaName = t.FSchemaName
and t2.FTableName = t.FTableName
) ca
PSchemaName
PTableName
FSchemaName
FTableName
Cmd
emp
emphdr1
emp
empdtl1
SELECT [F].[hdrid], [P].[id]<br>FROM [emp].[empdtl1] AS [F] <br>JOIN [emp].[emphdr1] AS [P] <br> ON [P].[id] = [F].[hdrid]<br>
emp
emphdr2
emp
empdtl2
SELECT [F].[hdrid], [P].[id], [F].[hdrkey], [P].[key]<br>FROM [emp].[empdtl2] AS [F] <br>JOIN [emp].[emphdr2] AS [P] <br> ON [P].[id] = [F].[hdrid]<br> AND [P].[key] = [F].[hdrkey]<br>
emp
emphdr2
emp
empdtl2
SELECT [F].[hdrid], [P].[id], [F].[hdrkey], [P].[key]<br>FROM [emp].[empdtl2] AS [F] <br>JOIN [emp].[emphdr2] AS [P] <br> ON [P].[id] = [F].[hdrid]<br> AND [P].[key] = [F].[hdrkey]<br>
emp
emphdr3
emp
empdtl3
SELECT [F].[hdrid], [P].[id], [F].[hdrkey1], [P].[key1], [F].[hdrkey2], [P].[key2]<br>FROM [emp].[empdtl3] AS [F] <br>JOIN [emp].[emphdr3] AS [P] <br> ON [P].[id] = [F].[hdrid]<br> AND [P].[key1] = [F].[hdrkey1]<br> AND [P].[key2] = [F].[hdrkey2]<br>
emp
emphdr3
emp
empdtl3
SELECT [F].[hdrid], [P].[id], [F].[hdrkey1], [P].[key1], [F].[hdrkey2], [P].[key2]<br>FROM [emp].[empdtl3] AS [F] <br>JOIN [emp].[emphdr3] AS [P] <br> ON [P].[id] = [F].[hdrid]<br> AND [P].[key1] = [F].[hdrkey1]<br> AND [P].[key2] = [F].[hdrkey2]<br>
emp
emphdr3
emp
empdtl3
SELECT [F].[hdrid], [P].[id], [F].[hdrkey1], [P].[key1], [F].[hdrkey2], [P].[key2]<br>FROM [emp].[empdtl3] AS [F] <br>JOIN [emp].[emphdr3] AS [P] <br> ON [P].[id] = [F].[hdrid]<br> AND [P].[key1] = [F].[hdrkey1]<br> AND [P].[key2] = [F].[hdrkey2]<br>
emp
emphdr3
emp
empdtl4
SELECT [F].[hdrid], [P].[id], [F].[hdrkey1], [P].[key1]<br>FROM [emp].[empdtl4] AS [F] <br>JOIN [emp].[emphdr3] AS [P] <br> ON [P].[id] = [F].[hdrid]<br> AND [P].[key1] = [F].[hdrkey1]<br>
emp
emphdr3
emp
empdtl4
SELECT [F].[hdrid], [P].[id], [F].[hdrkey1], [P].[key1]<br>FROM [emp].[empdtl4] AS [F] <br>JOIN [emp].[emphdr3] AS [P] <br> ON [P].[id] = [F].[hdrid]<br> AND [P].[key1] = [F].[hdrkey1]<br>
演示 db<>fiddle here
示例数据:
DECLARE @Tbl_List TABLE
(
[PSchemaName] sysname
, [PTableName] sysname
, [PColumnName] sysname
, [FSchemaName] sysname
, [FTableName] sysname
, [FColumnName] sysname
, [ColumnOrder] TINYINT
) ;
INSERT INTO @Tbl_List
VALUES
( 'emp', 'emphdr1', 'id', 'emp', 'empdtl1', 'hdrid', 1 )
, ( 'emp', 'emphdr2', 'id', 'emp', 'empdtl2', 'hdrid', 1 )
, ( 'emp', 'emphdr2', 'key', 'emp', 'empdtl2', 'hdrkey', 2 )
, ( 'emp', 'emphdr3', 'id', 'emp', 'empdtl3', 'hdrid', 1 )
, ( 'emp', 'emphdr3', 'key1', 'emp', 'empdtl3', 'hdrkey1', 2 )
, ( 'emp', 'emphdr3', 'key2', 'emp', 'empdtl3', 'hdrkey2', 3 )
, ( 'emp', 'emphdr3', 'id', 'emp', 'empdtl4', 'hdrid', 1 )
, ( 'emp', 'emphdr3', 'key1', 'emp', 'empdtl4', 'hdrkey1', 2 ) ;
PSchemaName PTableName PColumnName FSchemaName FTableName FColumnName ColumnOrder
emp emphdr1 id emp empdtl1 hdrid 1
emp emphdr2 id emp empdtl2 hdrid 1
emp emphdr2 key emp empdtl2 hdrkey 2
emp emphdr3 id emp empdtl3 hdrid 1
emp emphdr3 key1 emp empdtl3 hdrkey1 2
emp emphdr3 key2 emp empdtl3 hdrkey2 3
emp emphdr3 id emp empdtl4 hdrid 1
emp emphdr3 key1 emp empdtl4 hdrkey1 2
目标:
动态地 create/output 一个 SELECT 语句 - JOINs FSchema/FTable 与 PSchema/PTable on (F/S)ColumnName 按照 ColumnOrder 中指定的顺序 - 在新专栏。
查询将派生为... "SELECT [F].[{FColumnName}], [P].[{PColumnName}] FROM [{FSchemaName}].[{FTableName}] AS [F] JOIN [{PSchemaName}].[{ PTableName}] AS [P] ON [P].[{PColumnName}] = [F].[{FColumnName}] ;" (同样,ON 子句是按照 [ColumnOrder] 字段中指定的顺序派生的)
预期输出:
PSchemaName PTableName FSchemaName FTableName CMD
emp emphdr1 emp empdtl1 SELECT [F].[hdrid], [P].[id] FROM [emp].[empdtl1] AS [F] JOIN [emp].[emphdr1] AS [P] ON [P].[id] = [F].[hdrid] ;
emp emphdr2 emp empdtl2 SELECT [F].[hdrid], [F].[hdrkey], [P].[id], [P].[key] FROM [emp].[empdtl2] AS [F] JOIN [emp].[emphdr2] AS [P] ON [P].[id] = [F].[hdrid] AND [P].[key] = [F].[hdrkey] ;
emp emphdr3 emp empdtl3 SELECT [F].[hdrid], [F].[hdrkey1], [F].[hdrkey2], [P].[id], [P].[key1], [P].[key2] FROM [emp].[empdtl3] AS [F] JOIN [emp].[emphdr3] AS [P] ON [P].[id] = [F].[hdrid] AND [P].[key1] = [F].[hdrkey1] AND [P].[key2] = [F].[hdrkey2] ;
emp emphdr3 emp empdtl4 SELECT [F].[hdrid], [F].[hdrkey1], [P].[id], [P].[key1] FROM [emp].[empdtl3] AS [F] JOIN [emp].[emphdr4] AS [P] ON [P].[id] = [F].[hdrid] AND [P].[key1] = [F].[hdrkey1] ;
我的尝试:
我还在想办法。连接单个列可以通过 FOR XML 实现,不确定我将如何在两列之间连接“=”并为多列连接添加“AND”..
使用 Group By
和 String_Agg
函数,您可以生成所需的输出。
SELECT PSchemaName,PTableName,FSchemaName,FTableName,
CONCAT('SELECT ',fselectcol,',',pselectcol,' FROM ',FTableName ,' f JOIN ',PTableName,' p ON ',joincol) AS CMD
FROM
(SELECT
MAX(PSchemaName) AS PSchemaName,
MAX(PTableName) AS PTableName,
MAX(FSchemaName) AS FSchemaName,
FTableName,
STRING_AGG('p.' + PColumnName,',') pselectcol,
STRING_AGG('f.' + FColumnName,',') fselectcol,
STRING_AGG('p.' + PColumnName + ' = f.' + FColumnName,' AND ') joincol
FROM @Tbl_List
GROUP BY FTableName) t
编辑: SQL-SERVER 2016
不支持 String_Agg
,所以使用 XML PATH
SELECT
MAX(PSchemaName) AS PSchemaName,
MAX(PTableName) AS PTableName,
MAX(FSchemaName) AS FSchemaName,
FTableName,
CONCAT('SELECT ',MAX(fselectcol),',',MAX(pselectcol),' FROM ',FTableName ,' f JOIN ',MAX(PTableName),' p ON ', SUBSTRING(MAX(joincol),0,LEN(MAX(joincol)) - 3)) AS CMD
FROM
(SELECT t1.*,
STUFF(
(SELECT ',p.' + PColumnName
FROM @Tbl_List AS t2
WHERE t2.FTableName = t1.FTableName
FOR XML PATH('')), 1, 1, NULL) AS pselectcol,
STUFF(
(SELECT ',f.' + FColumnName
FROM @Tbl_List AS t2
WHERE t2.FTableName = t1.FTableName
FOR XML PATH('')), 1, 1, NULL) AS fselectcol,
STUFF(
(SELECT 'p.' + PColumnName + ' = f.' + FColumnName,' AND '
FROM @Tbl_List AS t2
WHERE t2.FTableName = t1.FTableName
FOR XML PATH('')), 1, 0, NULL) AS joincol
FROM @Tbl_List AS t1) T
GROUP BY FTableName
演示 db<>fiddle - sql server 2016
演示 db<>fiddle
混合使用 Cross Apply 和 For Xml 来生成查询。
SELECT
[PSchemaName], [PTableName], [FSchemaName], [FTableName]
, [Cmd] = CONCAT('SELECT ', [Fields], char(10),
'FROM ', [From], char(10),
'JOIN ', [Join], char(10), [On])
FROM
(
SELECT
[FSchemaName], [FTableName], [PSchemaName], [PTableName]
, [From] = CONCAT(QUOTENAME([FSchemaName]), '.', QUOTENAME([FTableName]), ' AS [F] ')
, [Join] = CONCAT(QUOTENAME([PSchemaName]), '.', QUOTENAME([PTableName]), ' AS [P] ')
FROM @Tbl_List
GROUP BY
[FSchemaName], [FTableName], [PSchemaName], [PTableName]
) t
CROSS APPLY (
SELECT
[Fields] = STUFF(
(SELECT ', [F].'+ QUOTENAME([FColumnName])
+', [P].'+ QUOTENAME([PColumnName])
FROM @Tbl_List t2
WHERE t2.FTableName = t.FTableName
FOR XML PATH('')), 1, 2, NULL)
, [On] = ' ON' + STUFF(
(SELECT ' AND [P].'+ QUOTENAME([PColumnName])
+' = [F].'+ QUOTENAME([FColumnName]) +char(10)
FROM @Tbl_List t2
WHERE t2.FTableName = t.FTableName
FOR XML PATH('')), 1, 4, NULL)
from @Tbl_List t2
where t2.FSchemaName = t.FSchemaName
and t2.FTableName = t.FTableName
) ca
PSchemaName | PTableName | FSchemaName | FTableName | Cmd |
---|---|---|---|---|
emp | emphdr1 | emp | empdtl1 | SELECT [F].[hdrid], [P].[id]<br>FROM [emp].[empdtl1] AS [F] <br>JOIN [emp].[emphdr1] AS [P] <br> ON [P].[id] = [F].[hdrid]<br> |
emp | emphdr2 | emp | empdtl2 | SELECT [F].[hdrid], [P].[id], [F].[hdrkey], [P].[key]<br>FROM [emp].[empdtl2] AS [F] <br>JOIN [emp].[emphdr2] AS [P] <br> ON [P].[id] = [F].[hdrid]<br> AND [P].[key] = [F].[hdrkey]<br> |
emp | emphdr2 | emp | empdtl2 | SELECT [F].[hdrid], [P].[id], [F].[hdrkey], [P].[key]<br>FROM [emp].[empdtl2] AS [F] <br>JOIN [emp].[emphdr2] AS [P] <br> ON [P].[id] = [F].[hdrid]<br> AND [P].[key] = [F].[hdrkey]<br> |
emp | emphdr3 | emp | empdtl3 | SELECT [F].[hdrid], [P].[id], [F].[hdrkey1], [P].[key1], [F].[hdrkey2], [P].[key2]<br>FROM [emp].[empdtl3] AS [F] <br>JOIN [emp].[emphdr3] AS [P] <br> ON [P].[id] = [F].[hdrid]<br> AND [P].[key1] = [F].[hdrkey1]<br> AND [P].[key2] = [F].[hdrkey2]<br> |
emp | emphdr3 | emp | empdtl3 | SELECT [F].[hdrid], [P].[id], [F].[hdrkey1], [P].[key1], [F].[hdrkey2], [P].[key2]<br>FROM [emp].[empdtl3] AS [F] <br>JOIN [emp].[emphdr3] AS [P] <br> ON [P].[id] = [F].[hdrid]<br> AND [P].[key1] = [F].[hdrkey1]<br> AND [P].[key2] = [F].[hdrkey2]<br> |
emp | emphdr3 | emp | empdtl3 | SELECT [F].[hdrid], [P].[id], [F].[hdrkey1], [P].[key1], [F].[hdrkey2], [P].[key2]<br>FROM [emp].[empdtl3] AS [F] <br>JOIN [emp].[emphdr3] AS [P] <br> ON [P].[id] = [F].[hdrid]<br> AND [P].[key1] = [F].[hdrkey1]<br> AND [P].[key2] = [F].[hdrkey2]<br> |
emp | emphdr3 | emp | empdtl4 | SELECT [F].[hdrid], [P].[id], [F].[hdrkey1], [P].[key1]<br>FROM [emp].[empdtl4] AS [F] <br>JOIN [emp].[emphdr3] AS [P] <br> ON [P].[id] = [F].[hdrid]<br> AND [P].[key1] = [F].[hdrkey1]<br> |
emp | emphdr3 | emp | empdtl4 | SELECT [F].[hdrid], [P].[id], [F].[hdrkey1], [P].[key1]<br>FROM [emp].[empdtl4] AS [F] <br>JOIN [emp].[emphdr3] AS [P] <br> ON [P].[id] = [F].[hdrid]<br> AND [P].[key1] = [F].[hdrkey1]<br> |
演示 db<>fiddle here