sql >> データベース >  >> RDS >> Sqlserver

SQLServerのCTEのガイド

    共通テーブル式 別名CTE SQL Serverでは、T-SQLで一時的な結果セットを提供します。 SQL Select、SQL Insert、SQL Delete、またはSQLUpdateステートメント内で参照できます。

    このオプションはSQLServer2005以降で利用可能であり、開発者が多くのJOIN、集計、およびデータフィルタリングを含む複雑で長いクエリを作成するのに役立ちます。通常、開発者はサブクエリを使用してT-SQLコードを記述し、SQLServerはクエリの実行が終了するまでこれらのCTEを一時的にメモリに保存します。クエリが完了すると、メモリから削除されます。

    SQL ServerのCTE:構文

    WITH <common_table_expression> ([column names])
    AS
    (
       <query_definition>
    )
    <operation>
    
    • CTE名を使用して、Select、Insert、Update、Delete、またはMergeステートメントを実行するためにCTE名を参照します。
    • 列名はコンマで区切られます。クエリ定義で定義された列と一致する必要があります。
    • クエリ定義には、単一のテーブルからのselectステートメント、または複数のテーブル間の結合が含まれます。
    • 結果を取得するには、CTE式の名前を参照できます。

    たとえば、次の基本的なCTEクエリは、次の部分を使用します。

    • 共通テーブル式名– SalesCustomerData
    • 列リスト– [CustomerID]、[FirstName]、[LastName]、[CompanyName]、[EmailAddress]、[Phone]
    • クエリ定義には、[SalesLT]。[Customer]テーブルからデータを取得するselectステートメントが含まれています
    • 最後の部分では、CTE式でselectステートメントを使用し、where句を使用してレコードをフィルタリングします。
    WITH SalesCustomerdata ([CustomerID],[FirstName],[LastName],[CompanyName],[EmailAddress],[Phone])
    AS(
    SELECT [CustomerID]
          ,[FirstName]
          ,[LastName]
          ,[CompanyName]
          ,[EmailAddress]
          ,[Phone]
       FROM [SalesLT].[Customer] 
    )
    SELECT * FROM SalesCustomerdata where Firstname like 'Raj%' 
    ORDER BY CustomerID desc
    

    別の例では、CTEから平均総売上高を計算します。クエリ定義には、GROUPBY句が含まれています。後で、平均値を計算するためにAVG()関数を使用します。

    WITH Salesdata ([SalesOrderID],[Total])
    AS(
    SELECT [SalesOrderID]
             ,count(*) AS total
              FROM [SalesLT].[SalesOrderHeader]
            GROUP BY [SalesOrderID]
    )
    SELECT avg(total) FROM salesdata
    

    CTEを使用して、SQLテーブルにデータを挿入することもできます。 CTEクエリ定義には、結合を使用して既存のテーブルからフェッチできる必要なデータが含まれています。後で、CTEにクエリを実行して、ターゲットテーブルにデータを挿入します。

    ここでは、SELECT INTOステートメントを使用して、CTEselectステートメントの出力から[CTETest]という名前の新しいテーブルを作成します。

    WITH CTEDataInsert
    AS 
    (
    SELECT
        p.[ProductID]
        ,p.[Name]
        ,pm.[Name] AS [ProductModel]
        ,pmx.[Culture]
        ,pd.[Description]
    FROM [SalesLT].[Product] p
        INNER JOIN [SalesLT].[ProductModel] pm
        ON p.[ProductModelID] = pm.[ProductModelID]
        INNER JOIN [SalesLT].[ProductModelProductDescription] pmx
        ON pm.[ProductModelID] = pmx.[ProductModelID]
        INNER JOIN [SalesLT].[ProductDescription] pd
        ON pmx.[ProductDescriptionID] = pd.[ProductDescriptionID]
    )
    SELECT * INTO CTETest FROM CTEDataInsert
    GO
    

    列を挿入されたデータと一致させる既存のテーブルを指定することもできます。

    WITH CTEDataInsert
    AS 
    (
    SELECT
        p.[ProductID]
        ,p.[Name]
        ,pm.[Name] AS [ProductModel]
        ,pmx.[Culture]
        ,pd.[Description]
    FROM [SalesLT].[Product] p
        INNER JOIN [SalesLT].[ProductModel] pm
        ON p.[ProductModelID] = pm.[ProductModelID]
        INNER JOIN [SalesLT].[ProductModelProductDescription] pmx
        ON pm.[ProductModelID] = pmx.[ProductModelID]
        INNER JOIN [SalesLT].[ProductDescription] pd
        ON pmx.[ProductDescriptionID] = pd.[ProductDescriptionID]
    )
    INSERT into CTETest select * FROM CTEDataInsert
    GO
    

    共通テーブル式を使用して、SQLテーブルのレコードを更新または削除することもできます。次のクエリは、CTEでDELETEステートメントとUPDATEステートメントを使用します。

    CTEのステートメントを更新

    WITH Salesdata ([SalesOrderID],[Freight])
    AS(
    SELECT [SalesOrderID]
             ,[Freight]
              FROM [SalesLT].[SalesOrderHeader]
            )
    UPDATE SalesData SET [Freight]=100.00 WHERE [SalesOrderID]=71774
    Go
    

    CTEのステートメントを削除

    WITH Salesdata ([SalesOrderID],[Freight])
    AS(
    SELECT [SalesOrderID]
             ,[Freight]
              FROM [SalesLT].[SalesOrderHeader]
            )
    delete SalesData  WHERE [SalesOrderID]=71774
    GO
    SELECT * FROM [SalesLT].[SalesOrderHeader] WHERE SalesOrderID=71774
    

    複数のCTE

    T-SQLスクリプトで複数のCTEを宣言し、それらに対して結合操作を使用できます。複数のCTEに対して、T-SQLは区切り文字としてコンマを使用します。

    次のクエリには、2つのCTEがあります。

    1. CTESales
    2. CTESalesDescription

    後で、selectステートメントで、両方のCTEでINNERJOINを使用して結果を取得します。

    WITH CTESales
    AS 
    (
    SELECT
         p.[ProductID]
        ,p.[Name]
        ,pm.[Name] AS [ProductModel]
        ,pmx.[Culture]
        ,pmx.[ProductDescriptionID]
       FROM [SalesLT].[Product] p
        INNER JOIN [SalesLT].[ProductModel] pm
        ON p.[ProductModelID] = pm.[ProductModelID]
        INNER JOIN [SalesLT].[ProductModelProductDescription] pmx
        ON pm.[ProductModelID] = pmx.[ProductModelID]
        INNER JOIN [SalesLT].[ProductDescription] pd
        ON pmx.[ProductDescriptionID] = pd.[ProductDescriptionID]
    ),CTESalesDescription
    
    AS (
    
    SELECT  description AS describe,[ProductDescriptionID]
    from [SalesLT].[ProductDescription]  
    )
    
    SELECT  productid, [Name],[ProductModel],describe
    FROM CTESales 
    INNER JOIN CTESalesDescription 
        ON 
    CTESales.[ProductDescriptionID] = CTESalesDescription.[ProductDescriptionID]
    

    再帰的な共通テーブル式

    再帰CTEは、条件が満たされるまで、繰り返される手続き型ループで実行されます。次のT-SQLの例では、IDカウンターを使用し、WHERE条件が満たされるまでレコードを選択します。

    Declare @ID int =1;
    ;with RecursiveCTE as  
       (  
          SELECT @ID as ID
            UNION ALL  
          SELECT  ID+ 1
      FROM  RecursiveCTE  
      WHERE ID <5
        )  
     
    SELECT * FROM RecursiveCTE
    

    SQL Serverでの再帰CTEのもう1つの使用法は、階層データを表示することです。 従業員がいると仮定します テーブルであり、すべての従業員、その部門、およびそのマネージャーのIDのレコードがあります。

    --Script Reference: Microsoft Docs
    
    CREATE TABLE dbo.MyEmployees  
    (  
    EmployeeID SMALLINT NOT NULL,  
    FirstName NVARCHAR(30)  NOT NULL,  
    LastName  NVARCHAR(40) NOT NULL,  
    Title NVARCHAR(50) NOT NULL,  
    DeptID SMALLINT NOT NULL,  
    ManagerID INT NULL,  
     CONSTRAINT PK_EmployeeID PRIMARY KEY CLUSTERED (EmployeeID ASC)   
    );  
    INSERT INTO dbo.MyEmployees VALUES   
     (1, N'Ken', N'Sánchez', N'Chief Executive Officer',16,NULL)  
    ,(273, N'Brian', N'Welcker', N'Vice President of Sales',3,1)  
    ,(274, N'Stephen', N'Jiang', N'North American Sales Manager',3,273)  
    ,(275, N'Michael', N'Blythe', N'Sales Representative',3,274)  
    ,(276, N'Linda', N'Mitchell', N'Sales Representative',3,274)  
    ,(285, N'Syed', N'Abbas', N'Pacific Sales Manager',3,273)  
    ,(286, N'Lynn', N'Tsoflias', N'Sales Representative',3,285)  
    ,(16,  N'David',N'Bradley', N'Marketing Manager', 4, 273)  
    ,(23,  N'Mary', N'Gibson', N'Marketing Specialist', 4, 16);
    

    次に、従業員階層データを生成する必要があります。 selectステートメントでUNIONALLを使用して再帰CTEを使用できます。

    WITH DirectReports(Name, Title, EmployeeID, EmployeeLevel, Sort)  
    AS (SELECT CONVERT(VARCHAR(255), e.FirstName + ' ' + e.LastName),  
            e.Title,  
            e.EmployeeID,  
            1,  
            CONVERT(VARCHAR(255), e.FirstName + ' ' + e.LastName)  
        FROM dbo.MyEmployees AS e  
        WHERE e.ManagerID IS NULL  
        UNION ALL  
        SELECT CONVERT(VARCHAR(255), REPLICATE ('|    ' , EmployeeLevel) +  
            e.FirstName + ' ' + e.LastName),  
            e.Title,  
            e.EmployeeID,  
            EmployeeLevel + 1,  
            CONVERT (VARCHAR(255), RTRIM(Sort) + '|    ' + FirstName + ' ' +   
                     LastName)  
        FROM dbo.MyEmployees AS e  
        JOIN DirectReports AS d ON e.ManagerID = d.EmployeeID  
        )  
    SELECT EmployeeID, Name, Title, EmployeeLevel  
    FROM DirectReports   
    ORDER BY Sort;
    

    CTEは、以下に示すように従業員レベルの詳細を返します。

    一般的なテーブル式に関する重要なポイント

    • CTEを再利用することはできません。そのスコープは、外部のSELECT、INSERT、UPDATE、またはMERGEステートメントに制限されています。
    • 複数のCTESを使用できます。ただし、UNION ALL、UNION、INTERSECT、またはEXCERPT演算子を使用する必要があります。
    • 非再帰CTEで複数のCTEクエリ定義を定義できます。
    • CTEクエリ定義でORDERBY(TOPなし)、INTO、OPTIONS句をクエリヒントとともに使用したり、FORBROWSEを使用したりすることはできません。

    たとえば、次のスクリプトでは、TOP句なしでORDERBY句を使用しています。

    WITH CTEDataInsert
    AS 
    (
    SELECT
        p.[ProductID]
        ,p.[Name]
        ,pm.[Name] AS [ProductModel]
        ,pmx.[Culture]
        ,pd.[Description]
    FROM [SalesLT].[Product] p
        INNER JOIN [SalesLT].[ProductModel] pm
        ON p.[ProductModelID] = pm.[ProductModelID]
        INNER JOIN [SalesLT].[ProductModelProductDescription] pmx
        ON pm.[ProductModelID] = pmx.[ProductModelID]
        INNER JOIN [SalesLT].[ProductDescription] pd
        ON pmx.[ProductDescriptionID] = pd.[ProductDescriptionID]
        ORDER BY productid
    )
    select * FROM CTEDataInsert 
    GO
    

    次のエラーが発生します:

    • CTEにインデックスを作成することはできません。
    • CTEで定義された列名は、selectステートメントで返された列と一致する必要があります。

    selectステートメントがその値を返す間、CTEには以下のコードの[Phone]列がありません。したがって、強調表示されたエラーメッセージが表示されます。

    • T-SQLスクリプトに複数のステートメントがある場合、CTEの前の前のステートメントはセミコロンを使用して終了する必要があります。

    たとえば、最初のselectステートメントにはセミコロンが含まれていません。したがって、不正な構文エラーが発生します CTEスクリプトで。

    セミコロン演算子を使用して最初のselectステートメントを終了すると、スクリプトは正常に機能します。

    • 列名を外部で宣言しない場合、selectステートメントで重複する列を使用することはできません。

    たとえば、次のCTE定義は、重複する列[Phone]を指定します。エラーを返します。

    ただし、外部列を定義すると、エラーは発生しません。さまざまな計算のために、出力に1つの列が複数回必要な場合に必要です。

    重要:共通テーブル式(CTE)は、一時テーブルまたはテーブル変数の代わりにはなりません。

    • TempテーブルはTempDBで作成され、通常のテーブルと同様にインデックス制約を定義できます。セッションで一時テーブルを複数回参照することはできません
    • テーブル変数もTempDBに存在し、バッチ実行中に存在する変数のように機能します。テーブル変数にインデックスを定義することはできません。
    • CTEは単一の参照目的であり、インデックスを定義することはできません。これはメモリに存在し、参照が行われた後にドロップされます。

    結論

    Common Table Expressions(CTE)を使用すると、開発者はクリーンで効果的なコードを記述できます。一般に、一時テーブルのように複数の参照を必要としないCTEを使用できます。ここでは、SELECT、INSERT、UPDATE、DETELTEステートメント、および再帰CTEのさまざまなシナリオを検討しました。

    SQL Complete SSMSアドインなどの最新のツールを使用すると、CTEの処理がさらに簡単になります。アドインはその場でCTEを提案できるため、CTEに関連するタスクがはるかに簡単になります。また、CTEの詳細については、Microsoftのドキュメントを参照してください。


    1. MySQLで関数ベースのインデックスを作成することは可能ですか?

    2. プロアクティブなSQLServerヘルスチェック、パート2:メンテナンス

    3. SQL CREATE TABLE構文–DBMSによってリストされます

    4. SQLのネストされたウィンドウ関数