質問をすることでしか得られない、回答やアドバイスがある。

15分調べてもわからないことは、質問しよう!

ただいまの
回答率

88.06%

オラクル SQL 予約日重複チェックについて

解決済

回答 3

投稿 編集

  • 評価
  • クリップ 0
  • VIEW 2,799

score 35

現在ある予約管理システムを作成しております(MVCモデル)

選択された予約from年月日〜予約to年月日を、
バインド変数を使用し、DBに登録されているデータと重複していないかチェックしたいと思っております。

SQLのtableは

CREATE TABLE RESERVE\(
STARTDAY  DATE,
ENDDAY  DATE,
ROOMCODE  CHAR\(2\),
PEOPLE  NUMBER\(2\),
BASS  CHAR\(1\),
REPRESENTATIVE  VARCHAR\(20\),
ID  CHAR\(9\),
PRIMARY KEY\(ID\),
\);

上記のように作成しております。

こちらを元に、jspファイルから入力された日付の重複チェックをdaoクラスで行います。
重複していたらCOUNTが1以上を返すようにしたいのですが、
重複していても0を返してしまいます。

作成したSQL文は下記です

String SELECT_COUNT_QUERY = "

SELECT COUNT\(\*\) AS COUNT FROM RESERVE
WHERE ROOMCODE=\?\(ID\)
AND \(\(STARTDAY <= \?\(from年月日\) 
AND \?\(from年月日\) < ENDDAY\)
AND \(STARTDAY < \?\(to年月日\) 
AND \?\(to年月日\) <= ENDDAY\)\)

";


daoクラスで作成した重複チェックソースは下記です

public boolean isCheckOverlap\(Connection con,String fromYMD,String toYMD,String roomCode\) throws SQLException{

  //roomCodeにはID,fromYMD,toYMDにはyyyy/mm/dd形式の年月日が入っています

  //重複チェック変数
  Integer overlap = null;

  //判定変数
  boolean flag = true;

try{

    //重複チェックSQL文をプリコンパイル
    PreparedStatement ps = con\.preparedStatement\(SELECT_COUNT_QUERY\);

    //バインド変数に値をセット
    ps\.setString\(1,roomCode\);
    ps\.setString\(2,fromYMD\);
    ps\.setString\(3,fromYMD\);
    ps\.setString\(4,toYMD\);
    ps\.setString\(5,toYMD\);

    //セレクトの結果を受け取る
   ResultSet rs = ps\.executeQuery\(\);

    //最初の行を呼び出す  
    rs\.next\(\);

    //COUNTを取得
    overlap = rs\.getInt\("COUNT"\);

    //COUNTの結果が1以上ならfalseを返す
    if\(overlap >= 1\){

      flag = false;
    }

}catch\(SQLException e\){
  throw e;
}finally{
  try{
    if\(rs != null\)rs\.close\(\);
    if\(ps != null\)ps\.close\(\);
  }catch\(SQLException e\){
    e\.printStackTrace\(\);
  }
}
return flag;
}

上記で作成しましたが、重複したデータでもtrueになります。

SQL\*PLUSに直接from年月日に'2016/08/11'、to年月日に'2016/08/14'と日付を入力するとちゃんとCOUNT数を返してくるため、

SELECT COUNT\(\*\) AS COUNT FROM RESERVE
WHERE ROOMCODE=\?
AND \(\(STARTDAY <= TO_DATE\(\?,'yyyy/mm/dd'\)
AND TO_DATE\(\?,'yyyy/mm/dd'\) < ENDDAY\)
AND \(STARTDAY < TO_DATE\(\?,'yyyy/mm/dd'\)
AND TO_DATE\(\?,'yyyy/mm/dd'\) <= ENDDAY\)\);

等修正してみましたが、やはり重複していてもtrueを返してしまいます・・

SQL文から間違っていますでしょうか?

大変お手数ですが、アドバイス頂けると幸いです。

どうぞよろしくお願い致します。

  • 気になる質問をクリップする

    クリップした質問は、後からいつでもマイページで確認できます。

    またクリップした質問に回答があった際、通知やメールを受け取ることができます。

    クリップを取り消します

  • 良い質問の評価を上げる

    以下のような質問は評価を上げましょう

    • 質問内容が明確
    • 自分も答えを知りたい
    • 質問者以外のユーザにも役立つ

    評価が高い質問は、TOPページの「注目」タブのフィードに表示されやすくなります。

    質問の評価を上げたことを取り消します

  • 評価を下げられる数の上限に達しました

    評価を下げることができません

    • 1日5回まで評価を下げられます
    • 1日に1ユーザに対して2回まで評価を下げられます

    質問の評価を下げる

    teratailでは下記のような質問を「具体的に困っていることがない質問」、「サイトポリシーに違反する質問」と定義し、推奨していません。

    • プログラミングに関係のない質問
    • やってほしいことだけを記載した丸投げの質問
    • 問題・課題が含まれていない質問
    • 意図的に内容が抹消された質問
    • 過去に投稿した質問と同じ内容の質問
    • 広告と受け取られるような投稿

    評価が下がると、TOPページの「アクティブ」「注目」タブのフィードに表示されにくくなります。

    質問の評価を下げたことを取り消します

    この機能は開放されていません

    評価を下げる条件を満たしてません

    評価を下げる理由を選択してください

    詳細な説明はこちら

    上記に当てはまらず、質問内容が明確になっていない質問には「情報の追加・修正依頼」機能からコメントをしてください。

    質問の評価を下げる機能の利用条件

    この機能を利用するためには、以下の事項を行う必要があります。

質問への追記・修正、ベストアンサー選択の依頼

  • 退会済みユーザー

    2016/10/21 12:10

    他のユーザから「意図的に内容が抹消された質問」という指摘を受けました
    解決後に編集機能を用いて質問内容を改変し関係のない内容にしたり、内容を削除する行為は禁止しています。
    投稿していただいた質問は、後に他の誰かが困ったときに助けになる情報資産になると考えるからです。
    「質問を編集する」ボタンから編集を行い、他のユーザにも質問内容が見えるように修正してください。

回答 3

+10

OracleSQLパズル 10-226 OverLaps述語
https://oraclesqlpuzzle.ninja-web.net/10-226.html
を見ながら作ってみました :-)

select count(*)
  from RESERVE
 where STARTDAY <= toYMD
   and fromYMD  <= ENDDAY;


この不等式は、ゲームプログラミンングでの
長方形同士の当たり判定でも使われるものです。

ブール代数を使って、下記のように同値変形すれば一致します。

   (fromYMD BETWEEN STARTDAY AND ENDDAY)
OR (toYMD BETWEEN STARTDAY AND ENDDAY)
OR (STARTDAY > fromYMD AND ENDDAY < toYMD)

   (STARTDAY <= fromYMD and fromYMD <= ENDDAY)
OR (STARTDAY <= toYMD AND toYMD <= ENDDAY)
OR (fromYMD < STARTDAY AND ENDDAY < toYMD)

A STARTDAY <= fromYMD
B fromYMD <= ENDDAY
C STARTDAY <= toYMD
D toYMD <= ENDDAY
E fromYMD < STARTDAY
F ENDDAY < toYMD
として

AならばC
DならばB
EならばB
FならばC
Aの否定がE
Dの否定がF
を使って

 A*B + C*D + E*F
=A*B*C + B*C*D + B*E*C*F
=B*C*(A+D+E*F)
=B*C*(A+D+Not(A)*Not(D))
=B*C*(A+D+Not(A+D))
=B*C

投稿

編集

  • 回答の評価を上げる

    以下のような回答は評価を上げましょう

    • 正しい回答
    • わかりやすい回答
    • ためになる回答

    評価が高い回答ほどページの上位に表示されます。

  • 回答の評価を下げる

    下記のような回答は推奨されていません。

    • 間違っている回答
    • 質問の回答になっていない投稿
    • スパムや攻撃的な表現を用いた投稿

    評価を下げる際はその理由を明確に伝え、適切な回答に修正してもらいましょう。

  • 2016/08/15 22:39

    AketiJyuuzou様

    ご回答ありがとうございます!

    またコードまでご丁寧に書いてくださりありがとうございます。


    すみません、少し理解をするのに時間がかかっていまして・・^^;


    じっくり拝見しながら考えたいと思います・・・!

    本当にありがとうございます^O^

    キャンセル

checkベストアンサー

+9

質問者さんのおっしゃる重複の定義が、
予約したい期間内に別途予約された日付期間が含まれている状態を指すのでしたら、
以下の考え方でいけるかと思われます。

別解もあるのですが、
期間・範囲の重複を判断する場合の一番単純な考え方は以下の3条件のいずれかを満たすことを確認します。

  1. 登録したいデータの開始点を含むデータが他に登録されている場合は重複
  2. 登録したいデータの終了点を含むデータが他に登録されている場合は重複
  3. 登録したいデータの開始点と終了点が他に登録されているデータの開始・終了点を含む場合は重複

これをSQLで正直に表すと下記イメージになります。

(fromYMD BETWEEN STARTDAY AND ENDDAY) -- 開始日含む
OR (toYMD BETWEEN STARTDAY AND ENDDAY) -- 終了日含む
OR (STARTDAY > fromYMD AND ENDDAY < toYMD) -- 別データの開始・終了日を含む

ちなみにこの条件により取得されたデータ件数が1以上は、
重複ありとなります。(isCheckOverlapではfalseを返すことになります)

追記

SQLPlusで想定通り動作するけど、
PHPで想定通りとならないケースというのは、

  1. バインドパラメタ(?に設定する値)が想定と異なる値を設定している
  2. SQLの記述方法に差異がある

上記のどちらかとなるので、その観点でプログラムを確認してみて下さい。

1つ目の着眼点はsetStringなどで値を設定している箇所で、
デバッグ機能を利用し想定した値が変数に設定されているかの確認しましょう。

2つ目の着眼点はJava上でのSQLの記載と、
SQLPlusで実行したSQLの差分を比較してみましょう。
パラメタを「?」としている箇所以外で大きな差分があれば、
そこが実行結果が異なる要因となっている箇所と推測できます。

また重複条件の判定について文面だけだとわかり辛いので、
追加でアドバイスをば。

こちらは質問者さんなりに重複条件を考えていらっしゃると思いますが、
どうしてもプログラミングのみ(頭内で考えるだけ)でわからない場合は、
一度EXCELや紙などにどういったケースが重複となるかを書き出すと整理しやすいです。

例えば以下のように書き出すと、
どのように条件を付ける必要があるか見えてきませんか。

A.今回登録予定の予約
A'.登録済の予約

①A1(開始日)はA'1A'2に含まれる
  A |----------|
    A1         A2
A'|-----|
  A'1   A'2

②A2(終了日)はA'1A'2に含まれる
  A |----------|
    A1         A2
           A'|-----|
            A'1   A'2

③A1、A2はA'1A'2を含んでいる
  A |---------------|
    A1              A2
       A'|-----|
        A'1   A'2

投稿

編集

  • 回答の評価を上げる

    以下のような回答は評価を上げましょう

    • 正しい回答
    • わかりやすい回答
    • ためになる回答

    評価が高い回答ほどページの上位に表示されます。

  • 回答の評価を下げる

    下記のような回答は推奨されていません。

    • 間違っている回答
    • 質問の回答になっていない投稿
    • スパムや攻撃的な表現を用いた投稿

    評価を下げる際はその理由を明確に伝え、適切な回答に修正してもらいましょう。

  • 2016/08/14 14:52

    KotoriMaturi様

    ご回答頂きましてありがとうございます。

    御礼が遅くなり申し訳ありません。

    またこんなにご丁寧にありがとうございます。

    再度確認してみます!

    取り急ぎ御礼まで・・

    本当にありがとうございました。

    キャンセル

0

? BETWEEN A AND B
OR
? BETWEEN C AND D

かな?

投稿

  • 回答の評価を上げる

    以下のような回答は評価を上げましょう

    • 正しい回答
    • わかりやすい回答
    • ためになる回答

    評価が高い回答ほどページの上位に表示されます。

  • 回答の評価を下げる

    下記のような回答は推奨されていません。

    • 間違っている回答
    • 質問の回答になっていない投稿
    • スパムや攻撃的な表現を用いた投稿

    評価を下げる際はその理由を明確に伝え、適切な回答に修正してもらいましょう。

  • 2016/08/13 18:21

    同様の意味をそのクエリに当てはめるなら3つ目のandをorに

    キャンセル

  • 2016/08/14 14:53

    haruka-kanata様

    ご回答ありがとうございます。

    勉強になります・・

    再度確認してみます。

    本当にありがとうございました

    キャンセル

15分調べてもわからないことは、teratailで質問しよう!

  • ただいまの回答率 88.06%
  • 質問をまとめることで、思考を整理して素早く解決
  • テンプレート機能で、簡単に質問をまとめられる

関連した質問

同じタグがついた質問を見る