Когда использовать Common Table Expression (CTE)

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

Ответ 1

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

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

Для захватывающих определений Microsoft Из книги в Интернете:

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

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

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

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

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

Ответ 2

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

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

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

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

Ответ 3

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

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

Предположим, что есть две таблицы: вопросы и ответы, связанные вместе с Question.ID = Answers.Question_Id (и quiz 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

Ответ 4

Одним из сценариев, которые я нашел полезным для использования 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

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

Ответ 5

Возможно, более целесообразно подумать о 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;

Enjoy

Ответ 6

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

Общая таблица Выражение: - Общее выражение таблицы может быть определено как временный набор результатов или, другими словами, его замена представлений в 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')

Я создал двух сотрудников таблицы и Dept и вставил по 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, мы пишем предложение "с", затем указываем имя выражения таблицы, здесь я назвал имя "CTE_Example"

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

В последней строке я использовал "Выбрать * из 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.

Ответ 7

Это очень полезно, когда вы хотите выполнить "заказанное обновление".

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

Ответ 8

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

Ответ 9

Одна точка, которая еще не указана, это скорость. Я знаю, что это старый ответ на вопрос, но я думаю, что это заслуживает прямого комментария/ответа:

Они кажутся избыточными, как то же самое можно сделать с производными таблицами

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

Ответ 10

 ;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;

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