Search code examples
sqlsql-servert-sqlsql-server-2016

TSQL - Dynamically generate queries from a record set


Sample Data:

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

Goal:

Dynamically create/output a SELECT statement - that JOINs FSchema/FTable with PSchema/PTable on (F/S)ColumnName in the order specified in ColumnOrder - in a new column.

query would be derived like.. "SELECT [F].[{FColumnName}], [P].[{PColumnName}] FROM [{FSchemaName}].[{FTableName}] AS [F] JOIN [{PSchemaName}].[{PTableName}] AS [P] ON [P].[{PColumnName}] = [F].[{FColumnName}] ;" (again, the ON clause is derived in the order specified in the [ColumnOrder] field)

Expected Output:

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] ;

My attempt:

I'm still trying to figure out a way. Concatenating a single column can be achieved by FOR XML, not sure how I would go about concatenating "=" between two columns and adding "AND" for joins with multiple columns..


Solution

  • Using Group By and the String_Agg function, you can generate the desired output.

    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
    

    EDIT: SQL-SERVER 2016 does not support String_Agg, so use 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
    

    demo in db<>fiddle - sql server 2016

    demo in db<>fiddle