TSQL - 从记录集中动态生成查询

Posted

技术标签:

【中文标题】TSQL - 从记录集中动态生成查询【英文标题】:TSQL - Dynamically generate queries from a record set 【发布时间】:2022-01-07 13:57:19 【问题描述】:

样本数据:

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

目标:

动态创建/输出一个 SELECT 语句 - 将 FSchema/FTable 与 (F/S)ColumnName 上的 PSchema/PTable 以 ColumnOrder 中指定的顺序连接 - 在一个新列中。

查询将被派生为.. “选择 [F].[FColumnName], [P].[PColumnName] FROM [FSchemaName].[FTableName] AS [F] JOIN [PSchemaName].[PTableName] AS [P] 开 [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”..

【问题讨论】:

根据问题指南,请不要发布代码、数据、错误消息等的图像 - 将文本复制或输入到问题中。请保留将图像用于图表或演示渲染错误,无法通过文本准确描述的事情。 这样处理你的潜在问题可能不是最好的。您可以编写视图或存储过程,它们将返回您需要的数据,并且肯定会更容易维护和调整。 @SteveFord,我不确定我是否理解您对我正在尝试做的事情的看法/存储过程的思考过程。你能告诉我更多吗? @007 您已经询问了如何从记录集运行动态查询,但您没有解释您的问题是什么以及为什么您试图通过将查询存储在表中来解决它。据推测,如果我可以访问您的应用程序并且可以写入此表,我可能会进行 SQL 注入攻击!如果您告诉我们您想要实现的总体目标,可能会有更好的方法。 啊,明白了。我正在尝试批量生成可以按需运行的 SELECT 语句(基于从另一组查询派生的数据)。这是一个临时过程,输出不会存储在任何地方。 【参考方案1】:

使用Group ByString_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中的演示

【讨论】:

在 OP 明确标记的 2016 年,您将收到错误消息:“'STRING_AGG' 不是可识别的内置函数名称。”... 是的,我使用 Sql 2016 的 xml 路径 太棒了!非常有趣的方法。谢谢!!现在我需要分解它以了解此查询在做什么。干杯!【参考方案2】:

混合使用 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>

dbfiddle here

上的演示

【讨论】:

感谢@LukStorms 提供了一个很好的选择。真的很感激。干杯!!

以上是关于TSQL - 从记录集中动态生成查询的主要内容,如果未能解决你的问题,请参考以下文章

使用jQuery从数组数组动态生成<table>时如何排除ID列

TSQL - 如何从一个结果集中的多个表中加入 1..*?

从 sql 中动态选择阈值

在 MS Access 中,创建动态查询后,如何使用记录集中的相应值更新表单上的文本框?

从不同的记录集中动态分配控制源

ABAP-动态程序生成