Convert SQL to EF Linq - c#

I have the following query:
SELECT COUNT(1)
FROM Warehouse.WorkItems wi
WHERE wi.TaskId = (SELECT TaskId
FROM Warehouse.WorkItems
WHERE WorkItemId = #WorkItemId)
AND wi.IsComplete = 0;
And since we are using EF, I'd like to be able to use the Linq functionality to generate this query. (I know that I can give it a string query like this, but I would like to use EF+Linq to generate the query for me, for refactoring reasons.)
I really don't need to know the results of the query. I just need to know if there are any results. (The use of an Any() would be perfect, but I can't get the write code for it.)
So... Basically, how do I write that SQL query as a LINQ query?
Edit: Table Structure
WorkItemId - int - Primary Key
TaskId - int - Foreign Key on Warehouse.Tasks
IsComplete - bool
JobId - int
UserName - string
ReportName - string
ReportCriteria - string
ReportId - int - Foreign Key on Warehouse.Reports
CreatedTime - DateTime

The direct translation could be something like this
var result = db.WorkItems.Any(wi =>
!wi.IsComplete && wi.TaskId == db.WorkItems
.Where(x => x.WorkItemId == workItemId)
.Select(x => x.TaskId)
.FirstOrDefault()));
Taking into account the fact that SQL =(subquery), IN (subquery) and EXISTS(subquery) in nowadays modern databases are handled identically, you can try this instead
var result = db.WorkItems.Any(wi =>
!wi.IsComplete && db.WorkItems.Any(x => x.WorkItemId == workItemId
&& x.TaskId == wi.TaskId));

Turns out that I just needed to approach the problem from a different angle.
I came up with about three solutions with varying Linq syntaxes:
Full method chain:
var q1 = Warehouse.WorkItems
.Where(workItem => workItem.TaskId == (from wis in Warehouse.WorkItems
where wis.WorkItemId == workItemId
select wis.TaskId).First())
.Any(workItem => !workItem.IsComplete);
Mixed query + method chain:
var q2 = Warehouse.WorkItems
.Where(workItem => workItem.TaskId == Warehouse.WorkItems
.Where(wis => wis.WorkItemId == workItemId)
.Select(wis => wis.TaskId)
.First())
.Any(workItem => !workItem.IsComplete);
Full query:
var q3 = (from wi in Warehouse.WorkItems
where wi.TaskId == (from swi in Warehouse.WorkItems
where swi.WorkItemId == workItemId
select swi.TaskId).First()
where !wi.IsComplete
select 1).Any();
The only problems with this is that it comes up with some really jacked up SQL:
SELECT
(CASE
WHEN EXISTS(
SELECT NULL AS [EMPTY]
FROM [Warehouse].[WorkItems] AS [t0]
WHERE (NOT ([t0].[IsComplete] = 1)) AND ([t0].[TaskId] = ((
SELECT TOP (1) [t1].[TaskId]
FROM [Warehouse].[WorkItems] AS [t1]
WHERE [t1].[WorkItemId] = #p0
)))
) THEN 1
ELSE 0
END) AS [value]

You can use the Any() function like so:
var result = Warehouse.WorkItems.Any(x => x.WorkItemId != null);
In short, you pass in your condition, which in this case is checking whether or not any of the items in your collection have an ID
The variable result will tell you whether or not all items in your collection have ID's.
Here's a helpful webpage to help you get started with LINQ: http://www.dotnetperls.com/linq

Subquery in the original SQL was a useless one, thus not a good sample for Any() usage. It is simply:
SELECT COUNT(*)
FROM Warehouse.WorkItems wi
WHERE WorkItemId = #WorkItemId
AND wi.IsComplete = 0;
It looks like, since the result would be 0 or 1 only, guessing the purpose and based on seeking how to write Any(), it may be written as:
SELECT CASE WHEN EXISTS ( SELECT *
FROM Warehouse.WorkItems wi
WHERE WorkItemId = #WorkItemId AND
wi.IsComplete = 0 ) THEN 1
ELSE 0
END;
Then it makes sense to use Any():
bool exists = db.WorkItems.Any( wi => wi.WorkItemId == workItemId & !wi.IsComplete );
EDIT: I misread the original query in a hurry, sorry. Here is an update on the Linq usage:
bool exists = db.WorkItems.Any( wi =>
db.WorkItems
.SingleOrDefault(wi.WorkItemId == workItemId).TaskId == wi.TaskId
&& !wi.IsComplete );
If the count was needed as in the original SQL:
var count = db.WorkItems.Count( wi =>
db.WorkItems
.SingleOrDefault(wi.WorkItemId == workItemId).TaskId == wi.TaskId
&& !wi.IsComplete );
Sorry again for the confusion.

Related

Order by descending by row field in query

I want to write a EF query which does order by ascending or descending based on condition. Following is the my pseudo code:
var result= q.OrderByDescending(x => x.StatusId == 3)
if( x.StatusId == 3)
then order by x.ReserveDate
if( x.StatusId != 3 )
then order by descending x.LastUpdateDate
How can i do this?
Update
This is not same as q = condition ? q.OrderBy(..) : q.OrderByDescending(..) as marked in referenced duplicate question, sorting order differs based on value within the row instead of a flag outside query.
You can supply complex expressions in OrderBy...
// you might have to give bounding start,end for
// for this query to work correctly...
var end = DateTime.Now;
var start = end.AddYears(-100);
var result = q.OrderBy(
x => x.StatusId == 3 ?
// this will sort by in ascending order
DbFunctions.DiffDays(x.ReserveDate, start) :
// this will sort in descending order
DbFunctions.DiffDays(end, x.LastUpdateDate) );
SQL Generated will be
SELECT
...
...
FROM ( SELECT CASE
WHEN ([Extent2].[StatusId ] = 3)
THEN DATEDIFF (day, #p__linq__0, [Extent1].[ReserveDate])
ELSE
DATEDIFF (day, [Extent1].[LastUpdateDate], #p__linq__1)
END AS [C1]
FROM [dbo].[Table] AS [Extent1]
) AS [Project1]
ORDER BY [Project1].[C1]
As BoessB's comment has it, this is easiest with two concatenated queries:
var q1 = from x in source
where x.StatusId == 3
order by x.ReserveDate;
var q2 = from x in source
where x.StatusId != 3
order by x.LastUpdateDate descending;
var results = await q1.Concat(q2).ToListAsync();
It might be possible to do in a single expression if you can create a derived field (let clauses help) from ReserveDate and LastUpdateDate that sorts in the right way. But I would suggest splitting the query will be clearer.

Linq query to SQL query

I have a Linq Query that works well but I need to write the SQL Query
Can Anybody help me write it?
this query will search the database foreach a.h and a.HV in the view with the filters of time and model and in the end it checks the option Filter.M that if it is selected it will search for all the data selected in this DropDownCheckBoxes`
How can i write the this where and select part in SQL command?
ret1 = (from a in View
where
a.LastRefreshTime>=Filter.From && a.LastRefreshTime<=Filter.To && a.ModelCode == mdlCode &&
Filter.PN.Select(epn => epn.Substring(0, 11)).Contains(a.H) &&
Filter.PN.Select(epn => epn.Substring(14, 2)).Contains(a.HV)
select new RData
{
v = a.v,
Date = a.LastRefreshTime,
UserId = a.UserId,
M = a.Name,
}).Distinct().AsQueryable();
ret = ret1.Where(nr =>
Filter.M == null || !Filter.M.Any() || Filter.M.Contains(nr.M)
).ToList();
Here's a start for you
select a.v v,
a.LastRefreshTime "Date",
a.UserId,
a.Name
from a
where a.LastRefreshTime>= arg_filter_from
and a.LastRefreshTime<= arg_filter_to
and a.ModelCode = arg_mdlCode
.
.
.
In this query you'll need to replace 'arg_...' with the appropriate values or arguments you want.
Contains is roughly equivalent to "IN" in SQL. For example:
where a.Name in ('jim', 'bob', 'joe')
In can also be used with a subselect which is roughly what I think Filter.PN.Select is doing though I'm not a linq expert. Example:
where a.H in (Select foo from PN_Table)
Or simpler example continuing on the my previous name example:
where a.Name in (select first_name from table)
If we supposed that the Filter.PN list represent a table FilterPN in your sql database, that will be your converted code for the first linq query
select distinct a.v, a.LastRefreshTime, a.UserId, a.Name
from [view] a
where a.LastRefreshTime>= 'Filter.From' and
a.LastRefreshTime<='Filter.To' and a.ModelCode = 'mdlCode' and
exists(select top 1 * from FilterPN where Substring(epn, 1, 11) = a.H) and
exists(select top 1 * from FilterPN where Substring(eenter code herepn, 15, 2) = a.HV)
think to replace the enquoted variables with ur real values 'Filter.To'...

Linq-to-SQL: arithmetic operation on consecutive elements

For example, I have a table:
Date |Value
----------|-----
2015/10/01|5
2015/09/01|8
2015/08/01|10
Is there any way using Linq-to-SQL to get a new sequence which will be an arithmetic operation between consecutive elements in the previously ordered set (for example, i.Value - (i-1).Value)? It must be executed on SQL Server 2008 side, not application side.
For example dataContext.GetTable<X>().OrderByDescending(d => d.Date).Something(.......).ToArray(); should return 3, 2.
Is it possible?
You can try this:
var q = (
from i in Items
orderby i.ItemDate descending
let prev = Items.Where(x => x.ItemDate < i.ItemDate).FirstOrDefault()
select new { Value = i.ItemValue - (prev == null ? 0 : prev.ItemValue) }
).ToArray();
EDIT:
If you slightly modify the above linq query to:
var q = (from i in Items
orderby i.ItemDate descending
let prev = Items.Where(x => x.ItemDate < i.ItemDate).FirstOrDefault()
select new { Value = (int?)i.ItemValue - prev.ItemValue }
).ToArray();
then you get the following TSQL query sent to the database:
SELECT ([t0].[ItemValue]) - ((SELECT [t2].[ItemValue]
FROM (SELECT TOP (1) [t1].[ItemValue]
FROM [Items] AS [t1]
WHERE [t1].[ItemDate] < [t0].[ItemDate]) AS [t2]
)) AS [Value]
FROM [Items] AS [t0]
ORDER BY [t0].[ItemDate] DESC
My guess now is if you place an index on ItemDate field this shouldn't perform too bad.
I wouldn't let SQL do this, it would create an inefficient SQL query (I think).
I could create a stored procedure, but if the amount of data is not too big I can also use Linq to objects:
List<x> items=dataContext.GetTable<X>().OrderByDescending(d => d.Date).ToList();//Bring data to memory
var res = items.Skip(1).Zip(items, (cur, prev) => cur.Value - prev.Value);
At the end, I might use a foreach for readability

LINQ Count multiple values from a collection

In SQL what I'm trying to accomplish is
SELECT
SUM(CASE WHEN Kendo=1 THEN 1 ELSE 0 END) as KendoCount,
SUM(CASE WHEN Icenium=1 THEN 1 ELSE 0 END) as IceniumCount
FROM
Contacts
I'd like to do this in a C# program using LINQ.
Contacts is a List where Contact has many Booleans such as Kendo and Icenium and I need to know how many are true for each of the Booleans.
At least with LINQ to SQL, the downside of the count functions is that it requires separate SQL requests for each .count method. I suspect Jessie is trying to run a single scan over the table rather than multiple scans for each predicate. Depending on the logic and number of columns you are creating, this may not perform as well. Closer to the original request, try using sum with a ternary if clause as such (from Northwind):
from e in Employees
group e by "" into g
select new {
isUs = g.Sum (x => x.Country == "USA" ? 1 : 0),
NotUs = g.Sum (x => x.Country != "USA" ? 0 : 1)
}
LINQ to SQL generates the following (YMMV with other ORM's):
SELECT SUM(
(CASE
WHEN [t1].[Country] = #p1 THEN #p2
ELSE #p3
END)) AS [isUs], SUM(
(CASE
WHEN [t1].[Country] <> #p4 THEN #p5
ELSE #p6
END)) AS [NotUs]
FROM (
SELECT #p0 AS [value], [t0].[Country]
FROM [Employees] AS [t0]
) AS [t1]
GROUP BY [t1].[value]
var KendoCount = db.Contacts.Where(x => x.Kendo).Count();
var IceniumCount = db.Contacts.Where(x => x.Icenium).Count();
I would do this as two separate queries:
int kendoCount = db.Contacts.Count(c => c.Kendo);
int iceniumCount = db.Contacts.Count(c => c.Icenium);
Given that these queries will automatically translate into optimized SQL, this will likely be similar in speed or even potentially faster than any query option, and is far simpler to understand.
Note that, if this is for Entity Framework, you'll need to write this as:
int kendoCount = db.Contacts.Where(c => c.Kendo).Count();
int iceniumCount = db.Contacts.Where(c => c.Icenium).Count();
var result = Contacts
.GroupBy(c => new
{
ID = "",
})
.Select(c => new
{
KendoCount = c.Sum(k => k.Kendo ? 1 : 0),
IceniumCount = c.Sum(k => k.Icenium ? 1: 0),
})
.ToArray()

How can I convert this SQL Query into LINQ (OVER (PARTITION BY Date))

Here's the query I'm trying to convert into Linq:
SELECT R.Code,
R.FlightNumber,
S.[Date],
S.Station,
R.Liters,
SUM(R.Liters) OVER (PARTITION BY Year([Date]), Month([Date]), Day([Date])) AS Total_Liters
FROM S INNER JOIN
R ON S.ID = R.SID
WHERE (R.Code = 'AC')
AND FlightNumber = '124'
GROUP BY Station, Code, FlightNumber, [Date], Liter
ORDER BY R.FlightNumber, [Date]
Thanks for any help.
UPDATE: Here is the Linq code I'm trying it on; I cannot make the OVER PARTITION by Date.
var test =
(from record in ent.Records join ship in ent.Ship on record.ShipID equals ship.ID
orderby ship.Station
where ship.Date > model.StartView && ship.Date < model.EndView && ship.Station == model.Station && record.FlightNumber == model.FlightNumber
group record by new {ship.Station, record.Code, record.FlightNumber, ship.Date, record.AmountType1} into g
select new { g.Key.Station, g.Key.Code, g.Key.FlightNumber, g.Key.Date, AmmountType1Sum = g.Sum(record => record.AmountType1) });
Execute query first without aggregation:
var test =
(from record in ent.Records join ship in ent.Ship on record.ShipID equals ship.ID
orderby ship.Station
where ship.Date > model.StartView && ship.Date < model.EndView && ship.Station == model.Station && record.FlightNumber == model.FlightNumber
select new {ship.Station, record.Code, record.FlightNumber, ship.Date, record.AmountType1};
Then calculate sum
var result =
from row in test
select new {row.Station, row.Code, row.FlightNumber, row.Date, row.AmountType1,
AmountType1Sum = test.Where(r => r.Date == row.Date).Sum(r => r.AmountType1) };
This should produce the same effect as database query. Code above may contain errors, because I wrote it only here.
I've answered a similar thread on: LINQ to SQL and a running total on ordered results
On that thread it was like this:
var withRuningTotals = from i in itemList
select i.Date, i.Amount,
Runningtotal = itemList.Where( x=> x.Date == i.Date).
GroupBy(x=> x.Date).
Select(DateGroup=> DateGroup.Sum(x=> x.Amount)).Single();
In you situation, you might have to join the two tables together first while grouping, then run the same concept above on the joined table result.

Categories