繁体   English   中英

SQL CTE与临时表

[英]SQL CTE vs Temp Table

我在这里遇到了一些困难。 我的主要目标是能够在C#和Entity Framework中使用它,而我们的指令从高处开始就是远离存储过程。

我有2个表:一个外部参照和一个(Celko)树表。

/**
 ** Table [dbo].[EntityXref]
 **/
IF EXISTS(SELECT * FROM sys.tables WHERE name = N'EntityXref' AND type = N'U')
    DROP TABLE [dbo].[EntityXref]
GO
CREATE TABLE dbo.[EntityXref]
( Id                BIGINT IDENTITY(1,1) NOT NULL
, EntityId          INT NOT NULL
, EntityTypeId      INT NOT NULL
, ChildEntityId     INT NOT NULL
, ChildEntityTypeId INT NOT NULL
, CONSTRAINT [PK_EntityXref] PRIMARY KEY NONCLUSTERED ([Id] ASC) WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON) ON [PRIMARY]
, CONSTRAINT [UQ_EntityXref] UNIQUE CLUSTERED (EntityId, EntityTypeId, ChildEntityId, ChildEntityTypeId)
)


/**
 ** Table [dbo].[EntityTree]
 **/
IF EXISTS(SELECT * FROM sys.tables WHERE name = N'EntityTree' AND type = N'U')
    DROP TABLE dbo.EntityTree
GO
CREATE TABLE dbo.EntityTree
( Id            BIGINT IDENTITY(1,1) NOT NULL
, SystemId      INT NOT NULL DEFAULT 1 
, EntityId      INT NOT NULL -- could be an AgencyId, UserId, ClientId, VendorId, etc
, EntityTypeId  INT NOT NULL -- Defines the entity type
, isActive      BIT NOT NULL
, lft           BIGINT NOT NULL 
, rgt           BIGINT NOT NULL 
, CONSTRAINT [PK_EntityTree] PRIMARY KEY CLUSTERED ([Id] ASC) WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON) ON [PRIMARY]
, CONSTRAINT [UQ_Entity]     UNIQUE NONCLUSTERED (EntityId, EntityTypeId)
, CONSTRAINT [UQ_Left]       UNIQUE NONCLUSTERED ([lft])
, CONSTRAINT [UQ_LeftRight]  UNIQUE NONCLUSTERED ([lft], [rgt])
)
GO

基本树数据如下所示:

客户 - >代理商 - >用户 - >客户

我们还有管理多个代理商的用户,因此也就是外部参照(可怜的名字)表。 我正在测试一个用户,该用户概述了98%的代理商,我需要所有客户。 所以,我的难题是:

注意:

  • EntityTypeId = 7 - >用于生成报告的用户帐户
  • EntityTypeId = 4 - >客户帐户

这需要4秒才能运行但不能表示为视图:

DECLARE @t TABLE
( childentityid INT
, childentitytypeid INT
)

INSERT INTO @t
SELECT et.RootEntityId, et.RootEntityTypeId
  FROM dbo.EntityXref et
 WHERE et.EntityId = 17088 AND et.EntityTypeId = 7    

SELECT * 
  FROM @t a
 INNER JOIN dbo.GetMyCaseLoad b ON a.RootEntityId = b.ParentEntityId AND a.RootEntityTypeId = b.ParentEntityTypeId
GO

这需要36-40秒才能运行(此连接上有几种不同的排列!)

WITH xrefParent (parentEntityId, parentEntityTypeId)    --, rootEntityId, rootEntityTypeId)
  AS (SELECT ChildEntityId, ChildEntityTypeId /*, EntityId, EntityTypeId */ FROM dbo.EntityXref WHERE EntityId = 17088 AND EntityTypeId = 7) 
SELECT *
  FROM GetMyCaseLoad cl
 INNER JOIN xrefParent p ON cl.ParentEntityId = p.parentEntityId AND cl.ParentEntityTypeId = p.parentEntityTypeId
-- WHERE p.rootEntityId = 17088 AND p.rootEntityTypeId = 7
GO

有关如何将临时表的好处纳入实体框架消费视图的任何想法?


添加定义:

CREATE VIEW GetMyCaseLoad AS
SELECT Parent.Id            [ParentRecordId]
     , Parent.EntityId      [ParentEntityId]
     , Parent.EntityTypeId  [ParentEntityTypeId]
     , Child.SystemId       [ChildSystemId]
     , Child.Id             [ChildRecordId]
     , Child.EntityId       [ChildEntityId]
     , Child.EntityTypeId   [ChildEntityTypeId]
     , Child.isActive       [ChildIsActive]
     , Child.lft            [ChildLeft]
     , Child.rgt            [ChildRight]
  FROM dbo.EntityTree Parent
     , dbo.EntityTree Child
 WHERE Child.lft > Parent.lft
   AND Child.rgt < Parent.rgt
   AND Child.EntityTypeId = 4
GO


CREATE VIEW GetMyFullCaseLoad AS
SELECT x.Id             [XrefRecordId]
     , x.EntityId       [XrefParentEntityId]
     , x.EntityTypeId   [XrefParentEntityTypeId]
     , c.ParentRecordId
     , c.ParentEntityId
     , c.ParentEntityTypeId
     , c.ChildRecordId
     , c.ChildEntityId
     , c.ChildEntityTypeId
     , c.ChildIsActive
     , c.ChildLeft
     , c.ChildRight
     , x.CanRead
     , x.CanWrite 
  FROM EntityXref x
 INNER JOIN dbo.GetMyCaseLoad c ON x.ChildEntityId = c.ParentEntityId AND x.ChildEntityTypeId = c.ParentEntityTypeId
GO

第二种观点是我们正在努力加速的。

侧面注意:当前系统需要大约2-3分钟才能恢复记录。 第二个视图或CTE基于新的数据结构(邻接树与设置树)在40秒内完成。 随着临时表4秒。

CTE的问题在于它们没有实现,它们没有专用统计信息(它们依赖于底层对象的统计信息),它们没有索引(尽管在某些情况下它们可以使用引用表上的索引)。

临时表的优点是它们本质上是物化的(在tempdb中),它们可以有索引(如果你定义它们)并且绝对有专门的统计数据。

在很多情况下,这意味着使用临时表而不是CTE可以产生更好的执行计划。 使用CTE几乎永远不会加快速度,而很多情况下临时表都会加速。

我会推迟比我更高的权力,并留下他的引用:

绝不应该使用CTE来提高性能。 使用CTE几乎不会加快速度,因为它只是一次性视图。 你可以用它们做一些巧妙的事情,但加速查询并不是真的其中之一。

该引用来自对“CTE和临时表之间有什么区别?”这一问题的接受答案

PS:我看到你在第一个查询中使用了TABLE变量。 这与临时表不同。 临时表几乎总是在性能方面击败TABLE变量。 有关TABLE变量和临时表之间差异的好文章,请阅读“SQL Server中临时表和表变量之间有什么区别?”这一问题的接受答案

在dbo.EntityXref(EntityId,EntityTypeId)上添加索引?

我猜这是因为这真的是临时表的唯一区别,如果你没有索引,它只会在插入临时表时进行一次扫描。 在其他排列中,它可能成为循环表扫描。

唯一比表扫描更糟的是循环表扫描。

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM