web-dev-qa-db-ja.com

共通表式(CTE)を使用する場合

共通テーブル式について読み始めましたが、それらを使用する必要があるユースケースは考えられません。派生テーブルでも同じことができるので、それらは冗長に見えるでしょう。足りないものやよく理解できないものはありますか?誰かが私にCTEのケースを作るために通常の選択、派生または一時テーブルクエリでの制限の簡単な例を与えることができますか?どんな簡単な例も高く評価されるでしょう。

204
imak

一例として、同じデータセットを複数回参照/結合する必要がある場合は、CTEを定義することでそれを実行できます。したがって、それはコードの再利用の一形態になる可能性があります。

自己参照の例は再帰です。 CTEを使用した再帰クエリ

おもしろいマイクロソフトの定義について Books Onlineから引用:

CTEは以下の目的で使用できます。

  • 再帰クエリを作成します。詳細については、 共通のテーブル式を使用した再帰クエリを参照してください。

  • ビューを一般的に使用する必要がない場合は、ビューに置き換えてください。つまり、定義をメタデータに格納する必要はありません。

  • スカラー副選択から派生した列、または決定論的ではないか外部アクセスを持つ関数のいずれかによって、グループ化を有効にします。

  • 同じステートメントで結果表を複数回参照してください。

175
John Sansom

私はそれらを使用して、複雑なクエリ、特に複雑な結合とサブクエリを分割します。私は、クエリの意図に頭を悩ませるために、それらを「疑似ビュー」としてますます使用しています。

それらについての私の唯一の不満はそれらが再利用できないということです。たとえば、同じCTEを使用する可能性がある2つの更新ステートメントを含むストアドプロシージャがあるとします。しかし、CTEの「範囲」は最初のクエリだけです。

問題は、「単純な例」はおそらくCTEを本当に必要としないことです。

それでも、とても便利です。

43
n8wrl

Cteを使う理由は2つあります。

Where句で計算値を使用します。これは派生テーブルより私には少しきれいに見えます。

Questions.ID = Answers.Question_Id(およびクイズID)で結合された2つのテーブル - Questions and Answersがあるとします。

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

これは私が質疑応答のリストを得たいと思うもう一つの例です。 Answersを結果の中の質問と一緒にグループ化したいと思います。

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
38
BrianK

CTEを使用すると便利なシナリオの1つは、1つ以上の列に基づいてDISTINCT行のデータを取得し、テーブル内のすべての列を返す場合です。標準的なクエリでは、最初に個別の値を一時テーブルにダンプしてから元のテーブルに結合して残りの列を取得する必要がある場合や、結果が返される可能性がある非常に複雑なパーティションクエリを作成する場合1回実行しますが、ほとんどの場合、それは判読できず、パフォーマンスの問題が発生します。

しかしCTEを使うことによって(Tim Schmelterが で答えたように、レコードの最初のインスタンスを選択します

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

ご覧のとおり、これは読みやすく保守しやすいものです。そして他のクエリと比較して、パフォーマンスがはるかに優れています。

18
TheDanMan

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;

楽しい

12
Vic

今日は、SQL Server 2005で導入され、それ以降のバージョンでも利用可能になる新機能であるCommon table expressionについて学びます。

共通表式: - 共通表式は、一時的な結果セットとして定義することも、言い換えれば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')

EmployeeとDeptという2つのテーブルを作成し、各テーブルに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

文の各行を1行ずつ理解していきましょう。

CTEを定義するために "with"節を書き、それからテーブル式に名前を付けます。ここでは "CTE_Example"として名前を付けました。

それから "As"と書き、コードを2つの括弧(---)で囲みます。複数のテーブルを括弧で囲むことができます。

最後の行では、 "Select * from CTE_Example"を使用しています。最後のコード行では、Common table式を参照しています。そのため、ビューのように定義できます。バッチと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から行を削除していて、CTEで使用されている参照先テーブル "DEPT"からデータを削除します。

6

「順序付き更新」を実行したいときにとても便利です。

MS SQLではUPDATEでORDER BYを使用することはできませんが、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を使用して更新および注文する方法

3
bside