【问题标题】:Joining on one of Two Tables Based on Parameter基于参数连接两个表之一
【发布时间】:2011-09-03 14:51:57
【问题描述】:

不确定这是否可以做到,但这是我正在尝试做的。

我有两张桌子: 表 1 称为任务,它包含所有可能的任务名称 表 2 称为 Task_subset,它仅包含表 1 中包含的任务名称的子集

我有一个名为@TaskControl 的变量,它作为参数传入,它要么等于Table1 要么等于Table2

根据@TaskControl 变量的值,我想加入我的一个任务表

例如:

If @TaskControl = 'Table1':
Select * From Orders O Join Task T on T.id = O.id

If @TaskControl = 'Table2):
Select * From Orders O Join Task_subset T on T.id = O.id

我将如何做到这一点,Sql Server 08

【问题讨论】:

  • TaskTask_subset 是否具有相同的列结构?

标签: sql tsql sql-server-2008 join


【解决方案1】:

不要过于复杂。将其放入存储过程中,如下所示:

CREATE PROCEDURE dbo.MyProcedure(@TaskControl varchar(20))
AS

If @TaskControl = 'Table1'
    Select * From Orders O Join Task T on T.id = O.id

ELSE If @TaskControl = 'Table2'
    Select * From Orders O Join Task_subset T on T.id = O.id

ELSE SELECT 'Invalid Parameter'

或者只是没有 proc 的直接 TSQL:

If @TaskControl = 'Table1'
    Select * From Orders O Join Task T on T.id = O.id

ELSE If @TaskControl = 'Table2'
    Select * From Orders O Join Task_subset T on T.id = O.id

【讨论】:

    【解决方案2】:

    完全按照你现在的方式去做是最好的方法。拥有一个尝试以某种方式动态连接两个语句之一的单个语句是您想要的最后一件事。 T-SQL 是一种用于数据访问的语言,而不是用于 DRY 代码重用编程的语言。如果您尝试使用单个语句,那么优化器必须提出一个始终有效的计划,无论@TaskControl 的值如何,因此该计划将始终必须连接两个表。

    关于这个主题的更长时间的讨论是Dynamic Search Conditions in T-SQL(您的动态连接与动态搜索属于同一主题)。

    【讨论】:

    • 我不同意。当前编写 OPs 查询的方式产生了 3 种可能的执行路径,其中一种根本不执行 select 语句(意味着没有结果集)。要获得完整的测试覆盖率,您需要执行所有 3 个执行路径的测试用例。它使客户端代码复杂化,并且他们必须处理可能没有结果集而不是空结果集的可能性:两件非常不同的事情。
    • @Nicholas Carey:您正在描述为什么这个过程一开始就不应该存在的所有充分理由,以及为什么调用路径的分离应该发生在堆栈的更高层(在客户端)。但是,没有一个引用的原因是编写一个查询的正当理由。一个糟糕的计划意味着运行时损失(搜索与扫描)的数量级,并且没有任何代码健康优势(可测试性、可维护性等)可以弥补 10 倍或 100 倍的运行时损失。
    【解决方案3】:

    如果它们与UNION 兼容,您可以试一试。从这个端的快速测试来看,它似乎只能访问相关的表。

    不过,我更同意 JNK 和 Remus 的回答。这确实对每次调用都有重新编译成本,而且没有太大的好处。

    ;WITH T AS
    (
    SELECT  'Table1' AS TaskControl, id 
    FROM Task
    UNION ALL
    SELECT  'Table2' AS TaskControl, id 
    FROM Task_subset
    )
    SELECT *
    FROM T
     JOIN Orders O on T.id = O.id 
    WHERE TaskControl = @TaskControl
    OPTION (RECOMPILE)
    

    【讨论】:

      【解决方案4】:

      我不知道性能会有多好,而且当您添加其他可选表时,这不会很好地扩展,但这应该适用于您提出的情况。

      SELECT
          O.some_column,
          COALESCE(T.some_task_column, TS.some_task_subset_column)
      FROM
          Orders O
      LEFT OUTER JOIN Tasks T ON
          @task_control = 'Tasks' AND
          T.id = O.id
      LEFT OUTER JOIN Task_Subsets TS ON
          @task_control = 'Task Subsets' AND
          TS.id = O.id
      

      【讨论】:

        【解决方案5】:

        试试下面的。应该避免存储过程计划被第一次执行存储过程时传递的参数值绑定(详见SQL Server Parameter Sniffing):

        create proc dbo.foo
        
          @TaskControl varchar(32)
        
        as
        
          declare @selection varchar(32)
          set @selection = @TaskControl
        
            select *
            from dbo.Orders t
            join dbo.Task   t1 on t1.id = t.id
            where @selection = 'Table1'
          UNION ALL  
            select *
            from dbo.Orders      t
            join dbo.Task_subset t1 on t1.id = t.id
            where @selection = 'Table2'
        
          return 0
        go
        

        存储过程不应为每次调用重新编译,正如@Martin 建议的那样,可能会发生,但传入的参数值 1 不应影响绑定的执行计划。但是如果性能是一个问题,请使用分析器运行 sql 跟踪,并查看缓存的执行计划是否被重用或是否触发了重新编译。

        但有一件事:不过,您需要确保UNION 中的每个select 返回完全相同的列。 UNION 中的每个 select 必须具有相同数量的列,并且每列必须具有通用类型(或默认转换为通用类型)。第一个select 定义了结果集中列的数量、类型和名称。

        【讨论】:

          猜你喜欢
          • 1970-01-01
          • 2021-10-26
          • 1970-01-01
          • 2021-05-17
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 2019-05-30
          • 1970-01-01
          相关资源
          最近更新 更多