いくつかのビジネスロジックに従って格納およびクエリされる3つのタイプのアイテムがあります。
create table a_table
(
item_a varchar2(30),
item_b varchar2(16),
item_c varchar2(2),
-- other columns
);
そしてインデックス
create unique index idx_1 on a_table (item_a);
create unique index idx_2 on a_table (item_b);
create unique index idx_3 on a_table (item_c);
特定のタイプの特定のアイテムのデータは、次のように簡単に読み取られます。
-- reading item_a
select ... from a_table where item_a = '...';
-- reading item_b
select ... from a_table where item_b = '...';
-- ...
アイテムのタイプがaの場合、タイプbにすることはできないため、各行にはitem_a
の1つのみが必要です、item_b
またはitem_c
に値を指定し、その他はnullにする必要があります。 (これはどのような方法でも強制できます。insert
ionsに影響するかどうかは問題ではありません。)
理論的には、アイテムのタイプの数は増える可能性があります(4番目のアイテムタイプが将来どこかに追加される可能性があります)。
この事実は、醜い相互に排他的な(与えられた例ではチェックされていない)列を回避する別のソリューションに向かってプッシュされました。
create table a_table
(
item varchar2(30) not null,
item_type varchar2(10) not null,
-- other columns
);
create unique index idx_1 on a_table (item_type, item);
-- reading item_X
select ... from a_table where item='...' and item_type='item_X';
項目タイプの数が増加した場合の最初のソリューションの明らかな欠点とその他の考えられる設計上の欠陥を無視して、アクセス間に重要なパフォーマンスの違いはありますか(インデックス付き列を使用した行の選択)a最初の場合と同じようにテーブルにアクセスし、2番目の場合と同じようにテーブルにアクセスする?
item_a
およびitem_b
のカーディナリティーは数百万になる可能性があります。item_c
のカーディナリティーは確かに200未満です。
item_a
、item_b
、およびitem_c
の選択数が等しいと仮定します。(item_c, item_a, item_b)
iffを検討します。3つの個別のインデックスよりも(パフォーマンスの点で)優れていますが、両方のインデックス付きの列がある2番目のソリューションで比較を行う必要があることを忘れないでください。 where
条件で使用されます。両方のケースで最良の「正しい」インデックスを使用すると想定します。パフォーマンスの観点からは、非常に近いと思います。
しかし、いくつかの考え:
1つの列インデックスはNULL値を格納しません。したがって、個々のインデックスは小さくなります。複合インデックス(item_type、item)のサイズは、個々のインデックスの合計よりも大きくなります。極端な場合、インデックスの高ささえ異なる場合があります。
3つの個別のインデックスのうち2つは一意になるため、一意でないインデックスと比較して、読み取りの一貫性が1つ少なくなります。
Item_c列の場合、Oracleは正確なヒストグラムを作成できます。
(item_type、item)の場合、OracleはアイテムタイプAおよびBのクエリのカーディナリティを正しく計算しますが、「item_type = 'C'およびitem =:X」の場合は過小評価される可能性があります。これは問題ではない可能性がありますが、結合を使用した複雑なクエリでは、効果のない計画になる可能性があります。確認する必要があります。
インデックス(item_a、item_b、item_c)を(item_type、item)と比較しようとしています-他の人は、「item_a =:X」のようなクエリでのみ機能し、「item_b =:Y」の場合は機能しないと述べています。したがって、item_b列とitem_c列に同じ数のクエリがある場合、最初のケースにインデックス(item_a、item_b、item_c)を使用することは、単に悪い考えです。
create table hr.tt nologging
as
select case when mod(rownum,3) = 0 then rownum end AS item_a,
case when mod(rownum,3) = 1 then rownum end AS item_b,
case when mod(rownum,3) = 2 then round(dbms_random.value(1,200)) end AS item_c,
o.*
from all_objects o,
(select * from dual connect by level < 100) d
where rownum <= 5e6;
create unique index hr.item_a_idx on hr.tt(item_a) nologging;
create unique index hr.item_b_idx on hr.tt(item_b) nologging;
create index hr.item_c_idx on hr.tt(item_c) nologging;
create table hr.tt2 nologging
as
select case when mod(rownum,3) in (0,1) then rownum
else round(dbms_random.value(1,200))
end AS item,
case when mod(rownum,3) = 0 then 'A'
when mod(rownum,3) = 1 then 'B'
when mod(rownum,3) = 2 then 'C' end AS item_type,
o.*
from all_objects o,
(select * from dual connect by level < 100) d
where rownum <= 5e6;
create index hr.item_item_type_idx on hr.tt2(item_type, item) nologging;
いくつかのクエリ
SYS@mydb> select * from hr.tt where item_a = 300;
------------------------------------------------------------------------------------------
| Id | Operation | Name | Rows | Bytes | Cost (%CPU)| Time |
------------------------------------------------------------------------------------------
| 0 | SELECT STATEMENT | | 1 | 197 | 3 (0)| 00:00:01 |
| 1 | TABLE ACCESS BY INDEX ROWID| TT | 1 | 197 | 3 (0)| 00:00:01 |
|* 2 | INDEX UNIQUE SCAN | ITEM_A_IDX | 1 | | 2 (0)| 00:00:01 |
------------------------------------------------------------------------------------------
Statistics
----------------------------------------------------------
...
4 consistent gets
SYS@mydb> select * from hr.tt2 where item_type = 'A' and item = 300;
1 row selected.
--------------------------------------------------------------------------------------------------
| Id | Operation | Name | Rows | Bytes | Cost (%CPU)| Time |
--------------------------------------------------------------------------------------------------
| 0 | SELECT STATEMENT | | 1 | 115 | 4 (0)| 00:00:01 |
| 1 | TABLE ACCESS BY INDEX ROWID| TT2 | 1 | 115 | 4 (0)| 00:00:01 |
|* 2 | INDEX RANGE SCAN | ITEM_ITEM_TYPE_IDX | 1 | | 3 (0)| 00:00:01 |
--------------------------------------------------------------------------------------------------
Statistics
----------------------------------------------------------
5 consistent gets
以下は、オプティマイザの計算が間違っている例です(Oracle 11.2)。オプティマイザーは、 "INDEX RANGE SCAN"に1行しかないと考えます。実際には8438行あります。これら2つの列の拡張オプティマイザ統計は役立つ場合とそうでない場合があります。
SYS@mydb> select * from hr.tt2 where item_type = 'C' and item = 150;
8438 rows selected.
Execution Plan
----------------------------------------------------------
Plan hash value: 4063424880
--------------------------------------------------------------------------------------------------
| Id | Operation | Name | Rows | Bytes | Cost (%CPU)| Time |
--------------------------------------------------------------------------------------------------
| 0 | SELECT STATEMENT | | 1 | 115 | 4 (0)| 00:00:01 |
| 1 | TABLE ACCESS BY INDEX ROWID| TT2 | 1 | 115 | 4 (0)| 00:00:01 |
|* 2 | INDEX RANGE SCAN | ITEM_ITEM_TYPE_IDX | 1 | | 3 (0)| 00:00:01 |
--------------------------------------------------------------------------------------------------
Predicate Information (identified by operation id):
---------------------------------------------------
2 - access("ITEM_TYPE"='C' AND "ITEM"=150)
Statistics
----------------------------------------------------------
8 recursive calls
0 db block gets
8618 consistent gets
. . .
8438 rows processed
以下は、個々のitem_c列の同じケースです。ここでオプティマイザの計算は正しいです-推定8133行、8290行が選択されています。
SYS@mydb> select * from hr.tt where item_c = 150;
8290 rows selected.
Execution Plan
----------------------------------------------------------
------------------------------------------------------------------------------------------
| Id | Operation | Name | Rows | Bytes | Cost (%CPU)| Time |
------------------------------------------------------------------------------------------
| 0 | SELECT STATEMENT | | 8133 | 826K| 7737 (1)| 00:01:33 |
| 1 | TABLE ACCESS BY INDEX ROWID| TT | 8133 | 826K| 7737 (1)| 00:01:33 |
|* 2 | INDEX RANGE SCAN | ITEM_C_IDX | 8133 | | 19 (0)| 00:00:01 |
------------------------------------------------------------------------------------------
Predicate Information (identified by operation id):
---------------------------------------------------
2 - access("ITEM_C"=150)
Statistics
----------------------------------------------------------
0 recursive calls
0 db block gets
8499 consistent gets
. . .
8290 rows processed
私はOracleを話しませんが、最初のシナリオでは、論理的またはパフォーマンスの面で利点はありません。 @ brian-leachがコメントで示唆しているように、相互排他性を強制する必要がありますが、挿入が遅くなります。
このシナリオを使用すると、item_dをコレクションに追加することについて心配するだけでなく、item_xが追加の属性を取得するとどうなりますか?モデル2は適切な方法で簡単に拡張でき、モデル1はnull可能な属性の地獄の穴になります
あなたは明らかに、2番目のオプションの方が優れていると強く確信しています(強く同意します)。どちらのモデルでも、1000000 item_a、1000000 item_b、1000 item_cを生成するのは簡単な作業です。
いつものように、それはすべて、データへのアクセス方法accessに依存します。
Item_1でのみテーブルをクエリする場合、他の2つにインデックスを付けるのは時間の無駄になります。
あなたが最も一般的にテーブルをitem_1でクエリし、sometimesをitem_2同様に、item_1とitem_2の複合インデックスは良いアイデアかもしれません。データベースは、item_1のみのクエリにも複合インデックスを使用できますが、あなたdoは、あなた最も頻繁にクエリによって。
調べる最善の方法は? テストしてください。
テストデータベースを起動し、いくつかの代表的なデータを読み込み、いくつかのインデックスを作成し、一般的なクエリの実行計画を取得します。
Item/item_typeテーブルは、「エンティティ/属性値」と呼ばれる概念を使用しており、一般に、スケーリングが非常に悪いです。適切なインデックス作成により、パフォーマンスが向上します。