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

JOINでSQLUPDATEステートメントを使用するための5つの手間のかからないヒント

    "おっと!私の悪い。」 SQL UPDATEが失敗した後、これを何回言いましたか?注意しないと、テーブルの更新がDELETEステートメントの形式で深刻な結果をもたらす可能性があります。 JOINでUPDATEを使用して複雑にすると、さらに悪化する可能性があります。そのため、[実行]をクリックするか、Ctrlキーを押しながらEキーを押す前に、よく考えておく必要があります。

    したがって、今日は、手間をかけずにJOINを使用してSQL UPDATEをコーディングする方法を学び、「おっと!私の悪い」再び。

    しかし、練習する前に、構文から始めます。また、初心者はJOINを使用したSQLServerUPDATEについて安心できます。次に、いくつかのデータといくつかの例を準備します。そして最後に、安全上のヒントを調べてください。

    [sendpulse-form id =” 12968”]

    SQL UPDATEJOIN構文

    UPDATE table1
    SET column1 = <expression1 | value1> [, column2 = <expression2 | value2>, <columnN = expression3 | value3>]
    FROM table1
    [INNER | OUTER LEFT | OUTER RIGHT] JOIN table2 on table1.keycolumn = table2.keycolumn
    [WHERE condition]
    

    これからいくつかのポイントを箇条書きにする必要があります。

    1. 少なくとも1列または数列のテーブルを一度に1つずつ更新できます。
    2. JOINを追加するには、FROM句が必要です。 FROM句のオブジェクトは、更新されるオブジェクトと同じである場合と同じでない場合があります。
    3. INNERまたはOUTERJOINのいずれかを使用できます(後の例を参照)。
    4. WHERE句を使用して更新できるのはデータのサブセットのみです。

    例を示す前に、データを準備しましょう。

    テストデータ

    映画を愛するために、ユーザー評価付きの映画タイトルのデータベースを作成しましょう。

    CREATE DATABASE [Movies]
    GO
    
    USE [Movies]
    GO
    
    CREATE TABLE [dbo].[Titles](
    	[TitleID] [int] IDENTITY(1,1) NOT NULL,
    	[Title] [varchar](50) NOT NULL,
    	[ReleaseDate] [date] NOT NULL,
    	[OverallUserRating] [varchar](10) NULL,
     CONSTRAINT [PK_Titles] PRIMARY KEY CLUSTERED 
    (
    	[TitleID] ASC
    ))
    GO
    
    CREATE TABLE [dbo].[UserRatings](
    	[UserRatingID] [int] IDENTITY(1,1) NOT NULL,
    	[TitleID] [int] NOT NULL,
    	[User] [varchar](50) NOT NULL,
    	[Rating] [tinyint] NOT NULL,
     CONSTRAINT [PK_UserRatings] PRIMARY KEY CLUSTERED 
    (
    	[UserRatingID] ASC
    ))
    GO
    
    ALTER TABLE [dbo].[UserRatings]  WITH CHECK ADD  CONSTRAINT [FK_UserRatings_Titles] FOREIGN KEY([TitleID])
    REFERENCES [dbo].[Titles] ([TitleID])
    GO
    
    ALTER TABLE [dbo].[UserRatings] CHECK CONSTRAINT [FK_UserRatings_Titles]
    GO
    
    ALTER TABLE [dbo].[UserRatings]  WITH CHECK ADD  CONSTRAINT [CK_UserRatings_Rating] CHECK  (([Rating]>=(1) AND [Rating]<=(5)))
    GO
    
    ALTER TABLE [dbo].[UserRatings] CHECK CONSTRAINT [CK_UserRatings_Rating]
    GO
    

    データベースとテーブルができたので、いくつかのデータを挿入しましょう:

    INSERT INTO Titles
    (Title, ReleaseDate)
    VALUES 
    ('The Avengers', '05/04/2012'),
    ('Avengers: Age of Ultron','5/1/2015'),
    ('Avengers: Infinity War','4/27/2018'),
    ('Avengers: Endgame','4/26/2019'),
    ('Captain America: Civil War','5/6/2016')
    GO
    
    INSERT INTO UserRatings(TitleID, [User], Rating) 
    VALUES 
    (1,'Natasha',5),
    (1,'Bruce',3),
    (1,'Tony',4),
    (1,'Bucky',5),
    (2,'Steve',4),
    (2,'Wanda',3),
    (2,'Pietro',2),
    (2,'Clint',5),
    (3,'Hope',5),
    (3,'Sam',5),
    (3,'Nick',3),
    (3,'James',5),
    (4,'Scott',5),
    (4,'Wong',5),
    (4,'Peter',5),
    (4,'Carol',4),
    (4,'Shuri',5)
    GO
    

    SQL Server UPDATE with 参加

    OverallUserRatingを更新するという同じ目標を持つさまざまな例を検討します。 タイトル テーブル。評価は1から5まで可能です。OverallUserRating 映画タイトルのすべての評価の平均です。

    テーブルの初期状態は次のとおりです。

    UPDATELEFTJOINの例

    -- SQL UPDATE with LEFT OUTER JOIN
    SELECT
     a.TitleID
    ,CAST(CAST(AVG(CAST(b.Rating AS DECIMAL(3,2))) AS DECIMAL(3,2)) AS varchar(9)) AS AverageRating
    INTO #ComputedRatings
    FROM titles a
    INNER JOIN UserRatings b ON a.TitleID = b.TitleID
    GROUP BY a.TitleID
    
    -- mark 'No Rating' if there are no existing ratings
    UPDATE Titles
    SET OverallUserRating = ISNULL(b.AverageRating,'No Rating') 
    FROM Titles a
    LEFT JOIN #ComputedRatings b ON a.TitleID = b.TitleID
    

    最初のSELECTステートメントは、 UserRatingsに基づいて映画タイトルごとの平均評価を計算します。 テーブル。結果は、# ComputedRatingsという一時テーブルにダンプされます。 。 INNER JOINを使用しているため、評価のない映画のタイトルは破棄されます。

    UPDATEステートメントで、タイトル テーブルは、# ComputedRatingsのLEFTJOINを使用して更新されました 一時テーブル。平均評価がnullの場合 、値は評価なしになります 。サンプルでは、​​キャプテンアメリカ:シビルウォー まだユーザー評価はありません–図2を参照してください。

    SQL UPDATEINNERJOINの例

    方法は次のとおりです。

    -- SQL UPDATE with INNER JOIN
    SELECT
     a.TitleID
    ,ISNULL(CAST(CAST(AVG(CAST(b.Rating AS DECIMAL(3,2))) AS DECIMAL(3,2)) AS varchar(9)),'No Rating')  AS AverageRating
    INTO #ComputedRatings
    FROM titles a
    LEFT JOIN UserRatings b ON a.TitleID = b.TitleID
    GROUP BY a.TitleID
    
    
    UPDATE Titles
    SET OverallUserRating = b.AverageRating
    FROM Titles a
    INNER JOIN #ComputedRatings b ON a.TitleID = b.TitleID
    

    上記のコードを実行すると、結果は図2と同じになります。しかし、2つのコードの違いは何ですか?

    • 最初のSELECTステートメントは、前のLEFT JOINの例とは異なり、NULLユーザー評価を考慮します。ユーザー評価のない映画のタイトルは破棄されません。したがって、今回は評価なし キャプテンアメリカ:シビルウォーの価値 すでに検討されています。
    • AverageRatingを直接割り当てた内部結合 すべてのTitleIDなので、値の方が適切です。 会計処理されます。

    現在、一時テーブルの代わりに、共通テーブル式(CTE)も使用できます。変更されたコードは次のとおりです:

    -- SQL UPDATE with JOIN using INNER JOIN from a CTE
    ;WITH ComputedRatings AS
    (SELECT
     a.TitleID
    ,ISNULL(CAST(CAST(AVG(CAST(b.Rating AS DECIMAL(3,2))) AS DECIMAL(3,2)) AS varchar(9)),'No Rating')  AS AverageRating
    FROM Titles a
    LEFT JOIN UserRatings b ON a.TitleID = b.TitleID
    GROUP BY a.TitleID)
    UPDATE Titles
    SET OverallUserRating = b.AverageRating
    FROM Titles t
    INNER JOIN ComputedRatings cr ON t.TitleID = cr.TitleID
    

    詳細情報

    • SQL JOINの究極のガイド:INNER JOIN –パート1
    • SQL JOINの究極のガイド:OUTER JOIN –パート2

    更新の使用 コマンド 参加あり 安全に(5つのヒント)

    安全とは、意図したレコードを更新することです。また、更新する予定のないレコードには触れないことです。 ここでは、5つのシナリオを扱います。

    SELECTステートメントで最初にレコードを表示する

    このヒントは、いくつかのレコードに最適です。したがって、更新するレコードに影響を与える前に、次のことを試してください。

    それは簡単です。見た目が良ければ、UPDATE句とSET句のコメントを外します。 SELECT句をコメントとしてマークします。その後、あなたは行ってもいいです。このようにして、プロアクティブであるため、ダメージコントロールを最小限に抑えます。

    一時テーブルを使用して試運転を行う

    エラーが発生する可能性があるかどうかわからない場合は、次に、更新するテーブルレコードを一時テーブルにダンプしてみます。その後、そこから試運転を行います。

    ランタイムエラーはありませんか?結果は良さそうですか?次に、一時テーブルを元のテーブルに置き換えます。このようにして、途中でランタイムエラーが発生しないことがわかります。

    また、一時テーブルは元のテーブルのコピーを保存する方法の1つであることに注意してください。メモリ最適化テーブルまたはデータベースバックアップを使用することもできます。データベースのバックアップを使用すると、更新する必要のあるレコードをより自由に操作できます。ただし、これの欠点はストレージスペースです。

    詳細情報

    • CREATE TABLE(Transact-SQL)–一時テーブル

    OUTPUT句をUPDATEに追加してみてください

    更新を実行する前に時間を取り戻したいですか?懐疑的な方は、OUTPUT句を使用して、過去と現在を表示できます。以下の例では、テーブル変数は、更新後に以前の値と現在の値をダンプするのに役立ちます。次に、テーブル変数を選択して結果を確認できます。

    DECLARE @tvTitles AS TABLE(TitleID INT NOT NULL,
                               OldOverallRatings VARCHAR(10) NULL,
    			    NewOverAllRatings varchar(10) NOT NULL)
    
    ;WITH ComputedRatings AS
    (
    	SELECT
    	a.TitleID
    	,ISNULL(CAST(CAST(AVG(CAST(b.Rating AS DECIMAL(3,2))) AS DECIMAL(3,2)) AS varchar(9)),'No Rating')  AS AverageRating
    	FROM titles a
    	LEFT JOIN UserRatings b ON a.TitleID = b.TitleID
    	GROUP BY a.TitleID
    )
    UPDATE #tmpTitles
    SET OverallUserRating = cr.AverageRating
    OUTPUT INSERTED.TitleID, DELETED.OverallUserRating, INSERTED.OverallUserRating
    INTO @tvTitles
    FROM #tmpTitles t
    INNER JOIN ComputedRatings cr ON t.TitleID = cr.TitleID
    

    このコードについて注意すべき点がいくつかあります:

    • テーブル変数は、前の値と現在の値のコンテナとして機能します。
    • CTEによる通常の更新は正常です。安全にプレイするために、引き続き一時テーブルを使用しています。
    • OUTPUT句は、前の値と現在の値をテーブル変数にダンプするために適用されます。 INSERTEDには新しい値が含まれ、DELETEDには古い値が含まれます。

    テーブル変数からSELECTを発行する場合、予想される内容は次のとおりです。

    結果は図3のようになりますが、将来を見据えています。これは過去を調べます。異なる場合は、その間に問題が発生しています。

    幸いなことに、テーブル変数の古い値を使用して、以前の状態に復元できます。しかし、それが同じであれば、もう一度、あなたは行ってもいいです。 OUTPUT句をコメントとしてマークするか削除してから、一時テーブルを元のテーブルに置き換えることができます。

    詳細情報

    • OUTPUT句(Transact-SQL)

    TRY…CATCHを使用して将来のエラーを処理する

    前の3つのヒントは、コードを作成してテストするときに役立ちます。しかし、すべてを予測できるわけではないことは誰もが知っています。したがって、コードにセーフティネットを追加する必要があります。

    セーフティネットと言えば、T-SQLにはC#やC++などのTRY…CATCHエラー処理ブロックがあります。

    前の例から変更されたコードを見てみましょう:

    BEGIN TRY						  
      ;WITH ComputedRatings AS
      (
        SELECT
         a.TitleID
        ,ISNULL(CAST(CAST(AVG(CAST(b.Rating AS DECIMAL(3,2))) AS DECIMAL(3,2)) AS varchar(20)),
                'No User Ratings Yet') AS AverageRating
        FROM titles a
        LEFT JOIN UserRatings b ON a.TitleID = b.TitleID
        GROUP BY a.TitleID
      )
      UPDATE Titles
      SET OverallUserRating = cr.AverageRating
      FROM Titles t
      INNER JOIN ComputedRatings cr ON t.TitleID = cr.TitleID
    
    END TRY
    BEGIN CATCH
     SELECT  
      ERROR_NUMBER() AS ErrorNumber  
     ,ERROR_SEVERITY() AS ErrorSeverity  
     ,ERROR_STATE() AS ErrorState  
     ,ERROR_PROCEDURE() AS ErrorProcedure  
     ,ERROR_LINE() AS ErrorLine  
     ,ERROR_MESSAGE() AS ErrorMessage;  
    
    END CATCH
    

    上記のコードを変更して、文字列の切り捨てエラーを強制しました。 OverallUserRating タイトルの列 テーブルは10文字までしか収容できません。 CTEでは20文字に変更しました。それは収まりません。 CATCHブロックは、エラー発生の瞬間を引き継ぎ、エラー情報を提供します。

    結果は次のとおりです。

    エラーをトリガーしました。実行時に予期しないエラーをキャッチする必要がある場合、これはそれを処理する1つの方法です。

    詳細情報

    • TRY…CATCH(Transact-SQL)

    トランザクション処理を使用する

    最後に、トランザクション。これにより、JOINを使用したUPDATEやその他のDMLステートメントを含め、エラーが発生する前のすべてを以前の状態に確実に復元できます。これは、上記のヒント4への良い追加です。

    トランザクションを含めるためにコードをもう一度変更しましょう:

    BEGIN TRANSACTION
    
    BEGIN TRY						  
      ;WITH ComputedRatings AS
      (
        SELECT
         a.TitleID
        ,ISNULL(CAST(CAST(AVG(CAST(b.Rating AS DECIMAL(3,2))) AS DECIMAL(3,2)) AS varchar(20)),
                'No User Ratings Yet') AS AverageRating
        FROM titles a
        LEFT JOIN UserRatings b ON a.TitleID = b.TitleID
        GROUP BY a.TitleID
      )
      UPDATE Titles
      SET OverallUserRating = cr.AverageRating
      FROM Titles t
      INNER JOIN ComputedRatings cr ON t.TitleID = cr.TitleID
    
      COMMIT TRANSACTION
    END TRY
    BEGIN CATCH
     SELECT  
      ERROR_NUMBER() AS ErrorNumber  
     ,ERROR_SEVERITY() AS ErrorSeverity  
     ,ERROR_STATE() AS ErrorState  
     ,ERROR_PROCEDURE() AS ErrorProcedure  
     ,ERROR_LINE() AS ErrorLine  
     ,ERROR_MESSAGE() AS ErrorMessage;  
    
     ROLLBACK TRANSACTION
    END CATCH
    

    トランザクションを除いて、前の例と同じです。したがって、文字列の切り捨てエラーが強制されます。 COMMIT TRANSACTIONを通過するのではなく、ROLLBACK TRANSACTIONを指定したCATCHブロックで、値を以前の状態に戻します。

    これは、更新、挿入、削除を安全に行うための方法です。

    :dbForge Studio for SQL Serverのクエリビルダー機能を使用して、ダイアグラム内の任意のクエリを視覚的に設計できます。

    詳細情報

    • T-SQLのベストプラクティス
    • プロのようにT-SQLクエリを作成する方法

    結論

    JOINを使用したSQLUPDATEの構文を見てきました。例と5つの手間のかからないヒントがあなたをさらに啓発しました。要件は例が示すものとは異なる場合がありますが、要点はわかりました。あなたはまだ間違いを犯すことができます。ただし、それらをほぼゼロに減らすことは可能です。

    これらのアイデアをあなたの状況に適用してみませんか?

    この投稿が役に立った場合は、お気に入りのソーシャルメディアプラットフォームで気軽に広めてください。また、すばらしいアイデアを追加したい場合は、コメントセクションにようこそ。


    1. すべてのパラメータを使用してNSISでpostgresをインストールするにはどうすればよいですか?

    2. PostgreSQLはテキストに部分文字列が出現する回数をカウントします

    3. unsigned int / long型をEntityFrameworkで使用するにはどうすればよいですか?

    4. 2つのフィールドでgroupbyを使用し、SQLでカウントする