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

1日に複数のレコードを選択するにはどうすればよいですか?

    最大3つのレコードを選択したい 特定の日付範囲からの1日あたり。

    SELECT date_time, other_column
    FROM  (
       SELECT *, row_number() OVER (PARTITION BY date_time::date) AS rn
       FROM   tbl
       WHERE  date_time >= '2012-11-01 0:0'
       AND    date_time <  '2012-12-01 0:0'
       ) x
    WHERE  rn < 4;
    

    主なポイント

    • ウィンドウ関数row_number()を使用します rank() またはdense_rank() 質問によると間違っているでしょう-タイムスタンプが重複している3つ以上のレコードが選択される可能性があります。

    • どちらを定義しないので 1日に必要な行数の場合、正解はORDER BYを含めないことです。 ウィンドウ関数の句。質問に一致する任意の選択肢を提供します。

    • WHEREを変更しました

      からの句
      WHERE  date_time >= '20121101 00:00:00'  
      AND    date_time <= '20121130 23:59:59'
      

      WHERE  date_time >=  '2012-11-01 0:0'  
      AND    date_time <   '2012-12-01 0:0'
      

      '20121130 23:59:59.123'のようなコーナーケースでは、構文が失敗します。 。

      @Craigが提案したこと:

      date_time::date BETWEEN '2012-11-02' AND '2012-11-05'
      

      ..は正しく機能しますが、パフォーマンスに関するアンチパターンです。式のデータベース列にキャストまたは関数を適用する場合、プレーンインデックスは使用できません。

    PostgreSQL8.3のソリューション

    ベストソリューション :より新しいバージョン、できれば現在のバージョン9.2にアップグレードします。

    その他のソリューション

    数日間のみ UNION ALLを使用できます :

    SELECT date_time, other_column
    FROM   tbl t1
    WHERE  date_time >= '2012-11-01 0:0'
    AND    date_time <  '2012-11-02 0:0'
    LIMIT  3
    )
    UNION ALL 
    (
    SELECT date_time, other_column
    FROM   tbl t1
    WHERE  date_time >= '2012-11-02 0:0'
    AND    date_time <  '2012-11-03 0:0'
    LIMIT  3
    )
    ...
    

    ここでは括弧はオプションではありません。

    もっと日 generate_series()には回避策があります -私がここに投稿したようなもの (詳細へのリンクを含む)。

    plpgsql関数で解決したかもしれません ウィンドウ関数が登場する前の昔:

    CREATE OR REPLACE FUNCTION x.f_foo (date, date, integer
                             , OUT date_time timestamp, OUT other_column text)
      RETURNS SETOF record AS
    $BODY$
    DECLARE
        _last_day date;          -- remember last day
        _ct       integer := 1;  -- count
    BEGIN
    
    FOR date_time, other_column IN
       SELECT t.date_time, t.other_column
       FROM   tbl t
       WHERE  t.date_time >= $1::timestamp
       AND    t.date_time <  ($2 + 1)::timestamp
       ORDER  BY t.date_time::date
    LOOP
       IF date_time::date = _last_day THEN
          _ct := _ct + 1;
       ELSE
          _ct := 1;
       END IF;
    
       IF _ct <= $3 THEN
          RETURN NEXT;
       END IF;
    
       _last_day := date_time::date;
    END LOOP;
    
    END;
    $BODY$ LANGUAGE plpgsql STABLE STRICT;
    
    COMMENT ON FUNCTION f_foo(date3, date, integer) IS 'Return n rows per day
    $1 .. date_from (incl.)
    $2 .. date_to  (incl.)
    $3 .. maximim rows per day';
    

    電話:

    SELECT * FROM f_foo('2012-11-01', '2012-11-05', 3);
    


    1. UPDLOCK、HOLDLOCKについて混乱している

    2. MySQL REPLACE()–サブストリングのすべてのインスタンスを別のストリングに置き換えます

    3. 私のDBAは病気です-SysAdminsのためのデータベースフェイルオーバーのヒント

    4. SQLAlchemyパスワードが提供されていませんエラー