动态连接可能不存在的表

时间:2013-10-18 20:42:10

标签: sql sql-server sql-server-2008 join dynamic-sql

动态创建表格的好方法是什么,然后将我创建的所有表格连接在一起?

背景

我正在尝试编写一个存储过程,该过程从数据库的各个部分收集数据并提供它以供使用。例如,如果我想向Alice和Bob发送发票,则此存储过程将查找发票的所有信息。诀窍在于Alice和Bob并不总是需要我拥有的所有信息。如果鲍勃是免税但是用simoleons进行交易,我需要包含有关货币汇率的信息,但不提供税务信息。我想给存储过程一个订单号,并将结果格式化回给客户。

到目前为止,我的解决方案是创建一个包含客户需求的表。然后我的存储过程检查表格:如果需要货币数据,我创建并填充@Currency,或@Taxes保存税务数据,依此类推。我检查需求,然后根据需要创建表。最后,我需要将数据加入一个记录集。

我的问题是我说错误

  

关键字'table'附近的语法不正确

错误说

  

必须声明表变量@Summary

在第1行我定义@Summary这些错误仅在我使用动态SQL加入表时出现。当我注释掉动态SQL并将语句复制粘贴到它应该创建的语句时,我得到了我想要的结果。

代码

我尝试在Microsoft SQL Server Management Studio中使用以下动态SQL

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 

问题

我的代码有问题吗?有一个更好的方法吗?我的另一个选择是在每个分支的底部都有IF Exists树和整个 JOIN语句(因为我似乎无法中断JOIN子句IF的)。问题是我需要2 ^ n JOIN个语句才能加入n个可能的表。在这种情况下,n可能高达20或30。

2 个答案:

答案 0 :(得分:1)

我想我发现代码中存在问题(可能有3个问题 - 请参阅下面的“可疑”1&amp; 2) -

1)[改变于10月21日,在OP的评论之后]最大的问题:在最终的“EXECUTE sp_executesql @SQLQuery ...”中传递的表参数有时不会被声明。

1a)@Summary实际上从未被声明...你声明并设置@SummaryTable,然后使用@Summary。 只需将其更改为@SummaryTable(我在下面的(4)中做了),我认为这将阻止您的第二条错误消息(“必须声明表变量@Summary”)。

1b)所有其他表格有时声明:它们的每个DECLARE语句都在“IF EXISTS”中。我建议(I)使声明无条件(outide IF EXISTS),但仍然有条件地INSERT ......或(II)使EXECUTE命令的格式随可用内容而变化。 (我怀疑未使用的表变量需要什么......) 在下面的第4点(添加10/21)中,我举了一个例子,我没有用我自己的数据库测试(测试花费更多时间)...所以请让我知道如何它去......

2)[有问题1]混合案例的简单案例;)注意行......

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

...首先有一个大写的“SQL”,然后是混合大小写的“Sql”。

为什么我说这是“有问题的” - 如果您的服务器的COLLATION不区分大小写,那么您在上面输入的内容就可以了。

3)[有问题2]你有'@TableName'和'@Table Name'(带空格)。我发现你的问题可能只是你输入它的方式。

点(4),在更新中添加回答 - 可能代码

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

答案 1 :(得分:0)

我决定使用的解决方案是创建一个表,然后使用ALTER TABLE根据需要添加列。

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>