ほとんどのプログラミング言語のメソッドの複雑さは、静的ソースコードアナライザーを使用して循環的複雑度で測定できます。 SQLクエリの複雑さを測定するための同様のメトリックはありますか?
クエリが返されるまでの時間を測定するのは簡単ですが、クエリの複雑さを定量化できるようにしたい場合はどうすればよいですか?
[編集/注]実行プランを取得することは便利ですが、この場合、必ずしもそれを特定しようとしているわけではありません。サーバーがクエリを実行するのがどれほど難しいかを探しているのではなく、開発者がクエリを書くのがどれほど難しいか、そして欠陥が含まれている可能性を識別するメトリックを探しています。
【編集・注2】確かに、複雑さの測定が役に立たない場合もありますが、役に立たない場合もあります。このトピックの詳細については、 この質問 を参照してください。
ソフトウェアの複雑さの一般的な尺度には、 循環的複雑度 (制御フローの複雑さの尺度)および Halstead複雑度 (算術の複雑さの尺度)が含まれます。
SQLクエリの「制御フロー」は、クエリの「and」および「or」演算子に最も関連しています。
「計算の複雑さ」は、SUMや暗黙のJOINSなどの演算子に最もよく関連しています。
SQLクエリの構文の各単位を「制御フロー」か「計算」かについて分類する方法を決定したら、循環的またはハルステッドメジャーを簡単に計算できます。
SQLオプティマイザーがクエリIthinkに対して行うことは、まったく関係ありません。複雑さの測定の目的は、クエリをどれだけ効率的に評価できるかではなく、人がクエリを理解するのがどれほど難しいかを特徴づけることです。
同様に、DDLが言うこと、またはビューが含まれるかどうかは、そのような複雑さの測定に含まれるべきではありません。これらのメトリックの背後にある仮定は、使用された抽象化内の機械の複雑さは、単にそれを呼び出すだけでは面白くないということです。おそらく、その抽象化はコーダーによってよく理解されていることを行うからです。これが、HalsteadおよびCyclomaticメジャーがカウントに呼び出されたサブルーチンを含まない理由であり、ビューとDDL情報がそれらの「呼び出された」抽象化であるという良い例を作ることができると思います。
最後に、これらの複雑さの数値がどれほど完全に正しいか完全に間違っているかは、複雑さに関する真実を反映していて、相互に比較できる限り、それほど重要ではありません。このようにして、どのSQLフラグメントが最も複雑であるかを選択し、それらをすべてソートして、最も複雑なフラグメントにテストの注意を集中させることができます。
クエリプランの取得が質問に答えるかどうかはわかりません。クエリプランは、データが返される(またはフィルターで使用される)前に、データに対して実行される計算に関する複雑さの一部を隠します。クエリプランでは、関連性のある重要なデータベースが必要です。実際、複雑さと実行の長さはやや反対です。 「良い、速い、安い-任意の2つを選んでください」のようなもの。
最終的には、間違いを犯す可能性、または私が書いたコードを理解できない可能性についてですか?
何かのようなもの:
WHERE
またはHAVING
の後の述語ごとに+1GROUP BY
式ごとに+1UNION
またはINTERSECT
ごとに+1CASE
式ごとに+1ストアドプロシージャのサイズ、オブジェクトの依存関係の数、パラメータの数の概要を示すスクリプトをお気軽に試してください-
SQLクエリは、手続き型ではなく宣言型です。目標を達成する方法を指定していません。 SQLエンジンは、攻撃の手続き型計画を作成します。これは、複雑さを探すのに適した場所である可能性があります。 EXPLAIN(またはEXPLAIN PLAN)ステートメントの出力を調べてみてください。これは、エンジンがクエリを実行するために使用するステップの大まかな説明になります。
そのようなことを行ったツールはわかりませんが、クエリをより複雑にするものは、次のように測定されるようです。結合の数条件の数関数の数サブクエリの数異なるデータタイプへのキャストの数caseステートメントの数ループまたはカーソルの数トランザクションのステップ数
ただし、複雑なクエリの方が欠陥の可能性が最も高いように見えることは事実ですが、単純なクエリは、理解していない人によって作成される可能性が高いため、欠陥が含まれている可能性が非常に高いことがわかります。データモデル、したがってそれらは正しく機能しているように見えるかもしれませんが、実際には間違ったデータを返します。ですから、そのようなメトリックがあなたに多くを教えてくれるかどうかはわかりません。
クエリの読みやすさに関連する複雑さのスコアを計算する簡単なアルゴリズムのアイデアは次のとおりです。
これは非常にうまく機能するはずです。たとえば、サブクエリのカウントは、SELECT
およびFROM
キーワードの数のカウントに似ています。
さまざまなウェイトテーブルでこのアルゴリズムを使用することにより、さまざまな次元で複雑さを測定することもできます。たとえば、クエリ間の微妙な比較を行います。または、SQLエンジンに固有のキーワードまたは関数を使用するクエリのスコアを高くします(例:GROUP_CONCAT
MySQLの場合)。
SQLキーワードの場合を考慮して、アルゴリズムを微調整することもできます。一貫して大文字でない場合は、複雑さを増してください。またはインデントを説明するため(キャリッジリターン、行上のキーワードの位置)
注:標準のフォーマッターを適用してコードの行数を数えることを提案した@redcalxの回答に触発されました。ただし、完全なAST(抽象構文木))を構築しないため、私のソリューションはより単純です。
これを行うツールがない場合、実用的なアプローチは、分析されるクエリが一貫してフォーマットされていることを確認してから、コードの行数をカウントすることです。
または、ファイルに保存するときにクエリのサイズをバイト単位で使用します(すべてのクエリが同じ文字エンコードを使用して保存されることに注意してください)。
見事ではありませんが、私が思うに他に何もない場合の複雑さの合理的な代用です。
SQL Serverを使用している場合は、実行プランでクエリのコスト(具体的にはサブツリーのコスト)を確認する必要があります。
Here は、実行プランで確認する必要があるいくつかの事項を説明するリンクです。
プログラミング言語では、時間計算量または空間計算量を計算するためのいくつかの方法があります。
同様に、SQLと比較することもできます。プロシージャの場合と同様に、プログラミング言語に似たループの行数はありますが、通常SQLのプログラミング言語での入力のみとは異なり、入力とともにテーブル内のデータに完全に依存します。動作するビューなどに加えて、クエリ自体のオーバーヘッドの複雑さ。
単純な行ごとのクエリのように
Select * from table ;
// This will totally depend on no of
records say n hence O(n)
Select max(input) from table;
// here max would be an extra
overhead added to each
Therefore t*O(n) where t is max
Evaluation time
RDBMSによっては、RDBMSがクエリをフェッチする際に実行する手順を分析するのに役立つクエリプランツールが存在する場合があります。
SQL Server Management Studio Expressには、クエリ実行プランが組み込まれています。 Pervasive PSQLには、クエリプランファインダーがあります。 DB2にも同様のツールがあります(それらの名前を忘れてしまいました)。
良い質問です。問題は、次のようなSQLクエリの場合です。
SELECT * FROM foo;
複雑さは、「foo」が何であるか、およびデータベースの実装に依存する場合があります。次のような関数の場合:
int f( int n ) {
if ( n == 42 ) {
return 0;
}
else {
return n;
}
}
そのような依存関係はありません。
ただし、SELECTの有用なメトリックは、それほど正確ではない場合でも、いくつか考え出すことができるはずだと思います。これがどのような答えになるのか興味があります。