SQL SELECT depending on multiple variables and tables - c#

I'm trying to create a SELECT query, but the problem here is that I get multiple variables that suitable for different table.
To make it clear I'll present the database tables :
Category
CatID
CatName
Subcategory
CatID
SubCatID
SubCatName
Company
CompanyID
CompName
SubCat
Model
CompID
ModelID
ModelName
Posts
PostID
ModelID
Now, I get some parameters like : Cat, Subcat, Category, ModelID and Name, and what i need is to select all the matching results of PostID that suits for the parameters.
E.g, if I get the following parameters :
Cat = "Electronics"
Subcat="Computers"
Company="Apple"
ModelName="Macbook Air"
Keyword="new"
I need to get the following results of Posts where the category equals to the Cat variables, and the subcategory matches the SubCat Variable and so on.. and in addition to filter the result using %Like% for the keyword variable that I got.
Unfortunately I failed after a lot of tries trying to solve it. Tried using the Inner JOIN in different ways, but it didn't work.
I would really appreciate if someone can help me with this, because I got not idea how to continue form this point, and I don't have a lot of time till I need to finish this project.
Thanks in advance

Select posts.*
from Posts inner join
model on posts.modelId = model.modelId and model.ModelName = 'Macbook Air' inner join
Company on model.compID = company.companyID and Company.CompName = 'Apple' inner join
Subcategory on company.subCat = Subcategory.SubcatID and subcategory.subcatName ='Computers' inner join
Category on subcategory.catID = category.catID and category.CatName = 'Electronics'
where posts.keywordField like '%new%'
I had to make some assumptions there, like what field you are trying to search for your keyword in. But you get the general idea: at each inner join you can specify an additional condition that will limit your results, and in this manner apply your parameters to your query.

One possible way to make this work would be to create a View so you only have one table to search against.
Select c.*, sc.SubCatName, com.*, ModelName, p.*
from Category c
join Subcategory sc on c.CatID = sc.CatID
join Company com on sc.SubCatID = com.SubCatID
join Model m on com.CompID = com.CompID
join Posts p on p.ModelID = m.ModelID

Related

c# entity framework - query and join multiple tables / entities

Assuming I have the following tables (with similarly formed models, plus a few virtual props)
Tabel 1: "AgentCompany" (Simply links an agents ID to a company)
AgentCompanyId, AgentId, CompanyId
Table 2: "Company"
CompanyId, CountryId
Table 3: "Country"
CountryId, Name
The model for AgentCompany has a virtual property for "Company" and The model for "Company" also has a virtual "Country" property.
My query currently looks like
var res = (from ap in Repo.AgentCompany
join p in Repo.Company on ap.CompanyId equals p.CompanyId
join c in Repo.Country on ap.Country.Name equals c.Name
where ap.AgentId == AgentFilter
select new AgentCompany
{
Company = p
});
This works, and i get all my data except that the Company (p) has its country as Null.
Im trying to fiugre out how to get this syntax to work so that since I can query the country as c shown above, i want p.Country = c, so in my AgentCompany object result, the Company property is the actual company, and the Company's virtual Country property points to a COuntry object.
Anyone know how I might achieve this? I have tried a few question searches and tried a few suggestions related to an inner join, or an "Include" method but I haven't been able to achieve what I want here.
Thanks in advance!

Having trouble constructing the correct Linq query syntax in c#.net for a join after join

I have troubles creating the correct query for a list of applications I have. I need to display them in a dashboard so consultants can handle the applications.
These applications can be uploaded by organisations or individuals.
Every consultant is 'owner' of a few organisations and individuals, in the dashboard they should only see the applications from individuals/organisations that they are also the 'owner' of.
The applications table:
Application
-----------
ID | requestIndividualID | requestOrganisationID | ...
depending on who made the application the requesters ID will be saved in the table. So requestIndividualID or requestOrganisationID, one of them will be null, the other not.
The Individuals table:
Individual
----------
ID | name | ownerID
Now comes the tricky part, the ownerID is not on the Organisation table, but on the OrganisationSegment table, something like this:
Organisation
------------
ID | name | city
OrganisationSegment
-------------------
organisationID | ownerID
The problem comes when I want to join the Organisation table with the OrganisationsSegment table, since it is possible that the organisationID is null.
Currently I have this code that is throwing errors:
var applications = from pa in _context.Applications
join individu in _context.Individu on pa.requestIndividualID equals individu.ID into IndividuResultList
from individu in IndividuResultList.DefaultIfEmpty()
join organisation in _context.Organisations on pa.requestOrganisationID equals organisation.ID into organisationsList
from organisation in organisationsList
join organisationSegment in _context.OrganisationSegment on organisation.ID equals organisationSegment.OrganisationId into organisationSegmentList
from organisationSegment in organisationSegmentList.DefaultIfEmpty()
select new {Data = new {PortalApplications = pa, individu, organisation, organisationSegment}};
After this code I use the result to make a list where I can apply the filter for the owner Id.
When I execute this code, it throws System.InvalidOperationException : Nullable object must have a value. error.
My current solution is making 2 results, one for all the application that have a requestIndividualID and one for all the applications with a requestOrganisationID.
Then I apply the filter (the specific ownerID) to these 2 separate results, and adding the results in a list, since the results came from the same table, thats really easy.
But imo this is a quicky and dirty way of handling it.
I want the correct and clean answer.
Can somebody please help me?
Also i have trouble finding any help on the internet for this, so if you found some info, please link it as well, or tell me how to improve my google skills for issues like this (correct naming of the issue).
I believe this is easier if you join the Orginisation and OrginisationSegment tables first, assuming every Orginisation has an ownerID in OrginisationSegment.
var OrganisationsWithOwner = from o in _context.Organisations
join os in _context.OrganisationSegment on o.ID equals os.OrganisationId
join i in _context.Individu on os.OwnerID equals i.ID
select new { o, i };
Now you can join with Individu and the new OrginisationWithOwner and return the one that has a match.
var applications = from pa in _context.Applications
join indOwner in _context.Individu on pa.requestIndividualID equals indOwner.ID into IndividuResultList
from indOwner in IndividuResultList.DefaultIfEmpty()
join o in OrganisationsWithOwner on pa.requestOrganisationID equals o.o.ID into organisationsList
from o in organisationsList.DefaultIfEmpty()
select new { PortalApplications = pa, owner = (indOwner != null ? indOwner : o.i), organisation = o.o };

Group child list into single parent object - AdventureWorks query - Lambda Expression

I am working with the AdventureWorks database and I am using the following query :
SELECT c.CustomerID, c.AccountNumber, soh.SalesOrderID, soh.SalesOrderNumber, sod.UnitPrice, sod.ProductID, p.Name
FROM Sales.SalesOrderHeader soh
inner join sales.SalesOrderDetail sod on soh.SalesOrderID = sod.SalesOrderID
inner join sales.Customer c on c.CustomerID = soh.CustomerID
inner join Production.Product p on p.ProductID = sod.ProductID
I have code already that populates the data like this just as it comes back:
Based on that I am wanting to merge the child list objects into a single parent to end up with a one to many structure if that makes sense.
As of right now I am hoping to use a lambda expression to do this. I have tried the following code but I don't want to have to specify --> o.CustomerID, o.AccountNumber in the group by because i would have to specify ALL the properties of my Customer class to have access to the final select as you can see in the code.
.GroupBy(o => new { o.CustomerID, o.AccountNumber }).Select(group => new Customer2
{
CustomerID = group.Key.CustomerID,
AccountNumber = group.Key.AccountNumber,
Orders = group.SelectMany(x => x.Orders).ToList()
}).ToList();
I would like the end result to look like this for each customer:
I hope this makes sense and thanks in advance for the help.

LINQ Null Join with Pivot Table

I'm trying to get a list of servers thay may or may not belong to 1 or more groups to display in a grid.
Example
ServerID IP GroupID
1 192.168.1.44 1
1 192.168.1.44 10
2 192.168.1.45 1
3 192.168.1.46 2
4 192.168.1.47 null
5 192.168.1.48 null
If I have no records In the GroupServer Table. (Since there is no groups or groups exist but they are not assigned) I expect to get something like this:
ServerID IP GroupID
1 192.168.1.44 null
2 192.168.1.45 null
3 192.168.1.46 null
4 192.168.1.47 null
5 192.168.1.48 null
Since is a Many-to-Many relationship. I have
Group Table
Server Table
GroupServer Table
I could not find a LINQ Pivot Table example.
So I tried to buid my own.
var query = (from sg in context.ServerGroups
join servers in context.Servers on sg.ServerID equals servers.ID
join groups in context.Groups on sg.GroupID equals groups.ID
into serverxgroup
from gAddrBilling in serverxgroup.DefaultIfEmpty()
select new
{
ServerID = sg.ServerID,
ServerIP = server.IP,
GroupID = sg.GroupID
});
The Query above does not retrieve anything
And I quiet dont understand what the "from gAddrBilling" is for. Since I modify a snippet I was trying to make work. So I wonder if someone has already faced a problem like this and give me some hint, snippet or advice about what is what I'm missing.
Thank you.
First, this is not a pivot query, but a regular query on many-to-may relationship via explicit junction table.
Second, looks like you are using Entity Framework, in which case you'd better define and use navigation properties rather than manual joins.
Third, and the most important, the structure of the query is wrong. If you want to get a list of servers that may or may not belong to 1 or more groups, then you should start your query from Servers (the table which records you want to be always included, not from link table where some ServerID are missing) and then use left outer joins to the other tables like this:
var query =
from s in servers in context.Servers
join sg in context.ServerGroups on s.ID equals sg.ServerID
into s_sg from sg in s_sg.DefaultIfEmpty() // make the above LEFT OUTER JOIN
// You can remove the next two lines if all you need is the GroupId
// and keep them if you need some other Group field in the select
join g in context.Groups on sg.GroupID equals g.ID
into sg_g from g in sg_g.DefaultIfEmpty() // make the above LEFT OUTER JOIN
select new
{
ServerID = s.ID,
ServerIP = s.IP, // or sg.IP?
GroupID = (int?)sg.GroupID
};

Linq to Sql - Populate JOIN result into a List

I am not sure if this can be done, but here's the scenario.
I want to turn this sql into linq:
SELECT * FROM Department d
INNER JOIN Employee e ON e.DepartmentID = d.DepartmentID
Department - Employee is 1 to many relationship.
I have created a custom object that I would like to populate the result into.
public class DepartmentSummary
{
public Department Department { get; set; }
public List<Employee> Employees {get; set;}
}
The Linq I came up with is
var result = from d in dba.Department
join e in dba.Employee d.DepartmentID equals e.DepartmentID into j1
select new DepartmentSummary
{
Department = d,
Employees = j1.ToList()
};
I tried it out and it's not working. Can anyone shed some light for me please? I would like to perform an inner join between Department and Employee. For each Department in the resultset, I would like to create one DepartmentSummary object which holds that department and a list of employees belonging to that department.
Does Linq provides an ad hoc solution for this or must I iterates through the result set and create a list of DepartmentSummary manually?
Thanks,
EDIT:
Looks like this works for me
var result = from d in dba.Department
join e in dba.Employee d.DepartmentID equals e.DepartmentID into j1
where j1.Count() > 0
select new DepartmentSummary
{
Department = d,
Employees = j1.ToList()
};
The thing is that you're not really taking one SQL and trying to create a Linq-query out of it.
If you were, you'd notice that your SQL query does not really produce one row per department, but it will repeat the department information for each employee in that department.
Now, an initial naive look would suggest you use a group-by clause, since that would allow you to split the data into individual groupings for each department, but groupings in SQL does not really give you a key+all-matching-rows type of result, rather it allows you to do aggregate calculations, like "for each department, how many employees do I have".
So, in order to do what you want, you need to basically do a normal join, which will give you each employee, coupled with the appropriate department information (ie. each employee will be linked to his/her department), and then you need to construct the rest of the data structure yourself.
Now, having said that, if you have the proper relationships set in your data context related classes, each department should already have some kind of property that contains all employees in that department, so perhaps the simple query is just "give me all departments", and then you can, for each department, retrieve the employees?
Of course, doing that would likely execute one SQL for each department, but in this case, you're back to "give me all employees with their department information" and you have to build code to handle the rest.
LINQ to SQL doesn't understand your ToList() call, but you might be able to select the sequence of joined elements and then use LINQ to Objects (via AsEnumerable()) to map to your DepartmentSummary object:
var qResult = from d in dba.Department
join e in dba.Employee d.DepartmentID equals e.DepartmentID into j1
select new
{
Department = d,
Employees = j1
};
var result = from d in qResult.AsEnumerable()
select new DepartmentSummary()
{
Department = d.Department,
Employees = e.Employees.ToList()
};
Sounds like you're looking to get around lazy loading?
DataLoadOptions dlo = new DataLoadOptions();
dlo.LoadWith<Department>(d => d.Employees);
using (var dba = new MyDataContext())
{
dba.LoadOptions = dlo;
var result = from d in dba.Department
select d;
}
Now, if you don't have a relationship defined between Department and Employees (the Linq2Sql designer will do this for you if you have database relationships setup) then you should look into doing that. It makes it all dramatically easier. In fact, you don't even need your campaign summary.
This problem is due to the nature of the query. When you join Department to Employee, you'll get back one record for every Employee. This means that your ToList() statement is expecting multiple employees per department, but due to the join, always getting one.
Change your query to
var result =
from d in dba.Department
select new tCampaignSummary
{
Department = d,
Employees = dba.Employee.Where(e => e.DepartmentID ==
d.DepartmentID).ToList()
};
I've tested this and it works.
What it does differently is selects only one record per Department (not per employee) then it gets the zero to many corresponding employees for each dept and converts them to a list.
Good luck!
EDIT
As requested, here is the generated SQL:
SELECT [t0].*, [t1].*
(
SELECT COUNT(*)
FROM [dbo].[Employee] AS [t2]
WHERE [t2].[DepartmentID] = [t0].[DepartmentID]
) AS [value]
FROM [dbo].[Department] AS [t0]
LEFT OUTER JOIN [dbo].[Employee] AS [t1]
ON [t1].[DepartmentID] = [t0].[DepartmentID]
ORDER BY [t0].[DepartmentID], [t1].[IndexID]
The only modification is that LINQ will not do [t0].*, instead it will enumerate each field. Since I had to guess at the fields, I left them out to make the SQL clearer.

Categories