LINQ to SQL: múltiples combinaciones en múltiples columnas. es posible?

131

Dado:

Una tabla nombrada TABLE_1con las siguientes columnas:

  • ID
  • ColumnA
  • ColumnB
  • ColumnC

Tengo consulta SQL, donde TABLE_1se une sobre sí mismo dos veces con sede fuera de ColumnA, ColumnB, ColumnC. La consulta podría verse así:

Select t1.ID, t2.ID, t3.ID
  From TABLE_1 t1
  Left Join TABLE_1 t2 On
       t1.ColumnA = t2.ColumnA
   And t1.ColumnB = t2.ColumnB
   And t1.ColumnC = t2.ColumnC
  Left Join TABLE_1 t3 On
       t2.ColumnA = t3.ColumnA
   And t2.ColumnB = t3.ColumnB
   And t2.ColumnC = t3.ColumnC
... and query continues on etc.

Problema:

Necesito que la consulta se reescriba en LINQ. He intentado darle una puñalada:

var query =
    from t1 in myTABLE1List // List<TABLE_1>
    join t2 in myTABLE1List
      on t1.ColumnA equals t2.ColumnA
      && t1.ColumnB equals t2.ColumnA
    // ... and at this point intellisense is making it very obvious
    // I am doing something wrong :(

¿Cómo escribo mi consulta en LINQ? ¿Qué estoy haciendo mal?

aarona
fuente

Respuestas:

242

Unirse en varias columnas en Linq to SQL es un poco diferente.

var query =
    from t1 in myTABLE1List // List<TABLE_1>
    join t2 in myTABLE1List
      on new { t1.ColumnA, t1.ColumnB } equals new { t2.ColumnA, t2.ColumnB }
    ...

Debe aprovechar los tipos anónimos y componer un tipo para las múltiples columnas con las que desea comparar.

Esto parece confuso al principio, pero una vez que se familiarice con la forma en que el SQL está compuesto de las expresiones, tendrá mucho más sentido, debajo de las cubiertas esto generará el tipo de unión que está buscando.

EDITAR Agregar un ejemplo para la segunda unión según el comentario.

var query =
    from t1 in myTABLE1List // List<TABLE_1>
    join t2 in myTABLE1List
      on new { A = t1.ColumnA, B = t1.ColumnB } equals new { A = t2.ColumnA, B = t2.ColumnB }
    join t3 in myTABLE1List
      on new { A = t2.ColumnA, B =  t2.ColumnB } equals new { A = t3.ColumnA, B = t3.ColumnB }
    ...
Quintin Robinson
fuente
44
Esto funciona muy bien para dos uniones. Lo necesito para trabajar con TRES uniones. Lo sentimos, el segundo bloque de código fue un poco engañoso.
aarona
46
Si obtiene un error del compilador sobre la inferencia de tipos, verifique dos cosas, (1) son los tipos iguales y (2) son los nombres de columna iguales. La parte de los nombres es una trampa. Este ejemplo no se compilará incluso si todas las columnas son varchars join T2 in db.tbl2 on new { T1.firstName, T1.secondName } equals new { T2.colFirst, T2.colSecond }. Sin embargo, si lo cambia a esto, se compilarájoin T2 in db.tbl2 on new { N1 = T1.firstName, N2 = T1.secondName } equals new { N1 = T2.colFirst, N2 = T2.colSecond }
user2023861
44
El problema de nomenclatura puede eliminarse de t1 en myTABLE1List y unirse a t2 en myTABLE1List en la nueva {colA = t1.ColumnA, colB = t1.ColumnB} es igual a la nueva {colA = t2.ColumnA, colBBt2.ColumnB}
Baqer Naqvi
1
permítame editar el ejemplo, ya que necesitaba asignaciones a propiedades anónimas
AceMark
1
Algo está mal aquí ... con LINQ. Puedo unirme en varias tablas, puedo unirme en múltiples campos ... sin embargo, no puedo hacerlo para ambos, como se muestra en el ejemplo aquí. Digamos que solo tienes una unión en 1 campo ... y tienes una segunda unión que la sigue. Si cambia la primera unión (o ambas) para usar simplemente {x.field} es igual a nueva {y.field}, hay un error del compilador. Funcionalmente no cambiaste nada. Usando .Net 4.6.1.
user2415376
12

En LINQ2SQL rara vez necesita unirse explícitamente cuando usa uniones internas.

Si tiene relaciones de clave externa adecuadas en su base de datos, obtendrá automáticamente una relación en el diseñador LINQ (de lo contrario, puede crear una relación manualmente en el diseñador, aunque realmente debería tener relaciones adecuadas en su base de datos)

relación padre-hijo

Luego puede acceder a tablas relacionadas con la "notación de puntos"

var q = from child in context.Childs
        where child.Parent.col2 == 4
        select new
        {
            childCol1 = child.col1,
            parentCol1 = child.Parent.col1,
        };

generará la consulta

SELECT [t0].[col1] AS [childCol1], [t1].[col1] AS [parentCol1]
FROM [dbo].[Child] AS [t0]
INNER JOIN [dbo].[Parent] AS [t1] ON ([t1].[col1] = [t0].[col1]) AND ([t1].[col2] = [t0].[col2])
WHERE [t1].[col2] = @p0
-- @p0: Input Int (Size = -1; Prec = 0; Scale = 0) [4]
-- Context: SqlProvider(Sql2008) Model: AttributedMetaModel Build: 4.0.30319.1

En mi opinión, esto es mucho más legible y le permite concentrarse en sus condiciones especiales y no en la mecánica real de la unión.

Editar
Esto, por supuesto, solo es aplicable cuando desea unirse a la línea con nuestro modelo de base de datos. Si desea unirse "fuera del modelo" debe recurrir a las uniones manuales como en la respuesta de Quintin Robinson

Albin Sunnanbo
fuente
11

Title_Authors es una búsqueda de dos cosas que se unen a la vez en los resultados del proyecto y continúan encadenando

        DataClasses1DataContext db = new DataClasses1DataContext();
        var queryresults = from a in db.Authors                                          
                    join ba in db.Title_Authors                           
                    on a.Au_ID equals ba.Au_ID into idAuthor
                    from c in idAuthor
                    join t in db.Titles  
                    on c.ISBN equals t.ISBN 
                    select new { Author = a.Author1,Title= t.Title1 };

        foreach (var item in queryresults)
        {
            MessageBox.Show(item.Author);
            MessageBox.Show(item.Title);
            return;
        }
BionicCyborg
fuente
10

U también puede usar:

var query =
    from t1 in myTABLE1List 
    join t2 in myTABLE1List
      on new { ColA=t1.ColumnA, ColB=t1.ColumnB } equals new { ColA=t2.ColumnA, ColB=t2.ColumnB }
    join t3 in myTABLE1List
      on new {ColC=t2.ColumnA, ColD=t2.ColumnB } equals new { ColC=t3.ColumnA, ColD=t3.ColumnB }
Baqer Naqvi
fuente
3
AHHH !! ¡Esto funciona! Y la DIFERENCIA CLAVE es que debe hacer la parte "ColA =" para que en la otra se una el mismo campo. Durante años no hice eso, pero también necesitaría 1 unión en múltiples campos. Pero ahora necesito más, y SOLO FUNCIONA si asigno un nombre de variable a los campos como en este ejemplo.
user2415376
3

Me gustaría dar otro ejemplo en el que se usan múltiples (3) combinaciones.

 DataClasses1DataContext ctx = new DataClasses1DataContext();

        var Owners = ctx.OwnerMasters;
        var Category = ctx.CategoryMasters;
        var Status = ctx.StatusMasters;
        var Tasks = ctx.TaskMasters;

        var xyz = from t in Tasks
                  join c in Category
                  on t.TaskCategory equals c.CategoryID
                  join s in Status
                  on t.TaskStatus equals s.StatusID
                  join o in Owners
                  on t.TaskOwner equals o.OwnerID
                  select new
                  {
                      t.TaskID,
                      t.TaskShortDescription,
                      c.CategoryName,
                      s.StatusName,
                      o.OwnerName
                  };
usuario3477428
fuente
9
No es lo mismo: la pregunta es acerca de unir tablas basadas en múltiples columnas en cada una, no unir varias tablas basadas en una sola columna en cada una.
Isócrono
1

También puede unirse si el número de columnas no es el mismo en ambas tablas y puede asignar el valor estático a la columna de la tabla

from t1 in Table1 
join t2 in Table2 
on new {X = t1.Column1, Y = 0 } on new {X = t2.Column1, Y = t2.Column2 }
select new {t1, t2}
Ankit Arya
fuente
-6

En mi opinión, esta es la forma más sencilla de unir dos tablas con múltiples campos:

from a in Table1 join b in Table2    
       on (a.Field1.ToString() + "&" + a.Field2.ToString())     
       equals  (b.Field1.ToString() + "&" + b.Field2.ToString())  
     select a
Praveen Kumar
fuente
En SQL, hacer esto sería significativamente más lento que unirse en cada columna por separado (aunque aún sería bastante rápido si el conjunto de datos no es grande). Presumiblemente, linq generaría el SQL obvio, así que tenga en cuenta el rendimiento si usa esta solución.
EGP
-10

Puede escribir su consulta de esta manera.

var query = from t1 in myTABLE1List // List<TABLE_1>
            join t2 in myTABLE1List
               on t1.ColumnA equals t2.ColumnA
               and t1.ColumnB equals t2.ColumnA

Si desea comparar su columna con varias columnas.

Anvesh
fuente
1
@ user658720 Bienvenido a StackOverFlow :). Sugeriría que formatee su código para que sea más fácil de leer. Puede seleccionar el texto y hacer clic en el botón de código en el editor.
aarona