Tips

重複不可でインデックスが設定されていないフィールドを探し出す方法

日付2010/09/08
ID76165 (英語原文参照)
バージョンv11
プラットフォームMac & Win

4D v11 SQL以降, インデックスが設定されていないフィールドでも, 重複不可属性が設定できるようになりました。しかし, そのような組み合わせは概してパフォーマンスに良くない影響を及ぼすので注意が必要です。

フィールドが重複不可でありながらインデックスが作成されていない場合, データベースはレコードが追加されるたびに一意性を確証するためにすべてのレコードをスキャンしなければなりません。テーブルに大量のレコードが登録されていれば, それだけこの処理は時間を要します。

実際, レコードの数がそれほど多くないのであれば, 特にインデックスは不要かもしれません。しかし, 100万件を超える規模になれば, 遅延は体感できるほどの水準に達する可能性があります。インデックスを使用しないシーケンシャル検索は, キャッシュを大幅に占有するため, クライアントサーバー構成では他のユーザーに与える影響も考慮しなければなりません。

もうひとつ, インデックスの設定が推奨されるのは, バッチ処理など, 多数のレコードをまとめて追加するような場合です。そのような処理では, 50,000件程度のレコード数であっても, レコードの作成がかなり遅くなることが知られています。

該当するようなフィールドの存在を調べたい場合, 下記のようなコードを実行することにより, インデックスがない重複不可フィールドを探すことができ, その特性に応じてインデックスの是非を判断する材料にすることができます。解析の結果は, Logsフォルダ内にテキスト形式で"UniqueNotIndexed.txt"ファイルとして出力されます。

C_LONGINT($maxTableNumber_l;$currentTable_l)
C_LONGINT($maxFieldCount_l;$currentField_l)
C_LONGINT($dontCare_l)  ` For GET FIELD PROPERTIES values that are not used.
C_BOOLEAN($dontCare_f;$isIndexed_f;$isUnique_f)
C_TEXT($logHeader_t;$logRecord_t;$logfile_t)
C_TEXT($delim_t;$lf_t)
C_TIME($logfile_h)
C_TEXT($tableName_t;$fieldName_t;$note_t)
C_LONGINT($recIsInTable_l)

$delim_t:=Char(Tab )
$lf_t:=Char(Line feed )

` Column names for the log file, tab delimited.
$logHeader_t:="Table"+$delim_t
$logHeader_t:=$logHeader_t+"Field"+$delim_t
$logHeader_t:=$logHeader_t+"Records"+$delim_t
$logHeader_t:=$logHeader_t+"Note"+$lf_t

$logfile_t:=Get 4D folder(Logs Folder )+"UniqueNotIndexed.txt"

$logfile_h:=Create document($logfile_t)

If (OK=1)
 
 SEND PACKET($logfile_h;$logHeader_t)
 
 $maxTableNumber_l:=Get last table number
 
 For ($currentTable_l;1;$maxTableNumber_l)
  If (Is table number valid($currentTable_l))
   $maxFieldCount_l:=Get last field number(Table($currentTable_l))
   For ($currentField_l;1;$maxFieldCount_l)
    If (Is field number valid($currentTable_l;$currentField_l))

       ` Note the following line breaks over two lines in text, 
       ` it is one statement in the method:
     GET FIELD PROPERTIES($currentTable_l;$currentField_l;$dontCare_l;$dontCare_l;$isIndexed_f;$isUnique_f;$dontCare_f)
     
     If (($isUnique_f) & (Not($isIndexed_f)))
      
      $tableName_t:=Table name(Table($currentTable_l))
      $fieldName_t:=Field name(Field($currentTable_l;$currentField_l))
      $recIsInTable_l:=Records in table(Table($currentTable_l)->)
      
      Case of 
       : ($recIsInTable_l<=10000)
        $note_t:="Indexing optional."
        
       : (($recIsInTable_l>10000) & ($recIsInTable_l<=50000))
        $note_t:="Indexing recommended."
        
       : ($recIsInTable_l>50000)
        $note_t:="Indexing requried."
      End case 
      
      $logRecord_t:=$tableName+$delim_t
      $logRecord_t:=$logRecord_t+$fieldName_t+$delim_t
      $logRecord_t:=$logRecord_t+String($recIsInTable_l)+$delim_t
      $logRecord_t:=$logRecord_t+$note_t+$lf_t
      
      SEND PACKET($logfile_h;$logRecord_t)
      
     End if 
    End if 
   End for 
  End if 
 End for 
 
 CLOSE DOCUMENT($logfile_h)
End if