Thursday, June 22, 2017

Linq query with multiple subqueries

Leave a Comment

I'm working on converting an Oracle Sql query to Linq, and not sure how to proceed. Here is the Sql query:

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; 

I have tried to break it down into three separate queries when converting to linq.

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

Now I'm trying to assemble them all into one query, but unsure how to do it. Here is the direction I am going:

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

I have a query that kinds works, but the it takes almost two minutes to execute (compared to .02s for the Oracle query) and the order of the results is incorrect. Anyone see what I'm missing?

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; 

4 Answers

Answers 1

Remember that you are just build a query here. Nothing is executed until you do a ToList() or .FirstOrDefault() or whatever. SO, you can use the queries in other queries, and it will create one big SQL statement when executed.

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; 

However, I'm pretty sure you can reduce query2 and query3 down to just:

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

Answers 2

Try something like this. I model some classes just for getting the error out. If you group by ShipSeq you don't need distinct. Just take first item from group will give same results.

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

Answers 3

query2 and query3 Merged here into inner query

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; 

OR you can try Linqer http://www.sqltolinq.com

Answers 4

There could be many reasons why your query is slow in EF - I would suggest using a profiler.

The probable reasons are either EF creates an inefficient query (usually the database should create its own optimizations, but I have had bad experiences with EF and Oracle), or, depending on how many results it loads, mapping it to actual objects is very expensive.

In general, although seemingly not a popular opinion in the .NET world, I would suggest either creating a View, or using dbcontext.Database.SqlQuery<CustomerShip>(sql) whenever you have a complex query, especially when using Oracle, at least from my experiences with it in the past (has been some time, so I might be wrong.)

If You Enjoyed This, Take 5 Seconds To Share It

0 comments:

Post a Comment