web-dev-qa-db-ja.com

フィールドとしてサブクエリを連続させる

私はそのようなクエリをsequlizedで生成しようとしています:

SELECT 
    "Customers"."id", 
    (SELECT SUM("Orders"."amount") FROM "Orders"
     WHERE "Orders"."CustomerId" = "Customers"."id") AS "totalAmount",
    "Customer"."lastName" AS "Customer.lastName",
    "Customer"."firstName" AS "Customer.firstName" 
FROM "Customers" AS "Customer";

選択するフィールドがたくさんあり、それらすべてでグループ化したくないので、GROUP BY句を回避しようとしています(効率的ではないと思います)。

私はそれをsequelizeで実現するためにいくつかの方法を試しましたが、{include: ...}{attributes: [[...]]}を含めましたが、うまくいきませんでした。

何か案は?または、1つの大きなGROUP BY句を使用して、すべての「通常の」フィールドをグループ化する必要がありますか?

14
danprinz

あなたの最良の選択肢は:

_    return Customer.findAll({
        attributes: Object.keys(Customer.attributes).concat([
            [sequelize.literal('(SELECT SUM("Orders"."amount") FROM "Orders" WHERE "Orders"."CustomerId" = "Customer"."id")'), 'totalAmount']
        ])
    });
_

これは、拡張機能のようです #1869

残念ながら、現在、モデル/結合テーブルを介したクエリは実行できません。

あなたの質問はまた、接線的に this one に関連しています。ここで、質問は、「ユーザークエリに関連付けられたテーブルごと」のようなものでした。

include句の テストコード を見ると、グループオプションが表示されません。これは、この機能の証拠がないためです。

ソリューション:

もちろん、 "raw"クエリを渡すだけでもかまいません。

_    return sequelize.query(
        'SELECT *, (SELECT SUM("Orders"."amount") FROM "Orders" WHERE "Orders"."CustomerId" = "Customer"."id") AS "totalAmount" FROM "Customers" AS "Customer";',
        Customer,
        {raw: false}
    );
_

これにより、必要なものが得られ、Customerインスタンスにラップされます。

または、別のpromiseを返すインスタンスメソッドを追加することもできます。

_instanceMethods: {
    getOrderSummary: function () {
        return Order.findAll({
            where: {
                CustomerId: this.id
            },
            attributes: [
                [sequelize.fn('SUM', sequelize.col('amount')), 'sum'],
                'CustomerId'],
            group: ['CustomerId']
        });
    }
}
_

インスタンスメソッドのバージョンはあまりきれいではありませんが、問題なく動作し、状況によってはより適切な場合があります。

私が見つけた最良の解決策は、クエリのattributeフィールドでSQLリテラルを使用することです。唯一の欠点は、他の属性を選択するとスレートが完全に消去されるように見えることと、「*」ではそれが切り取られないことです。したがって、Object.keys()で回避策を実行する必要があります。

_    return Customer.findAll({
        attributes: Object.keys(Customer.attributes).concat([
            [sequelize.literal('(SELECT SUM("Orders"."amount") FROM "Orders" WHERE "Orders"."CustomerId" = "Customer"."id")'), 'totalAmount']
        ])
    });
_

それでも魅力的に機能し、さらに興味深いネストされたSELECTに使用できます。そしてfindAllは正しいものを私たちに与えます:

_Executing (default): SELECT "id", "firstName", "lastName", "createdAt", "updatedAt", (SELECT SUM("Orders"."amount") FROM "Orders" WHERE "Orders"."CustomerId" = "Customer"."id") AS "totalAmount" FROM "Customers" AS "Customer";
{ id: 1,
  firstName: 'Test',
  lastName: 'Testerson',
  createdAt: Wed Feb 04 2015 08:05:42 GMT-0500 (EST),
  updatedAt: Wed Feb 04 2015 08:05:42 GMT-0500 (EST),
  totalAmount: 15 }
{ id: 2,
  firstName: 'Invisible',
  lastName: 'Hand',
  createdAt: Wed Feb 04 2015 08:05:42 GMT-0500 (EST),
  updatedAt: Wed Feb 04 2015 08:05:42 GMT-0500 (EST),
  totalAmount: 99 }
_

ところで、私はそれを逆にして、OrderモデルでGROUP BYを使用してCustomerモデルを選択することも試みましたが、うまくいきませんでした:

_    // Doesn't work
    return Order.findAll({
        attributes: [
            [Sequelize.fn('COUNT', '*'), 'orderCount'],
            'CustomerId'
        ],
        include: [
            {model: Customer, attributes: ['id']}
        ],
        group: ['CustomerId']
    });
_
42
srlm

Sequelize 4では、attributes.include構文を使用して属性を追加できます http://docs.sequelizejs.com/manual/tutorial/querying.html

return Customer.findAll({
    attributes: {
        include: [
           [sequelize.literal('(SELECT SUM("Orders"."amount") FROM "Orders" 
            WHERE "Orders"."CustomerId" = "Customer"."id")'), 'totalAmount']
        ]
    }
});
7
type