【问题标题】:Joining same table multiple times with Sequelize使用 Sequelize 多次加入同一个表
【发布时间】:2018-07-23 13:41:21
【问题描述】:

我有以下型号:

const User = Sequelize.define('user', {
    login: Sequelize.DataTypes.STRING,
    password: Sequelize.DataTypes.STRING,
    is_manager: Sequelize.DataTypes.BOOLEAN,
    notes: Sequelize.DataTypes.STRING
});

const Bike = Sequelize.define('bike', {
    model: Sequelize.DataTypes.STRING,
    photo: Sequelize.DataTypes.BLOB,
    color: Sequelize.DataTypes.STRING,
    weight: Sequelize.DataTypes.FLOAT,
    location: Sequelize.DataTypes.STRING,
    is_available: Sequelize.DataTypes.BOOLEAN
});

const Rate = Sequelize.define('rate', {
    rate: Sequelize.DataTypes.INTEGER
});
Rate.belongsTo(User);
User.hasMany(Rate);
Rate.belongsTo(Bike);
Bike.hasMany(Rate);

我想选择自行车的平均价格,加上每辆自行车当前用户的价格:

    Bike.findAll({
        attributes: {include: [[Sequelize.fn('AVG', Sequelize.col('rates.rate')), 'rate_avg']],
        },
        include: [{
            model: Rate,
            attributes: []
        }, {
            model: Rate,
            attributes: ['rate'],
            include: [{
                model: User,
                attributes: [],
                where: {
                    login: req.user.login
                }
            }]
        }],
        group: Object.keys(Bike.rawAttributes).map(key => 'bike.' + key) // group by all fields of Bike model
    })

它构造以下查询:SELECT [bike].[id], [bike].[model], [bike].[photo], [bike].[color], [bike].[weight], [bike].[location], [bike].[is_available], AVG([rates].[rate]) AS [rate_avg], [rates].[id] AS [rates.id], [rates].[rate] AS [rates.rate] FROM [bikes] AS [bike] LEFT OUTER JOIN [rates] AS [rates] ON [bike].[id] = [rates].[bikeId] LEFT OUTER JOIN ( [rates] AS [rates] INNER JOIN [users] AS [rates->user] ON [rates].[userId] = [rates->user].[id] AND [rates->user].[login] = N'user' ) ON [bike].[id] = [rates].[bikeId] GROUP BY [bike].[id], [bike].[model], [bike].[photo], [bike].[color], [bike].[weight], [bike].[location], [bike].[is_available];

失败了:SequelizeDatabaseError: The correlation name 'rates' is specified multiple times in a FROM clause.

如何正确编写查询?我需要 Sequelize 为第二次连接中使用的 rates 表分配另一个别名(并将其列添加到 GROUP BY 子句,但这是下一步)。

【问题讨论】:

    标签: node.js sequelize.js


    【解决方案1】:

    解决方案:

    Bike.findAll({
            attributes: {include: [[Sequelize.fn('AVG', Sequelize.col('rates.rate')), 'rate_avg']],
        },
        include: [{
            model: Rate,
            attributes: []
        }, {
            model: Rate,
            required : false , // 1. just to make sure not making inner join
            separate : true , // 2. will run query separately , so your issue will be solved of multiple times 
            attributes: ['rate'],
            include: [{
                model: User,
                attributes: [],
                where: {
                    login: req.user.login
                }
            }]
            group : [] // 3. <------- This needs to be managed , so please check errors and add fields as per error
        }],
        group: Object.keys(Bike.rawAttributes).map(key => 'bike.' + key) // group by all fields of Bike model
    })
    

    注意:阅读评论

    【讨论】:

      【解决方案2】:

      Sequelize 不支持通过同一关联包含两次(请参阅hereherehere)。在模型级别,您可以在 Bike 和 Rate 之间定义 2 种不同的关联,但必须更改模型、添加新的外键等,这是一个非常老套的解决方案。

      顺便说一句,它不会解决您的其他问题,即您仅按 Bike 进行分组,然后想选择用户的费率。要解决此问题,您还必须更改分组以包括用户费率。 (请注意,如果用户每辆自行车的费率超过 1 个,这也可能会造成一些效率低下,因为自行车的费率会针对每个用户的费率重复平均。)

      一个合适的解决方案是使用窗口函数,首先平均每辆自行车的费率,然后过滤掉所有不属于登录用户的费率。可能看起来像这样:

      SELECT *
      FROM (
          SELECT bike.*,
              users.login AS user_login,
              AVG (rates.rate) OVER (PARTITION BY bike.id) AS rate_avg
          FROM bike
          INNER JOIN rates ON rates.bikeId = bike.id
          INNER JOIN users ON rates.userId = users.id
      )
      WHERE user_login = :req_user_login
      

      不幸的是,据我所知,sequelize 目前不支持 FROM 子句中的子查询和以这种方式使用窗口函数,因此您必须退回到原始查询。

      【讨论】:

        【解决方案3】:

        您可以通过添加与该模型的额外相同关联但使用不同的别名 as: 'alias1'as: 'alias2' ,... - 所有这些都存在于相同的模型 + 相同类型的协会。
        还在 github 问题上发布了这个解决方案:https://github.com/sequelize/sequelize/issues/7754#issuecomment-783404779

        例如对于有很多接收者的聊天
        关联(根据需要复制)

        Chat.hasMany(Receiver, {
            // foreignKey: ...
            as: 'chatReceiver',
        });
        Chat.hasMany(Receiver, {
            // foreignKey: ...
            as: 'chatReceiver2',
        });
        

        现在您可以多次包含关联模型,所有模型都具有不同的别名,因此它不会被覆盖。
        因此,您可以在查询中使用它们,如下所示:

        Chat.findAll({
            attributes: ["id"],
            include: [{
                required: true,
                model: Receiver,
                as: 'chatReceiver', // Alias 1
                attributes: [],
                where: { userID: 1 }, // condition 1
            }, {
                required: true,
                model: Receiver,
                as: 'chatReceiver2', // Alias 2
                attributes: [],
                where: { userID: 2 }, // condition 2 as needed
            }]
        });
        

        【讨论】:

          猜你喜欢
          • 2016-04-05
          • 2011-05-06
          • 2021-04-01
          • 1970-01-01
          • 1970-01-01
          • 2012-05-30
          • 1970-01-01
          • 1970-01-01
          相关资源
          最近更新 更多