web-dev-qa-db-ja.com

テーブルのさまざまな基準からCOUNTを選択する

'jobs'という名前のテーブルがあります。特定のユーザーの場合、ジョブはアクティブ、アーカイブ、期限切れ、保留、またはクローズになります。現在、すべてのページリクエストが5つのCOUNTクエリを生成しており、最適化を試みるために、これを1つのクエリに削減しようとしています。これは私がこれまでに持っているものですが、5つの個別のクエリよりもかろうじて高速です。理解しやすいように各サブクエリの条件を簡略化したことに注意してください。ただし、完全なクエリは同じように機能します。

非効率的なサブクエリを使用せずに、同じクエリでこれらの5つのカウントを取得する方法はありますか?

SELECT
  (SELECT count(*)
    FROM "jobs"
    WHERE
      jobs.creator_id = 5 AND
      jobs.status_id NOT IN (8,3,11) /* 8,3,11 being 'inactive' related statuses */
  ) AS active_count, 
  (SELECT count(*)
    FROM "jobs"
    WHERE
      jobs.creator_id = 5 AND
      jobs.due_date < '2011-06-14' AND
      jobs.status_id NOT IN(8,11,5,3) /* Grabs the overdue active jobs
                                      ('5' means completed successfully) */
  ) AS overdue_count,
  (SELECT count(*)
    FROM "jobs"
    WHERE
      jobs.creator_id = 5 AND
      jobs.due_date BETWEEN '2011-06-14' AND '2011-06-15 06:00:00.000000'
  ) AS due_today_count

これはさらに2つのサブクエリに続きますが、あなたはその考えを理解していると思います。

このデータは、ジョブテーブルの同じサブセットのデータから基本的に5つの異なるCOUNTであるため、このデータを収集する簡単な方法はありますか?

データのサブセットは「creator_id = 5」であり、その後の各カウントは基本的に1〜2の追加条件です。現在、Postgresを使用していますが、近い将来MySQLに移行する可能性があることに注意してください。したがって、ANSI互換のソリューションを提供できれば、私は感謝します:)

17
nzifnab

これが典型的な解決策です。 caseステートメントを使用して、さまざまな条件を分類します。レコードが一致する場合、1、それ以外の場合は0になります。次に、値に対してSUMを実行します。

_  SELECT
    SUM(active_count) active_count,
    SUM(overdue_count) overdue_count
    SUM(due_today_count) due_today_count
  FROM 
  (

  SELECT 
    CASE WHEN jobs.status_id NOT IN (8,3,11) THEN 1 ELSE 0 END active_count,
    CASE WHEN jobs.due_date < '2011-06-14' AND jobs.status_id NOT IN(8,11,5,3)  THEN 1 ELSE 0 END  overdue_count,
    CASE WHEN jobs.due_date BETWEEN '2011-06-14' AND '2011-06-15 06:00:00.000000' THEN 1 ELSE 0 END  due_today_count

    FROM "jobs"
    WHERE
      jobs.creator_id = 5 ) t
_

UPDATE 0レコードがtとして返される場合に前述したように、これはすべての値のNullの単一の結果として発生します。 3つのオプションがあります

1)すべてのNULLの結果ではなく、レコードが返されないように、Having句を追加します

_   HAVING SUM(active_count) is not null
_

2)すべての合計に合体を追加するよりも、すべてのゼロを返したい場合

例えば

_ SELECT
      COALESCE(SUM(active_count)) active_count,
       COALESCE(SUM(overdue_count)) overdue_count
      COALESCE(SUM(due_today_count)) due_today_count
_

3)sbarroが示したようにCOUNT(NULL) = 0という事実を利用します。 null以外の値は、1である必要はないものであれば何でもかまいません。

例えば

_ SELECT
      COUNT(CASE WHEN 
            jobs.status_id NOT IN (8,3,11) THEN 'Manticores Rock' ELSE NULL
       END) as [active_count]
_
25
Conrad Frix

私はこのアプローチを使用し、COUNTをCASEWHENと組み合わせて使用​​します。

SELECT 
    COUNT(CASE WHEN 
        jobs.status_id NOT IN (8,3,11) THEN 1 
    END) as [Count1],
    COUNT(CASE WHEN 
        jobs.due_date < '2011-06-14' 
        AND jobs.status_id NOT IN(8,11,5,3) THEN 1
    END) as [COUNT2],
    COUNT(CASE WHEN
            jobs.due_date BETWEEN '2011-06-14' AND '2011-06-15 06:00:00.000000'
    END) as [COUNT3]
FROM 
    "jobs"
WHERE 
     jobs.creator_id = 5 
12
rsbarro

簡単な

SQL Server2012ではIIF論理関数が導入されました 。 SQL Server 2012以降を使用すると、CASE式の代わりにこの新しい関数を使用できるようになりました。 IIF関数はAzure SQL Databaseでも機能します(ただし、現時点ではでは機能しません)Azure SQL Data WarehouseまたはParallel Data Warehouse)。 CASE式の省略形です。

ケースが1つしかない場合は、IIF式ではなくCASE関数を使用していることに気付きます。これにより、_CASE WHEN condition THEN x ELSE y END_を記述し、代わりにIIF(condition, x, y)として記述しなければならないという苦痛が軽減されます。複数の条件(複数のWHENs)が満たされる可能性がある場合は、ネストされたCASE関数ではなく、通常のIIF式の使用を検討する必要があります。

SQL Serverでブール式がtrueと評価されるかfalseと評価されるかに応じて、2つの値のいずれかを返します。

構文

_IIF ( boolean_expression, true_value, false_value )
_

引数

_boolean_expression_
有効なブール式。

この引数がブール式でない場合、構文エラーが発生します。

_true_value_
_boolean_expression_がtrueと評価された場合に返される値。

_false_value_
_boolean_expression_がfalseと評価された場合に返される値。

備考

IIFは、CASE式を記述するための簡単な方法です。最初の引数として渡されたブール式を評価し、評価の結果に基づいて他の2つの引数のいずれかを返します。つまり、ブール式がtrueの場合は_true_value_が返され、ブール式がfalseまたは不明の場合は_false_value_が返されます。 _true_value_および_false_value_は任意のタイプにすることができます。ブール式、null処理、および戻り値の型のCASE式に適用されるのと同じ規則が、IIFにも適用されます。詳細については、 CASE(Transact-SQL) を参照してください。

IIFCASEに変換されるという事実は、この関数の動作の他の側面にも影響を及ぼします。 CASE式はレベル10までしかネストできないため、IIFステートメントも最大レベル10までしかネストできません。また、IIFは他のサーバーは、意味的に同等のCASE式であり、リモートのCASE式のすべての動作を備えています。


コード

SQLでのIIF関数の実装は、次のようになります( @ rsbarro in his answer で示されるのと同じロジックを使用)。

_SELECT 
    COUNT(
        IIF(jobs.status_id NOT IN (8,3,11), 1, 0)
    ) as active_count,
    COUNT(
        IIF(jobs.due_date < '2011-06-14' AND jobs.status_id NOT IN(8,11,5,3), 1, 0)
    ) as overdue_count,
    COUNT(
        IIF(jobs.due_date BETWEEN '2011-06-14' AND '2011-06-15 06:00:00.000000', 1, 0)
    ) as due_today_count
FROM 
    "jobs"
WHERE 
     jobs.creator_id = 5 
_
0
ctwheels