【问题标题】:Dynamically joining tables that may not exist动态连接可能不存在的表
【发布时间】:2013-10-18 20:42:09
【问题描述】:

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

背景

我正在尝试编写一个存储过程,该过程从数据库的各个部分收集数据并将其呈现以供使用。例如,如果我想向 Alice 和 Bob 发送发票,这个存储过程将找到发票的所有信息。诀窍是爱丽丝和鲍勃并不总是需要我拥有的所有信息。如果 Bob 是免税的,但交易的是 simoleons,我需要包含有关货币汇率的信息,但忽略税收信息。我想给存储过程一个订单号,然后将格式化的结果返回给客户。

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

我的问题是我说错了

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

和一个错误说

必须声明表变量@Summary

在我定义 @Summary 的第 1 行上,这些错误仅在我使用动态 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 

问题

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

【问题讨论】:

  • 我认为您的做法不太正确。不同的观点...尝试将每个用户视为过滤器,而不是一组不同的表连接。创建完全连接所有内容的完整 SQL 语句...使用 where 子句根据用户过滤行,并使用 case 语句根据用户需求表中的用户信息过滤掉列
  • 感谢您的回复。我会考虑这一点,尽管我不确定在这种情况下我能做到这一点。 表很大,所以我会担心基本上将数据库中的每个表连接成一个巨大的怪物,然后选择我需要的位。
  • 我认为最好的方法是使用您现有的表格。

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


【解决方案1】:

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

1) [在 OP 的 cmets 之后于 10/21 更改] 大问题:有时未声明在最终“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

【讨论】:

  • 感谢您的回复。 @TableName 只是一个占位符,我会在上面更正它。到目前为止,混合大小写对我来说从来都不是问题,但我也会更新它以保持一致性。
  • 在进一步研究您的回复时,我对某事感到好奇。参数定义的目的不是允许@SQLQuery 访问提供的参数吗?如果我将 @ 传递给 sp_executesql 过程,它应该已经被声明和设置。这只适用于标量吗?
  • 好的...谢谢...现在我对您的整个结构有了更清晰的了解。你是对的——因为你使用的是 parms,所以它们不在@SQLQuery 中是可以的——所以我完全改变了我的第 1 点(这是作弊吗?;)和用示例代码添加了第 4 点。 (我没有尝试使用我的表进行模拟,所以请告诉我它是如何运行的。)HTH。
【解决方案2】:

我决定使用的解决方案是创建一个表,然后根据需要使用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> 

【讨论】:

    猜你喜欢
    • 2012-11-10
    • 2013-11-18
    • 2021-02-23
    • 2012-08-13
    • 1970-01-01
    • 2011-08-15
    • 1970-01-01
    • 2015-05-14
    • 1970-01-01
    相关资源
    最近更新 更多