LINQ lambda syntax for SQL subquery + count(distinct on one column) - linq

How would I write the following SQL query in lambda-notation LINQ?
select 'myString', count(distinct val)
from myChildTable
where mytable_id in (
select id
from myTable
where col1_int = 6
and col2_int between 1 and 366
and col3_str = 'myString'
)
For reference, the two tables in question are:
create table myTable (
id int primary key identity,
col1_int int not null,
col2_int int not null,
col3_str varchar(1024)
)
create table myChildTable (
id int primary key identity,
mytable_id int not null foreign key references myTable(id),
val varbinary(13) not null
)

This could possibly be shortened but I find it easier to read if broken up:
// list ids from table
var ids = myTable.Where(x => x.Col1Int == 6 && x.Col2Int >= 1 && x.Col2Int <= 366 && x.Col3Str == "myString").Select(x => x.Id).ToList();
// use in subquery for values (non-distinct)
var vals = myChildTable.Where(x => ids.Contains(x.Id)).Select(x => x.Val).ToList();
// project to distinct dictionary of value, count
var dict = vals.Distinct().ToDictionary(x => x, x => vals.Count(y => y == x));
This seems to work using classes like these:
class ParentEntity
{
public int Id { get; set; }
public int Col1Int { get; set; }
public int Col2Int { get; set; }
public string Col3Str { get; set; }
public ParentEntity(int id, int col1Int, int col2Int, string col3Str)
{
Id = id;
Col1Int = col1Int;
Col2Int = col2Int;
Col3Str = col3Str;
}
}
class ChildEntity
{
public int Id { get; set; }
public int ParentEntityId { get; set; }
public string Val { get; set; }
public ChildEntity(int id, int parentEntityId, string val)
{
Id = id;
ParentEntityId = parentEntityId;
Val = val;
}
}

This query should to it:
from c in MyChildTables
where MyTables.Any (mt => mt.Col1_int == 6 && mt.Col2_int >= 1 &&
mt.Col2_int <= 366 && mt.Col3_str == "myString" && mt.Id == c.Id)
group c by true into g
select new
{
Col1 = "myString",
Col2 = g.Select (x => x.Val).Distinct().Count ()
}
Or if you rather have it in Lambda-notation:
MyChildTables
.Where (
c =>
MyTables
.Any (
mt =>
(((((mt.Col1_int == 6) && (mt.Col2_int >= 1)) && (mt.Col2_int <= 366)) && (mt.Col3_str == "myString")) && (mt.Id == c.Id))
)
)
.GroupBy (c => True)
.Select (
g =>
new
{
Col1 = "myString",
Col2 = g.Select (x => x.Val).Distinct ().Count ()
}
)

Related

Select all records in table B with same PK and different property values as in table A

I have two tables TA and TB. Both use the same PK (TB is a shadow table of TA). I need to identify all records from TA that are also in TB, but have one or more property values changed.
Sample code:
public class MyData
{
[Key]
public Guid PK { get; set; } = Guid.Empty;
public int Value1 { get; set; } = 0;
public int Value2 { get; set; } = 0;
}
What I need is something like find all records R from TA in TB where R(TA).PK == R(TB).PK && (R(TA).Value1 != R(TB).Value1 || R(TA).Value1 != R(TB).Value1)
However, I have no bloody clue how to write that down, neither as sql nor as Linq statement. I tried a lot of variants, but none was syntactically correct.
var result = ctx.TA.Where(a => ctx.TB.Any(b => a.PK == b.PK &&
(a.Value1 != b.Value1 || a.Value2 != b.Value2)));
Try the following query:
var query =
from ta in context.TableA
join tb in context.TableB on ta.PK equals tb.PK
where ta.Value1 != tb.Value1 || ta.Value2 != tb.Value2
select ta;

Strongly typed linq group by

Relevant Model;
public class category_trans
{
[Key]
[Column(Order = 1)]
public int category_id { get; set; }
[Key]
[Column(Order = 2)]
public int language_id { get; set; }
public string name { get; set; }
}
present Linq query, working:
IQueryable<category_trans> APC =
from ct in db.category_trans
from c in db.Categories
from l in db.ISO_Languages
where (
ct.category_id == c.ID
&& ct.language_id == l.ID
&& l.code.Substring(0,2) == culture
&& c.IsDeleted == false)
select ct;
I would like to group query result in order to get distinct category_trans.name (now I am getting multiple ones).
Trying
IQueryable<category_trans> APC =
from ct in db.category_trans
from c in db.Categories
from l in db.ISO_Languages
where (
ct.category_id == c.ID
&& ct.language_id == l.ID
&& l.code.Substring(0,2) == culture
&& c.IsDeleted == false)
group ct by ct.name into g
select new
{
category_id = g.category_id,
name = g.name
};
gives me errors on both g.category_id and g.name
IGrouping <string,category_trans> does not contain a definition for 'category_id'...
Why grouping seems to lose reference to model members and how may it be fixed?
Because a group can contain multiple, you can use the Key property for the name:
IQueryable<category_trans> APC =
from ct in db.category_trans
from c in db.Categories
from l in db.ISO_Languages
where (
ct.category_id == c.ID
&& ct.language_id == l.ID
&& l.code.Substring(0,2) == culture
&& c.IsDeleted == false)
group ct by ct.name into g
select new
{
category_id = g.First().category_id,
name = g.Key
};
I have used First to get the first category_id, you might want to use a different logic.

Linq: Using Distinct on calculated result

I have a query which calculates a range of dates which I want to run a distinct query on to return just Year/Month combinations. I can't work out how best to achieve this.
private IEnumerable<DateTime> MonthsWithItineraryData (int Id) // shipId
{
var months = (from i in context.Itineraries
join isd in context.ItineraryStartDates on i.Id equals isd.ItineraryId
join id in context.ItineraryDay on i.Id equals id.ItineraryId
where i.ShipId == Id
select (DateTime)DbFunctions.AddDays(isd.Date, id.Day)
);
return (months);
}
I think you want something like this:
return monts.GroupBy(x => new {x.Month, x.Year})
.Select(x => x.First());
or else you can a custom equality comparer as distinct argument:
public class DateTimeComparer : IEqualityComparer<DateTime>
{
public bool Equals(DateTime x, DateTime y)
{
return x.Year == y.Year && x.Month == y.Month;
}
public int GetHashCode(DateTime obj)
{
return obj.Year * 100 + obj.Month;
}
}
result = months.Distinct(new DateTimeComparer()).ToList();

Optimize queries for Union, Except, Join with LINQ and C#

I have 2 objects (lists loaded from XML) report and database (showed bellow in code) and i should analyse them and mark items with 0, 1, 2, 3 according to some conditions
TransactionResultCode = 0; // SUCCESS (all fields are equivalents: [Id, AccountNumber, Date, Amount])
TransactionResultCode = 1; // Exists in report but Not in database
TransactionResultCode = 2; // Exists in database but Not in report
TransactionResultCode = 3; // Field [Id] are equals but other fields [AccountNumber, Date, Amount] are different.
I'll be happy if somebody could found time to suggest how to optimize some queries.
Bellow is the code:
THANK YOU!!!
//TransactionResultCode = 0 - SUCCESS
//JOIN on all fields
var result0 = from d in database
from r in report
where (d.TransactionId == r.MovementID) &&
(d.TransactionAccountNumber == long.Parse(r.AccountNumber)) &&
(d.TransactionDate == r.MovementDate) &&
(d.TransactionAmount == r.Amount)
orderby d.TransactionId
select new TransactionList()
{
TransactionId = d.TransactionId,
TransactionAccountNumber = d.TransactionAccountNumber,
TransactionDate = d.TransactionDate,
TransactionAmount = d.TransactionAmount,
TransactionResultCode = 0
};
//*******************************************
//JOIN on [Id] field
var joinedList = from d in database
from r in report
where d.TransactionId == r.MovementID
select new TransactionList()
{
TransactionId = d.TransactionId,
TransactionAccountNumber = d.TransactionAccountNumber,
TransactionDate = d.TransactionDate,
TransactionAmount = d.TransactionAmount
};
//Difference report - database
var onlyReportID = report.Select(r => r.MovementID).Except(joinedList.Select(d => d.TransactionId));
//TransactionResultCode = 1 - Not Found in database
var result1 = from o in onlyReportID
from r in report
where (o == r.MovementID)
orderby r.MovementID
select new TransactionList()
{
TransactionId = r.MovementID,
TransactionAccountNumber = long.Parse(r.AccountNumber),
TransactionDate = r.MovementDate,
TransactionAmount = r.Amount,
TransactionResultCode = 1
};
//*******************************************
//Difference database - report
var onlyDatabaseID = database.Select(d => d.TransactionId).Except(joinedList.Select(d => d.TransactionId));
//TransactionResultCode = 2 - Not Found in report
var result2 = from o in onlyDatabaseID
from d in database
where (o == d.TransactionId)
orderby d.TransactionId
select new TransactionList()
{
TransactionId = d.TransactionId,
TransactionAccountNumber = d.TransactionAccountNumber,
TransactionDate = d.TransactionDate,
TransactionAmount = d.TransactionAmount,
TransactionResultCode = 2
};
//*******************************************
var qwe = joinedList.Select(j => j.TransactionId).Except(result0.Select(r => r.TransactionId));
//TransactionResultCode = 3 - Transaction Results are different (Amount, AccountNumber, Date, )
var result3 = from j in joinedList
from q in qwe
where j.TransactionId == q
select new TransactionList()
{
TransactionId = j.TransactionId,
TransactionAccountNumber = j.TransactionAccountNumber,
TransactionDate = j.TransactionDate,
TransactionAmount = j.TransactionAmount,
TransactionResultCode = 3
};
you may try something like below:
public void Test()
{
var report = new[] {new Item(1, "foo", "boo"), new Item(2, "foo2", "boo2"), new Item(3, "foo3", "boo3")};
var dataBase = new[] {new Item(1, "foo", "boo"), new Item(2, "foo22", "boo2"), new Item(4, "txt", "rt")};
Func<Item, bool> inBothLists = (i) => report.Contains(i) && dataBase.Contains(i);
Func<IEnumerable<Item>, Item, bool> containsWithID = (e, i) => e.Select(_ => _.ID).Contains(i.ID);
Func<Item, int> getCode = i =>
{
if (inBothLists(i))
{
return 0;
}
if(containsWithID(report, i) && containsWithID(dataBase, i))
{
return 3;
}
if (report.Contains(i))
{
return 2;
}
else return 1;
};
var result = (from item in dataBase.Union(report) select new {Code = getCode(item), Item = item}).Distinct();
}
public class Item
{
// You need also to override Equals() and GetHashCode().. I omitted them to save space
public Item(int id, string text1, string text2)
{
ID = id;
Text1 = text1;
Text2 = text2;
}
public int ID { get; set; }
public string Text1 { get; set; }
public string Text2 { get; set; }
}
Note that you need to either implement Equals() for you items, or implement an IEqualityComparer<> and feed it to Contains() methods.

Facing problem in Linq to sql

//Below mentioned class is created to understand the problem, while this is created through Linq2Sql.
public class JobAds
{
public int ID { get; set; }
public int EmployerRef { get; set;}
}
int? employerId = null;
var jobAdListing = JobAds.Where
( n => (!employerId.HasValue || n.EmployerRef == employerId.Value )).ToList();
The Issue is I'm getting "Nullable object must have a value." at the above line. After doing some debugging, I feel n.EmployerRef == employerId.Value is making some trouble, but unable to find anything good.
Just write liks this, you don't have to worry about the null values (since NULL == NULL equals false)
int? employerId = null;
var jobAdListing = tblCompanies.Where
(n => (n.fkUserID_responsible == employerId)).ToList();
Or you can keep your code and just remove the ".value"
var jobAdListing = JobAds.Where
( n => (!employerId.HasValue || n.EmployerRef == employerId)).ToList();
in my local playground a simmilar case works with this easiest approach:
using (UnitOfWork.Begin("LinqToSql"))
{
Guid? id1 = null;
Guid? id2 = this.personRepository.GetAll().First().FavouriteProjectId;
var all = this.personRepository.GetAll().Where(o => o.FavouriteProjectId == id1 || o.FavouriteProjectId == id2).ToArray();
}
for you, this should work too:
int? employerId = null;
int? employerType = null; /* OTHER Conditions */
var list = JobAds.Where(n => n.EmployerRef == employerId &&
n.EmployerTypeRef == employerType)).ToArray();

Resources