LINQ getting Distinct values - sql

I know there are several questions regarding this topic. However; I cannot find one that is directly related to my problem.
I have 3tables in a DB and the PK's from those 3 tables form a composite PK in a XRef table.
I need to be able to select Distinct items based on 2 of the keys just for display on a report.
public IEnumerable<AssemblyPrograms> GetProgramAssemblies()
{
var assembliesList = (from c in eModel.Assemblies.ToList()
join d in eModel.Programs_X_Assemblies_X_Builds
on c.AssemblyID equals d.AssemblyID
join p in eModel.Programs
on d.ProgramID equals p.ProgramID
join a in eModel.AssemblyTypes
on c.AssemblyTypeID equals a.AssemblyTypeID
select new AssemblyPrograms
{
AssemblyID = c.AssemblyID
,ProgramID = d.ProgramID
,AssemblyName = c.AssemblyName
,AssemblyPrefixName = c.AssemblyPrefixName
,ProgramName = p.ProgramName
,AssemblyTypeName = a.AssemblyTypeName
,AssemblyTypeID = a.AssemblyTypeID
});
return assembliesList;
}
This is my query and what I need to pull out of the tables
In my XRef table I have AssemblyID, ProgramID and BuildID as my composite PK.
There can be a many-many relationship from AssemblyID to ProgramID. The BuildID is the key that separates them.
I need to pull Distinct AssemblyID to ProgramID relationships for my report, the BuildID can be ignored.
I have tried .Distinct() in my query and a few other things to no avail.
I would appreciate any help anyone could give me.
Thanks

How about a Distinct overload that accepts a custom equality comparer? Something like this:
class AssemblyComparer : EqualityComparer<AssemblyPrograms> {
public override bool Equals(AssemblyPrograms x, AssemblyPrograms y) {
return x.ProgramID == y.ProgramID && x.AssemblyID == y.AssemblyID;
}
public override int GetHashCode(AssemblyPrograms obj) {
return obj.ProgramID.GetHashCode() ^ obj.AssemblyID.GetHashCode();
}
}

Related

NestJS TypeORM pagination with many-to-many relationship

I have method in my product service like the one below. I'm filtering the data and using nestjs-typeorm-paginate to paginate, but it doesn't work properly (with a page size of 10 it returns 4-5 records depending on the number of related rooms. Without using room relation everything works great)
async findAllPaginated(
options: IPaginationOptions,
filters?: any,
): Promise<any> {
const queryBuilder = this.productRepository.createQueryBuilder('product');
queryBuilder.leftJoinAndSelect('product.category', 'category');
/* Room uses many to many relation */
queryBuilder.leftJoinAndSelect('product.room', 'room');
if (filters.category) {
queryBuilder.andWhere(`category.id like '${filters.category}'`);
}
if (filters.room) {
queryBuilder.andWhere(`room.id like '${filters.room}'`);
}
if (filters.priceMin) {
queryBuilder.andWhere(`product.price >= ${filters.priceMin}`);
}
if (filters.priceMax) {
queryBuilder.andWhere(`product.price <= ${filters.priceMax}`);
}
if (filters.promoPriceMin) {
queryBuilder.andWhere(`product.promoPrice >= ${filters.promoPriceMin}`);
}
if (filters.promoPriceMax) {
queryBuilder.andWhere(`product.promoPrice <= ${filters.promoPriceMax}`);
}
if (filters.sortField) {
queryBuilder.orderBy(filters.sortField, filters.sortDirection);
}
return paginate<Product>(queryBuilder, options);
}
Generated SQL looks like this:
SELECT
`product`.`id` AS `product_id`,
`product`.`shortenUrl` AS `product_shortenUrl`,
`product`.`name` AS `product_name`,
`product`.`price` AS `product_price`,
`product`.`promoPrice` AS `product_promoPrice`,
`product`.`deliveryCost` AS `product_deliveryCost`,
`product`.`promoEndDate` AS `product_promoEndDate`,
`product`.`description` AS `product_description`,
`product`.`amount` AS `product_amount`,
`product`.`photo` AS `product_photo`,
`product`.`width` AS `product_width`,
`product`.`height` AS `product_height`,
`product`.`depth` AS `product_depth`,
`product`.`colorCode` AS `product_colorCode`,
`product`.`created` AS `product_created`,
`product`.`updated` AS `product_updated`,
`product`.`deletedAt` AS `product_deletedAt`,
`product`.`categoryId` AS `product_categoryId`,
`product`.`ordersId` AS `product_ordersId`,
`category`.`id` AS `category_id`,
`category`.`name` AS `category_name`,
`category`.`icon` AS `category_icon`,
`category`.`created` AS `category_created`,
`category`.`updated` AS `category_updated`,
`category`.`deletedAt` AS `category_deletedAt`,
`category`.`groupId` AS `category_groupId`,
`room`.`id` AS `room_id`,
`room`.`shortenUrl` AS `room_shortenUrl`,
`room`.`name` AS `room_name`,
`room`.`icon` AS `room_icon`,
`room`.`created` AS `room_created`,
`room`.`updated` AS `room_updated`,
`room`.`deletedAt` AS `room_deletedAt`
FROM
`product` `product`
LEFT JOIN `category` `category` ON
`category`.`id` = `product`.`categoryId` AND(`category`.`deletedAt` IS NULL)
LEFT JOIN `room_products_product` `room_product` ON
`room_product`.`productId` = `product`.`id`
LEFT JOIN `room` `room` ON
`room`.`id` = `room_product`.`roomId` AND(`room`.`deletedAt` IS NULL)
WHERE
`product`.`deletedAt` IS NULL
ORDER BY
`product`.`name` ASC
And it returns duplicated data with diffrent rooms data:
The data returned to the client looks fine, with no duplicates with the correct link to the rooms, but with incorrect pagination.
How can I paginate correctly in this case? Is this at all possible?
I would appreciate any help :)

Fluent Nhibernate join on non foreign key property

I can't find this anywhere, but it seems pretty trivial. So, please excuse if this is a duplicate.
I have something like:
public class Doctor : Entity
{
...some other properties here...
public virtual string Email { get; set; }
}
public class Lawyer : Entity
{
...some other properties here...
public virtual string Email { get; set; }
}
I want to return all doctors where there is no email match in the Lawyers table like:
select * from Doctors d
where d.Email not in
(select l.Email from Lawyers l where l.Email is not null)
or using a join:
select d.* from Doctors d
left join Lawyers l on l.Email = d.Email
where l.Email is null
The problem is that the Email is of course not set up as a foreign key. I have no mapped property on the Doctor entity that maps to Lawyer.
What I've tried so far:
ICriteria criteria = Session.CreateCriteria(typeof(Doctor))
.CreateAlias("Lawyers.Email", "LawyerEmail", JoinType.LeftOuterJoin)
.Add(Restrictions.IsNull("LawyerEmail"));
return criteria.List<Doctor>();
But, I get a "cannot resolve property Lawyer of MyPlatform.MyNamespace.Doctor" error. Any ideas how to set up my DoctorMap and adjust the criteria tomfoolery to achieve this?
NHibernate for the loss........Entity Framework for the win....
We can achieve that with a feature called subquery:
// a inner SELECT to return all EMAILs from Lawyer table
var subQuery = DetachedCriteria.For<Lawyer>()
.SetProjection(Projections.Property("Email"));
// the root SELECT to get only these Doctors
var criteria = session.CreateCriteria<Doctor>();
// whos email is not in the sub SELECT
criteria.Add(Subqueries.PropertyNotIn("Email", subQuery));
// get first 10
var result = criteria
.SetMaxResults(10)
.SetFirstResult(0) // paging
.List<Doctor>();

Group By Sum Linq to SQL in C#

Really stuck with Linq to SQL grouping and summing, have searched everywhere but I don't understand enough to apply other solutions to my own.
I have a view in my database called view_ProjectTimeSummary, this has the following fields:
string_UserDescription
string_ProjectDescription
datetime_Date
double_Hours
I have a method which accepts a to and from date parameter and first creates this List<>:
List<view_UserTimeSummary> view_UserTimeSummaryToReturn =
(from linqtable_UserTimeSummaryView
in datacontext_UserTimeSummary.GetTable<view_UserTimeSummary>()
where linqtable_UserTimeSummaryView.datetime_Week <= datetime_To
&& linqtable_UserTimeSummaryView.datetime_Week >= datetime_From
select linqtable_UserTimeSummaryView).ToList<view_UserTimeSummary>();
Before returning the List (to be used as a datasource for a datagridview) I filter the string_UserDescription field using a parameter of the same name:
if (string_UserDescription != "")
{
view_UserTimeSummaryToReturn =
(from c in view_UserTimeSummaryToReturn
where c.string_UserDescription == string_UserDescription
select c).ToList<view_UserTimeSummary>();
}
return view_UserTimeSummaryToReturn;
How do I manipulate the resulting List<> to show the sum of the field double_Hours for that user and project between the to and from date parameters (and not separate entries for each date)?
e.g. a List<> with the following fields:
string_UserDescription
string_ProjectDescription
double_SumOfHoursBetweenToAndFromDate
Am I right that this would mean I would have to return a different type of List<> (since it has less fields than the view_UserTimeSummary)?
I have read that to get the sum it's something like 'group / by / into b' but don't understand how this syntax works from looking at other solutions... Can someone please help me?
Thanks
Steve
Start out by defining a class to hold the result:
public class GroupedRow
{
public string UserDescription {get;set;}
public string ProjectDescription {get;set;}
public double SumOfHoursBetweenToAndFromDate {get;set;}
}
Since you've already applied filtering, the only thing left to do is group.
List<GroupedRow> result =
(
from row in source
group row by new { row.UserDescription, row.ProjectDescription } into g
select new GroupedRow()
{
UserDescription = g.Key.UserDescription,
ProjectDescription = g.Key.ProjectDescription,
SumOfHoursBetweenToAndFromDate = g.Sum(x => x.Hours)
}
).ToList();
(or the other syntax)
List<GroupedRow> result = source
.GroupBy(row => new {row.UserDescription, row.ProjectDescription })
.Select(g => new GroupedRow()
{
UserDescription = g.Key.UserDescription,
ProjectDescription = g.Key.ProjectDescription,
SumOfHoursBetweenToAndFromDate = g.Sum(x => x.Hours)
})
.ToList();

combine join with String.Contains in Linq query

i have the following linq query that create a left join between two tables:
var joinResultRows = from leftTable in dataSet.Tables[leftTableName].AsEnumerable()
join
rightTable in dataSet.Tables[rightTableName].AsEnumerable()
on leftTable.Field<string>(leftComparedColumnName) equals rightTable.Field<string>(rightComparedColumnName)
into leftJoinedResult
select new { leftTable, leftJoinedResult };
i want to get the rows that answer this:
the String value in the left column contains the string value in the right column.
i tried this :
var joinResultRows = from leftTable in dataSet.Tables[leftTableName].AsEnumerable()
join
rightTable in dataSet.Tables[rightTableName].AsEnumerable()
on leftTable.Field<string>(leftComparedColumnName).Contains(rightTable.Field<string>(rightComparedColumnName)) equals true
into leftJoinedResult
select new { leftTable, leftJoinedResult };
but it doesn't work cause rightTable isn't recognized in the left side of the join.
How do i create a join that results the String.Contains, do i do the contains in the 'where' clause or in the 'On' clause?
Have you tried a SelectMany?
var result =
from left in dataSet.Tables[leftTableName].AsEnumerable()
from right in dataSet.Tables[rightTableName].AsEnumerable()
where left.Field<string>(leftComparedColumnName).Contains(right.Field<string>(rightComparedColumnName))
select new { left, right };
Edit:
The following should have the desired effect:
class ContainsEqualityComparer: IEqualityComparer<string>
{
public bool Equals(string right, string left) { return left.Contains(right); }
public int GetHashCode(string obj) { return 0; }
}
var result =
dataSet.Tables[leftTableName].AsEnumerable().GroupJoin(
dataSet.Tables[rightTableName].AsEnumerable(),
left => left,
right => right,
(left, leftJoinedResult) => new { left = left, leftJoinedResult = leftJoinedResult },
new ContainsEqualityComparer());
The key comparison is run through the custom IEqualityComparer. Two rows will only be joined when GetHashCode() of left and right are the same, and Equals returns true.
Hope it helps.
I solved it by creating SelectMany (join) that the left table holds all records and the right holds null if there is no match:
var joinResultRows = from leftDataRow in dataSet.Tables[leftTableName].AsEnumerable()
from rightDataRow in dataSet.Tables[rightTableName].AsEnumerable()
.Where(rightRow =>
{
// Dont include "" string in the Contains, because "" is always contained
// in any string.
if ( String.IsNullOrEmpty(rightRow.Field<string>(rightComparedColumnName)))
return false;
return leftDataRow.Field<string>(leftComparedColumnName).Contains(rightRow.Field<string>(rightComparedColumnName));
}).DefaultIfEmpty() // Makes the right table nulls row or the match row
select new { leftDataRow, rightDataRow };
Thank you for the tip :)

Joining tables in LINQ/SQL

I have below a collection of rows and each row consists of productid, unitid, countryid.
I need to find the details for each row in the corresponding tables (products, units, country)
for product - select product name, updatedby
for unitid - select unit name , updatedby
for countryid - select countryname, uploadby
and returning the rows which has the same format
Id = product id or unitid or countryid
name = proudct name or unit name or countryname
modified = product updatedby or unit updated by or country uploadby
So, in summary -
1. For a Collection of rows
a. use the id to get the extra details from the respective table
b. return the same type of collection for the results
2. do step 1 for
2.a For RegularToys (Run this logic on TableBigA)
2.b For CustomToys(Run this logic on TableB)
3. Return all the rows
by adding 2.a and 2.b
How to write an sql/linq query for this? thanks
If I'm understanding correctly, you want to use a given ID to find either a product, a unit or a country but you're not sure which. If that's the case, then you can build out deferred queries like this to find the given entity:
var prod = from p in db.Products
where p.ProductId = id
select new { Id = p.ProductId, Name = p.ProductName, Modified = p.UpdatedBy };
var unit = from u in db.Units
where p.UnitId = id
select new { Id = u.UnitId, Name = u.UnitName, Modified = p.UpdatedBy };
var ctry = from c in db.Countries
where c.CountryId = id
select new { Id = c.CountryId, Name = c.CountryName, Modified = c.UploadBy };
And then execute the queries until you find an entity that matches (with ?? being the null-coalesce operator that returns the right value if the left result is null).
var res = prod.SingleOrDefault() ??
unit.SingleOrDefault() ??
ctry.SingleOrDefault() ??
new { Id = id, Name = null, Modifed = null };
Without any further details I can't be more specific about the condition below, but I think you are asking for something along these lines. I'm assuming your Id's are int's (but this can be easily changed if not) and you already have an Entity Data Model for the tables you describe.
Create a class for your common data:
class RowDetail
{
public int Id { get; set; }
public string Name { get; set; }
public string Modified { get; set; }
}
Pull the information out of each of the sub tables into a new record:
IEnumerable<RowDetail> products =
from p in db.Products
where <<CONDITION>>
select
new RowDetail()
{
Id = p.ProductId,
Name = p.ProductName,
Modified = p.UpdatedBy
};
IEnumerable<RowDetail> units =
from u in db.Units
where <<CONDITION>>
select
new RowDetail()
{
Id = u.UnitId,
Name = u.UnitName,
Modified = u.UpdatedBy
};
IEnumerable<RowDetail> countries =
from c in db.Countries
where <<CONDITION>>
select
new RowDetail()
{
Id = c.CountryId,
Name = c.CountryName,
Modified = c.UploadBy
};
Finally pull all the records together in a single collection:
IEnumerable<RowDetail> results = products.Union(units).Union(countries);
I'm not sure if this is exactly what you are looking for, so feel free to give feedback and/or more details if further assistance is required.