Когда использовать Common Table Expression (CTE) - PullRequest
205 голосов
/ 20 января 2011

Я начал читать о Распространенном табличном выражении и не могу вспомнить случай использования, в котором мне нужно было бы их использовать.Они кажутся избыточными, как то же самое можно сделать с производными таблицами.Я что-то упускаю или плохо понимаю?Может ли кто-нибудь дать мне простой пример ограничений с помощью регулярных запросов на выборку, производную или временную таблицу, чтобы привести случай CTE?Любые простые примеры будут высоко оценены.

Ответы [ 8 ]

176 голосов
/ 20 января 2011

Один пример, если вам нужно ссылаться на один и тот же набор данных несколько раз, вы можете сделать это, определив CTE. Следовательно, это может быть формой повторного использования кода.

Примером самореференции является рекурсия: Рекурсивные запросы с использованием CTE

За захватывающие определения Microsoft Взято из Книги Онлайн:

CTE может использоваться для:

  • Создать рекурсивный запрос. Для получения дополнительной информации см. Рекурсивные запросы с использованием общих табличных выражений.

  • Замена представления, когда общее использование представления не требуется; то есть вам не нужно хранить определение в метаданных.

  • Включить группировку по столбцу, полученному из скалярного подвыбора, или функции, которая не является детерминированной или имеет внешний доступ.

  • Ссылка на полученную таблицу несколько раз в одном и том же выражении.

44 голосов
/ 20 января 2011

Я использую их для разбиения сложных запросов, особенно сложных объединений и подзапросов. Я обнаружил, что все больше и больше использую их как «псевдо-представления», чтобы помочь мне понять смысл запроса.

Моя единственная жалоба на них - они не могут быть повторно использованы. Например, у меня может быть сохраненный процесс с двумя операторами обновления, которые могут использовать один и тот же CTE. Но «область действия» CTE - это только первый запрос.

Проблема в том, что «простые примеры», вероятно, не нуждаются в CTE!

Тем не менее, очень удобно.

38 голосов
/ 14 ноября 2012

Есть две причины, по которым я вижу использование cte.

Использовать вычисленное значение в предложении where. Это кажется немного чище, чем производная таблица.

Предположим, есть две таблицы - Вопросы и Ответы, объединенные при помощи Вопросов. ID = Ответы. Вопрос_Id (и идентификатор теста)

WITH CTE AS
(
    Select Question_Text,
           (SELECT Count(*) FROM Answers A WHERE A.Question_ID = Q.ID) AS Number_Of_Answers
    FROM Questions Q
)
SELECT * FROM CTE
WHERE Number_Of_Answers > 0

Вот еще один пример, где я хочу получить список вопросов и ответов. Я хочу, чтобы ответы были сгруппированы с вопросами в результатах.

WITH cte AS
(
    SELECT [Quiz_ID] 
      ,[ID] AS Question_Id
      ,null AS Answer_Id
          ,[Question_Text]
          ,null AS Answer
          ,1 AS Is_Question
    FROM [Questions]

    UNION ALL

    SELECT Q.[Quiz_ID]
      ,[Question_ID]
      ,A.[ID] AS  Answer_Id
      ,Q.Question_Text
          ,[Answer]
          ,0 AS Is_Question
        FROM [Answers] A INNER JOIN [Questions] Q ON Q.Quiz_ID = A.Quiz_ID AND Q.Id = A.Question_Id
)
SELECT 
    Quiz_Id,
    Question_Id,
    Is_Question,
    (CASE WHEN Answer IS NULL THEN Question_Text ELSE Answer END) as Name
FROM cte    
GROUP BY Quiz_Id, Question_Id, Answer_id, Question_Text, Answer, Is_Question 
order by Quiz_Id, Question_Id, Is_Question Desc, Name
18 голосов
/ 18 марта 2015

Один из сценариев, который я нашел полезным для использования CTE, - это когда вы хотите получить DISTINCT строки данных, основанные на одном или нескольких столбцах, но вернуть все столбцы в таблице. При стандартном запросе вам, возможно, сначала придется выгрузить отдельные значения во временную таблицу, а затем попытаться соединить их обратно с исходной таблицей, чтобы получить остальные столбцы, или вы можете написать чрезвычайно сложный запрос на раздел, который может вернуть один запуск, но, по всей вероятности, он будет нечитабелен и вызовет проблемы с производительностью.

Но с помощью CTE (как ответил Тим Шмельтер на Выберите первый экземпляр записи )

WITH CTE AS(
    SELECT myTable.*
    , RN = ROW_NUMBER()OVER(PARTITION BY patientID ORDER BY ID)
    FROM myTable 
)
SELECT * FROM CTE
WHERE RN = 1

Как видите, это намного легче читать и поддерживать. И по сравнению с другими запросами, намного лучше в производительности.

13 голосов
/ 19 февраля 2016

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

  • Создать рекурсивный запрос.
  • Используйте набор результатов CTE более одного раза в запросе.
  • Повышайте ясность в своем запросе, уменьшая большие куски идентичных подзапросов.
  • Включите группировку по столбцу, полученному вНабор результатов CTE

Вот пример вырезания и вставки для игры:

WITH [cte_example] AS (
SELECT 1 AS [myNum], 'a num' as [label]
UNION ALL
SELECT [myNum]+1,[label]
FROM [cte_example]
WHERE [myNum] <=  10
)
SELECT * FROM [cte_example]
UNION
SELECT SUM([myNum]), 'sum_all' FROM [cte_example]
UNION
SELECT SUM([myNum]), 'sum_odd' FROM [cte_example] WHERE [myNum] % 2 = 1
UNION
SELECT SUM([myNum]), 'sum_even' FROM [cte_example] WHERE [myNum] % 2 = 0;

Наслаждайтесь

6 голосов
/ 08 февраля 2014

Сегодня мы узнаем о выражении Common table, которое является новой функцией, которая была введена в SQL Server 2005 и доступна и в более поздних версиях.

Выражение Common Table: - Можно определить выражение Common Tableкак временный набор результатов или, другими словами, его замена представлений в SQL Server.Общее табличное выражение допустимо только в пакете оператора, где оно было определено, и не может использоваться в других сеансах.

Синтаксис объявления CTE (общего табличного выражения): -

with [Name of CTE]
as
(
Body of common table expression
)

ПозволяетВозьмем пример: -

CREATE TABLE Employee([EID] [int] IDENTITY(10,5) NOT NULL,[Name] [varchar](50) NULL)

insert into Employee(Name) values('Neeraj')
insert into Employee(Name) values('dheeraj')
insert into Employee(Name) values('shayam')
insert into Employee(Name) values('vikas')
insert into Employee(Name) values('raj')

CREATE TABLE DEPT(EID INT,DEPTNAME VARCHAR(100))
insert into dept values(10,'IT')
insert into dept values(15,'Finance')
insert into dept values(20,'Admin')
insert into dept values(25,'HR')
insert into dept values(10,'Payroll')

Я создал две таблицы сотрудников и отдел и вставил 5 строк в каждую таблицу.Теперь я хотел бы присоединиться к этим таблицам и создать временный набор результатов для дальнейшего его использования.

With CTE_Example(EID,Name,DeptName)
as
(
select Employee.EID,Name,DeptName from Employee 
inner join DEPT on Employee.EID =DEPT.EID
)
select * from CTE_Example

Давайте возьмем каждую строку утверждения одну за другой и поймем.

Чтобы определить CTE, мы пишем предложение «with», затем мы даем имя табличному выражению, здесь я дал имя как «CTE_Example»

Затем мы пишем «As» и заключаем нашекод в двух скобках (---), мы можем объединить несколько таблиц во вложенных скобках.

В последней строке я использовал «Select * from CTE_Example», мы ссылаемся на выражение общей таблицы впоследняя строка кода, поэтому мы можем сказать, что это похоже на представление, где мы определяем и используем представление в одном пакете, а CTE не сохраняется в базе данных как постоянный объект.Но это ведет себя как представление.мы можем выполнить оператор удаления и обновления для CTE, и это будет иметь прямое влияние на таблицу, на которую ссылаются, которая используется в CTE.Давайте рассмотрим пример, чтобы понять этот факт.

With CTE_Example(EID,DeptName)
as
(
select EID,DeptName from DEPT 
)
delete from CTE_Example where EID=10 and DeptName ='Payroll'

В приведенном выше заявлении мы удаляем строку из CTE_Example, и она удаляет данные из ссылочной таблицы "DEPT", которая используется в CTE.

4 голосов
/ 26 июля 2018

Это очень полезно, когда вы хотите выполнить «упорядоченное обновление».

MS SQL не позволяет использовать ORDER BY с UPDATE, но с помощью CTE вы можете сделать это следующим образом:

WITH cte AS
(
    SELECT TOP(5000) message_compressed, message, exception_compressed, exception
    FROM logs
    WHERE Id >= 5519694 
    ORDER BY Id
)
UPDATE  cte
SET     message_compressed = COMPRESS(message), exception_compressed = COMPRESS(exception)

Смотрите здесь для получения дополнительной информации: Как обновить и заказать с помощью MS sql

0 голосов
/ 18 декабря 2015
 ;with cte as
  (
  Select Department, Max(salary) as MaxSalary
  from test
  group by department
  )  
  select t.* from test t join cte c on c.department=t.department 
  where t.salary=c.MaxSalary;

попробуйте это

...