Saltar al contenido

Secuela: unir con varias columnas

Agradeceríamos tu apoyo para difundir nuestras secciones en referencia a las ciencias de la computación.

Solución:

Necesitas definir tu propia on cláusula de la JOIN declaración

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...
);

Con respecto a la duda de @TophatGordon en el comentario de la respuesta aceptada: si necesitamos tener asociaciones configuradas en el modelo o no.
También revisé el problema de github planteado en 2012 que todavía está en abierto estado.
Así que también estaba en la misma situación e intentaba configurar mi propio ON condición para la unión externa izquierda.
Cuando traté directamente de usar el on: ... dentro de Table1.findAll(...include Table2 with ON condition...), no funcionó. Me arrojó un error:

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

Mi caso de uso fue hacer coincidir dos no primarios-key columnas de Table1 a dos columnas en Table2 en la combinación externa izquierda. Mostraré cómo y qué logré:


No se confunda con los nombres de las tablas y las columnas, ya que tuve que cambiarlos de los originales que usé.

Entonces tuve que crear una asociación en Table1 (Tarea) como:

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, 
    );
;

Así que la consulta de búsqueda se ve así:

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
    ,
    ],
);

Consulta resultante:

  • Una condición coincidente se toma de [foreignKey] = [sourceKey]
  • La segunda condición de coincidencia se obtiene por sequelize.where(...) utilizada en 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";

Otro enfoque para lograr lo mismo que el anterior para resolver problemas cuando se usa Table1 en include, es decir, cuando Table1 aparece como tabla de segundo nivel o se incluye desde otra tabla, digamos 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, 
    );
;

Entonces, la consulta de búsqueda de Table0 se ve así: Además, ForeignKey y sourceKey no se considerarán, ya que ahora usaremos custom 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')
                    ),
                ],
            ,
        ],
    
);

Omita la parte de abajo si sus tablas ya están creadas...


Establecer restricciones para falsecomo si Sequelize intentara crear la segunda tabla (Subtarea), podría arrojar un error (DatabaseError [SequelizeDatabaseError]: there is no unique constraint matching given keys for referenced table "task") debido a la siguiente consulta:

crear una tabla si no existe "sub_task" ("some_id" INTEGER, "some_id_2" INTEGER hace referencia a "tarea" ("some_id") en la cascada de eliminación en la cascada de actualización, "data" INTEGER);

si establecemos restricción: falsecrea esta consulta a continuación que no generará un error de restricción único ya que estamos haciendo referencia a una columna no principal:

crear tabla si no existe "sub_task" ("some_id" INTEGER, "some_id_2" INTEGER, "data" INTEGER);

Te mostramos las comentarios y valoraciones de los lectores

Si posees algún recelo y capacidad de modernizar nuestro enunciado eres capaz de dejar una acotación y con gusto lo interpretaremos.

¡Haz clic para puntuar esta entrada!
(Votos: 2 Promedio: 5)



Utiliza Nuestro Buscador

Deja una respuesta

Tu dirección de correo electrónico no será publicada. Los campos obligatorios están marcados con *