Erweiterte mehrere Join in Unterabfrage mit LINQ
Frage
Ich habe den Nachmittag damit verbracht, meinen Geist herum zu wickeln, wie die folgende Abfrage in LINQ zu übersetzen, aber ich kann ganz da nicht bekommen.
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 mit zwei Ausweich Sprachen zu holen, so dass, wenn die Daten nicht in der Sprache existieren 1 ist es geholt in Sprache 2, und wenn 2 nicht existiert ist es hergeholt für Sprache 0 das eine globale Übersetzung.
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 generiert die folgende SQL:
-- 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 erhalte es eine Menge von CROSS APPLY ist zu tun. Hat jemand irgendwelche Vorschläge?
Lösung
Nun nach einem guten Schlaf und einige Knirschen Dinge wurde ein wenig heller und fand ich die Lösung :) Für alle, die es merkwürdig, 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]
)
Andere Tipps
Auf der Basis meist auf Ihrer Beschreibung dessen, was die Abfrage tun soll, ich glaube, Sie in der Lage sein kann, um das gleiche Ergebnis mit dem entsprechenden „orderby“ -Klausel und Abrufen nur das erste Ergebnis zu erreichen. Wie folgt aus:
var result =
(
from d in InformationData
where d.ProductId == 3212
orderby ((d.language == 0) ? Int32.MaxValue : d.language)
select d
).First();
Bearbeiten : Sie können die Suche Vorrang steuern, indem die orderby Klausel erstreckt. Wenn beispielsweise der Vorrang 2 sein soll, dann 3, dann 1, dann etwas anderes, man könnte 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();