Question

What's a good way to dynamically create tables, then join together all the tables that I have created?

Background

I'm trying to write a stored procedure that collects data from various parts of the database and presents it for use. For example if I wanted to send invoices to both Alice and Bob, this stored procedure would find all the information for the invoice. The trick is that Alice and Bob don't always need all the information I have. If Bob is tax exempt but trades in simoleons, I'll need to include information about currency exchange rates but leave out tax info. I want to give the stored procedure an order number and get back results formatted to the customer.

My solution so far is to create a table holding customer requirements. Then my stored procedure checks the table: if currency data is required I create and populate @Currency, or @Taxes holds tax data and so on. I check the requirement, then create the table as needed. At the end I need to Join the data into one record set.

My problem is that I have a error saying

Incorrect syntax near the keyword 'table'

AND an error saying

Must declare the table variable @Summary

on line 1 where i define @Summary These errors only show up when I use dynamic SQL to join the tables. When I comment out the dynamic SQL and copy-paste the statements into the one it should create, I have the result I want.

Code

I tried to use the following dynamic SQL in Microsoft SQL Server Management Studio

create procedure <procedure> (@OrderNumber varchar(20)) AS

DECLARE @CustomerName varchar(35) -- retrieved based on @OrderNumber
DECLARE @SQLQuery nvarchar(500)
DECLARE @ParamDef nvarchar(500)

DECLARE @SummaryTable table
(
    ID varchar(20) --=@OrderNumber 
    , <Stuff>
)
SET @SQLQuery = 'Select * From @SummaryTable'
SET @ParamDef = '@SummaryTable table'

IF EXISTS(Select <TableName> from CustRequirements where <TableName> = 1 AND Customer = @CustomerName)
BEGIN
    --Create table
    DECLARE @<TableName> table
    (
        ID varchar(20)
        , <Stuff>
    )

    --Populate 
    Insert into <TableName> Select <RelevantData> From <DataSource> where <Condition based on @OrderNumber or @CustomerName>

    --Pepare Dynamic SQL
    Set @SQLQuery = @SQLQuery + ' FULL OUTER JOIN @<TableName> ON <TableName>.ID = SummaryTable.ID'
    SET @ParamDef = ', @<TableName> table'

END
            <repeat for other tables>

EXECUTE sp_executesql @SQLQuery, @ParamDef, @Summary, @LineItems, @Taxes, @Currency 

Question

Is there something wrong with my code? Is there a better way to do this? The other option I though of was to have IF Exists trees with the entire JOIN statement at the bottom of each branch (Since I can't seem to interrupt the JOIN clause with IF's). The problem with that is that I'll need 2^n JOIN statements to join n possible tables. n in this case might be as high has 20 or 30.

Was it helpful?

Solution 2

The solution I've decided tp use is to create a table then use ALTER TABLE to add columns as needed.

CREATE Table #Output 
(
    InvoiceNumber       varchar(20)
    , <stuff> 
)

IF EXISTS(Select <ThisSegment> FROM CustRequirements WHERE <ThisSegment> = 1 AND Customer = @CustomerName)
BEGIN
    ALTER TABLE #Output ADD <ThisSegment> <dataType>
    UPDATE #Output 
        SET <ThisSegment> = <data> from <DataSource> 
            WHERE <InvoiceNumber = DataSource.invoice> AND <Other conditions> 
END
<Repeat as needed> 

OTHER TIPS

I think I see a problem in the code (maybe 3 problems -- see "questionable" 1 & 2 below) --

1) [Changed on 10/21, after OP's comments] The big problem: table parameters passed in the final "EXECUTE sp_executesql @SQLQuery..." are sometimes not declared.

1a) @Summary is actually never declared... you declared and set @SummaryTable, then use @Summary. Just change that to @SummaryTable (I did that in (4) below), and I think that will prevent your second error message ("Must declare the table variable @Summary").

1b) All the other tables are sometimes declared: each of their DECLARE statements are within an "IF EXISTS". I suggest either (I) make the declares unconditional (outide IF EXISTS), but still conditionally INSERT... or (II) make the format of the EXECUTE command vary with what's available. (I doubt the unused table variables need anything in them....) In point 4 below (added 10/21), I give an example that I have NOT TESTED with my own databases (that test would take a lot more time)... so please let me know how it goes...

2) [questionable 1] Simple case of mixed case ;) Note that the line...

Set @SQLQuery = @SqlQuery + ' FULL OUTER JOIN @<TableName> ON <TableName>.ID = SummaryTable.ID'

... first has an uppercase "SQL", then mixed-case "Sql".

Why I said this is "questionable" -- if your server's COLLATION is case-insensitive, what you typed above would be fine.

3) [questionable 2] You have both '@TableName' and '@Table Name' (with a space). I realize that may just be how you typed it, in posting your question.

Point (4), added in update to answer -- possible code

create procedure <procedure> (@OrderNumber varchar(20)) AS

DECLARE @CustomerName varchar(35) -- retrieved based on @OrderNumber
DECLARE @SQLQuery nvarchar(500)
DECLARE @ParamDef nvarchar(500)

DECLARE @SummaryTable table
(
    ID varchar(20) --=@OrderNumber 
    , <Stuff>
)
SET @SQLQuery = 'Select * From @SummaryTable'
SET @ParamDef = '@SummaryTable table'


--Create table variables, though they may not be populated
DECLARE @LineItems
(
    ID varchar(20)
    , <Stuff>
)
DECLARE @Taxes
(
    ID varchar(20)
    , <Stuff>
)
DECLARE @Currencytable
(
    ID varchar(20)
    , <Stuff>
)

IF EXISTS(Select <TableName> from CustRequirements where <TableName> = 1 AND Customer = @CustomerName)
BEGIN
    --Populate 
    Insert into <TableName> Select <RelevantData> From <DataSource> where <Condition based on @OrderNumber or @CustomerName>

    --Prepare Dynamic SQL
    Set @SQLQuery = @SQLQuery + ' FULL OUTER JOIN @<TableName> ON <TableName>.ID = SummaryTable.ID'
    SET @ParamDef = ', @<TableName> table'

END
            <repeat for other tables>

EXECUTE sp_executesql @SQLQuery, @ParamDef, @SummaryTable, @LineItems, @Taxes, @Currency
Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top