2009-05-07 10 views
2

J'ai passé l'après-midi à essayer de comprendre comment traduire la requête suivante en LINQ, mais je ne peux pas vraiment y arriver.Jointure multiple avancée dans la sous-requête en utilisant LINQ

declare @productId int; set @productId = 3212; 

select * from InformationData data where productId = @productId and orgId = 1 
and exists(
    select id from (
     select coalesce(id1.id, id2.id, id3.id) as id from (
      select productId,attributeId from InformationData where productId = @productId group by productId,attributeId 
     ) id 
     left outer join InformationData id1 on id1.productId = id.productId and id1.attributeId = id.attributeId and id1.language = 1 
     left outer join InformationData id2 on id2.productId = id.productId and id2.attributeId = id.attributeId and id2.language = 2 
     left outer join InformationData id3 on id3.productId = id.productId and id3.attributeId = id.attributeId and id3.language = 0 
    ) row 
    where row.id = data.id 
) 

Le but de la requête est de récupérer des données à partir d'une table en utilisant 2 langues secondaires, donc si les données n'existe pas dans la langue 1, il est tiré par les cheveux dans la langue 2, et si 2 n'existe pas est récupéré pour la langue 0 qui est une traduction globale.

Je peux obtenir la requête interne correcte la plupart du temps (sauf de id1.language = 1, je ne peux pas sembler obtenir à se joindre à un membre de la table, je me joins à, des idées?)

ce

est mon code (code LINQPad):

(
    from data in (
     from d in InformationData where d.ProductId == 3212 group d by new { d.ProductId, d.AttributeId } into p select new { ProductId = p.Key.ProductId, AttributeId = p.Key.AttributeId } 
    ) 
    join x1 in InformationData on new { a = data.ProductId, b = data.AttributeId } equals new { a = x1.ProductId, b = x1.AttributeId } into f1 
     from r1 in f1.DefaultIfEmpty() 
     where r1.Language == 1 
    join x2 in InformationData on new { a = data.ProductId, b = data.AttributeId } equals new { a = x2.ProductId, b = x2.AttributeId } into f2 
     from r2 in f2.DefaultIfEmpty() 
     where r2.Language == 2 
    join x3 in InformationData on new { a = data.ProductId, b = data.AttributeId } equals new { a = x3.ProductId, b = x3.AttributeId } into f3 
     from r3 in f3.DefaultIfEmpty() 
     where r3.Language == 2 
    select new { Id = ((int?)r1.Id) ?? ((int?)r2.Id) ?? r3.Id } 
).Dump(); 

qui génère l'instruction SQL suivante:

-- Region Parameters 
DECLARE @p0 Int SET @p0 = 3212 
DECLARE @p1 Int SET @p1 = 2 
DECLARE @p2 Int SET @p2 = 2 
DECLARE @p3 Int SET @p3 = 1 
-- EndRegion 
SELECT COALESCE([t2].[id],COALESCE([t3].[id],[t4].[id])) AS [Id] 
FROM (
    SELECT [t0].[productId], [t0].[attributeId] 
    FROM [InformationData] AS [t0] 
    WHERE [t0].[productId] = @p0 
    GROUP BY [t0].[productId], [t0].[attributeId] 
    ) AS [t1] 
LEFT OUTER JOIN [InformationData] AS [t2] ON ([t1].[productId] = [t2].[productId]) AND ([t1].[attributeId] = [t2].[attributeId]) 
LEFT OUTER JOIN [InformationData] AS [t3] ON ([t1].[productId] = [t3].[productId]) AND ([t1].[attributeId] = [t3].[attributeId]) 
LEFT OUTER JOIN [InformationData] AS [t4] ON ([t1].[productId] = [t4].[productId]) AND ([t1].[attributeId] = [t4].[attributeId]) 
WHERE ([t4].[language] = @p1) AND ([t3].[language] = @p2) AND ([t2].[language] = @p3) 

Mais je ne peux pas mettre cela en même temps que le reste de la requête, peut-être que je suis achat fatigué je continue à me faire t faire beaucoup de CROSS APPLY. Est-ce que quelqu'un a des suggestions?

Répondre

3

Eh bien, après une bonne nuit de sommeil et quelques choses croquant obtenu un peu plus lumineux et j'ai trouvé la solution :) pour toute personne qui est curieux ici, il est


(
    from i in InformationData 
    where (
     from data in (
      from d in InformationData where d.ProductId == 3212 group d by new { d.ProductId, d.AttributeId } into p select new { ProductId = p.Key.ProductId, AttributeId = p.Key.AttributeId } 
     ) 
     join x1 in InformationData on new { a = data.ProductId, b = data.AttributeId, c = 1} equals new { a = x1.ProductId, b = x1.AttributeId, c = x1.Language } into f1 
      from r1 in f1.DefaultIfEmpty() 
     join x2 in InformationData on new { a = data.ProductId, b = data.AttributeId, c = 2 } equals new { a = x2.ProductId, b = x2.AttributeId, c = x2.Language } into f2 
      from r2 in f2.DefaultIfEmpty() 
     join x3 in InformationData on new { a = data.ProductId, b = data.AttributeId, c = 0 } equals new { a = x3.ProductId, b = x3.AttributeId, c = x3.Language } into f3 
      from r3 in f3.DefaultIfEmpty() 
     select new { Id = ((int?)r1.Id) ?? ((int?)r2.Id) ?? r3.Id } 
    ).Any(d => d.Id == i.Id) 
    select i 
).Dump(); 

Et voici le SQL généré


-- Region Parameters 
DECLARE @p0 Int SET @p0 = 3212 
DECLARE @p1 Int SET @p1 = 1 
DECLARE @p2 Int SET @p2 = 2 
DECLARE @p3 Int SET @p3 = 0 
-- EndRegion 
SELECT [t0].[id] AS [Id], [t0].[attributeId] AS [AttributeId], [t0].[productId] AS [ProductId], [t0].[value] AS [Value], [t0].[orgId] AS [OrgId], [t0].[version] AS [Version], [t0].[language] AS [Language], [t0].[metaType] AS [MetaType], [t0].[overload] AS [Overload], [t0].[parentId] AS [ParentId] 
FROM [InformationData] AS [t0] 
WHERE EXISTS(
    SELECT NULL AS [EMPTY] 
    FROM (
     SELECT COALESCE([t3].[id],COALESCE([t4].[id],[t5].[id])) AS [value] 
     FROM (
      SELECT [t1].[productId], [t1].[attributeId] 
      FROM [InformationData] AS [t1] 
      WHERE [t1].[productId] = @p0 
      GROUP BY [t1].[productId], [t1].[attributeId] 
      ) AS [t2] 
     LEFT OUTER JOIN [InformationData] AS [t3] ON ([t2].[productId] = [t3].[productId]) AND ([t2].[attributeId] = [t3].[attributeId]) AND (@p1 = [t3].[language]) 
     LEFT OUTER JOIN [InformationData] AS [t4] ON ([t2].[productId] = [t4].[productId]) AND ([t2].[attributeId] = [t4].[attributeId]) AND (@p2 = [t4].[language]) 
     LEFT OUTER JOIN [InformationData] AS [t5] ON ([t2].[productId] = [t5].[productId]) AND ([t2].[attributeId] = [t5].[attributeId]) AND (@p3 = [t5].[language]) 
     ) AS [t6] 
    WHERE [t6].[value] = [t0].[id] 
    ) 
2

Basé principalement sur votre description de ce que la requête est censée faire, je pense que vous pourriez être en mesure d'accomplir le même résultat avec la clause "orderby" appropriée et de récupérer seulement le premier résultat. Comme ceci:

var result = 
(
    from d in InformationData 
    where d.ProductId == 3212 
    orderby ((d.language == 0) ? Int32.MaxValue : d.language) 
    select d 
).First(); 

EDIT: Vous pouvez contrôler la priorité de recherche en étendant la clause orderby. Par exemple, si la priorité doit être de 2, puis 3, puis 1, alors rien d'autre, vous pouvez le faire:

var result = 
(
    from d in InformationData 
    where d.ProductId == 3212 
    orderby 
     (d.language == 2) ? 0 
     : (d.language == 3) ? 1 
     : (d.language == 1) ? 2 
     : Int32.MaxValue 
    select d 
).First(); 
+0

Malheureusement, il est pas si simple que je peux avoir le cas où je dois fallback de 1 -> 2 -> 0 ou 3 -> 2 -> 0. Il peut être utile de faire un tri basé sur une condition de tri croissant ou décroissant, je vais essayer. Je suis toujours très curieux quant à la façon de résoudre ce problème, car je traduis une application assez complexe avec beaucoup de requêtes. – Runeborg

+0

J'ai ajouté un exemple de séquence de précédence/de repli plus compliquée. –

+0

Bonne suggestion, je vais essayer ça. Bien que actuellement je ne peux pas le profiler en raison de beaucoup de latence. – Runeborg