2009-05-07 13 views
2

Ich habe den Nachmittag verbracht versuchen, meine Gedanken darüber, wie die folgende Abfrage in LINQ übersetzen, aber ich kann nicht ganz dorthin gelangen.Erweiterte multiple Join in Unterabfrage mit 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 
) 

Der Zweck der Abfrage von Daten aus einer Tabelle Sprachen mit 2 Rückfall zu holen, so dass, wenn die Daten in Sprache 1 ist es in der Sprache 2 geholt existieren nicht, und wenn 2 nicht existiert tut es abgerufen für die Sprache 0, die eine globale Übersetzung ist.

Ich kann die innere Abfrage meist korrekt erhalten (mit Ausnahme von id1.language = 1, kann ich nicht scheinen, um es auf einem Mitglied der Tabelle Ich bin Beitritt zu verbinden, irgendwelche Ideen?)

Dies ist mein Code (LINQPad Code):

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

Welche der folgenden SQL erzeugt:

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

Aber ich kann das nicht mit dem Rest der Abfrage zusammen, vielleicht bin ich nur müde kaufen, ich bekomme immer wieder t viel CROSS APPLY zu machen. Hat jemand irgendwelche Vorschläge?

Antwort

3

Nun nach einem guten Schlaf und einige Knirschen Dinge bekam etwas heller und ich fand die Lösung :) Für jeden, der neugierig ist es hier ist


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

Und hier ist die erzeugte SQL


-- 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

Basierend auf Ihrer Beschreibung dessen, was die Abfrage tun soll, können Sie das gleiche Ergebnis mit der entsprechenden "orderby" -Klausel erreichen und nur das erste Ergebnis abrufen. Wie folgt aus:

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

EDIT: Sie können die Suche Vorrang steuern, indem die orderby Klausel erstreckt. Zum Beispiel sollte, wenn der Vorrang 2 sein, dann 3, dann 1, dann etwas anderes, können Sie dies tun:

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

leider aus, es ist nicht so einfach, wie ich den Fall haben kann, wo ich Rückfall benötigen 1 -> 2 -> 0 oder 3 -> 2 -> 0. Es kann funktionieren, eine Sortierung basierend auf einer Bedingung zu machen, ob aufsteigend oder absteigend, aber ich werde es versuchen. Ich bin immer noch sehr neugierig, wie ich das lösen kann, da ich eine ziemlich komplexe Anwendung mit vielen Abfragen übersetze. – Runeborg

+0

Ich fügte ein Beispiel für eine kompliziertere Präzedenz/Fallback-Sequenz hinzu. –

+0

Guter Vorschlag, ich werde das ausprobieren. Obwohl ich es derzeit aufgrund einer großen Latenz nicht profilieren kann. – Runeborg