由于文件组“ DEFAULT”中的磁盘空间不足,无法为数据库“ TEMPDB”分配新页面

时间:2019-03-13 14:20:23

标签: sql-server tempdb

由于文件组“ DEFAULT”中的磁盘空间不足,无法为数据库“ TEMPDB”分配新页面。通过在文件组中放置对象,向文件组添加其他文件或为文件组中的现有文件设置自动增长来创建必要的空间。

执行存储过程(包含具有近2.5亿条记录的select语句)的存储过程时,会遇到此错误。

即使我在tempdb中有大约650GB的空间也遇到了此错误。想知道我可以做些什么

查询:

SELECT u.Id, u.place, u.name, u.lname, LOWER(ue.email) AS Email, MIN(dp.BirthTime) AS Time
FROM tableA u
    JOIN tableB ue ON ue.id = u.id AND ue.Did = 0
    JOIN tableC dp ON dp.Id = u.Id
        JOIN tableB dpe ON dpe.Id = dp.Id
            LEFT JOIN tableB idpe ON idpe.Email = dpe.Email
                LEFT JOIN tableE idp ON idp.Id = idpe.Id
                    LEFT JOIN tableD pidp ON pidp.Id = idp.Id 
        JOIN tableD cp ON dp.Id = cp.Id
where ISNULL(cp.FName,'') = '' AND ISNULL(cp.LName,'') = '' AND ISNULL(cp.IsActive,0) = 0 AND ISNULL(dp.Isinactive,0) = 0
    AND ISNULL(pidp.FName,'') = '' AND ISNULL(pidp.LName,'') = ''
    AND ISNULL(pidp.Isactive,0) = 0 AND ISNULL(idp.IsInactive,0)
    AND ISNULL(u.EHome, '') != ''
GROUP BY u.Id, u.Isactive, u.name, u.EServer, ue.Email

2 个答案:

答案 0 :(得分:0)

我可以看到查询的一些问题,这将使其效率非常低:

  1. LEFT JOIN tableD pidp-您退出了该表,然后在WHERE子句中应用了过滤条件:

    ISNULL(pidp.FName,'') = ''AND ISNULL(pidp.LName,'') = '' AND ISNULL(pidp.Isactive,0) = 0

ISNULL([col name], '') != ''-非常低效的构造,无法确保不会使用列上的任何索引(如果存在)。 如果列不可为空,则将过滤条件移至联接并删除ISNULL

ON pidp.Id = idp.Id AND pidp.FName = ''
    AND pidp.LName = '' AND pidp.Isactive = 0

这也适用于ISNULL(idp.IsInactive,0)参数。

  1. GROUP BY语句可解决由弱联接/错误的查询设计引起的问题-这是查询设计真的很差的标志,并且很可能是临时表使用过多的唯一原因。

我会按照以下方式将查询重写为某些内容(我在步骤1中未包含建议,因为它们取决于您特定的表定义):

SELECT u.Id, u.place, u.name, u.lname, LOWER(ue.email) AS Email, MIN(dp.BirthTime) AS Time
FROM tableA u
    JOIN tableB ue ON ue.id = u.id AND ue.Did = 0
WHERE ISNULL(u.EHome, '') != ''
    AND u.Id IN(
        SELECT dp.id
        FROM tableC dp
            JOIN tableB dpe ON dpe.Id = dp.Id
                LEFT JOIN tableB idpe ON idpe.Email = dpe.Email
                    LEFT JOIN tableE idp ON idp.Id = idpe.Id
                        LEFT JOIN tableD pidp ON pidp.Id = idp.Id 
            JOIN tableD cp ON dp.Id = cp.Id
        where ISNULL(cp.FName,'') = '' AND ISNULL(cp.LName,'') = '' AND ISNULL(cp.IsActive,0) = 0 AND ISNULL(dp.Isinactive,0) = 0
            AND ISNULL(pidp.FName,'') = '' AND ISNULL(pidp.LName,'') = ''
            AND ISNULL(pidp.Isactive,0) = 0 AND ISNULL(idp.IsInactive,0)
         )

答案 1 :(得分:0)

TempDB不是问题。

查询具有多个带有非SARGable表达式的联接。必须先处理所有联接,然后才能过滤任何where子句数据。如果您的最终结果(过滤和分组)为2.5亿行,那么我预计联接后的结果将达到数十亿行。必须先在tempdb中实现该结果,然后才能进行过滤和分组。

尝试将此ISNULL(cp.FName,'') = ''更改为此(cp.FName = '' OR cp.FName IS NULL)以及所有类似的表达式。这至少可以允许在联接之前进行一些过滤。

第二步是删除结果集中未使用的联接。您仅从表U,UE和DP返回数据。将其他内部联接移到存在子句中

EXISTS (SELECT 1 FROM tableB dpe WHERE dpe.Id = dp.Id)

这会将初始联接产品减少到更易于管理的状态。

您的左联接将更复杂地解决,但如果不属于结果集,则也不应成为联接。我阅读了您的查询,说您想在数据丢失的地方过滤结果。例如,仅当FName为空字符串,空值或没有匹配记录时,才返回FName。换句话说,您不想看到填充有非零/非空白值的FName,LName,IsActive记录。

AND NOT EXISTS ( SELECT 1 FROM tableD AS pidp where pidp.Id = idp.Id AND fname <> '' AND lname <> '' AND isactive = 0)

如果全部填充了3条,则此语句将排除它们。如果只想在其中任何一个字段具有值的情况下排除它们,请将EXISTS子句内的AND更改为OR

尝试这些更改,我希望您完全避免使用过时的TempDB。