10

ユーザーがオブジェクトを少なくとも 1 回表示したかどうかを記録しているテーブルがあるため、次のようになります。

 HasViewed
     ObjectID  number (FK to Object table)
     UserId    number (FK to Users table)

両方のフィールドは NOT NULL であり、一緒に主キーを形成します。

私の質問は、誰かが (最初のオブジェクトの後) 何回オブジェクトを表示したかは気にしないので、挿入を処理するための 2 つのオプションがあるということです。

  • SELECT count(*) ... を実行し、レコードが見つからない場合は、新しいレコードを挿入します。
  • 常にレコードを挿入し、DUP_VAL_ON_INDEX 例外 (そのようなレコードが既に存在することを示す) をスローする場合は、それを無視します。

2 番目のオプションを選択することの欠点は何ですか?

アップデート:

「例外によるオーバーヘッドは、最初の選択によるオーバーヘッドよりも悪いですか?」

4

5 に答える 5

14

これはコーディングが最も簡単なので、通常は DUP_VAL_ON_INDEX 例外を挿入してトラップするだけです。これは、挿入前に存在をチェックするよりも効率的です。私たちが処理する例外は Oracle によって生成されるため、これを「悪臭」(ひどい言い回し!) とは考えていません。これは、フロー制御メカニズムとして独自の例外を生成するようなものではありません。

Igor のコメントのおかげで、これについて 2 つの異なるベンチマークを実行しました: (1) 最初の試行を除くすべての挿入試行が重複している場合、(2) すべての挿入が重複していない場合。現実は、2 つのケースの間のどこかにあるでしょう。

注: テストは Oracle 10.2.0.3.0 で実行されました。

ケース 1: ほとんどが重複

最も効率的なアプローチは(重要な要因による)、挿入中に存在を確認することです。

prompt 1) Check DUP_VAL_ON_INDEX
begin
   for i in 1..1000 loop
      begin
         insert into hasviewed values(7782,20);
      exception
         when dup_val_on_index then
            null;
      end;
   end loop
   rollback;
end;
/

prompt 2) Test if row exists before inserting
declare
   dummy integer;
begin
   for i in 1..1000 loop
      select count(*) into dummy
      from hasviewed
      where objectid=7782 and userid=20;
      if dummy = 0 then
         insert into hasviewed values(7782,20);
      end if;
   end loop;
   rollback;
end;
/

prompt 3) Test if row exists while inserting
begin
   for i in 1..1000 loop
      insert into hasviewed
      select 7782,20 from dual
      where not exists (select null
                        from hasviewed
                        where objectid=7782 and userid=20);
   end loop;
   rollback;
end;
/

結果 (解析のオーバーヘッドを避けるために 1 回実行した後):

1) Check DUP_VAL_ON_INDEX

PL/SQL procedure successfully completed.

Elapsed: 00:00:00.54
2) Test if row exists before inserting

PL/SQL procedure successfully completed.

Elapsed: 00:00:00.59
3) Test if row exists while inserting

PL/SQL procedure successfully completed.

Elapsed: 00:00:00.20

ケース 2: 重複なし

prompt 1) Check DUP_VAL_ON_INDEX
begin
   for i in 1..1000 loop
      begin
         insert into hasviewed values(7782,i);
      exception
         when dup_val_on_index then
            null;
      end;
   end loop
   rollback;
end;
/

prompt 2) Test if row exists before inserting
declare
   dummy integer;
begin
   for i in 1..1000 loop
      select count(*) into dummy
      from hasviewed
      where objectid=7782 and userid=i;
      if dummy = 0 then
         insert into hasviewed values(7782,i);
      end if;
   end loop;
   rollback;
end;
/

prompt 3) Test if row exists while inserting
begin
   for i in 1..1000 loop
      insert into hasviewed
      select 7782,i from dual
      where not exists (select null
                        from hasviewed
                        where objectid=7782 and userid=i);
   end loop;
   rollback;
end;
/

結果:

1) Check DUP_VAL_ON_INDEX

PL/SQL procedure successfully completed.

Elapsed: 00:00:00.15
2) Test if row exists before inserting

PL/SQL procedure successfully completed.

Elapsed: 00:00:00.76
3) Test if row exists while inserting

PL/SQL procedure successfully completed.

Elapsed: 00:00:00.71

この場合、DUP_VAL_ON_INDEX が 1 マイルも勝ちます。どちらの場合も、「挿入前に選択」が最も遅いことに注意してください。

したがって、挿入が重複しているかどうかの相対的な可能性に応じて、オプション 1 または 3 を選択する必要があるようです。

于 2008-12-09T10:59:47.580 に答える
1

2番目のオプションにマイナス面があるとは思いません。名前付き例外の完全に有効な使用法であり、ルックアップのオーバーヘッドを回避できると思います。

于 2008-12-08T21:11:45.050 に答える
1

これを試して?

SELECT 1
FROM TABLE
WHERE OBJECTID = 'PRON_172.JPG' AND
      USERID='JCURRAN'

そこにある場合は 1 を返し、そうでない場合は NULL を返します。

あなたの場合、無視しても安全に見えますが、パフォーマンスのために、共通パスでの例外を避ける必要があります。「例外はどれくらい一般的ですか?」という質問です。無視するのに十分な数はありませんか?または非常に多くの別の方法を使用する必要がありますか?

于 2008-12-08T21:15:38.960 に答える
0

通常、例外処理は遅くなります。ただし、めったに発生しない場合は、クエリのオーバーヘッドを回避できます。
主に例外の頻度に依存すると思いますが、パフォーマンスが重要な場合は、両方のアプローチでベンチマークを行うことをお勧めします。

一般的に言えば、一般的なイベントを例外として扱うことは悪臭です。このため、別の視点からも見ることができます。
それが例外である場合、それは例外として扱われるべきです - そしてあなたのアプローチは正しいです。
それが一般的なイベントである場合は、明示的に処理してから、レコードが既に挿入されているかどうかを確認する必要があります。

于 2008-12-08T21:21:39.853 に答える
0

私見では、オプション 2 を使用するのが最善です: 既に述べたこと以外に、スレッド セーフを考慮する必要があります。オプション1を使用し、複数のスレッドがPL/SQLブロックを実行している場合、2つ以上のスレッドがselectを同時に起動する可能性があり、その時点でレコードがない場合、すべてのスレッドが挿入され、一意の制約エラーが発生します。

于 2012-12-05T08:43:37.410 に答える