2017-04-22 13 views
2

私は再帰のいくつかの種類を必要とすると信じている、トラブル次のような問題を解決し、クエリを書いを抱えている:子の「カテゴリ」と子の「カテゴリ」のすべての行:再帰的?

私はそれらのそれぞれが特定のhouse_typeを持つ、housesを持つテーブルが、P。E. house_typesは互いに相続し、house_typesという表でも宣言されています。

table: houses 
id | house_type 
1 | house 
2 | bungalow 
3 | villa 
etcetera... 

table: house_types 
house_type | parent 
house  | null 
villa  | house 
bungalow | villa 
etcetera... 

この論理では、バンガローもヴィラであり、ヴィラもまた家です。だから私がすべてのヴィラを手に入れたいときは、2番と3番の家が現れます。すべての家を取得したいときは1,2,3番が表示されます。すべてのバンガローが必要なときは3番だけが表示されます。

再帰的なクエリとその回答はどうすればよいでしょうか。私はnode.jsアプリケーションでknex/objection.jsを使用します。ここで

+0

関連:http://stackoverflow.com/questions/39805736/get-join-table-as-array-of-results-with-postgresql-nodejs –

答えて

2

は、階層内のすべてのペアを取得する再帰CTEである:

with recursive house_types as (
     select 'house' as housetype, null as parent union all 
     select 'villa', 'house' union all 
     select 'bungalow', 'villa' 
    ), 
    cte(housetype, alternate) as (
     select housetype, housetype as alternate 
     from house_types 
     union all 
     select ht.housetype, cte.alternate 
     from cte join 
      house_types ht 
      on cte.housetype = ht.parent 
    ) 
select * 
from cte; 

house_types CTEは単にデータを設定することです。)

あなたは、その後に他のデータにこれを参加できます階層の任意のレベルを取得します。

+0

ニース、これは私が行きました。ちょうど私が実際のデータベースに親を持つhouse_typesをたくさん得たので、これをas()の部分でどのように修正すればよいでしょうか?私はすべての可能性を入力する必要がありますか? –

+0

@MatsdeSwart。 。 。あなたのクエリにはCTE 'house_types'は必要ありません。それはそれがどのように機能するかを示すことです。そのCTEを削除できます。 –

1

@ gordon-linoffsで始めるには、すばらしいです。ここでは、knex/objection.jsを使って具体的な方法を追加する方法について説明します。

かなり厄介なdbデザインのようです。私は型データを非正規化して、再帰的な共通テーブル式なしでクエリを作成しやすくする(knexは現在それらをサポートしていない)。とにかくここ

objection.jsモデルを行うと、あなたがしようとしているクエリを作成することができることのためにJavaSript側の情報denormalisationを入力する方法をいくつかの実行可能なコードです:https://runkit.com/mikaelle/stackoverflow-43554373

stackoverflowのものコードが含まれているのが好きなので、私もここに貼り付けてコピーします。例ではDBバックエンドとしてsqlite3を使用していますが、同じコードはpostgresでも動作します。

const _ = require('lodash'); 
require("sqlite3"); 

const knex = require("knex")({ 
    client: 'sqlite3', 
    connection: ':memory:' 
}); 

const { Model } = require('objection'); 

// init schema and test data 
await knex.schema.createTable('house_types', table => { 
    table.string('house_type'); 
    table.string('parent').references('house_types.house_type'); 
}); 

await knex.schema.createTable('houses', table => { 
    table.increments('id'); 
    table.string('house_type').references('house_types.house_type'); 
}); 

await knex('house_types').insert([ 
    { house_type: 'house', parent: null }, 
    { house_type: 'villa', parent: 'house' }, 
    { house_type: 'bungalow', parent: 'villa' } 
]); 

await knex('houses').insert([ 
    {id: 1, house_type: 'house' }, 
    {id: 2, house_type: 'villa' }, 
    {id: 3, house_type: 'bungalow' } 
]); 

// show initial data from DB 
await knex('houses') 
    .join('house_types', 'houses.house_type', 'house_types.house_type'); 

// create models 
class HouseType extends Model { 
    static get tableName() { return 'house_types' }; 

    // http://vincit.github.io/objection.js/#relations 
    static get relationMappings() { 
    return { 
     parent: { 
     relation: Model.HasOneRelation, 
     modelClass: HouseType, 
     join: { 
      from: 'house_types.parent', 
      to: 'house_types.house_type' 
     } 
     } 
    } 
    } 
} 

class House extends Model { 
    static get tableName() { return 'houses' }; 

    // http://vincit.github.io/objection.js/#relations 
    static relationMappings() { 
    return { 
     houseType: { 
     relation: Model.HasOneRelation, 
     modelClass: HouseType, 
     join: { 
      from: 'houses.house_type', 
      to: 'house_types.house_type' 
     } 
     } 
    } 
    } 
} 

// get all houses and all house types with recursive eager loading 
// http://vincit.github.io/objection.js/#eager-loading 
JSON.stringify(
    await House.query(knex).eager('houseType.parent.^'), null, 2 
); 

// however code above doesn't really allow you to filter 
// queries nicely and is pretty inefficient so as far as I know recursive 
// with query is only way how to do it nicely with pure SQL 

// since knex doesn't currently support them we can first denormalize housetype 
// hierarchy (and maybe cache this one if data is not changing much) 
const allHouseTypes = await HouseType.query(knex).eager('parent.^'); 

// initialize house types with empty arrays 
const denormalizedTypesByHouseType = _(allHouseTypes) 
    .keyBy('house_type') 
    .mapValues(() => []) 
    .value(); 

// create denormalized type array for every type 
allHouseTypes.forEach(houseType => { 
    // every type should be returned with exact type e.g. bungalow is bungalow 
    denormalizedTypesByHouseType[houseType.house_type].push(houseType.house_type); 
    let parent = houseType.parent; 
    while(parent) { 
    // bungalow is also villa so when searched for villa bungalows are returned 
    denormalizedTypesByHouseType[parent.house_type].push(houseType.house_type); 
    parent = parent.parent; 
    } 
}); 

// just to see that denormalization did work as expected 
console.log(denormalizedTypesByHouseType); 

// all villas 
JSON.stringify(
    await House.query(knex).whereIn('house_type', denormalizedTypesByHouseType['villa']), 
    null, 2 
); 
関連する問題