web-dev-qa-db-fra.com

séquencer la sous-requête en tant que champ

J'essaie d'obtenir qu'une telle requête soit générée par séquence:

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";

J'essaie d'éviter la clause GROUP BY, car j'ai beaucoup de champs à sélectionner et je ne veux pas les regrouper par tous (je pense que ce n'est pas efficace, n'est-ce pas?)

J'ai essayé plusieurs méthodes pour y arriver avec séquençage, notamment {include: ...} et {attributes: [[...]]}, mais sans aucune chance.

Des idées? ou peut-être devrais-je utiliser une grosse clause GROUP BY et laisser tous les champs "normaux" être regroupés?

11
danprinz

Votre meilleure option est:

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

Cela ressemble à une extension pour émettre # 1869 :

Interroger sur la table de modèle/joint est actuellement impossible.

Votre question est également liée de manière tangentielle à celle-ci , où la question était une sorte de "pour chaque table associée à une requête d'utilisateur".

En examinant le code de test pour la clause include, je ne vois aucune option de groupe, ce qui constitue une absence de preuve pour cette fonctionnalité.

Solutions:

Bien sûr, vous pouvez simplement passer la requête "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}
    );

Cela vous donnera envie de vouloir, et encapsulé dans des instances Customer.

Vous pouvez également ajouter une méthode d'instance qui renvoie une autre promesse:

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

La version de la méthode d'instance n'est pas très propre, mais elle fonctionne correctement et peut être plus appropriée selon votre situation.

La meilleure solution que j'ai trouvée consiste à utiliser un littéral SQL dans le champ attribute d'une requête. Le seul inconvénient est qu'il semble effacer l'ardoise lors de la sélection des autres attributs, et qu'un '*' ne le coupe pas. Donc, vous devez faire la solution de contournement avec 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']
        ])
    });

Pourtant, cela fonctionne un charme et vous pouvez l'utiliser pour certains SELECTs imbriqués plus intéressants. Et cette findAll nous donne le correct:

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 }

D'ailleurs, j'ai aussi essayé de le faire à l'envers et d'utiliser GROUP BY sur le modèle Order pour le sélectionner dans le modèle Customer, mais cela n'a pas fonctionné:

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

Dans Sequelize 4, vous pouvez ajouter des attributs supplémentaires à l'aide de la syntaxe attributes.includehttp://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']
        ]
    }
});
1
type