【问题标题】:Join to SELECT vs. Join to Tableset加入 SELECT 与加入表集
【发布时间】:2014-03-03 16:14:25
【问题描述】:

对于那里的数据库专家,我想知道在加入 SELECT 语句的结果和加入以前填充的表变量之间是否存在任何功能/性能差异。我正在使用 SQL Server 2008 R2。

示例(TSQL):

-- Create a test table
DROP TABLE [dbo].[TestTable]
CREATE TABLE [dbo].[TestTable](
    [id] [int] NOT NULL,
    [value] [varchar](max) NULL
) ON [PRIMARY]
-- Populate the test table with a few rows
INSERT INTO [dbo].[TestTable]
SELECT 1123, 'test1'

INSERT INTO [dbo].[TestTable]
SELECT 2234, 'test2'

INSERT INTO [dbo].[TestTable]
SELECT 3345, 'test3'

-- Create a reference table
DROP TABLE [dbo].[TestRefTable]
CREATE TABLE [dbo].[TestRefTable](
    [id] [int] NOT NULL,
    [refvalue] [varchar](max) NULL
) ON [PRIMARY]
-- Populate the reference table with a few rows
INSERT INTO [dbo].[TestRefTable]
SELECT 1123, 'ref1'

INSERT INTO [dbo].[TestRefTable]
SELECT 2234, 'ref2'

-- Scenario 1: Insert matching results into it's own table variable, then Join

-- Create a table variable 
DECLARE @subset TABLE ([id] INT NOT NULL, [refvalue] VARCHAR(MAX))

INSERT INTO @subset
SELECT * FROM [dbo].[TestRefTable] 
WHERE [dbo].[TestRefTable].[id] = 1123

SELECT  t.*, s.*
FROM [dbo].[TestTable] t
JOIN @subset s
ON t.id = s.id 

-- Scenario 2: Join directly to SELECT results
SELECT t.*, s.*
FROM [dbo].TestTable t
JOIN (SELECT * FROM [dbo].[TestRefTable] WHERE id = 1123) s
ON t.id = s.id 

在“真实”世界中,表格和表格变量是预定义的。我正在寻找的是能够使匹配的参考行可用于进一步的操作,但我担心额外的步骤会减慢查询速度。为什么一个比另一个更快有技术原因吗?两种方法之间可能会出现什么样的性能差异?我意识到很难(如果不是不可能的话)给出一个明确的答案,只是为这种情况寻找一些建议。

【问题讨论】:

  • 你永远不想在生产环境中使用这样的代码,SELECT t.*, s.*。 Seect * ii 是一个 SQl 反模式,当你有这样的连接时,你会返回不需要的字段,因为 jon 文件包含相同的数据。这是一个非常糟糕的做法。永远不要返回超出您需要的数据。
  • 这是一个很好的问题,也有一些很好的答案。我可能会因为“我也是”的评论而受到抨击,但 +1 还不够。

标签: sql sql-server performance tsql database-performance


【解决方案1】:

数据库引擎有一个优化器来找出执行查询的最佳方式。引擎盖下的内容比您想象的要多。例如,当 SQL Server 进行连接时,它至少可以选择四种连接算法:

  • 嵌套循环
  • 索引查找
  • 合并加入
  • 哈希连接

(更不用说这些的多线程版本了。)

了解其中的每一个是如何工作的并不重要。您只需要了解两件事:不同的算法在不同的情况下是最好的,SQL Server 会尽力选择最好的算法。

连接算法的选择只是优化器做的一件事。它还必须弄清楚连接的顺序、聚合结果的最佳方式、order by 是否需要排序、如何访问数据(通过索引或直接)等等。

当您拆分查询时,您是在对优化做出假设。在您的情况下,您假设第一件事是在特定表上进行选择。你可能是对的。如果是这样,您使用多个查询的结果应该与使用单个查询一样快。好吧,也许不是。在单个查询中,SQL Server 不必一次缓冲所有结果;它可以将结果从一个地方流式传输到另一个地方。它还可以利用并行性以防止拆分查询的方式。

总的来说,SQL Server 优化器非常好,所以最好让优化器一次性完成所有查询。肯定有例外,优化器可能不会选择最佳执行路径。有时解决这个问题就像确保表上的统计信息是最新的一样容易。其他时候,您可以添加优化器提示。在其他时候,您可以像您所做的那样重新构建查询。

例如,将数据加载到本地表中的一个地方是当表来自不同的服务器时。优化器可能没有关于表大小的完整信息来做出最佳决策。

换句话说,将查询保留为一条语句。如果你需要改进它,那么在它工作后专注于优化。您通常不必在优化上花费太多时间,因为引擎非常擅长。

【讨论】:

    【解决方案2】:

    这会得到相同的结果吗?

    SELECT t.*, s.*
    FROM dbo.TestTable AS t
    JOIN dbo.TestRefTable AS s ON t.id = s.id AND s.id = 1123
    

    基本上,这是来自TestTableTestRefTableid = 1123 的所有记录的交叉连接。

    【讨论】:

    • 你忘记了 on 语句的其余部分并创建了一个交叉连接
    • @HLGEM OP 基本上是在这里要求交叉连接。虽然我已经澄清了我的答案。
    • 那么这种方法比加入 SELECT 的结果更有效,还是对两者都进行同样的优化?
    • 我对 SQL 中的优化器并不完全熟悉,它可能会在幕后自动解析您的子查询。尽管尽可能不使用子查询,但通常最好这样做。
    【解决方案3】:

    加入表变量也会导致优化器的基数估计错误。优化器总是假定表变量只包含一行。它实际上拥有的行数越多,估计值就越差。这会导致优化器为表本身假定错误的行数,但在其他地方,对于可能随后连接到该结果的运算符,它可能会导致对该操作执行次数的错误估计。

    我个人认为 Table 参数应该用于方便地使用客户端应用程序将数据传入和传出服务器(C# .Net 应用程序很好地利用了它们),或者在存储过程之间传递数据,但也不应该使用proc本身的很多内容。在 Proc 代码本身中消除它们的重要性随着参数携带的预期行数而增加。

    Sub Selects 会表现得更好,或者立即复制到临时表中会很好。复制到临时表有开销,但同样,你拥有的行越多,开销就越值得,因为优化器的估计越来越差。

    【讨论】:

      【解决方案4】:

      一般来说,查询中的派生表可能会比连接到表变量更快,因为它可以利用索引并且它们在表变量中不可用。但是,临时表也可以创建索引,这可能会解决潜在的性能差异。

      此外,如果预计表变量记录的数量很少,那么索引无论如何都不会产生很大的差异,因此差异很小或没有差异。

      作为法律,您需要在自己的系统上进行测试,因为记录数量以及表设计和索引设计与最有效的方法有很大关系。

      【讨论】:

        【解决方案5】:

        我希望直接表连接比表到表变量更快,并且使用更少的资源。

        【讨论】:

          猜你喜欢
          • 1970-01-01
          • 1970-01-01
          • 2023-03-28
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 2018-11-23
          • 1970-01-01
          • 1970-01-01
          相关资源
          最近更新 更多