2017-06-08 1 views
4

Ich arbeite an der Konvertierung einer Oracle SQL-Abfrage nach Linq und nicht sicher, wie es weiter geht. Hier ist die SQL-Abfrage:Linq-Abfrage mit mehreren Unterabfragen

SELECT * 
FROM CustomerShip, 
    (SELECT DISTINCT b.ShipSeq AS shipSeq 
    FROM Orders a, 
      CustomerShip b 
    WHERE a.OrderId IN (SELECT OrderId 
          FROM Orders 
          WHERE CustomerId = @CustomerId 
          AND OrderType <> 'A') 
    AND b.CustomerId = @CustomerId 
    AND b.ShipSeq = a.CustShip 
    AND OrderStatus <> 'C' 
    GROUP BY b.ShipSeq) i 
WHERE CustomerId = @CustomerId 
AND (Address NOT LIKE '%RETAIL%STORE%') 
AND ShipSeq = i.ShipSeq(+) 
ORDER BY ShipTo DESC, OrderDate DESC; 

ich versucht haben, brechen sie in drei separate Abfragen, wenn zu Linq zu konvertieren.

var query1 = from c in CustomerShip 
      where c.CustomerId == customerId 
      && !c.Address.Contains("RETAIL") 
      && !c.Address.Contains("STORE") 
      orderby c.ShipTo descending, c.OrderDate descending 
      select c; 

var query2 = from o in Orders 
     where o.CustomerId == customerId 
     && !o.OrderType.Equals("A") 
     select o.OrderId; 

var query3 = (from o in Orders 
     from c in CustomerShip 
     where c.CustomerId == customerId 
     && c.ShipSeq == o.CustShip 
     && !o.OrderStatus.Equals("A") 
     select c.ShipSeq).Distinct(); 

Jetzt versuche ich, sie alle in einer Abfrage zusammenzustellen, aber nicht sicher, wie es geht. Hier ist die Richtung, ich gehe:

var query = from c in CustomerShip 

let subquery = from o in Orders 
       where o.CustomerId == customerId 
       && !o.OrderType.Equals("A") 
       select o.OrderId 

    from or in model.Orders 
    where subquery.Contains(or.OrderId) 
    && c.CustomerId == customerId 
    && c.ShipSeq == or.CustShip 
    && !or.OrderStatus.Equals("A") 
    group c by c.ShipSeq 
    into i 
    select c.ShipSeq 

where c.CustomerId == customerId 
&& !c.Address.Contains("RETAIL") 
&& !c.Address.Contains("STORE") 
orderby c.ShipTo descending, c.OrderDate descending 
select c, i; 

UPDATE

Ich habe eine Abfrage, die Arten funktionieren, aber das dauert es fast zwei Minuten durchführen (im Vergleich zu .02s für die Oracle-Abfrage) und Die Reihenfolge der Ergebnisse ist falsch. Wer sieht was ich vermisse?

var innerQuery = from x in model.Orders 
        where x.CustomerId == customerId 
        && !x.OrderType.Equals("A") 
        select x.OrderId; 

var result = from c in model.CustomerShip 
      join subQuery in 
      (
       (from o in model.Orders 
       from c in model.CustomerShip 
       where c.CustomerId == customerId 
       && innerQuery.Contains(o.OrderId) 
       && !o.FLAG_ORD_STATUS.Equals("C") 
       && c.ShipSeq == o.CustShip 
       select c.ShipSeq).Distinct() 

      ) on c.ShipSeq equals subQuery into temp 
      from x in temp.DefaultIfEmpty() 
      where c.CustomerId == customerId 
      && !c.Address.Contains("RETAIL") 
      && !c.Address.Contains("STORE") 
      orderby c.ShipTo descending, c.OrderDate descending 
      select c; 
+1

Lesen Sie sich auf die Join-Fähigkeit von Linq ... Sie können dies umschreiben, um Ihrem SQL sehr ähnlich zu sein, brechen Sie es nicht auf ... –

+0

Ich arbeite mich durch, aber ich bin verwirrt von dieser Zeile: 'AND ShipSeq = i.ShipSeq (+)'. Ist das einzigartig für Oracle? – PixelPaul

+0

@PixelPaul Die '(+)' ist Oracles alte Art, äußere Verbindungen anzuzeigen. Hier ist es bedeutet "Alle Kundendatensätze, plus die Datensätze von der inneren Abfrage, die auf ShipSeq übereinstimmen. Details hier: https://stackoverflow.com/q/6559261/12725 –

Antwort

5

Denken Sie daran, dass Sie hier nur eine Abfrage erstellen. Nichts wird ausgeführt, bis Sie eine ToList() oder .FirstOrDefault() oder was auch immer tun. SO können Sie die Abfragen in anderen Abfragen verwenden, und es wird eine große SQL-Anweisung erstellen, wenn sie ausgeführt wird.

var query2 = from o in Orders 
      where o.CustomerId == customerId 
      && !o.OrderType.Equals("A") 
      select o.OrderId; 

var query3 = (from o in Orders 
       join c in CustomerShip on o.CustShip equals c.ShipSeq 
       where c.CustomerId == customerId 
       && !o.OrderStatus.Equals("A") 
       && query2.Contains(o.OrderId) 
       select c.ShipSeq).Distinct(); 

var query1 = from c in CustomerShip 
      from i in query3 
      where c.CustomerId == customerId 
      && !c.Address.Contains("RETAIL") 
      && !c.Address.Contains("STORE") 
      && c.ShipSeq == i.ShipSeq 
      orderby c.ShipTo descending, c.OrderDate descending 
      select c; 

Aber ich bin ziemlich sicher, dass Sie query2 und Abfrage3 auf nur reduzieren können:

var query3 = (from o in Orders 
       join c in CustomerShip on o.CustShip equals c.ShipSeq 
       where c.CustomerId == customerId 
       && !o.OrderStatus.Equals("A") 
       && !o.OrderType.Equals("A") 
       select c.ShipSeq).Distinct(); 
1

so etwas wie dies versuchen. Ich modelliere einige Klassen, nur um den Fehler zu beseitigen. Wenn Sie nach ShipSeq gruppieren, brauchen Sie nicht distinct. Nehmen Sie einfach das erste Element aus der Gruppe, um die gleichen Ergebnisse zu erhalten.

using System; 
using System.Collections.Generic; 
using System.Linq; 
using System.Text; 
using System.Text.RegularExpressions; 

namespace ConsoleApplication60 
{ 
    class Program 
    { 
     static void Main(string[] args) 
     { 
      int customerID = 1234; 
      List<Order> CustomTypeA = Order.orders 
       .Where(x => (x.CustomerId == customerID) && (x.OrderType == "A") && (x.OrderStatus == "C")).ToList(); 

      var results = (from CustA in CustomTypeA 
          join CustShip in Ship.CustomerShip on CustA.CustomerId equals CustShip.CustomerId 
          select new { CustA = CustA, CustShip = CustShip}) 
          .Where(x => (!RetailStore(x.CustShip.Address)) && (x.CustA.CustShip == x.CustShip.ShipSeq)) 
          .OrderByDescending(x => x.CustShip.OrderDate) 
          .GroupBy(x => x.CustShip.ShipSeq) 
          .Select(x => x.FirstOrDefault()) 
          .Select(x => new { 
           CustomerID = x.CustShip.CustomerId, 
           Address = x.CustShip.Address, 
           OrderDate = x.CustShip.OrderDate 
          }).ToList(); 

     } 
     static Boolean RetailStore(string address) 
     { 
      string pattern = "RETAIL.*STORE"; 
      return Regex.IsMatch(address, pattern); 
     } 
    } 
    public class Order 
    { 
     public static List<Order> orders = new List<Order>(); 

     public int CustomerId { get; set; } 
     public string OrderType { get; set; } 
     public string CustShip { get; set; } 
     public string OrderStatus { get; set; } 
    } 
    public class Ship 
    { 
     public static List<Ship> CustomerShip = new List<Ship>(); 

     public int CustomerId { get; set; } 
     public string ShipSeq { get; set; } 
     public string Address { get; set; } 
     public DateTime OrderDate { get; set; } 
    } 
} 
0

query2 und Abfrage3 Eingebunden hier in innere Abfrage

var Innerquery = (from o in Orders 
       join c in CustomerShip on o.CustShip equals c.ShipSeq 
       where c.CustomerId == customerId 
       && !o.OrderStatus.Equals("A") 
       && !o.OrderType.Equals("A") 
       select c.ShipSeq).Distinct(); 

var query1 = from c in CustomerShip 
      from i in query3 
      where c.CustomerId == customerId 
      && innerquery.Contains(c.CustomerId) 
      && !c.Address.Contains("RETAIL") 
      && !c.Address.Contains("STORE") 
      && c.ShipSeq == i.ShipSeq 
      orderby c.ShipTo descending, c.OrderDate descending 
      select c; 

oder Sie können versuchen Linqer http://www.sqltolinq.com

0

Es könnte viele Gründe geben, warum Ihre Abfrage langsam in EF ist - ich würde vorschlagen, eine mit Profiler.

Die wahrscheinlichen Gründe sind entweder EF erstellt eine ineffiziente Abfrage (in der Regel sollte die Datenbank ihre eigenen Optimierungen erstellen, aber ich habe schlechte Erfahrungen mit EF und Oracle), oder, abhängig von der Anzahl der geladenen Ergebnisse, Mapping auf tatsächliche Objekte sind sehr teuer.

Im Allgemeinen, obwohl scheinbar keine populäre Meinung in der .NET-Welt, würde ich vorschlagen, entweder eine Ansicht zu erstellen, oder dbcontext.Database.SqlQuery<CustomerShip>(sql) verwenden, wenn Sie eine komplexe Abfrage haben, insbesondere bei der Verwendung von Oracle, zumindest aus meinen Erfahrungen damit die Vergangenheit (war einige Zeit, so könnte ich falsch liegen.)