简体   繁体   English

SQL CTE与临时表

[英]SQL CTE vs Temp Table

I am running into a bit of a stumper here. 我在这里遇到了一些困难。 My main goal is to be able to use this in C# and Entity Framework and our directive from on high is to stay away from stored procedures. 我的主要目标是能够在C#和Entity Framework中使用它,而我们的指令从高处开始就是远离存储过程。

I have 2 tables: a xref and a (Celko) tree table. 我有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

The basic tree data looks like: 基本树数据如下所示:

Customer -> Agencies -> Users -> Clients 客户 - >代理商 - >用户 - >客户

We also have Users who manage multiple agencies, hence the xref (poor name) table. 我们还有管理多个代理商的用户,因此也就是外部参照(可怜的名字)表。 I am testing with a single user that has overview of 98% of the agencies and I need all of the clients. 我正在测试一个用户,该用户概述了98%的代理商,我需要所有客户。 So, my conundrum: 所以,我的难题是:

NOTE: 注意:

  • EntityTypeId = 7 --> User account used for report generation EntityTypeId = 7 - >用于生成报告的用户帐户
  • EntityTypeId = 4 --> Client account EntityTypeId = 4 - >客户帐户

This takes 4 seconds to run but cannot be expressed as a view: 这需要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

This takes 36-40 seconds to run (several different permutations on this join!) 这需要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

Any ideas on how to get the benefits of the temp table into a view for consumption by Entity Framework? 有关如何将临时表的好处纳入实体框架消费视图的任何想法?


Added definitions: 添加定义:

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

The 2nd view is what we are trying to speed up. 第二种观点是我们正在努力加速的。

SIDE NOTE: The current system takes about 2-3 minutes to bring back records. 侧面注意:当前系统需要大约2-3分钟才能恢复记录。 The 2nd view or CTE does it in 40 seconds based on a new data structure (adjacency tree vs set tree). 第二个视图或CTE基于新的数据结构(邻接树与设置树)在40秒内完成。 With the temp table 4 seconds. 随着临时表4秒。

The problem with CTE's is that they are not materialized, they do not have dedicated statistics (they rely on statistics of the underlying objects), they don't have indexes (although in some cases they can use indexes on referenced tables). CTE的问题在于它们没有实现,它们没有专用统计信息(它们依赖于底层对象的统计信息),它们没有索引(尽管在某些情况下它们可以使用引用表上的索引)。

The upsides of temporary tables is that they are inherently materialized (in tempdb), they can have indexes (if you define them) and most definitely have dedicated statistics. 临时表的优点是它们本质上是物化的(在tempdb中),它们可以有索引(如果你定义它们)并且绝对有专门的统计数据。

In a lot of cases that means that using temporary tables instead of CTEs can produce better execution plans. 在很多情况下,这意味着使用临时表而不是CTE可以产生更好的执行计划。 Using a CTE will almost never speed up things, while a temporary table in a lot of cases will. 使用CTE几乎永远不会加快速度,而很多情况下临时表都会加速。

I will defer to a higher authority than me and leave you with his quote: 我会推迟比我更高的权力,并留下他的引用:

A CTE should never be used for performance. 绝不应该使用CTE来提高性能。 You will almost never speed things up by using a CTE because it's just a disposable view. 使用CTE几乎不会加快速度,因为它只是一次性视图。 You can do some neat things with them but speeding up a query isn't really one of them. 你可以用它们做一些巧妙的事情,但加速查询并不是真的其中之一。

This quote is from the accepted answer to the question "What's the difference between a CTE and a Temp Table?" 该引用来自对“CTE和临时表之间有什么区别?”这一问题的接受答案

PS: I see that you are using a TABLE variable in your first query. PS:我看到你在第一个查询中使用了TABLE变量。 This is not the same as a temporary table. 这与临时表不同。 A temporary table will almost always beat a TABLE variable in terms of performance. 临时表几乎总是在性能方面击败TABLE变量。 For a good essay on the difference between TABLE variables and temporary tables, read this accepted answer on the question "What's the difference between a temp table and table variable in SQL Server?" 有关TABLE变量和临时表之间差异的好文章,请阅读“SQL Server中临时表和表变量之间有什么区别?”这一问题的接受答案 .

Add an index on dbo.EntityXref(EntityId, EntityTypeId)? 在dbo.EntityXref(EntityId,EntityTypeId)上添加索引?

I'm guessing this because that's really the only difference that the temp table will make, that it will only table scan once when inserting into the temp table if you don't have an index. 我猜这是因为这真的是临时表的唯一区别,如果你没有索引,它只会在插入临时表时进行一次扫描。 In the other permutations, it might become a looped table scan. 在其他排列中,它可能成为循环表扫描。

The only thing worse than a table scan is a looped table scan. 唯一比表扫描更糟的是循环表扫描。

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

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