sql >> データベース >  >> RDS >> PostgreSQL

内側の結合のように機能する左の外側の結合

    クエリはおそらく次のように簡略化できます。

    SELECT u.name AS user_name
         , p.name AS project_name
         , tl.created_on::date AS changeday
         , coalesce(sum(nullif(new_value, '')::numeric), 0)
         - coalesce(sum(nullif(old_value, '')::numeric), 0) AS hours
    FROM   users             u
    LEFT   JOIN (
            tasks            t 
       JOIN fixins           f  ON  f.id = t.fixin_id
       JOIN projects         p  ON  p.id = f.project_id
       JOIN task_log_entries tl ON  tl.task_id = t.id
                               AND  tl.field_id = 18
                               AND (tl.created_on IS NULL OR
                                    tl.created_on >= '2013-09-08' AND
                                    tl.created_on <  '2013-09-09') -- upper border!
           ) ON t.assignee_id = u.id
    WHERE  EXISTS (SELECT 1 FROM tasks t1 WHERE t1.assignee_id = u.id)
    GROUP  BY 1, 2, 3
    ORDER  BY 1, 2, 3;
    

    これにより、これまでにタスクを実行したことがあるすべてのユーザーが返されます。
    プロジェクトごとおよび1日あたりのデータに加えて task_log_entriesで指定された日付範囲にデータが存在する場合 。

    主なポイント

    • 集計関数sum() NULLを無視します 値。 COALESCE() 行ごと 2つの合計の差として計算を再キャストするとすぐに、もう必要ありません:

       ,coalesce(sum(nullif(new_value, '')::numeric), 0) -
        coalesce(sum(nullif(old_value, '')::numeric), 0) AS hours
      

      ただし、 if すべて 選択範囲の列にNULLがあります または空の文字列、合計をCOALESCEにラップします 一度。
      私はnumericを使用しています floatの代わりに 、丸め誤差を最小限に抑えるためのより安全な代替手段。

    • usersの結合から明確な値を取得しようとする試み およびtasks taskに参加するので、無駄です もう一度下に。クエリ全体をフラット化して、クエリをよりシンプルかつ高速にします。

    • これらの位置参照 単なる表記上の便宜です:

      GROUP BY 1, 2, 3
      ORDER BY 1, 2, 3
      

      ...元のクエリと同じことを行います。

    • dateを取得するには timestampから dateにキャストするだけです :

      tl.created_on::date AS changeday
      

      ただし、WHEREの元の値でテストすることをお勧めします。 句またはJOIN 条件(可能な場合、ここで可能)、Postgresは列にプレーンインデックスを使用できます(可能な場合):

       AND (tl.created_on IS NULL OR
            tl.created_on >= '2013-09-08' AND
            tl.created_on <  '2013-09-09')  -- next day as excluded upper border
      

      日付リテラルに注意してください timestampに変換されます 00:00に 今日の現在の時刻ゾーン 。 次へを選択する必要があります 日と除外 それを上縁として。または、'2013-09-22 0:0 +2':: timestamptzのようなより明示的なタイムスタンプリテラルを指定します 。上部境界線の除外の詳細:

    • 要件every user who has ever been assigned to a task WHEREを追加します 条項:

      WHERE EXISTS (SELECT 1 FROM tasks t1 WHERE t1.assignee_id = u.id)
      
    • 最も重要なことLEFT [OUTER] JOIN 結合の左側にあるすべての行を保持します。 WHEREを追加する 権利の条項 テーブルはこの効果を無効にすることができます。代わりに、フィルター式をJOINに移動します 条項。 詳細はこちら:

    • 括弧 テーブルが結合される順序を強制するために使用できます。単純なクエリではめったに必要ありませんが、この場合は非常に便利です。この機能を使用してtaskに参加します 、fixinsprojects およびtask_log_entries 左に移動する前に-すべてをusersに参加させます -サブクエリなし。

    • テーブルエイリアス 複雑なクエリを簡単に作成できます。



    1. PHPの複数の画像ファイルのアップロードとフォルダおよびデータベースへの保存

    2. テーブルフィールドにハイフンを含めることはできますか?

    3. RマークダウンSQLチャンクでSQLパラメーターを使用する方法

    4. 未定義のプロパティを返すphp