This is a bit of a convoluted question, but I can't quite get the logic right in my head.
I have three tables TableA
, TableB
, and TableABBridge
. I need to retrieve all the values from TableABBridge
in which records from TableB
are only tied to a single record from TableA
.
Essentially, I have a tool that allows users to edit a specific property on values in TableA
. However, TableB
also has this same property and it needs to be kept synchronized with the property from entities in TableA
. So if I update the value in TableA
, all of the values in TableB
linked to the specific entity need to be updated, but ONLY if the entities in TableB
are linked to ONLY ONE entity from TableA
.
So for example
TableA
| ID | Prop |
|-------------------|
| 1| 1|
| 2| 3|
| 3| 3|
|-------------------|
TableB
| ID | Prop |
|-------------------|
| 1| 1|
| 2| 1|
| 3| 3|
| 4| 1|
|-------------------|
TableABBridge
| AId | BId |
|-------------------|
| 1| 1|
| 1| 2|
| 1| 4|
| 2| 3|
| 3| 3|
|-------------------|
In the above example, I would be able to edit the property in TableA
for ID
1 only as the values for TableB
for ID
1, 2, and 4 are ONLY tied to ID
1 from TableA
. However, I will not be able to edit the property for for ID
2 or 3 because they share a common entity from TableB
.
So essentially, using Linq, I need to figure out how to get a list of TableA
IDs that I am allowed to edit by querying TableABBridge
.
The SQL to accomplish this is:
SELECT MAX(AId), BId
FROM TableABBridge
GROUP BY BId
HAVING Count(AId) = 1
and the reverse (also needed)
SELECT a.Id
FROM TableABBridge a
JOIN
(SELECT BId
FROM TableABBridge
GROUP BY BId
HAVING Count(AId) > 1) b ON b.BId = a.BId
I believe one possible solution would be:
var bridges = context.TableABBridge.Select(n => new
{
n.AId,
n.BId
}).Distinct()
.GroupBy(n => n.BId, n => n.AId).Where(n => n.Count() > 1)
.Select(n=>n.Key);
var uneditable = context.TableABBridge.Where(n => bridges.Contains(n.BId)).Select(n => n.AId);
var editable = context.TableABBridge.Select(n => new
{
n.AId,
n.BId
}).Distinct()
.GroupBy(n => n.BId, n => n.AId).Where(n => n.Count() == 1)
.Select(n => n.FirstOrDefault().Value);
A LINQ-ish way to do it is this:
var diffs = from b in context.Bs
where b.ABs.Count() == 1
let a = b.ABs.FistOrDefault().A
where a.Prop != b.Prop
select new { b, ValueA = a.Prop };
foreach(var diff in diffs)
{
diff.b.Prop = diff.ValueA;
}
context.Bs where b.ABs.Count() == 1
gives you all Bs
having 1 junction record and hence one A
. Then a structure is created in which the B
and the value of A are stored side by side, so it's easy to do the update in a loop.
I said "LINQ-ish", because sometimes the natural way to express things in LINQ is different than what's more obvious in SQL ( b.ABs.Count()
vs. GROUP BY
). It will generate a subquery for the counts, maybe making it less efficient than grouping. But it should also be kept in mind that LINQ group by
isn't always translated into SQL GROUP BY
.
The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.