1

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

背景

我正在尝试编写一个存储过程,该过程从数据库的各个部分收集数据并将其呈现以供使用。例如,如果我想向 Alice 和 Bob 发送发票,这个存储过程将找到发票的所有信息。诀窍是爱丽丝和鲍勃并不总是需要我拥有的所有信息。如果 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语句的树(因为我似乎无法用 IF 中断 JOIN 子句)。问题是我需要 2^n语句来连接 n 个可能的表。在这种情况下,n 可能与 20 或 30 一样高。 JOINJOIN

4

2 回答 2

1

我想我在代码中看到了一个问题(可能是 3 个问题——参见下面的“有问题的”1 和 2)——

1) [在 10/21 更改,在 OP 的评论之后] 大问题:在最终“EXECUTE sp_executesql @SQLQuery ...”中传递的表参数有时没有声明。

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

1b)有时会声明所有其他表:它们的每个 DECLARE 语句都在“IF EXISTS”内。我建议(I)使声明无条件(在 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
于 2013-10-19T03:22:29.423 回答
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> 
于 2013-10-22T16:19:57.050 回答