76

申し訳ありませんが、これは一種の 2 つの部分からなる質問です。

私は SQL に非常に慣れておらず、勤務している小さなオフィス用のタイム レコーダー アプリケーションを開発しようとしています。現在、SQL バックエンドをいじっていて、複合ステートメントについて質問があります。

私が立ち往生しているのは、ユーザーが休憩のために出勤しようとしたが、シフトの開始時に出勤しなかった場合、SQL は既存の行を更新するのではなく、新しい行を作成する必要があることです。

これが私が試したものです:

    IF NOT EXISTS(SELECT * FROM Clock WHERE clockDate = '08/10/2012') AND userName = 'test')
    BEGIN
        INSERT INTO Clock(clockDate, userName, breakOut)
        VALUES({ fn NOW() }, 'test', { fn NOW() })
    END
    ELSE
    BEGIN
        UPDATE Clock
        SET breakOut = { fn NOW() }
        WHERE (clockDate = '08/10/2012') AND (userName = 'test')
    END

Visual Studio 2010 を使用して、ローカル マシン上の SQL Server Express 2008 に接続してこれを実行しています。「The Compound statement SQL construct or statement is not supported.」というエラーが表示されます。ただし、その後に 1 行が影響を受けているというメッセージが表示され、Clock テーブルを表示すると、予想どおりに表示されます。これを達成するための最良の方法は何ですか?

この質問の 2 番目の部分は、WHERE ステートメントにあります。今日の日付を入力するのではなく、clockDate 列に今日の日付を取得する関数はありますか? フロントエンドアプリケーションを構築するために先を考えようとしているだけです。

    IF NOT EXISTS(SELECT * FROM Clock WHERE clockDate = { fn CURRENT_DATE() }) AND userName = 'test')

繰り返しますが、これで必要な結果が得られますが、「'CURRENT_DATE' 付近の WHERE 句でエラーが発生しました。クエリ テキストを解析できません」というエラーが表示されるまでは結果が得られません。

私はこれを適切に説明したことを願っています、そしてあなたの助けに感謝します!!

編集:

@Rトーマス @w00te

では、clockDate を日付フィールドとして、breakOut を time(0) フィールドとして使用すると、うまくいくでしょうか? 「The Compound statement SQL construct or statement is not supported.」というメッセージがまだ表示されます。動作しているように見えますが、構文エラーです。

    IF NOT EXISTS (SELECT * FROM Clock WHERE (clockDate = GETDATE()) AND (userName = 'test'))
    BEGIN
        INSERT INTO Clock(clockDate, userName, breakOut)
        Values(GETDATE(), 'test', GETDATE())
    END
    ELSE
    BEGIN
        UPDATE Clock
        SET breakOut = GETDATE()
        WHERE (clockDate = GETDATE()) AND (userName = 'test')
    END

私のテーブルの結果は次のとおりです。

clockDate  userName  clockIn  breakOut  breakIn  clockOut

08/10/2012  test      NULL    11:24:38   NULL     NULL

これは私が望む結果ですが、このエラーは私を混乱させます。これは Visual Studio エラーですか、それとも SQL エラーですか? そして、Merge Statements を読み上げます。リンクをありがとう。

4

4 に答える 4

68

一見すると、元の試みはかなり近いように見えます。私は clockDate が DateTime フィールドであると仮定しているので、これを試してください:

IF (NOT EXISTS(SELECT * FROM Clock WHERE cast(clockDate as date) = '08/10/2012') 
    AND userName = 'test') 
BEGIN 
    INSERT INTO Clock(clockDate, userName, breakOut) 
    VALUES(GetDate(), 'test', GetDate()) 
END 
ELSE 
BEGIN 
    UPDATE Clock 
    SET breakOut = GetDate()
    WHERE Cast(clockDate AS Date) = '08/10/2012' AND userName = 'test'
END 

getdate は現在の日付を提供することに注意してください。日付 (時間なし) と比較しようとしている場合は、キャストする必要があります。そうしないと、時間要素によって比較が失敗します。


clockDate が datetime フィールドではない (単なる日付) 場合は、SQL エンジンが自動的に実行します。set/insert ステートメントでキャストする必要はありません。

IF (NOT EXISTS(SELECT * FROM Clock WHERE clockDate = '08/10/2012') 
    AND userName = 'test') 
BEGIN 
    INSERT INTO Clock(clockDate, userName, breakOut) 
    VALUES(GetDate(), 'test', GetDate()) 
END 
ELSE 
BEGIN 
    UPDATE Clock 
    SET breakOut = GetDate()
    WHERE clockDate = '08/10/2012' AND userName = 'test'
END 

他の人が指摘したように、merge ステートメントは、この同じロジックに取り組む別の方法です。ただし、場合によっては、特に大規模なデータ セットでは、merge ステートメントが非常に遅くなり、大量のトランザクション ログ アクティビティが発生することがあります。したがって、上記のように論理的に理解する方法を知ることは、依然として有効な手法です。

于 2012-08-10T17:36:28.663 に答える
37

他の人が MERGE ステートメントを調べる必要があることを示唆していますが、それを使用したソリューションを提供した人は誰もいなかったので、この特定の TSQL コンストラクトに独自の回答を追加しています。きっと気に入っていただけるはずです。

重要な注意点

あなたのコードには、ifステートメントのnot exists(select...)一部にタイプミスがあります。内部selectステートメントには条件が 1 つしかありませんが、中かっこの補完が無効なためwhere、UserName 条件は除外されます。not existsいずれにせよ、閉じ括弧が多すぎます。

これは、コードの後半のステートメントで 2 つのwhere条件を使用しているという事実に基づいていると思います。update

私の答えを続けましょう...

SQL Server 2008+ のサポート MERGE ステートメント

MERGE ステートメントは、 「挿入または更新」の状況に非常に適した美しい TSQL gemです。あなたの場合、次のコードのようになります。ストアド プロシージャのパラメーターである可能性が高い変数を宣言していることを考慮してください (私は疑っています)。

declare @clockDate date = '08/10/2012';
declare @userName = 'test';

merge Clock as target
using (select @clockDate, @userName) as source (ClockDate, UserName)
on (target.ClockDate = source.ClockDate and target.UserName = source.UserName)
when matched then
    update
    set BreakOut = getdate()
when not matched then
    insert (ClockDate, UserName, BreakOut)
    values (getdate(), source.UserName, getdate());
于 2014-02-28T10:39:31.943 に答える
6
IF NOT EXISTS(SELECT * FROM Clock
WHERE clockDate = '08/10/2012') AND userName = 'test')

余分な括弧があります。削除しても問題ないと思います:

IF NOT EXISTS(SELECT * FROM Clock WHERE
clockDate = '08/10/2012' AND userName = 'test')

また、GETDATE() は現在の日付を列に入れますが、時間が必要ない場合は少し遊ぶ必要があります。CONVERT(varchar(8), GETDATE(), 112) は日付(時間ではない)部分だけを与えると思います。

IF NOT EXISTS(SELECT * FROM Clock WHERE
clockDate = CONVERT(varchar(8), GETDATE(), 112)
AND userName = 'test')

おそらくやるべきです。

PS:マージステートメントを使用してください:)

于 2012-08-10T17:31:43.167 に答える
2

として置き換える必要がありますWHERE clockDate = { fn CURRENT_DATE() } AND userName = 'test'")"から余分なものを取り除いてください { fn CURRENT_DATE() })

于 2012-08-10T17:35:54.943 に答える