2016-11-16 5 views
3

存在しない場合は値を挿入し、更新された場合は値を挿入するために実行しているストアドプロシージャがあります。私は、データが、データは私が新しいテーブルに名前を挿入しようとしています。この存在する場合は行を更新するストアドプロシージャ

Name1  5  2016-11-16 09:46:40.490 
Name2  10  2016-11-16 09:48:35.240 
Name1  7  2016-11-16 09:35:24.350 
Name2  8  2016-11-15 02:27:44.670 

のように見えます

Table1 
Name (nvarchar) 
Data (int) 
Timestamp (datetime) 

から来ているテーブルを持って、日別の平均うち、整数、およびグループ。ここに第2のテーブルがあります。

Table2 
Name (nvarchar) 
Data (int) 
Timestamp (date) 

データはこのようになります。名前1は同じ日だったので平均化されました。 Name2は異なる日だったので、平均化されていませんでした。

Name1  6  2016-11-16 00:00:00.000 
Name2  10  2016-11-16 00:00:00.000 
Name2  8  2016-11-15 00:00:00.000 

タイムスタンプを日付に変換して、日にちにまとめる方が簡単になるようにしました。私のストアドプロシージャはこのようになります

IF NOT EXISTS (SELECT t.Name, t.Timestamp 
    FROM Table2 t 
    JOIN Table1 a 
    ON t.Name = a.Name AND t.Timestamp = CONVERT(date, a.Timestamp) 
    GROUP BY t.Name, t.Timestamp) 

INSERT INTO Table2 (Name, Timestamp, Data) 
SELECT 
    Name, 
    CAST(Timestamp AS DATE) as Date, 
    AVG(Data) as Average_Data 
FROM Table1 
GROUP BY CAST(Timestamp AS DATE), Name 

ELSE 
UPDATE Table1 
SET 
WHERE 

私は初めて実行するので、問題はありません。値は正しく追加され、グループ化されます。ただし、2回目の実行時には常に挿入されます。私は

SET Name = Name, Timestamp = Timestamp, Data = Date 
WHERE Name = Name, Timestamp = Timestamp 

それを削除する前に

私の更新ステートメントは、私は、これは権利ではありません知っているが、それは挿入が常に実行されるようにもアップデートを打つように見えることはありません。このような何かを見ていました。私が「存在しない場合」の選択を実行すると、データが表示され、存在するものとして表示されます。

私はIf Not Existsをクリーンアップし、動作中のUpdate文を作成するのに役立つと思います。

EDIT:

は、私が代わりに連続したUPDATEとINSERTのMERGE文を使用したいと

IF EXISTS(SELECT Name, Timestamp FROM Table2) 
UPDATE 
    Table2 
SET 
    Name = a.Name, 
    Timestamp = CONVERT(date, a.Timestamp), 
    Data = AVG(a.Data) 
FROM 
    Table2 t 
INNER JOIN 
    Table1 a 
ON t.Name = a.Name 
WHERE t.Name = a.Name AND t.Timestamp = CONVERT(date, a.Timestamp) 

ELSE 
    INSERT INTO Table2 (Name, Timestamp, Data) 
     SELECT 
      Name, 
      CAST(Timestamp AS DATE) as Date, 
      AVG(Data) as Average_Data 
     FROM Table1 
     GROUP BY CAST(Timestamp AS DATE), Name 
END 
+1

あなたのロジックは、あなたがここで必要なものではありません。最初に更新を行い、行が存在するかどうかを確認する必要はありません。更新されていない場合は更新されません。次に、存在しない場所を使用するようにinsertステートメントを変更したいとします。 –

+0

ありがとうございます。私はそのショットを与える – maltman

+0

私は最初に実行するように更新プログラムを変更し、それはまだ毎回値を挿入しているように見えます。タイムスタンプで問題になることはありますか?私はそれを日付に変換していますが、それが変換される前にそれを確認しているのですか? – maltman

答えて

1

私は個人的には、「アップサート」として知られているものに古いスタイルのテクニックを使用することを好みます。 MERGEはうまくいきますが、すべてを1回で処理するので、問題をデバッグするのに本当の苦痛です。

これは、より柔軟性が高く、問題のデバッグが容易な挿入と更新を分けるため、私が好むアプローチです。また、INSERTの左結合を、相関サブクエリでNOT EXISTSを使用するように切り替えることもできますが、ほとんどの場合、パフォーマンスの差はごくわずかです。

UPDATE t 
SET 
    Name = a.Name, 
    Timestamp = CONVERT(date, a.Timestamp), 
    Data = AVG(a.Data) 
FROM 
    Table2 t 
INNER JOIN 
    Table1 a 
ON t.Name = a.Name 
WHERE t.Name = a.Name AND t.Timestamp = CONVERT(date, a.Timestamp) 


INSERT INTO Table2 (Name, Timestamp, Data) 
SELECT 
    Name, 
    CAST(Timestamp AS DATE) as Date, 
    AVG(Data) as Average_Data 
FROM Table1 a 
left join Table2 t on a.Name = t.Name 
where t.Name is null 
GROUP BY Name, CAST(Timestamp AS DATE) 
    , Name 

- EDIT--

私は投稿時に私もあなたの更新で集計に気付きませんでした。これを簡単に回避することができます。

with cte as 
(
    select Name = a.Name 
     , Timestamp = CONVERT(date, a.Timestamp) 
     , AverageData = AVG(a.Data) 
    FROM 
     Table2 t 
    INNER JOIN 
     Table1 a 
    ON t.Name = a.Name 
    WHERE t.Name = a.Name AND t.Timestamp = CONVERT(date, a.Timestamp) 
    GROUP BY Name, CAST(Timestamp AS DATE) 
) 

update t 
set Name = c.Name 
    , Timestamp = c.Timestamp 
    , Date = c.AverageData 
from Table2 t 
join cte c on c.Name = t.Name 
    and c.Timestamp = t.Timestamp 
+0

IFを削除すると、UPDATEステートメントで集計を設定できない。 – maltman

+0

コードを更新しました。私はそこに集合していることに気付かなかった。 ;) –

+0

私はそれが好きです。両方の答えの組み合わせの種類。そして、それは完璧に動作します。しかし、ちょうど質問。なぜあなたは更新プログラムの共通表現を作成しなければならないのですか?私はGoogleにすることができますが、私は怠け者だと思います。ありがとう、ショーン。 – maltman

2

以下のコードを更新しました。 集計のため、以下の私の答えでは、MERGEでCTEを使用します。

-- CREATE AND INSERT TABLES 
DROP TABLE TABLE1 
CREATE TABLE 
TABLE1 
    ( Name nvarchar(5) 
    , Data int 
    , Timestamp date 
    ) 
INSERT INTO TABLE1 VALUES ('Name1',5 ,'2016-11-16') 
        ,  ('Name2',10 ,'2016-11-16') 
        ,  ('Name1',7 ,'2016-11-16') 
        ,  ('Name2',8 ,'2016-11-15') 
        ,  ('Name3',8 ,'2016-11-15') 
        ,  ('Name3',10 ,'2016-11-15') 
        ,  ('Name3',9 ,'2016-11-16') 
        ,  ('Name3',11 ,'2016-11-16') 


DROP TABLE TABLE2 
CREATE TABLE 
TABLE2 
    ( Name nvarchar(5) 
    , Data int 
    , Timestamp datetime 
    ) 
INSERT INTO TABLE2 VALUES ('Name1',0 ,'2016-11-16') 
          ,('Name2',0 ,'2016-11-16') 
          ,('Name2',0 ,'2016-11-15') 

    SELECT * FROM TABLE2 -- SHOW TABLE2 BEFORE MERGE 


BEGIN TRANSACTION 
-- HERE IS WHERE THE CODE THAT REPLACES YOUR QUERY ACTUALLY BEGINS 
    ;WITH CTE_AVG_DATA_TABLE1 
     AS (SELECT  Name 
      ,   Timestamp 
      ,   AVG(Data) AS [AVG(Data)] 
      FROM  TABLE1 
      GROUP BY Name 
      ,   Timestamp 
      ) 

     MERGE Table2    AS TARGET 
     USING CTE_AVG_DATA_TABLE1 AS SOURCE 
      ON  TARGET.Name = SOURCE.Name 
       AND TARGET.TIMESTAMP = SOURCE.TIMESTAMP 
     WHEN MATCHED THEN 
      UPDATE  
      SET  TARGET.Name   = SOURCE.Name 
      ,  TARGET.Timestamp = CONVERT(date, SOURCE.Timestamp) 
      ,  TARGET.Data   = SOURCE.[AVG(Data)] 
     WHEN NOT MATCHED THEN 
      INSERT (Name, Timestamp, Data) 
      VALUES (SOURCE.NAME, SOURCE.TIMESTAMP, SOURCE.[AVG(Data)]) 
     ; 

    SELECT * FROM TABLE2 -- SHOW TABLE2 AFTER MERGE 
ROLLBACK /*ALLOWS THIS TEST CODE TO BE RUN OVER AND OVER 
      ADDING ROWS TO INSERT STATEMENTS ABOVE TO SHOW THAT IT WORKS 
     */ 
+1

ありがとうございました。私はこれを撃つだろう。ちょうど不思議ですが、CTEを使用すると何が追加されますか? – maltman

+1

個人的なコーディングスタイルと読みやすさ - CTEは、平均を計算するのに必要な列のグループ化を可能にする単なる1つの方法です(または他の集計関数を使用する)。 インラインテーブル値関数またはサブクエリを使用でき、それぞれが同じ方法でコンパイラによって解釈され、存在する場合はソーステーブルの任意のインデックスにヒットします。 – WayTooSerious

+1

MERGEステートメントのパフォーマンスを最適化する https://technet.microsoft.com/en-us/library/cc879317(v=sql.105).aspx – WayTooSerious

関連する問題