7

この形式のネストされたデータをPostgreSQLからPHP連想配列に返そうとしています。

[
  'person_id': 1,
  'name': 'My Name',
  'roles': [
      [ 'role_id': 1, 'role_name': 'Name' ],
      [ 'role_id': 2, 'role_name': 'Another role name' ]
  ]
]

複合型を使用することは可能であるように思われます。この回答では、関数から複合型を返す方法について説明していますが、複合型の配列は扱いません。アレイに問題があります。

これが私のテーブルとタイプです:

CREATE TEMP TABLE people (person_id integer, name text);
INSERT INTO "people" ("person_id", "name") VALUES
    (1, 'name!');

CREATE TEMP TABLE roles (role_id integer, person_id integer, role_name text);
INSERT INTO "roles" ("role_id", "person_id", "role_name") VALUES
    (1, 1,  'role name!'),
    (2, 1,  'another role');

CREATE TYPE role AS (
    "role_name" text
);

CREATE TYPE person AS (
    "person_id" int,
    "name" text,
    "roles" role[]
);

私のget_people()関数は正常に解析されますが、実行時エラーがあります。現在、エラーが発生しています。array value must start with "{" or dimension information

CREATE OR REPLACE FUNCTION get_people()
    RETURNS person[] AS $$
DECLARE myroles role[];
DECLARE myperson people%ROWTYPE;
DECLARE result person[];
BEGIN
    FOR myperson IN
        SELECT *
        FROM "people"
    LOOP
        SELECT "role_name" INTO myroles
        FROM "roles"
        WHERE "person_id" = myperson.person_id;

        result := array_append(
            result,
            (myperson.person_id, myperson.name, myroles::role[])::person
        );
    END LOOP;

    RETURN result;
END; $$ LANGUAGE plpgsql;



回答の最後にあるErwinBrandstetterの質問に答えて更新
します。 そうです、SETOFを複合型で返すことができます。SELECTクエリはSETを返すため、SETは配列よりも扱いやすいことがわかりました。ネストされた配列を返す理由は、ネストされたデータを行のセットとして表すのが少し厄介だと思うからです。次に例を示します。

 person_id | person_name | role_name | role_id
-----------+-------------+-----------+-----------
         1 | Dilby       | Some role |    1978
         1 | Dilby       | Role 2    |       2
         2 | Dobie       | NULL      |    NULL

この例では、人物1には2つの役割があり、人物2には役割がありません。PL/pgSQL関数の別の1つにこのような構造を使用しています。このようなレコードセットをネストされた配列に変換する脆弱なPHP関数を作成しました。

この表現は正常に機能しますが、この構造にネストされたフィールドを追加することを心配しています。各人がグループの仕事も持っている場合はどうなりますか?ステータス?など。私の変換機能はもっと複雑になる必要があります。データの表現も複雑になります。人がn個の役割、m個のジョブ、およびo個のステータスを持っている場合、その人はmax(n, m, o)行にperson_id、、person_name、およびそれらが余分な行に無用に複製した他のデータ。パフォーマンスについてはまったく心配していませんが、これを可能な限り簡単な方法で実行したいと思います。もちろん..多分これが最も簡単な方法です!

これが、PostgreSQLでネストされた配列を直接処理したい理由を説明するのに役立つことを願っています。そしてもちろん、私はあなたが持っている提案を聞いてみたいです。

また、PHPでPostgreSQL複合型を扱う人にとって、このライブラリはPHPでのPostgreSQLのarray_agg()出力の解析に非常に役立つことがわかりました:https ://github.com/nehxby/db_type 。また、このプロジェクトは面白そうです:https ://github.com/chanmix51/Pomm

4

1 に答える 1

7

PostgreSQL 9.1.4でテストされたこの(改善および修正された)テストケースを考えてみましょう。

CREATE SCHEMA x;
SET search_path = x, pg_temp;

CREATE TABLE people (person_id integer primary key, name text);
INSERT INTO people (person_id, name) VALUES
 (1, 'name1')
,(2, 'name2');

CREATE TABLE roles (role_id integer, person_id integer, role_name text);
INSERT INTO roles (role_id, person_id, role_name) VALUES
 (1, 1, 'role name!')
,(2, 1, 'another role')
,(3, 2, 'role name2!')
,(4, 2, 'another role2');

CREATE TYPE role AS (
  role_id   int
 ,role_name text
);

CREATE TYPE person AS (
  person_id int
 ,name text
 ,roles role[]
);

働き:

CREATE OR REPLACE FUNCTION get_people()
    RETURNS person[]  LANGUAGE sql AS
$func$
SELECT ARRAY (
   SELECT (p.person_id, p.name
          ,array_agg((r.role_id, r.role_name)::role))::person
   FROM    people p
   JOIN    roles r USING (person_id)
   GROUP   BY p.person_id
   ORDER   BY p.person_id
   )
$func$;

電話:

SELECT get_people();

掃除:

DROP SCHEMA x CASCADE;

コア機能は次のとおりです。

  • 単純なSQLクエリのみをラップする非常に単純化された関数。
  • あなたの主な間違いは、あなたがrole_name textテーブルからそれをそうではないrolesタイプとして扱ったことでした。role

コードにそれ自体を語らせましょう。説明することが多すぎて、もう時間がありません。

これは非常に高度なものであり、このネストされたタイプを本当に返す必要があるかどうかはわかりません。ネストされていない複合型のSETのような、もっと簡単な方法があるのではないでしょうか。

于 2012-08-13T21:40:02.657 に答える