web-dev-qa-db-ja.com

group byとdistinctを使用した場合のパフォーマンスの大きな違い

500 000エントリを含むテーブルを使用してHSQLDBサーバーでいくつかのテストを実行しています。テーブルにはインデックスがありません。 5000の異なるビジネスキーがあります。それらのリストが必要です。当然、DISTINCTクエリから始めました。

SELECT DISTINCT business_key FROM memory WHERE
   concept <> 'case' or 
   attrib <> 'status' or 
   value <> 'closed'

約90秒かかります!!!

次に、GROUP BYを使用してみました:

SELECT business_key FROM memory WHERE
       concept <> 'case' or 
       attrib <> 'status' or 
       value <> 'closed'
GROUP BY business_key

そして、それは1秒かかります!!!

EXLAIN PLAN FORを実行した違いを理解しようとしましたが、両方のクエリに同じ情報を提供するようです。

EXLAIN PLAN FOR DISTINCT ...

isAggregated=[false]
columns=[
  COLUMN: PUBLIC.MEMORY.BUSINESS_KEY
]
[range variable 1
  join type=INNER
  table=MEMORY
  alias=M
  access=FULL SCAN
  condition = [    index=SYS_IDX_SYS_PK_10057_10058
    other condition=[
    OR arg_left=[
     OR arg_left=[
      NOT_EQUAL arg_left=[
       COLUMN: PUBLIC.MEMORY.CONCEPT] arg_right=[
       VALUE = case, TYPE = CHARACTER]] arg_right=[
      NOT_EQUAL arg_left=[
       COLUMN: PUBLIC.MEMORY.ATTRIB] arg_right=[
       VALUE = status, TYPE = CHARACTER]]] arg_right=[
     NOT_EQUAL arg_left=[
      COLUMN: PUBLIC.MEMORY.VALUE] arg_right=[
      VALUE = closed, TYPE = CHARACTER]]]
  ]
]]
PARAMETERS=[]
SUBQUERIES[]
Object References
PUBLIC.MEMORY
PUBLIC.MEMORY.CONCEPT
PUBLIC.MEMORY.ATTRIB
PUBLIC.MEMORY.VALUE
PUBLIC.MEMORY.BUSINESS_KEY
Read Locks
PUBLIC.MEMORY
WriteLocks

EXLAIN PLAN FOR SELECT ... GROUP BY ...

isDistinctSelect=[false]
isGrouped=[true]
isAggregated=[false]
columns=[
  COLUMN: PUBLIC.MEMORY.BUSINESS_KEY
]
[range variable 1
  join type=INNER
  table=MEMORY
  alias=M
  access=FULL SCAN
  condition = [    index=SYS_IDX_SYS_PK_10057_10058
    other condition=[
    OR arg_left=[
     OR arg_left=[
      NOT_EQUAL arg_left=[
       COLUMN: PUBLIC.MEMORY.CONCEPT] arg_right=[
       VALUE = case, TYPE = CHARACTER]] arg_right=[
      NOT_EQUAL arg_left=[
       COLUMN: PUBLIC.MEMORY.ATTRIB] arg_right=[
       VALUE = status, TYPE = CHARACTER]]] arg_right=[
     NOT_EQUAL arg_left=[
      COLUMN: PUBLIC.MEMORY.VALUE] arg_right=[
      VALUE = closed, TYPE = CHARACTER]]]
  ]
]]
groupColumns=[
COLUMN: PUBLIC.MEMORY.BUSINESS_KEY]
PARAMETERS=[]
SUBQUERIES[]
Object References
PUBLIC.MEMORY
PUBLIC.MEMORY.CONCEPT
PUBLIC.MEMORY.ATTRIB
PUBLIC.MEMORY.VALUE
PUBLIC.MEMORY.BUSINESS_KEY
Read Locks
PUBLIC.MEMORY
WriteLocks

[〜#〜] edit [〜#〜]:追加のテストを行いました。 HSQLDBに500 000のレコードがあり、すべての個別のビジネスキーがあるため、DISTINCTのパフォーマンスは3秒向上しました。これに対し、GROUP BYは約9秒かかりました。

MySQLでは、両方のクエリで同じことが行われます。

MySQL:500 000行-5000個の個別のビジネスキー:両方のクエリ:0.5秒MySQL:500 000行-すべての個別のビジネスキー:SELECT DISTINCT ...-11秒SELECT ... GROUP BY business_key-13秒

したがって、問題はHSQLDBにのみ関連しています。

なぜこのような劇的な違いがあるのか​​を誰かが説明できるなら、私はとても感謝しています。

67
Martin Dimitrov

2つのクエリは同じ質問を表しています。どうやらクエリオプティマイザーは2つの異なる実行計画を選択します。私の推測では、distinctアプローチは次のように実行されます。

  • すべてのbusiness_key値を一時テーブルにコピーします
  • 一時テーブルを並べ替える
  • 一時テーブルをスキャンし、前のアイテムとは異なる各アイテムを返します

group byは次のように実行できます。

  • business keyの各値をハッシュテーブルに保存して、テーブル全体をスキャンします
  • ハッシュテーブルのキーを返します

最初の方法は、メモリ使用量に対して最適化されます。一時テーブルの一部をスワップアウトする必要がある場合でも、適切に機能します。 2番目の方法は速度を最適化しますが、多くの異なるキーがある場合は潜在的に大量のメモリを必要とします。

十分なメモリまたはいくつかの異なるキーがあるため、2番目の方法は最初の方法よりも優れています。 2つの実行プラン間で10倍、さらには100倍のパフォーマンスの違いが見られることは珍しくありません。

66
Andomar