2

最初のクエリが2番目のクエリよりも高速になる理由を理解しようとしています。

SELECT * FROM DBADMIN.CR_TICKET WHERE QUEUE_ID=005555 AND STATUS=4

SELECT TICKET_ID FROM DBADMIN.CR_TICKET WHERE QUEUE_ID=005555 AND STATUS=4

最初の実行には約1分かかり、すべてのフィールドが返されます。2つ目は、時間がかかりすぎるため、数分後に停止します。TICKET_IDは有効な列名です。私はデータベースとSQLを初めて使用し、Oracle InstantClient11を参照してvb.netを使用しています。

Using connection As New OracleConnection()
        Dim command As OracleCommand = connection.CreateCommand()
        Dim reader As OracleDataReader = command.ExecuteReader()
        Dim str As String
        Dim strFieldName As String
        Dim strFieldType As String
        Dim sql As String = "SELECT * FROM DBADMIN.CR_TICKET WHERE QUEUE_ID=005555 AND STATUS=4"

        connection.ConnectionString = connectionString
        connection.Open()

        command.CommandText = sql

        While reader.Read()
            str = ""
            strFieldName = ""
            strFieldType = ""

            For e = 0 To reader.FieldCount - 1
                strFieldName = reader.GetName(e)
                strFieldType = reader.GetDataTypeName(e)
                str = str & strFieldName & " " & strFieldType & ": " & reader(e).ToString & vbCrLf

            Next

            Console.Write(str & vbCrLf & vbCrLf)
        End while

更新3/4/13:

まず、すばらしいアドバイスをありがとうございました。最終的にsql plusを実行して、次の実行プランを返すことができました。昨日たくさん読んだのですが、クエリが遅い理由の1つは、検索している列がインデックスに登録されていないことです。主キー列(すべてのテーブルに存在する)がデフォルトでインデックス付けされていることを発見しました。SQL文字列を次のように変更しました。

SQL> SELECT * FROM DBADMIN.CR_TICKET WHERE TICKET_ID > 'CR00000000' AND STATUS=4 AND (QUEUE_ID=005555 OR QUEUE_ID=005556) ORDER BY PRIORITY, TROUBLE_START;


| Id  | Operation                    | Name        | Rows  | Bytes | Cost (%CPU)

| Time     |

--------------------------------------------------------------------------------

------------

|   0 | SELECT STATEMENT             |             |     1 |   401 |     6  (17)

| 00:00:01 |

|   1 |  SORT ORDER BY               |             |     1 |   401 |     6  (17)

| 00:00:01 |

|*  2 |   TABLE ACCESS BY INDEX ROWID| CR_TICKET   |     1 |   401 |     5   (0)

| 00:00:01 |

|*  3 |    INDEX RANGE SCAN          | CRT_TID_IDX |     1 |       |     4   (0)

 | 00:00:01 |

--------------------------------------------------------------------------------

これはかなり速く実行されています。私の元のクエリは、ステートメントと一致させるためにデータベース内のすべてのレコードを読み取っていることを学びました。クエリを少し異なる方法で構造化してインデックス付き列を含めることにより、基本的にメインデータベースの一部のみを検索します。

SQL> SELECT * FROM DBADMIN.CR_TICKET WHERE QUEUE_ID=005555 AND STATUS=4 AND PRIORITY=1;

| Id  | Operation                   | Name         | Rows  | Bytes | Cost (%CPU)

| Time     |

--------------------------------------------------------------------------------

------------

|   0 | SELECT STATEMENT            |              |     1 |   401 | 14864   (1)

| 00:02:59 |

|*  1 |  TABLE ACCESS BY INDEX ROWID| CR_TICKET    |     1 |   401 | 14864   (1)

| 00:02:59 |

|*  2 |   INDEX RANGE SCAN          | CRT_STAT_IDX |   230K|       |   408   (2)

| 00:00:05 |



SQL> SELECT DBADMIN.CR_TICKET.TICKET_ID FROM DBADMIN.CR_TICKET WHERE    QUEUE_ID=005555     AND STATUS=4 AND PRIORITY=1;


| Id  | Operation                   | Name         | Rows  | Bytes | Cost (%CPU)

| Time     |

--------------------------------------------------------------------------------

------------

|   0 | SELECT STATEMENT            |              |     1 |    24 | 14845   (1)

| 00:02:59 |

|*  1 |  TABLE ACCESS BY INDEX ROWID| CR_TICKET    |     1 |    24 | 14845   (1)

| 00:02:59 |

|*  2 |   INDEX RANGE SCAN          | CRT_STAT_IDX |   230K|       |   408   (2)

| 00:00:05 |

--------------------------------------------------------------------------------

更新2013年5月25日

助けてくれてありがとう、感謝します。私はついにこのプロジェクトを再訪する機会があり、あなたの提案した質問を試しました。最初の1つは正常に実行され、次の2つは結果を返しませんでした。インデックス機能についておっしゃいましたね。これにより、データベースに永続的なインデックスが作成されますか?私はそれをしたくない。

select status, priority, count(*)
from dbadmin.cr_ticket
group by status, priority;

dbQuery1

4

1 に答える 1

1

本当の答えを得るには、両方のクエリの説明計画が必要です。

EXPLAIN PLAN を取得するには、SQL*Plus を介してデータベースに接続し、次のように入力します。

set lines 155
set page 999
explain plan for
SELECT * 
  FROM DBADMIN.CR_TICKET 
 WHERE QUEUE_ID=005555 
   AND STATUS=4;

@?/rdbms/admin/utlxpls

上記を行うことは危険ではありません。何も変更していません。実際のクエリを実行していません。SQL Developer や Toad などのグラフィカル ツールを使用している場合は、コードを選択するだけで、メニューに "show Explain Plan" が表示されるはずです。Toad では、ctrl+e を押すことができます。

現在、テーブルまたはインデックスの統計が古くなっていると思われます。「TICKED_ID」を含むインデックスがある場合がありますが、実際にはそうではありませんが、Oracle には「適切」に見えます。「SELECT *」に変更すると、使用する「カバーする」インデックスがなくなる可能性が高く、Oracle は代わりに別のより適切なインデックスを選択する可能性があります。

繰り返しますが、これは単なる推測作業です。両方のクエリの計画を教えてください。お手伝いします:)

2013-03-05 編集

WHERE TICKET_ID > 'CR00000000'特定の形式を想定しているため、おそらく実行しないでください。たとえば、TICKET_ID='A1' のレコードを挿入しても機能しません。

詳しくはわかりませんが、{queue_id, priority} のインデックスがおそらく良い候補になるでしょう。しかし、あなたのデータが私が思うように見える場合、小さなサブセットのみがステータス= 4であるため、代わりに関数ベースのインデックスを使用することでメリットが得られます.

次のようになります。

create index dbadmin.cr_ticket_fbx_stat_4
    on dbadmin.cr_ticket(case when status = 4 then 4 end);

このインデックスと status 列の通常のインデックスの違いは、ここでは status = 4 の行のみが実際にインデックス付けされることです。したがって、テーブルに 1,000,000 行があり、ステータスが 4 の行が 1,000 行しかない場合、インデックスには 1,000 エントリしか存在しません。本当に小さいでしょう。インデックスを使用するには、クエリを次のように記述する必要があります。

select *
  from dbadmin.cr_ticket
 where (case when status = 4 then 4 end) = 4;

次のクエリの結果を提供していただければ、さらにアドバイスできます。

select status, priority, count(*)
  from dbadmin.cr_ticket
group by status, priority;

select avg(count(*)), median(count(*)), min(count(*)), max(count(*))
  from dbadmin.cr_ticket
 group by queue_id;

select count(*), count(distinct queue_id)
  from dbadmin.cr_ticket; 
于 2013-03-03T22:38:10.093 に答える