2016-01-26 5 views
2

Я нахожусь здесь немного здесь. Моя основная цель - иметь возможность использовать это в C# и Entity Framework, и наша директива от максимума заключается в том, чтобы держаться подальше от хранимых процедур.SQL CTE vs Temp Table

У меня есть 2 стола: стол xref и (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 секунд для запуска (seve RAL различные перестановки по этому присоединиться!)

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 

Любые идеи о том, как получить преимущества из временной таблицы в представление для потребления Entity Framework?


Добавлены определения:

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-я точка зрения является то, что мы пытаемся ускорить.

СТОРОНА ПРИМЕЧАНИЕ: Текущая система занимает около 2-3 минут, чтобы вернуть записи. Второе представление или CTE делает это через 40 секунд на основе новой структуры данных (дерево смежности и дерево набора). С временной таблицей 4 секунды.

+0

Интересно, можете ли вы создать образец демо в sqlFiddle, чтобы мы могли что-то работать. Возможно, вам захочется создать более простой пример, чтобы показать ключевую проблему. Кроме того, вопрос о выполнении должен включать 'EXPLAIN PLAN' –

+0

Что такое' GetMyCaseLoad'? Это взгляд или таблица? –

+1

Я не совсем понимаю, что делает CTE для вас, кроме как сделать запрос труднее читать. Я сомневаюсь, что это смущает SQL Server, но, возможно, в какой-то момент в будущем это может запутать вас * или ваших коллег. –

ответ

5

Проблема с CTE заключается в том, что они не материализованы, у них нет специальной статистики (они полагаются на статистику базовых объектов), у них нет индексов (хотя в некоторых случаях они могут использовать индексы на ссылочных таблицах).

Недостатки временных таблиц состоят в том, что они по сути материализованы (в tempdb), они могут иметь индексы (если вы их определяете) и определенно имеете выделенную статистику.

Во многих случаях это означает, что использование временных таблиц вместо CTE может привести к лучшим планам выполнения. Использование CTE почти никогда не ускорит работу, а временная таблица во многих случаях будет.

я отложу на более высокий авторитет, чем у меня, и оставить вас с его цитатой:

КТР никогда не должен быть использован для повышения производительности. Вы почти никогда не будете ускорять работу, используя CTE, потому что это просто одноразовый вид. Вы можете делать с ними аккуратные вещи, но ускорение запроса на самом деле не является одним из них.

Эта цитата из accepted answer на вопрос «В чем разница между КТР и таблице Temp?»

PS: Я вижу, что вы используете переменную TABLE в своем первом запросе. Это не то же самое, что временная таблица. Временная таблица почти всегда будет бить переменную TABLE с точки зрения производительности. Для хорошего эссе о различии между переменными и временными таблицами , прочитайте это accepted answer по вопросу «В чем разница между временной таблицей и табличной переменной в SQL Server?».

0

Добавить индекс на dbo.EntityXref (EntityId, EntityTypeId)?

Я угадываю это, потому что это единственное отличие, которое сделает таблица temp, что он будет сканировать только один раз при вставке в таблицу temp, если у вас нет индекса. В других перестановках он может стать просмотром таблицы с циклическим столом.

Единственное, что хуже, чем сканирование таблицы, - это сканирование с циклическим столом.

Смежные вопросы