中小企業の働き方改革をサポート>>

SELECT FOR UPDATE
ですが、該当レコードのみ
ロックすると思うんですが、
該当レコードがない場合は、
ロックできないんでしょうか?

たとえば、(COLUMN_BBB が PK として)

SELECT * FROM TABLE_AAA
WHERE TABLE_AAA.COLUMN_BBB = 'BBB'
FOR UPDATE

で、そもそも

SELECT * FROM TABLE_AAA
WHERE TABLE_AAA.COLUMN_BBB = 'BBB'

となるレコードがない場合でも、
他トランザクションによる
該当レコードの INSERT を排他防御できるのか無理なのか、
教えていただけたらありがたいです。

すみません、時間的余裕があまりないので、
(すぐに回答ほしいです)でアップします。

このQ&Aに関連する最新のQ&A

A 回答 (1件)

ロックする対象がなければ、ロックのしようがありません。


インサートするレコードのキーを予約する意味での行為なら、そのような手法ではだめです。

代替え策ですが、FOR-UPDATEで存在チェック&インサート予約、その後にじっくりインサートするのでなく、
いきなりプライマリーキーだけインサートして、インサートできたなら、存在チェックOK&予約済みとし、
後でその他のデータを更新すると良いかと思います。
もし、更新を辞めたいなら、ロールバックすれば良いかと。
    • good
    • 1

このQ&Aに関連する人気のQ&A

お探しのQ&Aが見つからない時は、教えて!gooで質問しましょう!

このQ&Aを見た人はこんなQ&Aも見ています

関連するカテゴリからQ&Aを探す

このQ&Aを見た人が検索しているワード

このQ&Aと関連する良く見られている質問

QOracleの排他制御について教えてください

質問内容は結論から言うと、
INSERT文の時の排他制御について
知りたい。

以下の私の認識を踏まえた上で、
INSERT文の排他制御について
質問させてください。
なお、私の認識に誤りがあれば
指摘してください。

### 私の認識 start ######

1)Oracle
では、select文の時に
for updateを
書かなければ、なんのロックもかからず、
読み取り専用リソースへのアクセスで
ない限り、
ダーティーリードの可能性がある。

2)
select文で for update
を指定した場合は
該当行について
共有ロックがかかる。
行単位の共有ロックがかかる。
その際、
他のトランザクションが
for updateつきで
selectしてきても、
共有ロック同士なので、
互い排他制御しない。

(3)
update 文の場合は
該当行について、
占有ロックがかかる
行単位の占有ロックがかかる。

(4)
INSERT文の時には、
ロックをかけようにも
INSERT前の段階では、
ロック対象行は存在しない。
複数のトランザクションが
INSERTした行のPK
の値が偶然同じであった場合
ロストアップデートの危険があるので
私の創造では、INSERT文の時は
テーブル全体をロックしないと、
うまくいかないように思えます。

(5)
update, insert文については、
Oracleでは、自動的に該当行について
占有ロックを行う。
なお、INSERT文については、
下記の質問事項における疑問点
が解消されていないため、
行単位なのかどうか、私の中では
自身がもてないのが現状です。
### 私の認識 end ######

### 主な質問内容 start ###
私の認識の(4)を踏まえた上で
INSERT文の時のはいた制御
の範囲や挙動について、
教えてください。
### 主な質問内容 end ###

以上です。

質問内容は結論から言うと、
INSERT文の時の排他制御について
知りたい。

以下の私の認識を踏まえた上で、
INSERT文の排他制御について
質問させてください。
なお、私の認識に誤りがあれば
指摘してください。

### 私の認識 start ######

1)Oracle
では、select文の時に
for updateを
書かなければ、なんのロックもかからず、
読み取り専用リソースへのアクセスで
ない限り、
ダーティーリードの可能性がある。

2)
select文で for update
を指定した場合は
該当行について
共有ロッ...続きを読む

Aベストアンサー

#2 補足です。
2)
SELECT FOR UPDATE文は
NO WAIT オプションをつけたときは即時エラーに、
つけないときはロック解除待ちになります。
どちらを使うかはアプリケーションの作り方のポリシーによります。

セッション=クライアントとサーバの間の接続。データベースに接続してから切断するまでの間の接続が確立している状態

トランザクション=データベースの処理をひとまとまりにしたモノ。たとえば、口座Aから口座Bへの振り込み処理の場合、口座Aからの引き出し、口座Bへの振り込みの処理を1トランザクションとして扱います。どちらかが失敗した場合はロールバック(巻き戻し)、成功した場合はコミット(確定)します。

業務用アプリケーションの場合、大抵、1回のセッションの中では複数回のトランザクションが発生します。複数のユーザーがそのアプリケーションを利用していればその数だけセッションが張られます。(Webアプリに関してはその限りではないですが)

デッドロック=
Aと言うセッションがトランザクション内で
1-1 表Xのa行に更新をかけます。
1-2 次に表Yにb行に更新をかけます。
1-3 コミットします(トランザクションの終了)。

同時に
Bと言うセッションがトランザクション内で
2-1 表Yにb行に更新をかけます。
2-2 次に表Xのa行に更新をかけます。
2-3 コミットします(トランザクションの終了)。

これが、1-1 2-1 1-2 2-1 1-3 2-3
と言う順番で実行された場合デッドロックが発生します。つまりAは、X-aにロックをかけたままY-bにロックをかけようとして待ち状態になる。逆にBは、Y-bにロックをかけたまま、X-aにロックをかけようとして待ち状態になる。この状態をデッドロックと呼びます。

なお、Oracleでは、UNDOセグメントではなくロールバックセグメントと呼びます。

a)アーキテクチャに突っ込んだ話になるので、詳しくはOracle データベース概要のドキュメントを読んでください。

基本的に、SELECT中に他のセッション(アプリケーションの方が良いかな?)からの更新が確定した場合、SGA(システムグローバルエリア=Oracleサーバの共有メモリスペース)に前のデータのバックアップを取ります。これによって読み取り一貫性を保証します。ここにデータが入りきらない場合はデータベースのロールバックセグメント(たしか)に吐き出します。

b)
簡単に言えば、Oracleは、行ごとにロックフラグなどを記録する領域を持っています。マニュアルなどに書いてなければおそらく企業秘密です。

#2 補足です。
2)
SELECT FOR UPDATE文は
NO WAIT オプションをつけたときは即時エラーに、
つけないときはロック解除待ちになります。
どちらを使うかはアプリケーションの作り方のポリシーによります。

セッション=クライアントとサーバの間の接続。データベースに接続してから切断するまでの間の接続が確立している状態

トランザクション=データベースの処理をひとまとまりにしたモノ。たとえば、口座Aから口座Bへの振り込み処理の場合、口座Aからの引き出し、口座Bへの振り込みの処理を1トラン...続きを読む

Qエラーコードについて

オラクル初心者で、初めて質問します。
3点ほど、質問があります。どなたか宜しくお願いします。

(1)SELECT文発行時、抽出件数が0件(NotFound)、またはUPDATE文発行時、更新件数が0件の場合って、
エラーコードはORA-00000(正常終了)で合ってますか?

(2)エラーコードのリファレンス読んでたら、ORA-00001はUpdate、もしくはInsert時に一意制約に反しているとありますが、
Insert時は分かるのですがUpdate時ってどういう条件でORA-00001が出るのでしょうか?


(3)正常終了した場合も含めてエラーコードを確認することが可能なツール(フリーがBEST)とかご存知でしたら教えてください。

以上です。どうぞ宜しくお願いします。

Aベストアンサー

(1)SELECT文発行時、抽出件数が0件(NotFound)、またはUPDATE文発行時、更新件数が0件の場合って、
エラーコードはORA-00000(正常終了)で合ってますか?

ORA-00000というのは見たことがありませんのでコメントのしようがありません。マニュアルに記載がありましたか?

(2)エラーコードのリファレンス読んでたら、ORA-00001はUpdate、もしくはInsert時に一意制約に反しているとありますが、
Insert時は分かるのですがUpdate時ってどういう条件でORA-00001が出るのでしょうか?

例を示しておきます。

CREATE TABLE sample (
col varchar2(1)
);

CREATE UNIQUE INDEX col_idx ON sample (col);

INSERT INTO sample VALUES ('a');
INSERT INTO sample VALUES ('b');
COMMIT;

UPDATE sample SET col = 'a' WHERE col = 'b';

これで col = 'a' のデータは既に存在するので一意制約違反が発生するでしょう。

(3)正常終了した場合も含めてエラーコードを確認することが可能なツール(フリーがBEST)とかご存知でしたら教えてください。

開発ツール (接続方法。ODBC、JDBC等) にもより、それを記載されていないため、回答のしようがありません。

(1)SELECT文発行時、抽出件数が0件(NotFound)、またはUPDATE文発行時、更新件数が0件の場合って、
エラーコードはORA-00000(正常終了)で合ってますか?

ORA-00000というのは見たことがありませんのでコメントのしようがありません。マニュアルに記載がありましたか?

(2)エラーコードのリファレンス読んでたら、ORA-00001はUpdate、もしくはInsert時に一意制約に反しているとありますが、
Insert時は分かるのですがUpdate時ってどういう条件でORA-00001が出るのでしょうか?

例を示しておきます...続きを読む

QSELECT時の行ロックの必要性について

SELECT ~ FOR UPDATEやSELECT ~ LOCK IN SHARE MODEという行ロックがあり、この件について解説しているサイトをいろいろ見ているのですが、振る舞いが複雑だということはわかりました。

しかしそもそもなぜSELECTで行ロックするのか、運用上でどんなときに使うのかを解説しているサイトは見当たりませんでした。

私は、SELECTするときは単に

SELECT * FROM tb_a WHERE id = 1;

としか記述していません。

質問1.
なぜSELECTするのにトランザクションが必要なのでしょうか?運用上でどんなときにSELECTでトランザクションを使うのでしょうか?よく解説サイトには、

BEGIN;
SELECT * FROM tb_a WHERE id = 1 FOR UPDATE;
COMMIT;

と書いています。SELECTするのに行をロックする必要性がいまいちわからないです。

質問2.
トランザクションを開始したときにロックがかかるのではないのでしょうか?つまりBEGIN;でロックがかかるわけではないのですか?SELECTのクエリーにFOR UPDATEと書くということはこのSELECTのコードが実行された時点でロックがかかるのでしょうか?

質問3.
FOR UPDATEやLOCK IN SHARE MODEというのはSELECTにしか使えないのか、もしくはSELECTだから意味があるのでしょうか?UPDATEやDELETEは単にBEGINE;とCOMMIT;で囲えばいいだけですよね?

質問4.
以下のようなコードを解説しているサイトがあります。

BEGIN;
SELECT * FROM tb_a WHERE id = 1;
COMMIT;

SELECT文にはFOR UPDATEも書いていないのですが、これは何を意味するのでしょうか?つまり、SELECTするのになぜトランザクションを実行するのでしょうか?

一番知りたいのは運用上どのような場合にSELECTでトランザクションを使って行ロックするのか、ということです。
どうぞよろしくお願い致します。

SELECT ~ FOR UPDATEやSELECT ~ LOCK IN SHARE MODEという行ロックがあり、この件について解説しているサイトをいろいろ見ているのですが、振る舞いが複雑だということはわかりました。

しかしそもそもなぜSELECTで行ロックするのか、運用上でどんなときに使うのかを解説しているサイトは見当たりませんでした。

私は、SELECTするときは単に

SELECT * FROM tb_a WHERE id = 1;

としか記述していません。

質問1.
なぜSELECTするのにトランザクションが必要なのでしょうか?運用上でどんなときにSELECTでト...続きを読む

Aベストアンサー

>>質問3.select だから意味があります。

ただの読み込みではなく、「更新のための読み込み」であるということを明示しています。
updateやinsertは更新と決まっています。

>BIGIN;
UPDATE tb_a SET price = 残高-10000 WHERE id = xxx;
INSERT tb_a (price) VALUE (10000) WHERE id = yyy;
COMMIT;
ですよね?

合ってます。
2行だけロックされて他からは読み込めません。

>これはつまり、SELECT~FOR UPDATEとは選択のためのクエリーではなく「行ロックします」というクエリーなのですか?
そして上のBIGIN~UPDATE~INSERT~COMMITは以下のように書かないといけないのでしょうか?

BIGIN;
SELECT * FROM tb_a WHERE id = xxx FOR UPDATE;
SELECT * FROM tb_a WHERE id = yyy FOR UPDATE;
UPDATE tb_a SET price = 残高-10000 WHERE id = xxx;
INSERT tb_a (price) VALUE (10000) WHERE id = yyy;
COMMIT;

読みたいときにselect,更新したいときにupdate、追加したいときにinsertでいいです。
つまり、上2行のselect は不要なのですが、

UPDATE tb_a SET price = 残高-10000 WHERE id = xxx;
ここ、残高は先に読んで10000円あるかどうか確認しないといけませんよね。
こういう場合は
SELECT 残高 FROM tb_a WHERE id = xxx FOR UPDATE;
UPDATE tb_a SET price = 残高-10000 WHERE id = xxx;
となります。
他の情報で10000円があることが確認されていればselectは不要になります。

>>質問3.select だから意味があります。

ただの読み込みではなく、「更新のための読み込み」であるということを明示しています。
updateやinsertは更新と決まっています。

>BIGIN;
UPDATE tb_a SET price = 残高-10000 WHERE id = xxx;
INSERT tb_a (price) VALUE (10000) WHERE id = yyy;
COMMIT;
ですよね?

合ってます。
2行だけロックされて他からは読み込めません。

>これはつまり、SELECT~FOR UPDATEとは選択のためのクエリーではなく「行ロックします」というクエリーなのですか?
そして上のBIG...続きを読む

Q同じSELECT文同士でのデッドロックが発生

ORACLE10Gを使用したアプリ開発中です。

アプリ中、1テーブルに対して全く同じ条件で
複数レコードを悲観的ロック(for update)しながら取得し、
項目を更新するSQLを発行します。

単発では問題なかったのですが、処理時間の関係で
パラレルで実行された場合にデッドロックを検出することがあります。

ORACLEのudumpの中を確認すると全く同じSELECTのSQL同士で
デッドロックが発生したと表示されています。

こういった場合、どういう原因が考えられるでしょうか?
(同じ条件による悲観的ロックであれば多重実行でも
 処理待ちをするだけだと思っているのですが・・・)

よろしくお願いします。

Aベストアンサー

>2つの処理がロックしてしまう、というように聞こえますが
>こういうことはあるんでしょうか?
あるんです。Aのプロセスも、Bのプロセスも「誰もロックしてない」って思ってますから。
Aがロックを掛けた直後に、Bは「さっき調べた時は誰もロックしてなかったから、ロックしよう」と「上書きロック」をしてしまいます。

通常は1と3の間に2が割り込まず、2が後回しになる為、以下のようにAもBも正常終了します。
1.Aがレコードのロック状態を検査。ロック無しと判定
3.Aがレコードをロック
2.Bがレコードのロック状態を検査。ロック有りと判定
4.Bはレコードのロックが解除されるまで待ち合わせ
(以下略)

デットロックの直接の原因は「間に割り込みが入らず一連の動作として処理しなければならない、ロック状態の検査とロック設定の間に、別プロセスが割り込むため」です。

こういうケースは、データベースがサーバー上にあり、アクセスがネットを介して行われる場合で、2つのプロセスが同時に同じ処理を行った際に多発します。

回避には、独自にセマフォ変数やミューテックス変数を用意するなどして「データベースのロック機能以外を使って」排他制御が必要です。

>2つの処理がロックしてしまう、というように聞こえますが
>こういうことはあるんでしょうか?
あるんです。Aのプロセスも、Bのプロセスも「誰もロックしてない」って思ってますから。
Aがロックを掛けた直後に、Bは「さっき調べた時は誰もロックしてなかったから、ロックしよう」と「上書きロック」をしてしまいます。

通常は1と3の間に2が割り込まず、2が後回しになる為、以下のようにAもBも正常終了します。
1.Aがレコードのロック状態を検査。ロック無しと判定
3.Aがレコードをロ...続きを読む

QOracle(オラクル)で、日付時刻型の検索方法について

質問させていただきます。
データベースはオラクルを使っていて、
SQL文で、抽出するときにエラーが出て困っています。

日付時刻型が「2005/05/26 19:13:00」という感じで入ってます。
2005/05/26 を抽出したいのですが、
BETWEEN '2005/05/26 00:00:00' AND '2005/05/26 23:59:59'

だと、エラーでできません。
どなた様か、ご教授よろしくお願いしますm(_ _)m

Aベストアンサー

日付検索を行う場合は、以下のように書式を含める必要があります。

col BETWEEN TO_DATE('2005/05/26 00:00:00','YYYY/MM/DD HH24:MI:SS') AND TO_DATE('2005/05/26 23:59:59','YYYY/MM/DD HH24:MI:SS')

ただ、厳密には

col >= TO_DATE('2005/05/26', 'YYYY/MM/DD')
AND
col < TO_DATE('2005/05/27', 'YYYY/MM/DD')

と書くべきでしょうね。

QSELECTで1件のみ取得するには?

こんにちわ。
いまORACLE9iを使用している者です。

ACCESSでは
SELECT TOP 1 項目名 FROM テーブル名
ORDER BY 項目名;
で並べ替えたデータ群のうち,先頭の1件だけを
取ることができますが,
ORACLEでそのような機能(SQL)はあるでしょうか?
教えてください。
よろしくお願いします。

Aベストアンサー

order by と rownum を併用する場合は注意が必要です。

[tbl01]
cola | colb
------------
1000 | aaaa
1001 | bbbb

というデータがある場合、
select cola from tbl01 where rownum < 1 order by cola desc;
とすると、「1001」ではなく、「1000」が返されます。
これは、order by の前に rownum < 1 が適用されてしまうからです。

解決するには、
select aaa from (select cola aaa from tbl01 order by cola desc) where rownum = 1;
とすれば良いです。

Qテーブルからのselectにおいてデータの有無により結果をわけたい

id | point
----+-------
1 | 10
2 | 9
3 | 5
....
というテーブルがあるとします.
idを指定してpointを得たいのですが、そのidがこのテーブルに存在しない場合は空の結果ではなく0を返したいのです.
plpgsqlなどを使いif文で場合分けすればできることはわかっているのですがSQL文だけで(それもできれば1文で)これを実現する方法はあるでしょうか?
よろしくお願い致します。

Aベストアンサー

変則的ですが、これでよければidがユニークでなくても大丈夫ですし、集合関数を使わなくてもOKです。

select dm.id,case when ex1.point is null then 0 else ex1.point end from
(select ? as id) as dm left join ex1 on dm.id = ex1.id;

?を適当に変えてください。
chukenkenkouさんの発想はこれですよね。

Qsqlplusで表示が変なので、出力を整形したい。

いつもお世話になっています。

サーバにアクセスしてsqlplusで、
データを調べたいのですが、
出力形式が見づらくて困っています。

よくわからいのですが、
---------------------------
カラム名1
---------------------------
カラム名2
---------------------------
カラム名3
---------------------------
1の値 2の値
3の値
---------------------------
カラム名1
---------------------------
カラム名2
---------------------------
カラム名3
---------------------------

上記のように意味不明な形式で出てきます。

例えばこんな風に

select カラム1,カラム2,カラム3 from hoge;

カラム1 1の値
---------------------------
カラム2 2の値
---------------------------
カラム3 3の値

等のように分かりやすく表示できないでしょうか?

ちなみにOracle9iR2を使用しています。
sqlに関するツールは使用できないルールでして、あくまでsqlplusのコマンド上でみやすくしなければなりません。

分かりづらくですいませんが、皆さま、ご教授お願いします。

いつもお世話になっています。

サーバにアクセスしてsqlplusで、
データを調べたいのですが、
出力形式が見づらくて困っています。

よくわからいのですが、
---------------------------
カラム名1
---------------------------
カラム名2
---------------------------
カラム名3
---------------------------
1の値 2の値
3の値
---------------------------
カラム名1
---------------------------
カラム名2
---------------------------
カラム名3
-----------------------...続きを読む

Aベストアンサー

SQLPLUSを起動して、

SQL>set linesize 列数

でどうだ。

SQL>show linesize

で確認ができる。

Q3つの表の外部結合

表A、B、Cの3つがあり、Aのすべての行を出力したいと考えています。
外部結合を用いるのだとは思うのですが、3つの表に対して行う場合の
書き方がわからず困っています。
ご教授いただけないでしょうか?
select * from a,b,c
where a.商品ID =b.商品ID (+) and b.商品ID (+) =c.商品ID (+)
としてみましたが、うまくいきませんでした。

Aベストアンサー

ansi構文の趣旨からいえば、結合条件と絞り込み条件は分けて書くので・・

select *
from a
left join b on (a.商品ID =b.商品ID)
left join c on (b.商品ID =c.商品ID)
where a.年月 = 任意の値

と書くのが一般的でしょうね。

QUPDATE文のWHERE条件に他のテーブルのフィールドも入れたい

SQL文についてです。

表Aで列1と列2が'◎'のものについて、列3,列4,列5をそれぞれ'○','△','×'に更新したい場合は下記でOKだと思います。
UPDATE 表A
SET 列3 = '○', 列4 = '△', 列5 = '×'
WHERE 列1 = '◎' AND 列2 = '◎'

列1と列2をキーとして表Aと結合できる表Bが存在するとします。
条件に表Bの列6が'□'だった場合を追加したい時、どのような方法で行えば良いのでしょうか?
内部結合やWHERE EXISTSなどをいろいろ調べたのですが、SQL文に慣れていないためかうまくいきません。
初歩的な質問で申し訳ありませんが過去質問で参考になるものがなかったのでお願いします。

Aベストアンサー

UPDATE 表A
SET 列3 = '○', 列4 = '△', 列5 = '×'
WHERE 列1 = '◎' AND 列2 = '◎'
AND EXISTS
( SELECT 1 FROM 表B
WHERE 表B.列1 = 表A.列1
AND 表B.列2 = 表A.列2
AND 表B.列6 = '□' )
じゃだめですか。


このQ&Aを見た人がよく見るQ&A

人気Q&Aランキング

おすすめ情報