CTE (Common Table Expression)를 사용하는 경우


230

공통 테이블 표현식 에 대한 읽기를 시작했으며 이를 사용해야하는 사용 사례를 생각할 수 없습니다. 파생 테이블에서 동일한 작업을 수행 할 수 있으므로 중복되는 것처럼 보입니다. 내가 빠졌거나 이해하지 못하는 것이 있습니까? 누군가 CTE의 사례를 만들기 위해 정기적 인 선택, 파생 또는 임시 테이블 쿼리의 제한 사항에 대한 간단한 예를 들어 줄 수 있습니까? 모든 간단한 예는 높이 평가 될 것입니다.

답변:


197

예를 들어, 동일한 데이터 세트를 여러 번 참조 / 결합해야하는 경우 CTE를 정의하면됩니다. 따라서 코드 재사용 형식이 될 수 있습니다.

자체 참조의 예는 재귀입니다. CTE를 사용한 재귀 쿼리

온라인 설명서에서 가져온 흥미로운 Microsoft 정의 :

CTE를 사용하여 다음을 수행 할 수 있습니다.

  • 재귀 쿼리를 만듭니다. 자세한 내용은 공통 테이블 식을 사용한 재귀 쿼리를 참조하십시오 .

  • 뷰를 일반적으로 사용할 필요가없는 뷰를 대체합니다. 즉, 정의를 메타 데이터에 저장할 필요가 없습니다.

  • 스칼라 부속 선택에서 파생 된 열 또는 결정적이지 않거나 외부 액세스 권한이있는 함수를 기준으로 그룹화를 사용하십시오.

  • 동일한 명령문에서 결과 테이블을 여러 번 참조하십시오.


7
네. 파생 테이블을 자체 조인 할 수 없습니다. CTE에 대한 자체 참여는 여전히 두 번의 개별 호출로 당신을 떠날만한 가치가 있습니다.
Martin Smith

@Martin-놀랐습니다. 그 진술을 뒷받침 할 수 있습니까?
RichardTheKiwi

@ 존 감사합니다, 나는 4guysfromrolla.com/webtech/071906-1.shtml도 매우 유용합니다
imak

4
@cyberkiwi-어느 비트? 자체 참여는 두 가지 다른 호출로 이어질까요? 이 답변의 예를 참조하십시오 stackoverflow.com/questions/3362043/…
Martin Smith

4
CTE에 대한 흥미로운 사실. CTE가 두 번 이상 참조 될 때 CTE의 NEWID ()가 왜 바뀌는 지 항상 궁금했습니다. select top 100 * into #tmp from master..spt_values order by 1,2,3,4 select A.number, COUNT(*) from #tmp A inner join #tmp B ON A.number = B.number+1 group by A.numbervswith CTE AS (select top 100 * from master..spt_values order by 1,2,3,4) select A.number, COUNT(*) from CTE A inner join CTE B ON A.number = B.number+1 group by A.number
RichardTheKiwi

50

복잡한 쿼리, 특히 복잡한 조인 및 하위 쿼리를 분리하는 데 사용합니다. 쿼리의 의도를 이해하는 데 도움이되는 '의사보기'로 점점 더 많이 사용하고 있습니다.

그들에 대한 나의 유일한 불만은 재사용 할 수 없다는 것입니다. 예를 들어, 동일한 CTE를 사용할 수있는 두 개의 업데이트 문이있는 저장 프로 시저가있을 수 있습니다. 그러나 CTE의 '범위'는 첫 번째 쿼리 일뿐입니다.

문제는 '간단한 예'에는 실제로 CTE가 필요하지 않을 것입니다!

여전히 매우 편리합니다.


확인. 이 개념을 이해하는 데 도움이되는 비교적 복잡한 예를 사용하여 사례를 만들 수 있습니까?
imak

28
"그들에 대한 나의 유일한 불만은 다시 사용할 수 없습니다입니다"- CTE를 당신이 후보로 간주해야 다시 사용하려는 VIEW:)
onedaywhen

6
@onedaywhen : 이해했지만, 항상 편안한 것은 아닙니다. 때로는 프로세스 범위 내에서 CTE를 정의하고 선택하고 업데이트하거나 다른 테이블에서 유사한 데이터를 선택하는 데 사용하고 싶습니다.
n8wrl

5
동일한 CTE가 두 번 이상 필요할 때 임시 테이블에 공급 한 다음 원하는만큼 임시 테이블을 사용합니다.
Fandango68

43

cte를 사용하는 데는 두 가지 이유가 있습니다.

where 절에서 계산 된 값을 사용합니다. 이것은 파생 테이블보다 조금 더 깨끗해 보입니다.

Questions.ID = Answers.Question_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

10
CTE 대신 중첩 쿼리를 사용하기 위해 첫 번째 예제를 단순화 할 수 없습니까?
Sam

2
두 가지 예 모두 가능합니다.
나치

3
CTE없이 첫 번째 것을 추가했으면, 후자가 유용한 이유가 바로 명백합니다.
Ufos

HAVING서브를 사용하는 것과 유사 할 수있는 마지막 단계 필터 할 수있는 또 다른 방법입니다SELECT
윌리엄 Entriken

21

CTE를 사용하는 데 유용한 시나리오 중 하나는 하나 이상의 열을 기반으로 DISTINCT 행의 데이터를 가져오고 테이블의 모든 열을 반환하려는 경우입니다. 표준 쿼리를 사용하면 먼저 고유 값을 임시 테이블에 덤프 한 다음 원래 테이블에 다시 결합하여 나머지 열을 검색하거나 결과를 반환 할 수있는 매우 복잡한 파티션 쿼리를 작성할 수 있습니다 한 번 실행하지만 대부분의 경우 읽을 수 없으며 성능 문제가 발생합니다.

그러나 CTE를 사용함으로써 (팀 Schmelter 가 레코드의 첫 번째 인스턴스선택했을 때 )

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

보다시피, 이것은 읽고 유지하기가 훨씬 쉽습니다. 다른 쿼리와 비교할 때 성능이 훨씬 뛰어납니다.


16

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;

즐겨


7

오늘 우리는 SQL Server 2005에 도입되었고 이후 버전에서도 사용할 수있는 새로운 기능인 공통 테이블 표현식에 대해 배울 것입니다.

공통 테이블 표현식 :-공통 테이블 표현식은 임시 결과 세트 또는 다른 말로 SQL Server에서 뷰를 대체 할 수 있습니다. 공통 테이블 표현식은 명령문이 정의 된 배치 배치에서만 유효하며 다른 세션에서는 사용할 수 없습니다.

CTE (Common Table Expression) 선언 구문 :-

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')

두 개의 테이블 employee와 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를 정의하기 위해 "with"절을 쓴 다음 테이블 표현식에 이름을 지정합니다. 여기서는 "CTE_Example"이라는 이름을 지정했습니다.

그런 다음 "As"를 작성하고 코드를 두 개의 괄호 (---)로 묶으면 동봉 된 괄호로 여러 테이블을 조인 할 수 있습니다.

마지막 줄에서 "Select * from CTE_Example"을 사용했고, 마지막 코드 줄에서 공통 테이블 표현식을 참조하고 있습니다. 배치 및 CTE는 데이터베이스에 영구 객체로 저장되지 않습니다. 그러나 그것은보기처럼 행동합니다. CTE에서 delete 및 update 문을 수행 할 수 있으며 CTE에서 사용중인 참조 테이블에 직접적인 영향을 미칩니다. 이 사실을 이해하기 위해 예를 들어 봅시다.

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

위의 명령문에서 CTE_Example에서 행을 삭제하고 CTE에서 사용중인 참조 테이블 "DEPT"에서 데이터를 삭제합니다.


나는 여전히 요점을 모른다. 이것과 정확히 같은 조건의 DEPT에서 삭제하는 것의 차이점은 무엇입니까? 아무것도 쉽게 만들지 않는 것 같습니다.
Holger Jakobs

내가 틀렸다면 저를 정정하십시오. 그러나 실행 계획이 다를 수 있으며, Neeraj의 요점이라고 생각합니다. 동일한 목표를 달성하는 많은 방법이 있지만 상황에 따라 다른 것보다 장점이 있습니다. 예를 들어, 어떤 상황에서는 DELETE FROM 문을 통해 CTE를 읽는 것이 더 쉬울 수 있으며, 다른 상황에서는 그 반대 일 수도 있습니다. 성능이 향상되거나 악화 될 수 있습니다. 등
WonderWorker

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을 사용하여 업데이트하고 주문하는 방법


0

아직 지적하지 않은 한 가지는 속도 입니다. 나는 이것이 오래된 답변이라는 것을 알고 있지만 이것은 직접적인 의견 / 답변이 필요하다고 생각합니다.

파생 테이블로 동일하게 수행 할 수 있으므로 중복되는 것처럼 보입니다.

CTE를 처음 사용했을 때는 속도에 크게 놀랐습니다. 그것은 CTE에 매우 적합한 교과서와 같은 경우이지만 CTE를 사용한 모든 사건에서 상당한 속도 향상이있었습니다. 첫 번째 쿼리는 파생 테이블이 복잡하여 실행하는 데 시간이 오래 걸렸습니다. CTE를 사용하면 몇 초가 걸렸으며 충격을 받았습니다.


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

이 시도

당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.