how to return a query''s result set as the columns for another query

  • Good afternoon,

    I am struggling with what seems like it should be easy to do.  I need a query, to populate a datagrid, that has an unknown number of columns.  Some of the columns, the unknown ones, are the results of query based on a parameter.  The select statement (if it were possible) would resemble select  name, id, phone, (select * from table where id = @parameter1), address, zip

    This query example has five (5) known columns and an unknown amount (x) in the middle, that come from the inner query, but are actually just columns in the outer query.  This is like this because we need to make an exportable template, so we are using a datagrid to display and export to the end user, but the columns of the datagrid are created based on the results of the inner query that uses the parameters provided.

    Any ideas would be greatly appreciated...

    Brad

     

  • EXECUTE

    Executes a scalar-valued, user-defined function, a system procedure, a user-defined stored procedure, or an extended stored procedure. Also supports the execution of a character string within a Transact-SQL batch.

    Execute a character string:

    EXEC [ UTE ] ( { @string_variable | [ N ] 'tsql_string' } [ + ...n ] )

    Sample code:

    DECLARE @sql Varchar(5000)

    DECLARE @NumParameter1 INT

    DECLARE @SelectParameter1 Varchar(500)

    DECLARE @SelectParameter2 Varchar(500)

    DECLARE @SelectParameter3 Varchar(500)

    DECLARE @FromParameter1 Varchar(500)

    DECLARE @FromParameter2 Varchar(500)

    DECLARE @FromParameter3 Varchar(500)

    DECLARE @WhereParameter1 Varchar(500)

    DECLARE @WhereParameter2 Varchar(500)

    DECLARE @WhereParameter3 Varchar(500)

    SET @sql = ''

    Set @SelectParameter1 = 'a1,b1,c1,d1,e1'

    Set @SelectParameter2 = 'a2,b2,c2,d2,e2,f2,g2'

    Set @SelectParameter3 = 'a3,b3,c3,d3,e3,f3,g3,h3,i3'

    Set @FromParameter1 = 'tblABCDE'

    Set @FromParameter2 = 'tblABCDEFG'

    Set @FromParameter3 = 'tblABCDEFGHI'

    Set @WhereParameter1 = 'a1>100'

    Set @WhereParameter2 = 'a2>100'

    Set @WhereParameter3 = 'a3>100'

    SET @NumParameter1 = 0

    WHILE @NumParameter1 < 4

    Begin

     SET @NumParameter1 = @NumParameter1 + 1

     SELECT @sql =

     Case

      When @NumParameter1 = 1 Then 'Select ' + @SelectParameter1 + ' From ' + @FromParameter1 + ' where ' + @WhereParameter1

      When @NumParameter1 = 2 Then 'Select ' + @SelectParameter2 + ' From ' + @FromParameter2 + ' where ' + @WhereParameter2

      When @NumParameter1 = 3 Then 'Select ' + @SelectParameter3 + ' From ' + @FromParameter3 + ' where ' + @WhereParameter3

     End

     PRINT '@SQL=' + @sql

     --EXEC (@SQL)

    End

     

  • That doesn't capture what i am trying to accomplish, but maybe i am explaining it wrong, let me try to show some real life data and what I want as the final outcome. 

    **Final result is a table (resultSet) with these columns... StudName, StudentID, van$36$1, job$37$7, rug$38$2, lip$39$8, sad$40$13, TeacherName, GradeLevel

    These columns come from a table and have row values: StudName, StudentID, TeacherName, GradeLevel extracted from a simple table query like (select * from StudentClassTable)

    The remaining columns are comprised of the resultSet of the query below and have no row values, only column headers: (SELECT     QuestionMaster.qmText + '$' + CONVERT(varchar(10), QuestionMaster.qmQuestionID) + '$' + CONVERT(varchar(10), AssessmentDetail.adSequence)

                          AS colInfo

    FROM         AssessmentMaster INNER JOIN

                          AssessmentDetail ON AssessmentMaster.amAssessmentID = AssessmentDetail.adAssessmentID INNER JOIN

                          QuestionMaster ON AssessmentDetail.adQuestionID = QuestionMaster.qmQuestionID

    WHERE     (AssessmentMaster.amAssessmentID = @AssessmentID))

    This query for @AssessmentID = 58 in real life returns:

    van$36$1

    job$37$7

    rug$38$2

    lip$39$8

    sad$40$13

     

    'If i can get them to align as if from one table in a result set, I can dynamically create templates that when posted back to me will have values for all the dynamically created columns that i can then use to enter into the db. 

    Does this make sense? and if so, how to use results of a query for columns or fields in another query's output?

     

    Thanks for any and all input...

    Brad

     

     

     

     

  • Unfortunately you may have to use temp table and cursor to capture the columns dynamically as below:

    -- Declare local variables

    Declare @SQLCommand Varchar(2000)

    Declare @SelectString Varchar(500)

    Declare @QueryString Varchar(2000)

    Declare @AssessmentID INT

    Declare @Coulmn Varchar(50)

    Declare @Num INT

    -- Initialise local variables

    SET @Num = 0

    SET @SelectString = ''

    SET @AssessmentID = 120367

    -- Compose your query to capture columns in ''StudentClassTable'' and 2nd query

    SET @SQLCommand =

    '

    SELECT

     b.name [colInfo]

    FROM Sysobjects a, syscolumns b

    Where a.id=b.id

     and a.xtype=''U''

     and a.name = ''StudentClassTable''

    Union All

    (SELECT     QuestionMaster.qmText + ''$'' + CONVERT(varchar(10), QuestionMaster.qmQuestionID) + ''$'' + CONVERT(varchar(10), AssessmentDetail.adSequence)

                          AS [colInfo]

    FROM         AssessmentMaster INNER JOIN

                          AssessmentDetail ON AssessmentMaster.amAssessmentID = AssessmentDetail.adAssessmentID INNER JOIN

                          QuestionMaster ON AssessmentDetail.adQuestionID = QuestionMaster.qmQuestionID

    WHERE     (AssessmentMaster.amAssessmentID = ' + Cast(@AssessmentID as varchar(20)) + '))

    )'

    PRINT @SQLCommand

    if exists (select * from dbo.sysobjects where id = object_id(N'[dbo].[ColumnCapture]') and OBJECTPROPERTY(id, N'IsUserTable') = 1)

    drop table [dbo].[ColumnCapture]

    Create Table ColumnCapture (

    [colInfo] varchar(50) NULL)

    -- This will capture all the columns into the table ColumnCapture

    Insert Into dbo.ColumnCapture

    EXEC (@SQLCommand)

    -- Loop through cursor to compose column names

    DECLARE ColumnCapture_Cursor CURSOR FOR

    SELECT colInfo

    FROM titleauthor ColumnCapture

    OPEN ColumnCapture_Cursor

    FETCH NEXT FROM ColumnCapture_Cursor INTO @Coulmn

    WHILE @@FETCH_STATUS = 0

    BEGIN

     SET @Num = @Num + 1

     IF @Num = 1 -- first column

      SET @SelectString = @Coulmn

     ELSE

      SET @SelectString = @SelectString + ',' + @Coulmn

     PRINT @Coulmn

     FETCH NEXT FROM ColumnCapture_Cursor INTO @Coulmn

    END

    CLOSE ColumnCapture_Cursor

    DEALLOCATE ColumnCapture_Cursor

    -- Finally compose query as you require then execute the query

    SET @QueryString = @SelectString + ' From [your table] join bra bra bra Where your condition'

    EXEC (@QueryString)

    I hope this could help.

  • If you know your columns before runtime you can create the temp table and then populate it with your target data.

    create table #tmpWorkShop (name varchar(100), id int)

    INSERT INTO #tmpWorkShop

    EXEC SP_EXECUTESQL N'SELECT name, id from Workshop'

    I have used the above setup for nightly mail informationals.

    To dynamically create the table is an unhappy place I don't really want to visit today.

    Daryl

  • Could you live with returning the unknown columns as an xml doc at the end of the main resultset? Let the app handle it?

  • Hi Bradley,

    Take a look and let me know if this will work for you or there is something I misunderstood.  When you post try to provide create statements for tables and insert statements for test data.  That makes it more likely somebody will respond because it saves them the time of having to write all that themselves.  And, since you've probbly got them all in your test db anyway, it isn't hard to post it 😉

    Have a great day!  Kim

    Use tempdb

    --Create testing tables

    create table AssessmentMaster (amAssessmentId int)

    create table AssessmentDetail (AssessmentDetailId int,adAssessmentId int,adQuestionId int,adSequence int)

    create table QuestionMaster (qmQuestionId int,qmText varchar(50))

    --Insert test data

    insert into AssessmentMaster (amAssessmentId) values (58)

    insert into AssessmentDetail (AssessmentDetailId,adAssessmentId,adQuestionId,adSequence)

      values (1,58,1,1)

    insert into AssessmentDetail (AssessmentDetailId,adAssessmentId,adQuestionId,adSequence)

      values (1,58,7,2)

    insert into AssessmentDetail (AssessmentDetailId,adAssessmentId,adQuestionId,adSequence)

      values (1,58,2,3)

    insert into AssessmentDetail (AssessmentDetailId,adAssessmentId,adQuestionId,adSequence)

      values (1,58,8,4)

    insert into QuestionMaster (qmQuestionId,qmText) values (1,'van')

    insert into QuestionMaster (qmQuestionId,qmText) values (7,'job')

    insert into QuestionMaster (qmQuestionId,qmText) values (2,'rug')

    insert into QuestionMaster (qmQuestionId,qmText) values (8,'lips')

    --Create last table and insert data

    create table StudentClassTable (col1 varchar(25),col2 varchar(25),col3 varchar(25), col4 varchar(25))

    insert into StudentClassTable (col1,col2,col3,col4) values ('StudName', 'StudentID', 'TeacherName', 'GradeLevel' )

    declare @AssessmentID int set @AssessmentID=58

    --use coalesce to build a comma delimited list

    declare @list varchar(100)

    declare @list2 varchar(500)

    select @list=coalesce(@list+',','')+QuestionMaster.qmText + '$' +

     CONVERT(varchar(10), QuestionMaster.qmQuestionID) + '$' +

     CONVERT(varchar(10), AssessmentDetail.adSequence)

     FROM AssessmentMaster

     INNER JOIN AssessmentDetail ON AssessmentMaster.amAssessmentID = AssessmentDetail.adAssessmentID

     INNER JOIN QuestionMaster ON AssessmentDetail.adQuestionID = QuestionMaster.qmQuestionID

     WHERE     (AssessmentMaster.amAssessmentID = @AssessmentID)

    --combine headers, and comma delimited list

    select col1+','+col2+','+col3+','+col4+','+@list

    from StudentClassTable

    --RESULT

    >>StudName,StudentID,TeacherName,GradeLevel,van$1$1,job$7$2,rug$2$3,lips$8$4

  • Good morning,

    I have been busy on other tasks and apology for not continuing this ina more seemless manner.  i appreciate all who attempted to help, it's most appreciated...  that being said...

    I've combined some of what was suggested into a complete test scenario you can copy and run in segments or altogether from query analyzer to illustrate the work so far.

    The remaining issue is trying to dynamically create a temp table, which, from my tests, is not possible.  In the example included you'll see a permanent table that is just for testing called GradingTemplateColHeaders.  It's really the result from a query but hard-coded here for your use. The table in question is the AssInfo table.  It works if I create it as a permanent table, but all attempts to create it as a temp table using a dynamically constructed statement fail.  The "hard" table example below will allow you to better see the desired end result.  i prefer to avoid cursors if possible. The burden on memory to just give me back this blank table could be troublesome if many user called it at the same time.  So if anyone can give me an idea of a less ugly solution than making tables and destroying them on each call it would be welcomed.

    FYI - The desired outcome is used in a datagrid so the column headers are the vital piece necessary.  The datagrid is then output into an excel document for downloading, filling with values, and then uploading back into the system.  Since I never know how many columns may be present, or their names I am faced with this situation...

    Thanks,

    Brad

    here is everything you need to at least see what i want the finished product to do, as well as rough cleanup if needed.

     

    SET QUOTED_IDENTIFIER OFF

    GO

    SET ANSI_NULLS OFF

    GO

    use tempdb

    GO

    create  PROCEDURE AssessmentDynamicTable

    as

    declare @amAssessmentID as int

    declare @adSequence as int

    set @amAssessmentID = 58

    declare @list varchar(1000)

    --declare @list2 varchar(500)

    declare @SQLStatement nvarchar(4000)

    --declare @assInfo varchar(25)

    -- code1 below

    set @list = 'van$36$1 varchar(50),job$37$7 varchar(50),rug$38$2 varchar(50),lip$39$8 varchar(50),sad$40$13 varchar(50)'

    /*  -- not necessary for testing, works fine as written

    select @list=coalesce(@list+',','')+QuestionMaster.qmText + '$' +

     CONVERT(varchar(10), QuestionMaster.qmQuestionID) + '$' +

     CONVERT(varchar(10), AssessmentDetail.adSequence) + ' varchar(50)'

     FROM AssessmentMaster

     INNER JOIN AssessmentDetail ON AssessmentMaster.amAssessmentID = AssessmentDetail.adAssessmentID

     INNER JOIN QuestionMaster ON AssessmentDetail.adQuestionID = QuestionMaster.qmQuestionID

     WHERE     (AssessmentMaster.amAssessmentID = @amAssessmentID)

    */

    --code2,3 below

    CREATE TABLE [GradingTemplateColHeaders] (

     [District] [nvarchar] (50) ,

     [Primary Student ID] [nvarchar] (50) ,

     [Assmt Date] [nvarchar] (50)  ,

     [StudentName] [nvarchar] (50)  ,

     [TeacherName] [nvarchar] (50),

     [BDate] [nvarchar] (50)  ,

     [GradeLevel] [nvarchar] (50)  ,

     [School] [nvarchar] (50)  ,

     [smSchoolYear] [nvarchar] (50) ,

     [TeacherID] [nvarchar] (50)  ,

     [smSchoolID] [nvarchar] (50)  ,

     [smStudentID] [nvarchar] (50) ,

     [cdSschoolYear] [nvarchar] (50) ,

     [cdClassID] [nvarchar] (50)  ,

     [FTStudID] [nvarchar] (50)

    ) ON [PRIMARY]

    set @SQLStatement = 'create table AssInfo (' + @list + ')'

    --this code works fine, but only if it's a permanent table.  I need a temp table if possible

    /*  -- this was an attempt to create a temp table dynamically, which failed

    set @SQLStatement = 'create table #AssInfo ('

    set @SQLStatement = @SQLStatement + 'van$36$1 varchar(50), '

    set @SQLStatement = @SQLStatement + 'job$37$7 varchar(50), '

    set @SQLStatement = @SQLStatement + 'rug$38$2 varchar(50), '

    set @SQLStatement = @SQLStatement + 'lip$39$8 varchar(50), '

    set @SQLStatement = @SQLStatement + 'sad$40$13 varchar(50)) '

    */ --

    exec sp_executesql @SQLStatement --both forms of execute method have same results when attempting to create temp table

    SELECT     District, [Primary Student ID], [Assmt Date], AssInfo.*, StudentName, TeacherName, BDate, GradeLevel, School, smSchoolYear, TeacherID, smSchoolID,

                          smStudentID, cdSschoolYear, cdClassID, FTStudID

    FROM         GradingTemplateColHeaders, AssInfo

    --if exists

    drop table AssInfo

    drop table GradingTemplateColHeaders

    /* -- this code below actually works, but only using query analyzer, not in the procedure, or function, etc.

    create table #AssInfo (van$36$1 varchar(50),job$37$7 varchar(50),rug$38$2 varchar(50),lip$39$8 varchar(50),sad$40$13 varchar(50))

    select * from #AssInfo

    drop table #AssInfo

    */

    --select @SQLStatement

    GO

    SET QUOTED_IDENTIFIER OFF

    GO

    SET ANSI_NULLS ON

    GO

    -- HERE IS THE QUERY ANALYZER METHOD TO EXECUTE THE STORED PROCEDURE ABOVE

    DECLARE @rc int

    -- Set parameter values

    EXEC @rc = [tempdb].[dbo].[AssessmentDynamicTable]

    --AND THE FINAL CLEANUP YOU MAY NEED...

    if exists (select * from dbo.sysobjects where id = object_id(N'[dbo].[AssessmentDynamicTable]') and OBJECTPROPERTY(id, N'IsProcedure') = 1)

    drop procedure [dbo].[AssessmentDynamicTable]

    GO

  • Bradley,

    I found this article, about accessing temp tables created from dynamic sql:

    http://www.sommarskog.se/dynamic_sql.html

    "Temp tables created in the dynamic SQL will not be accessible from the calling procedure since they are dropped when the dynamic SQL exits. (Compare to how temp tables created in a stored procedure go away when you exit the procedure.) The block of dynamic SQL can however access temp tables created by the calling procedure. "

    "Sometimes I see persons on the newsgroups that are unhappy, because they create a temp table from dynamic SQL, and then they can't access it, because it disappeared when the dynamic SQL exited. When told that they have to create the table outside the dynamic SQL, they respond that they can't, because they don't know the structure of the table until run-time.

    One solution is to create a global temp table, one with two # in the name, for instance ##temp. Such a table is visible to all processes (so you may have to take precautions to make the name unique), and unless you explicitly drop it, it exists until your process exits.

    But the real question is: what are these guys up to? If you are working with a relational database, and you don't know the structure of your data until run-time, then there is something fundamentally wrong. As I have never been able to fully understand what the underlying business requirements are, I can't really provide any alternatives. But I would suggest that if you need to go this road, you should seriously consider to run your SQL from a client program. Because, all access to that table would have to be through dynamic SQL, and composing dynamic SQL strings is easier in languages with better string capabilities, be that C#, VB or Perl.

    OK, so there is one case where I can see people end up here, and that is if you want to run a dynamic crosstab – which definitely isn't a very relational operation, but neverthless is a very common user requirement. You may want to look at RAC, a third-party product. I have not used it myself, but I've heard reports from satisfied users."

  • Bradley,

    This works for me.  Basically you just create the temp table and query it within the same exec.  That keeps your temp table from dispappearing before you get the chance to query.  Scope, like the article I posted points out.

    Hope this helps, Kim

    /*

    -- HERE IS THE QUERY ANALYZER METHOD TO EXECUTE THE STORED PROCEDURE ABOVE

    DECLARE @rc int

    EXEC @rc = [tempdb].[dbo].[AssessmentDynamicTable]

    */

    SET QUOTED_IDENTIFIER OFF

    GO

    SET ANSI_NULLS OFF

    GO

    use tempdb

    GO

    alter  PROCEDURE AssessmentDynamicTable

    as

    declare @amAssessmentID as int

    declare @adSequence as int

    set @amAssessmentID = 58

    declare @list varchar(1000)

    declare @SQLStatement nvarchar(4000)

    -- code1 below

    set @list = 'van$36$1 varchar(50),job$37$7 varchar(50),rug$38$2 varchar(50),lip$39$8 varchar(50),sad$40$13 varchar(50)'

    --code2,3 below

    if exists (select * from dbo.sysobjects where id = object_id(N'[dbo].[GradingTemplateColHeaders]') and OBJECTPROPERTY(id, N'IsTable') = 1)

    drop table [dbo].[GradingTemplateColHeaders]

    CREATE TABLE [GradingTemplateColHeaders] (

     [District] [nvarchar] (50) ,

     [Primary Student ID] [nvarchar] (50) ,

     [Assmt Date] [nvarchar] (50)  ,

     [StudentName] [nvarchar] (50)  ,

     [TeacherName] [nvarchar] (50),

     [BDate] [nvarchar] (50)  ,

     [GradeLevel] [nvarchar] (50)  ,

     [School] [nvarchar] (50)  ,

     [smSchoolYear] [nvarchar] (50) ,

     [TeacherID] [nvarchar] (50)  ,

     [smSchoolID] [nvarchar] (50)  ,

     [smStudentID] [nvarchar] (50) ,

     [cdSschoolYear] [nvarchar] (50) ,

     [cdClassID] [nvarchar] (50)  ,

     [FTStudID] [nvarchar] (50)

    ) ON [PRIMARY]

    --create table and query right away before temp table disappears

    set @SQLStatement = 'create table #AssInfo (' + @list + ')

    SELECT     District, [Primary Student ID], [Assmt Date], #AssInfo.*, StudentName,

    TeacherName, BDate, GradeLevel, School, smSchoolYear, TeacherID, smSchoolID,

    smStudentID, cdSschoolYear, cdClassID, FTStudID

    FROM GradingTemplateColHeaders, #AssInfo

    '

    exec sp_executesql @SQLStatement

    GO

    SET QUOTED_IDENTIFIER OFF

    GO

    SET ANSI_NULLS ON

    GO

     

  • Thanks kim,

    I'll read through the article to see if I can get any clues how to proceed.  The rest of the information written is suspect because I actually do attempt to create and use the temp table within the procedure, as good programming dictates.  My example illustrates it's use in the form of a select statement.  The fact is that I cannot create the temp table... period.  So far my testing shows that I can only create a temp table through a direct statement where all parts are known and typed.  Any dynamics or parameters used for the columns causes it to fail.  Composing the dynamic string in my front end won't solve the fact that it won't execute in the procedure.  I included everything in the example just for ease of replication, but regardless where those strings are created the system will not allow me to obtain the desired result. If this is not possible dynamically in sql server then I will discard this work and create it all on the front end.  That's not the optimal place to achieve this however so i was hoping to achieve it here...

    Thanks for all your work though, your first post helped me immensely with reorganzing the result set of the first query into a delimited string using coalesce...

    Brad

  • Thank you so much Kim,

    I was completely misguided in trying to perform those parts separately.  I never use temp tables and msitakenly thought that the temp table existed for use within the entire stored prcoedures scope once created, but you have demonstrated how it's scope is much more limited. I believe that constitutes a very clean, working solution!

    Thank you very much,

    Brad

     

Viewing 12 posts - 1 through 11 (of 11 total)

You must be logged in to reply to this topic. Login to reply