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

ネストされた結合でrow_to_json()を使用する

    更新:PostgreSQL 9.4では、to_jsonの導入により、これが大幅に改善されています。 、json_build_objectjson_object およびjson_build_array 、ただし、すべてのフィールドに明示的に名前を付ける必要があるため、冗長です:

    select
            json_build_object(
                    'id', u.id,
                    'name', u.name,
                    'email', u.email,
                    'user_role_id', u.user_role_id,
                    'user_role', json_build_object(
                            'id', ur.id,
                            'name', ur.name,
                            'description', ur.description,
                            'duty_id', ur.duty_id,
                            'duty', json_build_object(
                                    'id', d.id,
                                    'name', d.name
                            )
                    )
        )
    from users u
    inner join user_roles ur on ur.id = u.user_role_id
    inner join role_duties d on d.id = ur.duty_id;
    

    古いバージョンについては、読み進めてください。

    それは単一の行に限定されていません、それはほんの少し苦痛です。 ASを使用して複合行タイプのエイリアスを作成することはできません 、したがって、効果を得るには、エイリアス化されたサブクエリ式またはCTEを使用する必要があります。

    select row_to_json(row)
    from (
        select u.*, urd AS user_role
        from users u
        inner join (
            select ur.*, d
            from user_roles ur
            inner join role_duties d on d.id = ur.duty_id
        ) urd(id,name,description,duty_id,duty) on urd.id = u.user_role_id
    ) row;
    

    http://jsonprettyprint.com/経由で生成:

    {
      "id": 1,
      "name": "Dan",
      "email": "[email protected]",
      "user_role_id": 1,
      "user_role": {
        "id": 1,
        "name": "admin",
        "description": "Administrative duties in the system",
        "duty_id": 1,
        "duty": {
          "id": 1,
          "name": "Script Execution"
        }
      }
    }
    

    array_to_json(array_agg(...))を使用することをお勧めします 1:多くの関係がある場合、ところで。

    上記のクエリは、理想的には次のように記述できる必要があります。

    select row_to_json(
        ROW(u.*, ROW(ur.*, d AS duty) AS user_role)
    )
    from users u
    inner join user_roles ur on ur.id = u.user_role_id
    inner join role_duties d on d.id = ur.duty_id;
    

    ...しかしPostgreSQLのROW コンストラクターはASを受け入れません 列エイリアス。悲しいことに。

    ありがたいことに、彼らは同じように最適化します。計画を比較する:

    • ネストされたサブクエリバージョン。 vs
    • 後者のネストされたROW 実行するようにエイリアスが削除されたコンストラクタバージョン

    CTEは最適化フェンスであるため、ネストされたサブクエリバージョンを言い換えて、チェーンCTE(WITH)を使用します。 式)はうまく機能しない可能性があり、同じ計画にはなりません。この場合、row_to_jsonにいくつかの改善が加えられるまで、ネストされた醜いサブクエリで立ち往生しています。 または、ROWの列名を上書きする方法 コンストラクターをより直接的に。

    とにかく、一般的に、原則は、列a, b, cを持つjsonオブジェクトを作成する場合です。 、そしてあなたはあなたがただ違法な構文を書くことができたらいいのにと思います:

    ROW(a, b, c) AS outername(name1, name2, name3)
    

    代わりに、行タイプの値を返すスカラーサブクエリを使用できます:

    (SELECT x FROM (SELECT a AS name1, b AS name2, c AS name3) x) AS outername
    

    または:

    (SELECT x FROM (SELECT a, b, c) AS x(name1, name2, name3)) AS outername
    

    さらに、jsonを作成できることを覚えておいてください 追加の引用符なしの値、例: json_aggの出力を配置する場合 row_to_json内 、内側のjson_agg 結果は文字列として引用されず、jsonとして直接組み込まれます。

    例えば任意の例で:

    SELECT row_to_json(
            (SELECT x FROM (SELECT
                    1 AS k1,
                    2 AS k2,
                    (SELECT json_agg( (SELECT x FROM (SELECT 1 AS a, 2 AS b) x) )
                     FROM generate_series(1,2) ) AS k3
            ) x),
            true
    );
    

    出力は次のとおりです。

    {"k1":1,
     "k2":2,
     "k3":[{"a":1,"b":2}, 
     {"a":1,"b":2}]}
    

    json_aggに注意してください product、[{"a":1,"b":2}, {"a":1,"b":2}]textとして、再度エスケープされていません

    これは、作成できることを意味します 行を構築するためのjson操作では、必ずしも非常に複雑なPostgreSQL複合型を作成してから、row_to_jsonを呼び出す必要はありません。 出力に。



    1. YEAR()の例– MySQL

    2. TimescaleDBの自動フェイルオーバーを実現する方法

    3. T-SQLの日時は、関数を使用して最も近い分と最も近い時間に丸められます

    4. Postgresを使用して一度に3つのテーブルにデータを挿入します