web-dev-qa-db-ja.com

PostgreSQL 9.0のcrosstab()クエリに合計の列を追加する

私の前の質問に続いて:

PostgreSQL 9.0でcrosstab()ピボットテーブルを作成しています

crosstab()関数を使用してagebandのピボットテーブルを作成できました。これを使用して、基本のジオメトリレステーブルのビューまたはテーブルを作成できます。

ただし、さらに分析するためにジオメトリを割り当てるためにgazetteers_and_addresses.unit_postcodeテーブルにリンクする必要があるため、これはまだあまり役に立ちません。

両方のテーブルのテーブル構造と、クロス集計の作成に使用した元のコードを添付します。

CREATE OR REPLACE VIEW adult_social_care.vw_ageband AS (
    SELECT * FROM crosstab(
        'SELECT postcode_nospace_, ageband, count(ageband) as total_count
         FROM adult_social_care.activities_in_localities_asc
         GROUP BY postcode_nospace_, ageband
         ORDER BY postcode_nospace_'

         ,$$VALUES ('18-24'::text), ('25-34'), ('35-44'), ('45-54'), ('55-64'), ('65-74'), ('75-84'), ('85-94'), ('95 AND OVER')$$)
    AS ct("postcode" text, "18-24" numeric, "25-34" numeric,"35-44" numeric, "45-54" numeric, "55-64" numeric, "65-74" numeric, "75-84" numeric, "85-94" numeric, "95 AND OVER" numeric));

テーブルの定義:

activities_in_localities_asc

CREATE TABLE adult_social_care.activities_in_localities_asc (
  ogc_fid integer NOT NULL,
  sort numeric(5,0),
  ageband character(12),
  postcode_nospace_ character(8),
  wkb_geometry geometry,
  CONSTRAINT activities_in_localities_asc_pkey PRIMARY KEY (ogc_fid)
);

unit_postcode

CREATE TABLE gazetteers_and_addresses.unit_postcode (
  oogc_fid serial NOT NULL,
  pc_area character(10),
  postcode_nospaces text,
  wkb_geometry geometry
);

可能であれば、最後に、すべてのフィールドの合計を示す別のフィールドを割り当てて、total_countを指定します。

これが可能であれば、1つのジオメトリレステーブルとunit_postcodeを使用して、さまざまな要素に動的ビューを作成できます。

2
daniel franklin

適切なクロス集計クエリ

まず、表の定義にchar(n)が含まれていると、提示されたクエリは機能しません- 前の質問で説明したように 。最初に textまたはvarchar に変換する必要があります。

これは現在のテーブル定義で機能します:

_SELECT * FROM crosstab(
   'SELECT postcode_nospace_::text, ageband::text, count(ageband) AS ct
    FROM   adult_social_care.activities_in_localities_asc
    GROUP  BY 1, 2
    ORDER  BY 1'

   ,$$VALUES ('18-24'::text), ('25-34'), ('35-44'), ('45-54'), ('55-64')
           , ('65-74'), ('75-84'), ('85-94'), ('95 AND OVER')$$
   )
AS t("postcode" text
   , "18-24" bigint, "25-34" bigint,"35-44" bigint, "45-54" bigint, "55-64" bigint
   , "65-74" bigint, "75-84" bigint, "85-94" bigint, "95 AND OVER" bigint);_

また、bigintではなくnumericを使用して、別の不要な型変換を保存しています(count()bigintを返します)。

詳細な説明:

より良いテーブル定義

ただし、@ dezsoが提案するように、テーブルのデータ型をALTERtextするほうがよいまたは、長さの制限を維持したい場合は、CHECK制約を追加するか、varchar(n)を使用します。 char(n) は使用しないでください。

_ALTER TABLE activities_in_localities_asc
   ALTER COLUMN ageband TYPE varchar(12)
 , ALTER COLUMN postcode_nospace_ TYPE varchar(8);

 ALTER TABLE gazetteers_and_addresses.unit_postcode
   ALTER COLUMN pc_area TYPE varchar(10);
_

そうすれば、元の列の値をキャストする必要がなくなります。

_SELECT * FROM crosstab(
   'SELECT postcode_nospace_, ageband, count(ageband)
    ...
_

ただし、理想的には、agebandenum または(私の好み)ルックアップテーブルを参照するIDであり、単純でエラーが発生しやすいtext ...キーワード:正規化

合計数を追加

テーブル上に構築しますが、textまたはvarchar()列を使用します。

元のクエリを [〜#〜] cte [〜#〜] にラップし、UNIONクエリで結果を2回呼び出します。2回目の呼び出しでは、郵便番号ごとの合計が追加されます。

_SELECT * FROM crosstab(
  $$WITH cte AS (
      SELECT postcode_nospace_, ageband, count(ageband) AS ct
      FROM   adult_social_care.activities_in_localities_asc  
      GROUP  BY 1, 2
      )
   TABLE  cte  -- original results
   UNION ALL   -- add total per postcode
   SELECT postcode_nospace_, 'total' AS ageband, sum(ct) AS ct
   FROM   cte
   GROUP  BY 1
   ORDER  BY 1$$  -- dollar-quotes to include single quotes easily

   ,$$VALUES ('18-24'::text), ('25-34'), ('35-44'), ('45-54'), ('55-64')
           , ('65-74'), ('75-84'), ('85-94'), ('95 AND OVER'), ('total')$$
   )
AS t("postcode" text
   , "18-24" bigint, "25-34" bigint,"35-44" bigint, "45-54" bigint, "55-64" bigint
   , "65-74" bigint, "75-84" bigint, "85-94" bigint, "95 AND OVER" bigint, "total" bigint);_

TABLEコマンドについて:

関連するCTEの例:

2