I am trying to figure out the proper query in linq to sql, but I just cant figure out how to do so. Lets say I have a table with the following (this table basically is a one to many relationship)
Id (PK) | SupervisorId | EmployeeId
1 1 5
2 1 6
3 1 7
4 2 5
5 2 6
6 3 7
7 4 7
8 4 8
I want my linq to sql query to find the supervisorId which has for employeeId 5 and 6. The query would return 2 only. I could use 2 where clause, but lets say I would want to input 3 employeeIds, my query would have to modified. If the passed permutation doesnt exist for one matched SupervisorId (ex: 5,6,8 in this case), the result would be null or empty.
The function would look like this:
int FindSuperVisorId(List<int> employeeIds);
I really dont know where to start in linq to sql for this type of scenario.
Thanks
So I'm pretty sure that this query should be converted property to LINQ to SQL, but I'm not completely sure.
So first we group by supervisor so that we have sequences of employees for that supervisor. Then we use Except with the employees you're interested in in both directions. If the the count of both of those Except calls is zero then the sets are exactly equal. There are more efficient ways of determining if two sets are equal in linq-to-objects, but I doubt they would be properly converted to SQL code.
var supervisorId = table.GroupBy(item => item.SupervisorId)
.Select(group => new
{
additionalItems = group.Select(item => item.EmployeeId).Except(employees),
missingItems = employees.Except(group.Select(item => item.EmployeeId)),
group = group
})
.Where(queries => queries.additionalItems.Count() == 0
&& queries.missingItems.Count() == 0)
.Select(queries => queries.group.Key)//gets the supervisorID
.FirstOrDefault();
I had to model your table as a many-many relationship as follows:
CREATE TABLE [dbo].[Employee](
[Name] [nvarchar](50) NOT NULL,
[Id] [int] IDENTITY(1,1) NOT NULL,
CONSTRAINT [PK_Employee] PRIMARY KEY CLUSTERED
(
[Id] ASC
)
CREATE TABLE [dbo].[SupervisorEmployees](
[SupervisorId] [int] NOT NULL,
[EmployeeId] [int] NOT NULL,
CONSTRAINT [PK_SupervisorEmployees] PRIMARY KEY CLUSTERED
(
[SupervisorId] ASC,
[EmployeeId] ASC
)
GO
ALTER TABLE [dbo].[SupervisorEmployees] WITH CHECK ADD CONSTRAINT [FK_SupervisorEmployees_Employee] FOREIGN KEY([SupervisorId])
REFERENCES [dbo].[Employee] ([Id])
GO
ALTER TABLE [dbo].[SupervisorEmployees] CHECK CONSTRAINT [FK_SupervisorEmployees_Employee]
GO
ALTER TABLE [dbo].[SupervisorEmployees] WITH CHECK ADD CONSTRAINT [FK_SupervisorEmployees_Employee1] FOREIGN KEY([EmployeeId])
REFERENCES [dbo].[Employee] ([Id])
GO
ALTER TABLE [dbo].[SupervisorEmployees] CHECK CONSTRAINT [FK_SupervisorEmployees_Employee1]
GO
Then using Entity Framework database first (not Linq to SQL unfortunately) the following LINQPad code works fine:
void Main()
{
FindSupervisorIds( new List<int>{5,6} ).Dump();
}
IEnumerable<int> FindSupervisorIds(List<int> employeeIds)
{
// two Excepts to do 'sequence equals'
var supervisors = Employees.Where (e =>
!e.Employees.Select (em => em.Id).Except(employeeIds).Any()
&& !employeeIds.Except(e.Employees.Select (em => em.Id)).Any()
);
return supervisors.Select (s => s.Id).Distinct();
}
int? FindSupervisorId(List<int> employeeIds)
{
var supervisors = FindSupervisorIds(employeeIds).ToList();
if(supervisors.Count == 1)
{
return supervisors.First ();
}
return null;
}
FindSupervisorIds generates a single SQL query. If you need to check there's only one matching supervisor it's probably best to call ToList() on the returned list of supervisors as in FindSupervisorId.
Trying to do the same thing with LINQ to SQL fails due to the calls to Except with the exception
'NotSupportedException: Local sequence cannot be used in LINQ to SQL implementations of query operators except the Contains operator.'
one possibility:
public int FindSuperVisorId(IEnumerable<Employee> employes)
{
var distinctSupervisors = employes.Select(e => e.SuperVisor).Distinct();
var superVisor = distinctSupervisors.Where(supervisor => employes.All(employee => employee.SuperVisor.Equals(supervisor))).FirstOrDefault();
return superVisor;
}
and in case you want all matches of same supervisors:
public IEnumerable<int> FindSuperVisorId(IEnumerable<Employee> employes)
{
var distinctSupervisors = employes.Select(e => e.SuperVisor).Distinct();
var equalSupervisors = distinctSupervisors .Where(supervisor => employes.All(employee => employee.SuperVisor.Equals(supervisor)));
return equalSupervisors;
}
or directly:
public IEnumerable<int> FindSuperVisorId(IEnumerable<Employee> employes)
{
return employes.Select(e => e.SuperVisor).Distinct()
.Where(supervisor => employes.All(employee => employee.SuperVisor.Equals(supervisor)));
}
Related
I have the following database structure in SQL Server
create table dbo.tebwf_versao
(
cd_workflow int NOT NULL,
cd_versao int NOT NULL,
nm_workflow varchar(200) NOT NULL,
constraint pkebwf_versao primary key (cd_workflow, cd_versao)
);
create table dbo.tebwf_versao_det
(
cd_workflow int NOT NULL,
cd_versao int NOT NULL,
cd_detalhe int not null,
dc_referencia varchar(200) NOT NULL,
constraint pkebwf_versao_det primary key (cd_workflow, cd_versao, cd_detalhe),
constraint fkebwf_versao_det__versao foreign key (cd_workflow, cd_versao)
references dbo.tebwf_versao (cd_workflow, cd_versao)
);
create table dbo.tebwf_versao_det_passo
(
cd_workflow int NOT NULL,
cd_versao int NOT NULL,
cd_detalhe int not null,
cd_passo smallint not null,
nm_passo varchar(200) NOT NULL,
constraint pkebwf_versao_det primary key (cd_workflow, cd_versao, cd_detalhe, cd_passo),
constraint fkebwf_versao_det_passo__versao_det foreign key (cd_workflow, cd_versao, cd_detalhe)
references dbo.tebwf_versao_det (cd_workflow, cd_versao, cd_detalhe)
);
The query I'm trying to replicate the following SQL query, and already bringing all the objects in a single Linq query with Include:
select *
from dbo.tebwf_versao vs
join dbo.tebwf_versao_det vsd on vs.cd_workflow = vsd.cd_workflow
and vs.cd_versao = vsd.cd_versao
join dbo.tebwf_versao_det_passo vsdp on vsd.cd_workflow = vsdp.cd_workflow
and vsd.cd_versao = vsdp.cd_versao
and vsd.cd_detalhe = vsdp.cd_detalhe
where vs.cd_workflow = 3
and vs.cd_versao = 1
and vsd.cd_detalhe = 1
and vsdp.cd_passo = 1;
Going through several posts, the recommendation is to use the Any command, and I've build the following Query:
var workflows = EBwfVersaos
.Include(wfv => wfv.EBwfVersaoDets
.Select(wfvd => wfvd.EBwfVersaoDetPassoes_CdDetalhe))
.Where(wfv => wfv.CdWorkflow == 3 && wfv.CdVersao == 1
&& wfv.EBwfVersaoDets.Any(wfvd => wfvd.CdDetalhe == 1 &&
wfvd.EBwfVersaoDetPassoes_CdDetalhe.Any (wfvdp => wfvdp.CdPasso == 1))).ToList();
However, this query does not render the same result set, since the brings a row from EBwfVersaos (tebwf_versao) if I have at least one row from EBwfVersaoDets (tebwf_versao_det) with a value of 1, but if I have 4 rows in that table with cd_workflow = 3 and cd_versao = 1, but with cd_detalhe equals to 1, 2, 3 and 4, all of them are returned by the Linq statement. I would like to have returned only the 1 row with the value of cd_detalhe = 1. The same applies to the second subquery. I also tried Linq Expression:
var workflows =
(from wf in EBwfWorkflows.Where(wf => wf.CdProduto == 1 && wf.CdEvento == 1)
join wfv in EBwfVersaos.Where(wfv => wfv.CdVersao == 1)
on wf.CdWorkflow equals wfv.CdWorkflow
join wfvd in EBwfVersaoDets.Where(wfvd => wfvd.CdDetalhe == 1)
on new { wfv.CdWorkflow, wfv.CdVersao} equals new {wfvd.CdWorkflow, wfvd.CdVersao}
select new {wf = wf, wfv = wfv, wfvd = wfvd}).ToList();
It works, but the results are not related, and I can't easily navigate between them. Besides the 3 tables below, I have several other related tables that I need to access, and it's being a real pain to get all the information I need into a single Linq query, while being able to filter, otherwise I'll get too much queried. Is there a way to have the include AND the where on those multiple levels?
I think this option isn't available on EntityFramework. However, you can try to use Entity Framework Plus: http://entityframework-plus.net/
To be more specific, you'll need run the following command on Nuget Package Manager:
Install-Package Z.EntityFramework.Plus.QueryIncludeFilter.EF6 -Version 1.6.0
Then, in your code:
using Z.EntityFramework.Plus;
...
var workflows = _Context.EBwfWorkflows
.Where(w => w.CdProduto == 1 && w.CdEvento == 1)
.IncludeFilter(w => w.EBwfVersaos.Where(v => v.CdVersao == 1))
.Where(w => w.EBwfVersaos.Any())
It will return only the "workflows" that have at least one EBwfVersao with CdVersao = 1 and also the workflows will have only the EBwfVersaos (workflow.EBwfVersaos) which have the CdVersao = 1.
Hope this helps.
Fairly new to LINQ and am trying to figure out how to write a particular query. I have a database where each CHAIN consists of one or more ORDERS and each ORDER consists of one or more PARTIALS. The database looks like this:
CREATE TABLE Chain
(
ID int NOT NULL PRIMARY KEY CLUSTERED IDENTITY(1,1),
Ticker nvarchar(6) NOT NULL,
Company nvarchar(128) NOT NULL
)
GO
CREATE TABLE [Order]
(
ID int NOT NULL PRIMARY KEY CLUSTERED IDENTITY(1,1),
Chart varbinary(max) NULL,
-- Relationships
Chain int NOT NULL
)
GO
ALTER TABLE dbo.[Order] ADD CONSTRAINT FK_Order_Chain
FOREIGN KEY (Chain) REFERENCES dbo.Chain ON DELETE CASCADE
GO
CREATE TABLE Partial
(
ID int NOT NULL PRIMARY KEY CLUSTERED IDENTITY(1,1),
Date date NOT NULL,
Quantity int NOT NULL,
Price money NOT NULL,
Commission money NOT NULL,
-- Relationships
[Order] int NOT NULL
)
GO
ALTER TABLE dbo.Partial ADD CONSTRAINT FK_Partial_Order
FOREIGN KEY ([Order]) REFERENCES dbo.[Order] ON DELETE CASCADE
I want to retrieve the chains, ordered by the earliest date among all the partials of all the orders for each particular chain. In T-SQL I would write the query as this:
SELECT p.DATE, c.*
FROM CHAIN c
CROSS APPLY
(
SELECT DATE = MIN(p.Date)
FROM PARTIAL p
JOIN [ORDER] o
ON p.[ORDER] = o.ID
WHERE o.CHAIN = c.ID
) AS p
ORDER BY p.DATE ASC
I have an Entity Framework context that contains a DbSet<Chain>, a DbSet<Order>, and a DbSet<Partial>. How do I finish this statement to get the result I want?:
IEnumerable<Chain> chains = db.Chains
.Include(c => c.Orders.Select(o => o.Partials))
.[WHAT NOW?]
Thank you!
.[WHAT NOW?]
.OrderBy(c => c.Orders.SelectMany(o => o.Partials).Min(p => p.Date))
Here c.Orders does join Chain to Order, while o.SelectMany(o => o.Partials) does join Order to Partial. Once you have access to Partial records, you can use any aggregate function, like Min(p => p.Date) in your case.
I have a database (SQLite) constructed with similar DDL:
CREATE TABLE [Player] (
[PlayerID] INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL,
[Name] TEXT UNIQUE NULL
);
CREATE TABLE [Position] (
[PlayerID] INTEGER NOT NULL,
[SingleHandID] INTEGER NOT NULL,
[Position] INTEGER NULL,
PRIMARY KEY ([PlayerID],[SingleHandID])
);
CREATE TABLE [SingleHand] (
[SingleHandID] INTEGER NOT NULL PRIMARY KEY AUTOINCREMENT,
[Stake] FLOAT NULL,
[Date] DATE NULL,
DataSetID INTEGER NULL
[IsPreflopAllIn] BOOLEAN NULL
);
CREATE UNIQUE INDEX [NameIndex] ON [Player](
[Name] ASC
CREATE INDEX [DataSetIndex] ON [SingleHand](
[DataSetID] ASC
);
It is mapped to Entity Framework model. I am working on large data sets up to 10 million records each.
My problem is, that I need to find all Hands where a specific Player is sting on any given Postion (plus some other filters, like date range).
While I can scan the database very quickly, to find data from a single table, for example:
//[playerIDs and selectedPos are cashed in memory]
context.Positions.Where(p => playerIDs.Contains(p.PlayerID) && selectedPos.Contains(p.Position)).Select(p => p.SingleHandID).Take(maxHands ?? 1);
When I need to do any join between tables, it starts to run very slowly, for example:
//accesing both Position and SingleHand table
context.Positions.Where(p => playerIDs.Contains(p.PlayerID) && selectedPos.Contains(p.Position) && p.SingleHand.DataSetID == dataSetNumber).Select(p => p.SingleHandID).Take(maxHands ?? 1);
What clever trick, comining queries, and code (ex, with local caching) can I pull, to make this run most efficent? I am using System.Data.SQLite provider.
Maybe I should add redundant DataSetID to Position table, then I can do my main query only on the Position table? Later, when I will have the IDs of all the matching hands, it should be quicker to add additional conditions (like date checking)
Create a new index:
CREATE INDEX [DataSetIndex2] ON [SingleHand](
[SingleHandID] ASC,
[DataSetID] ASC
);
That should help a lot.
You can also try something like this:
context.Positions
.Where(p => playerIDs.Contains(p.PlayerID) && SelectedPos.Contains(p.Position))
.Select(p => p.SingleHandID)
.Intersect(context.SingleHand
.Where(s=>s.DataSetId==dataSetNumber)
.Select(s=>s.SingleHandID))
.Take(maxHands ?? 1);
I need a ColumnName of a table in EF which I have it's ID.
My Tables are something like this :
Retailers
[RetailerId] [int] IDENTITY(1,1) NOT NULL,
[RetailerName] [varchar](50) NOT NULL,
[StateName1] [bit] NOT NULL,
[StateName2] [bit] NOT NULL,
[StateName3] [bit] NOT NULL,
States
[SId] [int] IDENTITY(1,1) NOT NULL,
[StateName] [varchar](50) Not Null
I receive an SId from a function and need to select all retailers which are located in that State.
Something like:
var listOfRetailers = (from m in db.Retailers where m.[a column that it's id is equal to SId] == true select m ).toList();
From your comments I think this is what you are looking for. But design wise what you have done is bad. I will explain shortly.
var listOfRetailers;
if(SId == 1)
{
listOfRetailers = db.Retailers.select(r=> r.StateName1.Equals(true)).ToList();
}
else if(SId == 2)
{
listOfRetailers = db.Retailers.select(r=> r.StateName2.Equals(true)).ToList();
}
else if(SId == 3)
{
listOfRetailers = db.Retailers.select(r=> r.StateName3.Equals(true)).ToList();
}
EDIT
Technically this is a bad design. Coz you are making the assumption that your States table will have 3 records
1 - ACT
2 - NSW
3 - NT
For every state you are have a corresponding column in Retailers table StateName1, StateName2, StateName3
Lets say you want to introduce a 4th state (say 4 - VIC). Now you will need to introduce a new column called StateName4 in your Retailers table. (by doing this there will be code level and DB level changes)
Also with the new columns you are introducing additional overheard of unused/ redundant data.
since this is a many-to-many situation (one state can have many retailers and one retailer can be in many states), the best approach would be to create
3 tables (Retailer, State, RetailerState), where by RetailerState table will play the role of mapping entries from State, Retailer tables
Retailer
---------
[RetailerId]
[RetailerName]
State
------
[SId]
[StateName]
RetailerState
--------------
[RetailerId]
[SId]
I am having trouble getting one of my LINQ to SQL queries to work correctly. I have three tables setup as follows:
Vendors
id - primary key
Manufacturers
id - primary key
ManufacturerVendorRelationships
id - primary key
manufacturer_id - foreign key to manufacturer.id
vendor_id - foreign key to vendor.id
I am trying to write a LINQ to SQL query that will fetch the Vendors that are not currently related to a given manufacturer. For example, the following table data should only result in 2 vendors for the manufacturer with ID of 1. Can someone please help me with the LINQ to SQL for this example? Right now, the application is doing this logic. Thanks in advance.
Vendors
ID
1
2
3
4
5
Manufacturers
ID
1
2
3
4
5
ManufacturerVendorRelationships
ID ManufacturerID VendorID
1 1 1
2 1 2
3 1 3
Maybe something like this:
var result=(
from v in db.Vendors
where !db.ManufacturerVendorRelationships
.Select(s=>s.VendorID)
.Contains(v.ID)
select v
);
Or if you want it like a field:
var result=(
from v in db.Vendors
select new
{
v.ID,
HasManufacturer=db.ManufacturerVendorRelationships
.Select(s=>s.VendorID)
.Contains(v.ID)
}
);
where db is the linq data context
int id= 1;
var result =
dc.Vendors
.Where(v => !dc.ManufacturerVendorRelationships
.Any(rel => rel.VendorId == v.Id && rel.ManufacturerId == id));