使用 from 子句优化的 SQL 更新查询

Posted

技术标签:

【中文标题】使用 from 子句优化的 SQL 更新查询【英文标题】:SQL Update Query with from clause Optimization 【发布时间】:2015-03-19 14:11:14 【问题描述】:

我有两个表被多个用户大量查询。平均每秒对这些表发出 100 多个(更新/选择)查询。

父母 孩子

*GrantParent 不参与join 所以我说的只有两个表

我需要为每个父母重新排序所有孩子。可以有 3000-4000 个父母,每个父母可能有大约相同数量的孩子。

列类型: 父 ID GUID 子索引 int 文件 ID Varchar IsDeleted 位

表在 PK 上有聚集索引,在 where 使用的列上有非聚集索引。

UPDATE C SET C.ChildIndex = T.ReOrderedChildIndex FROM [Child] C INNER JOIN
(
SELECT ROW_NUMBER() OVER (PARTITION BY dbo.Child.[ParentID] ORDER BY [ChildIndex] asc) AS ReOrderedChildIndex,
        dbo.Child.ChildIndex,
        dbo.Child.FileID, 
        dbo.Child.ParentID

FROM    dbo.Child  WITH (NOLOCK) INNER JOIN
                  dbo.Parent  WITH (NOLOCK) ON dbo.Child.ParentID = dbo.Parent.ParentID
WHERE   (dbo.Parent.GrandParentID = 1) AND (dbo.Child.IsDeleted = 0) 
) T 
ON C.FileID =T.FileID AND (C.ParentID=T.ParentID) AND (C.IsDeleted = 0)

即使我在所有数据选择存储过程中都使用了 WITH (NOLOCK),上面的查询看起来也需要更长的时间并且等待选择查询。

还有另一个查询以与上述查询中对孩子相同的方式对父母进行重新排序。

在“活动监视器”中,会显示选定存储过程的锁。

重新排序执行重新排序的最佳方式是什么?

我遇到以下问题,并认为它们源于这些查询: 1-随机发生死锁。 2- 经常发生连接池超时。

*Windows 应用程序使用启用了连接池的 Entlib 4.0 访问数据库,池最大大小为 200。

SQL Server 2008 R2

【问题讨论】:

Nolock 在数据更改查询、插入、更新、删除或合并中不起作用。 父子关系是否只允许一级? Parent->Child 或 Parent->Child1->Child2->Child3? 只有一层。孩子只能是孩子,父母只能是父母,所以不涉及递归或自连接。对于一位祖父母来说,所有父母都是独一无二的,对于每个父母来说,所有孩子都是独一无二的。关于更新中的 Nolock .. 我认为 update 有一个嵌套的选择,所以应该考虑选择 nolock 吗? 您可以 NOLOCK 父级,但您绝对不能告诉它不要锁定正在更新的表。 更新数据时不要使用 NOLOCK。您可能会损坏您的数据库。当查询速度不够快时,您需要改掉只在查询中抛出 NOLOCK 的习惯。那不是魔术快速按钮。 blogs.sqlsentry.com/aaronbertrand/bad-habits-nolock-everywhere 【参考方案1】:

我建议将您的数据重组为更灵活的架构。此模式将允许多个级别,因此您可以将 GrandParent、Parent 和 Child 合并到一个逻辑关系表和一个逻辑详细信息表中。您还可以利用索引来减少锁定并提高性能。

在任何关系发生变化后,您都必须重新构建层次结构。我在下面编写脚本的方式应该可以最大限度地减少对您系统的影响。您将不再更新整个表格,只更新已更改的部分。

架构:

CREATE TABLE dbo.EntityName
(
    ID INT IDENTITY(1,1),
    ParentID INT -- Todo: Add foreign key back to dbo.EntityName
    -- Todo: Add primary key
);
GO

CREATE TABLE dbo.Hierarchy
(
    ParentID INT,  -- Todo: Add foreign key back to dbo.EntityName
    ChildID INT, -- Todo: Add foreign key back to dbo.EntityName
    ChildLevel INT
);
GO

填充脚本(边缘稍微粗糙):

CREATE PROCEDURE [dbo].[uspBuildHierarchy]
AS
BEGIN
    SET NOCOUNT ON;

    CREATE TABLE #Hierarchy
    (
        ParentID INT,
        ChildID INT,
        ChildLevel INT
    );

    -- Add the root of your hierarchy
    INSERT INTO #Hierarchy VALUES (1, 1, 0); 

    DECLARE @ChildLevel INT = 1,
            @LastCount INT = 1;

    WHILE (@LastCount > 0)
    BEGIN
        INSERT INTO #Hierarchy
        SELECT
            E.ParentID,
            E.ID,
            @ChildLevel + 1
        FROM dbo.EntityName E
        INNER JOIN #Hierarchy H ON H.ChildID = E.ParentID
            AND H.ChildLevel = (@ChildLevel - 1)
        LEFT JOIN #Hierarchy EH ON EH.ParentID = E.ParentID
            AND EH.ChildID = E.ID
        WHERE EH.ChildLevel IS NULL;

        SET @LastCount = @@ROWCOUNT;
        SET @ChildLevel = @ChildLevel + 1;
    END

    MERGE INTO dbo.Hierarchy OH
    USING
    (
        SELECT
            ParentID,
            ChildID,
            ChildLevel
        FROM #Hierarchy
    ) NH
    ON OH.ParentID = NH.ParentID
        AND OH.ChildID = NH.ChildID
    WHEN MATCHED AND OH.ChildLevel <> NH.ChildLevel THEN
        UPDATE
            SET ChildLevel = NH.ChildLevel
    WHEN NOT MATCHED THEN
        INSERT
        VALUES
        (
            NH.ParentID,
            NH.ChildID,
            NH.ChildLevel
        )
    WHEN NOT MATCHED BY SOURCE
        THEN DELETE;
END
GO

查询一个实体的所有子级:

SELECT *
FROM dbo.EntityName E
INNER JOIN dbo.Hierarchy H ON H.ChildID = E.ID
    AND H.ParentID = @EntityNameID;

【讨论】:

目前标准化后我有 3 个表。祖父母,父母,孩子。每个实体都有许多相关属性,因此它们分别保存在相关表中。它如何适合您提出的架构? 此时您只告诉我们它们之间的关系,我必须知道这些表存储的内容。 为了进一步澄清,每个表还有大约 6 个列,这些列不参与这些重新排序/更新,因此,规范化形式比为父级和子级具有一个表更可取。也只有一层。一个父母不能是其他人的孩子,一个孩子只能有一个父母,所以据我所知,有两张桌子更好。不好吗? 它不一定是坏的,它只是固定的,需要一些重复。你在用 ChildIndex 做什么?您多久使用一次 ChildIndex?

以上是关于使用 from 子句优化的 SQL 更新查询的主要内容,如果未能解决你的问题,请参考以下文章

Oracle SQL - FROM 子句中的 JOIN 顺序会影响性能优化吗?

SQL通用优化方案(where优化索引优化分页优化事务优化临时表优化)

MySQL的子查询中FROM和EXISTS子句的使用教程

sql优化

查询优化(MySql/Sql):将函数移出 where 子句

sql优化