Sequelize подзапрос как поле

Я пытаюсь получить такой запрос, который будет сгенерирован 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: [[...]]}, но без везения.

Любые идеи? или, может быть, следует использовать одно большое предложение GROUP BY и позволить всем "регулярным" полям сгруппироваться?

Ответ 1

Ваш лучший вариант:

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

Это выглядит как расширение для выпуска # 1869:

В настоящее время неудачно невозможно выполнить запрос на сквозную модель/таблицу соединений.

Ваш вопрос также касательно связан с этим, где возник вопрос о том, что существует "для каждой таблицы, связанной с пользовательским запросом".

Глядя на тестовый код для предложения 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.

В качестве альтернативы вы можете добавить метод экземпляра, который возвращает другое обещание:

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

Версия метода экземпляра не очень чистая, но она работает нормально и может быть более подходящей в зависимости от вашей ситуации.

Лучшим решением, которое я нашел, является использование литерала SQL в поле attribute запроса. Единственным недостатком является то, что он, кажется, очищает шифер от выбора других атрибутов, а "*" не режет его. Итак, вам нужно сделать обходной путь с помощью 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 }

Кстати, я также попытался сделать это назад и с помощью GROUP BY в модели Order выбрать модель Customer, но это не сработало:

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

Ответ 2

В 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']
        ]
    }
});