【问题标题】:Optimizing queries on related tables in Sequelize在 Sequelize 中优化对相关表的查询
【发布时间】:2020-02-24 00:08:17
【问题描述】:

我想知道是否有更好的方法来获得以下结果:从通过多对多关系连接的表中过滤具有值的行列表。 在我的示例中,是具有某些乐器的歌曲。

这是我的歌曲模型:

module.exports = function(sequelize, DataTypes) {
    var Song = sequelize.define('songs', {
        id: {
            type: DataTypes.INTEGER(10).UNSIGNED,
            allowNull: false,
            primaryKey: true,
            autoIncrement: true
        },
        title: {
            type: DataTypes.STRING(50),
            allowNull: false
        },
    }, {
        tableName: 'songs',
        timestamps: false
    });

    Song.associate = function (models) {
        Song.belongsToMany(models.instruments, {through: 'songs_instruments', foreignKey: 'song_id'})
    };

    return Song
};

这是我的乐器模型:

module.exports = function(sequelize, DataTypes) {
    var Instrument = sequelize.define('instruments', {
        id: {
            type: DataTypes.INTEGER(10).UNSIGNED,
            allowNull: false,
            primaryKey: true,
            autoIncrement: true
        },
        name: {
            type: DataTypes.STRING(50),
            allowNull: false
        }
    }, {
        tableName: 'instruments',
        timestamps: false,
    });

    Instrument.associate = function (models) {
        Instrument.belongsToMany(models.songs, {through: 'songs_instruments', foreignKey: 'instrument_id'})
    };

    return Instrument
};

这是连接表的模型:

module.exports = function(sequelize, DataTypes) {
    return sequelize.define('songs_instruments', {
        id: {
            type: DataTypes.INTEGER(10).UNSIGNED,
            allowNull: false,
            primaryKey: true,
            autoIncrement: true
        },
        song_id: {
            type: DataTypes.INTEGER(10).UNSIGNED,
            allowNull: false,
            references: {
                model: 'songs',
                key: 'id'
            }
        },
        instrument_id: {
            type: DataTypes.INTEGER(10).UNSIGNED,
            allowNull: false,
            references: {
                model: 'instruments',
                key: 'id'
            }
        }
    }, {
        tableName: 'songs_instruments',
        timestamps: false
    });
};

这是我目前进行提取的方式(args['instruments'] 包含我要过滤歌曲的乐器列表):

const instruments = await db.instruments.findAll({
    where: {name: args['instruments']}
})

const songs_instruments = await db.songs_instruments.findAll({
    where: {instrument_id: instruments.map(instrument => instrument.id)}
})

const songs = await db.songs.findAll({
    where: {id: songs_instruments.map(song => song.song_id)}
})
return songs

虽然这可以正常工作,并且最终我得到了我期望的结果,但我不禁想知道是否有更有效的方式来查询数据库,因为这样做会导致总共三个查询待执行:

Executing (default): SELECT `id`, `name` FROM `instruments` AS `instruments` WHERE `instruments`.`name` IN ('Vocals', 'Trumpet', 'Guitar');
Executing (default): SELECT `id`, `song_id`, `instrument_id` FROM `songs_instruments` AS `songs_instruments` WHERE `songs_instruments`.`instrument_id` IN (1, 3, 7);
Executing (default): SELECT `id`, `title` FROM `songs` AS `songs` WHERE `songs`.`id` IN (1, 1, 2, 2, 3, 3);

我对 Sequelize 很陌生,因此我实现目标的方法可能不正确/次优。

【问题讨论】:

    标签: javascript mysql sql node.js sequelize.js


    【解决方案1】:

    哦,我完全错过了.. 已经很晚了.. 是的,你可以这样做.. 添加下面的 as 来别名这个连接,并在歌曲中做同样的事情:'instruments'......你没有'不需要获取连接记录..

    Instrument.associate = function (models) {
            Instrument.belongsToMany(models.songs, {through: 'songs_instruments', as: 'songs', foreignKey: 'instrument_id', otherKey: 'song_id'})
        };
    
    const instruments = await db.instruments.findAll({
        where: {name: args['instruments']},
        {
         include: [{
            model: db.songs,
            as: 'songs', 
            through: db.songs_instruments,
          }]
       }
    });
    

    或者类似于下面的内容...

    const songs = await db.songs.findAll({
              include: [{
                model: db.instruments,
                as: 'instruments', 
                through: db.songs_instruments,
                where: {name: args['instruments']}
              }],
            });
    

    【讨论】:

    • 你能把那个仪器型号代码按现在的样子重新输入一下吗?
    • 我尝试在belongsToMany 函数中添加as,然后按照您的建议进行调用(const songs = await instruments.getSongs();),我得到的是instruments.getSongs is not a function。我发现我必须这样做const songs = await instruments[0].getSongs();:这可行,但我不喜欢获取列表然后将其丢弃。有没有更好的方法(除了打电话db.instruments.findOne)?
    • 我不能把整个模型放在评论中,但是它看起来和以前一样,但是添加了as参数,所以associate函数的内容现在是Instrument.belongsToMany(models.songs, {as: 'songs', through: 'songs_instruments', foreignKey: 'instrument_id'})
    • 我的意思是更新您上面的代码,以便我们可以看到我们在哪里......将您的原始代码更改为现在在那些 sn-ps 中的代码
    • 我发现在 belongsToMany 函数中添加或不添加 as 没有任何区别:在这两种情况下,这都有效 const instruments = await db.instruments.findAll({ where: {name: args['instruments']} }) const songs = await instruments[0].getSongs(); 而这无效 const instruments = await db.instruments.findAll({ where: {name: args['instruments']} }) const songs = await instruments.getSongs();所以,问题在于getSongs 是单个仪器实例的方法,而不是findAll 返回的整个数组的方法。
    【解决方案2】:

    看起来您正在使用数据加载器,并且您的三个查询是批处理的..我认为你们都很好...您显然正在利用模型设置与 sequelize 所以它必须是通用的,我不明白这里有问题

    【讨论】:

    • 我认为应该有某种方法可以在一个查询中完成所有这些操作......像这样:SELECT * FROM songs s JOIN songs_instruments si ON (s.id = si.song_id) JOIN instruments i ON (i.id = si.instrument_id) WHERE i.name IN ('Keyboard', 'Trumpet'); 我知道getParents 等方法在belongsToMany 关系时可用已实现,所以我想知道是否可以以某种方式使用它们。因为现在,我没有从在 Sequelize 中设置关系中获得任何好处:我只是一次查询一个表,没有任何连接。
    猜你喜欢
    • 2020-03-29
    • 1970-01-01
    • 2016-09-30
    • 1970-01-01
    • 2016-02-13
    • 2016-09-03
    • 2019-02-21
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多