【问题标题】:SQL query plan and minimum row size exceeds the maximum allowable of 8060 bytesSQL 查询计划和最小行大小超过 8060 字节的最大允许值
【发布时间】:2021-05-14 13:19:45
【问题描述】:

对本地数据库运行查询时出现以下错误:

查询处理器无法生成查询计划,因为需要工作表,并且其最小行大小超过了允许的最大 8060 字节。需要工作表的一个典型原因是查询中的 GROUP BY 或 ORDER BY 子句。如果查询有 GROUP BY 或 ORDER BY 子句,请考虑减少子句中字段的数量和/或大小。考虑使用字段的前缀 (LEFT()) 或哈希 (CHECKSUM()) 进行分组或使用前缀进行排序。但是请注意,这会改变查询的行为。

在运行与此类似的查询时会发生这种情况(为缺乏细节道歉):

SELECT <about 183 columns>
FROM tableA
INNER JOIN tvf1(<params>) tvf1
  ON tvf1.id = tableA.X1
INNER JOIN tvf2(<params>) tvf2
  ON tvf2.id = tableA.X2
INNER JOIN tvf3(<params>) tvf3
  ON tvf3.id = tableA.X3
INNER JOIN tvf4(<params>) tvf4
  ON tvf4.id = tableA.X4
INNER JOIN tvf5(<params>) tvf5
  ON tvf5.id = tableA.X5

上面的表值函数都使用了GROUP BYROW_NUMBER()和其他聚合函数的组合。 在二进制调试时,注释掉上述任何两个连接都会导致错误不会发生,但不管怎样。

我的数据库在Compatibility Level 2019 上运行。 如果我尝试将Legacy Cardinality Estimation 设置为On,则错误不再发生,但我不明白此设置的作用。

编辑: 如果数据库兼容级别为2016,那么一切都按预期工作

我担心的是生产数据库将来可能会升级,并且可能会发生此错误。

编辑: 我现在已经设法将列数减少到少数,但是我的结果不一致。

SELECT 
         
        Other =  TvfGroupData.Other             
        ,GroupA = TvfGroupData.GroupA
        ,GroupB = TvfGroupData.GroupB
        ,GroupC = TvfGroupData.GroupC

        
    , [Max Created Date] = 
        (SELECT MAX(Value)
            FROM (VALUES 
                (Tvf1.CreatedDate)
                ,(Tvf2.CreatedDate)
                ,(Tvf3.CreatedDate)
                --,(TvfGroupData.CreatedDate)
                ,(Tvf3.CreatedDate)
                ,(Tvf4.CreatedDate)
            ) AS AllValues(Value)
        )

        
FROM TableA
LEFT JOIN Tvf1() ...
LEFT JOIN Tvf2() ...
LEFT JOIN TvfGroupData() ...
LEFT JOIN Tvf3() ...
LEFT JOIN Tvf4() ...

在上述查询中,以下场景有效:

  1. 仅排除 GroupA 列。
  2. 仅排除 GroupB、GroupC 列

其他组合都失败并出现错误:

查询处理器用尽了内部资源,无法生成查询计划。这是一个罕见的事件,仅适用于极其复杂的查询或引用大量表或分区的查询。请简化查询。如果您认为自己错误地收到了此消息,请联系客户支持服务以获取更多信息。

【问题讨论】:

  • 你对所有这些列做了什么?
  • 这是一个最终用户报告。大量统计数据等
  • 在使用数据库 20 年的时间里,我从来没有在一次查询中需要接近 180 多列。您是否使用了很多 (n) varchar 大小延迟的列?
  • 表中有数据类型及其列数: bigint(5) date(2) datetime2(4) decimal(73) int(41) nvarchar(57) varchar( 1)
  • 我建议您的结果集中不太可能需要 183 列;特别是当数据引擎有效地告诉您行太“宽”时。我也希望你的 TVF 是内联函数,而不是多行函数,

标签: sql-server


【解决方案1】:

基数估计器是 SQL Server 生成执行计划的方式,这意味着引擎将如何执行和组装不同的数据集。

引擎的最新变化通常(但并非总是)会产生更好的执行计划,从而在消耗更少资源的同时加快查询响应。

如果您查看 SQL Server 如何处理查询语句,则会在排除不需要的列之前收集完整的数据集。当组合多个数据集时,引擎可能会在加入另一个数据集之前先排除列,或者在排除列之前加入集合。它基于引擎在您的数据模式(统计数据)中“看到”的内容。

UDF 是查询计划优化器经常遇到的绊脚石,因为格式不正确的函数会掩盖数据统计信息并阻止引擎有效地将数据拼凑在一起。

总而言之,更新后的引擎正在查看您的数据并确定在消除不需要的列之前合并多个集合更有效。

我相信您可以通过在加入外部集合之前从函数中选择所需的列来解决此问题。

SELECT * 
FROM (select SpecificColumns from tableA) as tableA
INNER JOIN (select SpecificColumns from tvf1(<params>)) as tvf1
  ON tvf1.id = tableA.X1
INNER JOIN (select SpecificColumns from tvf2(<params>)) as tvf2
  ON tvf2.id = tableA.X2

或者,您可能需要重新考虑使用 Do-Everything 查询方法进行报告。 每行大约 8kb,您可能会将大量数据传递到您的报告系统。

你也可以试试 Cross Apply。

SELECT <about 183 columns>
FROM tableA
CROSS APPLY tvf1(<params>) tvf1
CROSS APPLY tvf2(<params>) tvf2
WHERE tvf1.id = tableA.X1
  AND tvf2.id = tableA.X2

CROSS APPLY 可以指示优化器以不同的顺序处理集合。

【讨论】:

    【解决方案2】:

    我仍然无法找到确切的问题。

    我现在的解决方法是将表值函数评估为表变量/临时表,然后加入它们

    所以已经改成这样了

    DECLARE @tvf1 AS TABLE ....
    INSERT INTO @tvf1 SELECT * FROM tvf1()...
    DECLARE @tvf2 AS TABLE ....
    INSERT INTO @tvf2 SELECT * FROM tvf2()...
    DECLARE @tvf3 AS TABLE ....
    INSERT INTO @tvf3 SELECT * FROM tvf3()...
    
    
    SELECT <about 183 columns>
    FROM tableA
    INNER JOIN @tvf1 tvf1
      ON tvf1.id = tableA.X1
    INNER JOIN @tvf2 tvf2
      ON tvf2.id = tableA.X2
    INNER JOIN @tvf3 tvf3
      ON tvf3.id = tableA.X3
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2014-09-02
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多