SQL Server 在表结果集中缓慢传输结果,但立即使用 XML

CWM*_*Mjr 3 performance sql-server sql-server-2008-r2 sql-server-2016

我有一个查询,左连接 100 多个表并返回 2,000 多列,但只有几行(10-20)。这部分目前不在我的控制范围内,它过去可以在少一些列的情况下正常运行(可能在正常的非宽表宽度下)。编译初始计划后执行是“即时”的,我实际上并没有将所有结果返回给客户端。如果我返回 1 行,它是即时的并立即返回结果。

当我返回所有 11 个左右的结果时,它会下降到大约需要 15 秒。但是,如果我添加任何“FOR XML [RAW [ELEMENTS, XSINIL]]”迭代,它也会立即返回所有结果以及 XML 开销。据我所知,这排除了查询的实际执行,并将其隔离到正在计算的查询结果和它们被发送到客户端之间。

我一直在寻找大约 4 天的时间来解释这一点。有没有办法通过结果集获得 XML 速度?为什么要这样做?

在 2008R2 和 2016 上尝试过 TCP/IP 和命名管道

更新

根据反馈,对于表格结果,发生的是第一次通过,大约需要 20 秒。SSMS 中的计时器一直运行到大约 11 秒,然后停止。然后,当显示结果时,计时器跳至 20 秒。下一次运行时计时器会挂起,然后在显示结果时,计时器会弹出大约 9-11 秒。

通过将主表的唯一 id 与表函数连接来选择 11 行的方式。我尝试用临时表替换表函数。为了选择一条记录,我基本上将数据传递到导致一个结果的表函数中。我也尝试使用直接的 id = 文字数字。对于那些,计时器在结果显示之前甚至不会离开 0。

我无法“插入”临时表,因为常规表的列太多。我也无法在其上创建索引视图来检查它。

我再次使用 FOR XML 运行查询并检查计时器。它位于 0:00 并表示查询完成。我再次尝试并立即在 xml 中打开结果,它显示了 11 行 xml 行,其中最后一行是 54,000 个字符。如果您剪切并粘贴表格结果,它确实只显示 2023。时间差异可能是每行中的数据量?有多个具有文本类型的列。

说到文本类型,它使我无法对完整查询执行“计数不同”以提取所有列但根本不发送它们。否则我使用 SSMS 并告诉它丢弃结果。我不确定这是否会影响查询计划。它运行得很快。

我希望让这个东西一瘸一拐地走下去,足以改变它的架构方式。从中长期来看,这根本是不可接受的。

到目前为止,感谢大家的洞察力和帮助。

Sol*_*zky 5

您一直提到的那些 XML 结果。我不认为你正在看到你认为你看到的东西。;-)

FOR XML魔法吗?

当网格中有 XML 结果时,单击包含结果集行号的左边距(应该只有 1 行)。这应该突出显示 XML 结果。现在点击Control-C复制 XML,然后在上面的查询编辑器中,将光标放在空行的开头并点击Control-V粘贴 XML。这应该将光标留在最右侧,在 XML 的末尾。向下看具有行号、列号和字符号的状态栏。它为列 # 和字符 # 显示了什么?我的显示 2033,但 XML 被剪裁了,应该还有更多。

现在单击结果集中的 XML 本身。这会将您带到另一个带有未剪辑的完整 XML 结果的选项卡。这很可能超过 2033 个字符的文本。

您所看到的“即时”结果FOR XML仅仅是因为 SSMS 只显示前 2033 个字符,而实际结果值多出 5000 或 5,000,000 个字符。而事实上,如果您的查询需要足够长的时间,你可以密切关注查询定时器在较低,右上角,如果你重新运行查询和正在返回10+行,该计数器仍然可以去结果网格完成显示其截断的 XML 片段。

行数和列数对显示时间有很大影响吗?

我整理了一些返回 2300 列的代码(如下所示)来测试这种行为。它FOR XML从未注释开始。初始运行后,无论您如何调整@Rows输入参数,它每次都会立即返回。将其设置为 10,然后是 20,然后是 40。XML 总是会立即返回。但也要注意查询计时器,因为它会继续运行,尤其是当您增加 的值时@Rows

然后注释掉SET添加的语句,FOR XML以便您在网格中获得 2300 个单独的字段。第一次运行可能需要 30 次。再次运行每次只需要 2 - 3 秒,即使我将@Rows4 增加到 10、20、40、50,甚至 80。因此,显示 2000 多列的结果所需的时间不是函数返回多少行,至少不是在这么小的范围内。

编码

DECLARE @Select NVARCHAR(MAX) = N'SELECT TOP (@Rows)
         OBJECT_NAME(sac1.[object_id]) AS [ObjectName], *';
DECLARE @From NVARCHAR(MAX) = N'FROM   [sys].[all_columns] sac1';
DECLARE @JoinTemplate NVARCHAR(MAX) = N'
INNER JOIN [sys].[all_columns] sac{{counter}}
        ON sac{{counter}}.[object_id] = sac1.[object_id]
       AND sac{{counter}}.[column_id] = sac1.[column_id]';

DECLARE @CRLF NCHAR(2) = NCHAR(0x0D) + NCHAR(0x0A);
DECLARE @Counter INT = 2;

WHILE (@Counter < 93)
BEGIN
  SET @From += REPLACE(@JoinTemplate, N'{{counter}}', CONVERT(NVARCHAR(5), @Counter));

  SET @Counter += 1;
END;


SET @Select += @CRLF + @From;
--SET @Select += @CRLF + N'ORDER BY OBJECT_NAME(sac1.[object_id])';
SET @Select += @CRLF + N' FOR XML RAW, ELEMENTS';

EXEC sp_executesql
  @Select,
  N'@Rows INT',
  @Rows = 4;
Run Code Online (Sandbox Code Playgroud)

取消注释具有 的SET行,ORDER BY以查看每次运行查询时需要完整的 30 - 40 秒(由于排序)。

  • [我猜是 2033 年而不是 2034 年?](http://stackoverflow.com/a/33961227/73226) (2认同)
  • @MartinSmith 是的,我很懒惰,从状态栏中复制了 Col#/Char#,但忘记考虑将光标放在最后一个字符之前的 1 个位置。我已经在我的回答中修正了这些信息。并对您的详细 SO 答案 +1 :)。 (2认同)