【问题标题】:Sequelize - Join with multiple columnSequelize - 加入多列
【发布时间】:2017-07-02 18:06:38
【问题描述】:

我喜欢把下面的查询转换成sequelize code

select * from table_a 
inner join table_b 
on table_a.column_1 = table_b.column_1
and table_a.column_2 = table_b.column_2

我尝试了许多方法并遵循了许多提供的解决方案,但我无法从 sequelize 代码中实现所需的查询。

我达到的最大值如下:

select * from table_a 
inner join table_b 
on table_a.column_1 = table_b.column_1

我也想要第二个条件。

and table_a.column_2 = table_b.column_2

有什么合适的方法来实现吗?

【问题讨论】:

    标签: node.js sequelize.js node-modules


    【解决方案1】:

    关于@TophatGordon 在接受的答案评论中的疑问:如果我们需要在模型中设置任何关联。
    还通过了仍处于 open 状态的github issue raised back in 2012
    所以我也遇到了同样的情况,并试图为左外连接设置我自己的ON 条件。
    当我直接尝试在Table1.findAll(...include Table2 with ON condition...) 中使用on: {...} 时,它不起作用。 它抛出了一个错误:

    EagerLoadingError [SequelizeEagerLoadingError]: Table2 is not associated to Table1!

    我的用例是将 Table1 中的两个非主键列与左外连接中 Table2 中的两个列进行匹配。我将展示我是如何以及取得了什么成就的:


    不要被表名和列名弄糊涂了,因为我必须从我使用的原始名称中更改它们。

    所以我不得不在 Table1(Task) 中创建一个关联,例如:

    Task.associate = (models) => {    
    
    Task.hasOne(models.SubTask, {
            foreignKey: 'someId', // <--- one of the column of table2 - SubTask: not a primary key here in my case; can be primary key also
            sourceKey: 'someId', // <---  one of the column of table1 - Task: not a primary key here in my case; can be a primary key also
            scope: {
                [Op.and]: sequelize.where(sequelize.col("Task.some_id_2"),
                    // '=',
                    Op.eq, // or you can use '=',
                    sequelize.col("subTask.some_id_2")),
            },
            as: 'subTask',
            // no constraints should be applied if sequelize will be creating tables and unique keys are not defined, 
            //as it throws error of unique constraint            
            constraints: false, 
        });
    };
    

    所以查找查询看起来像这样:

    Task.findAll({
        where: whereCondition,
        // attributes: ['id','name','someId','someId2'],
        include: [{
            model: SubTask, as: 'subTask', // <-- model name and alias name as defined in association 
            attributes: [], // if no attributes needed from SubTask - empty array
        },
        ],
    });
    

    结果查询:

    • 一个匹配条件取自 [foreignKey] = [sourceKey]
    • 第二个匹配条件由sequelize.where(...)获得,用于scope:{...}
    select
      "Task"."id",
      "Task"."name",
      "Task"."some_id" as "someId",
      "Task"."some_id_2" as "someId2"
    from
      "task" as "Task"
    left outer join "sub_task" as "subTask" on
      "Task"."some_id" = "subTask"."some_id"
      and "Task"."some_id_2" = "subTask"."some_id_2";
    

    另一种实现与上述相同的方法来解决在包含中使用 Table1 时出现的问题,即当 Table1 显示为 2 级表或包含在其他表中时 - 比如说 Table0

    Task.associate = (models) => {    
    
    Task.hasOne(models.SubTask, {
            foreignKey: 'someId', // <--- one of the column of table2 - SubTask: not a primary key here in my case; can be primary key also
            sourceKey: 'someId', // <---  one of the column of table1 - Task: not a primary key here in my case; can be a primary key also
            as: 'subTask',
            // <-- removed scope -->
            // no constraints should be applied if sequelize will be creating tables and unique keys are not defined, 
            //as it throws error of unique constraint            
            constraints: false, 
        });
    };
    

    所以来自 Table0 的查找查询看起来像这样:也不考虑foreignKey 和sourceKey,因为我们现在将使用自定义on: {...}

    Table0.findAll({
        where: whereCondition,
        // attributes: ['id','name','someId','someId2'],
        include: {
            model: Task, as: 'Table1AliasName', // if association has been defined as alias name 
            include: [{
                model: SubTask, as: 'subTask', // <-- model name and alias name as defined in association 
                attributes: [], // if no attributes needed from SubTask - empty array
                on: {
                    [Op.and]: [
                        sequelize.where(
                            sequelize.col('Table1AliasName_OR_ModelName.some_id'),
                            Op.eq, // '=',
                            sequelize.col('Table1AliasName_OR_ModelName->subTask.some_id')
                        ),
                        sequelize.where(
                            sequelize.col('Table1AliasName_OR_ModelName.some_id_2'),
                            Op.eq, // '=',
                            sequelize.col('Table1AliasName_OR_ModelName->subTask.some_id_2')
                        ),
                    ],
                },
            }],
        }
    });
    

    如果您的表已经创建,请跳过以下部分...


    将约束设置为 false,就像 sequelize 尝试创建第二个表(子任务)一样,由于以下查询,它可能会抛出错误 (DatabaseError [SequelizeDatabaseError]: there is no unique constraint matching given keys for referenced table "task")

    如果不存在则创建表 "sub_task" ("some_id" INTEGER, "some_id_2" INTEGER 在更新时删除级联时引用“任务”(“some_id”) 级联,“数据”整数);

    如果我们设置 constraint: false,它会在下面创建这个查询,而不是在我们引用非主列时抛出唯一约束错误:

    如果不存在则创建表 "sub_task" ("some_id" INTEGER, "some_id_2" INTEGER, "data" INTEGER);

    【讨论】:

    • 哇...这真的很及时,哈哈。我只是在寻找如何做到这一点,而 BOOM 早在 6 小时前就得到了答复。谢谢@Abhishek Shah!
    • @KyleFarris 2 个月前我来到了同一个帖子和 github issue ,但我没有得到任何具体的答案。今天我回来了一个解决方案,这可能至少有一点帮助。
    • Taskas 的一部分时,它是如何工作的?例如user-&gt;tasks 因为 Task 嵌套在包含中?
    • @Jayen 您能否参考答案的 Table0.findAll 部分。这有帮助吗?
    • 好的,谢谢。我最终在scope 中使用user-&gt;tasks 而不是where,因为我有一些类似的查询。使用separate:true 也可以,但由于其他原因我不能使用它。
    【解决方案2】:

    您需要在JOIN 语句中定义自己的on 子句

    ModelA.findAll({
        include: [
            {
                model: ModelB,
                on: {
                    col1: sequelize.where(sequelize.col("ModelA.col1"), "=", sequelize.col("ModelB.col1")),
                    col2: sequelize.where(sequelize.col("ModelA.col2"), "=", sequelize.col("ModelB.col2"))
                },
                attributes: [] // empty array means that no column from ModelB will be returned
            }
        ]
    }).then((modelAInstances) => {
        // result...
    });
    

    【讨论】:

    • 出现错误:TypeError:无法读取未定义的属性“indexOf”
    • 模型文件应该写什么?
    • 这是否需要您在任一模型上设置任何关联?我正在考虑做一些非常相似的事情。表上没有 FK,因此如果可能的话,不想添加任何关联。谢谢。
    • @TophatGordon 我试图为我的一个用例制定解决方案。请在此处查看我的answer,看看是否对您有帮助。
    猜你喜欢
    • 1970-01-01
    • 2020-04-30
    • 2018-10-01
    • 2021-04-13
    • 2015-06-11
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多