BLToolkit update with join - sql

Anyone know how can I write the following update code by using the BLToolkit syntax, where I need to join two tables, and update one of them. In SQL Server this is done like this:
update Table1 set
Col1 = T.Col1 - TT.Col2
from
#tempTable as TT
inner join Table1 as T on **T.ColX = TT.ColX and T.ColY = TT.ColY**
This is how I have done the updates so far.
db.SomeTable.Where( x => x.ColName == someColName )
.Update( x => new SomeTable
{
//update columns here
} );

Example from BLToolkit unit tests:
var q =
from c in db.Child
join p in db.Parent on c.ParentID equals p.ParentID
where c.ChildID == id && c.Parent.Value1 == 1
select new { c, p };
q.Update(db.Child, _ => new Child { ChildID = _.c.ChildID + 1, ParentID = _.p.ParentID });

Related

Linq to SQL - Query with multiple joins, sum, grouping, having

I have the following query that I would like to translate to linq.
SELECT
SUM(Credits.CreditAmount)
,Transactions.Id
,Person.FullName
,Person.Id
FROM
Person
JOIN
Transactions
ON Person.AccountId = Transactions.AccountId
JOIN Credits
ON Transactions.Id = Credits.TransactionId
WHERE
Person.Type = 'AccountHolder'
AND Person.Status = 'Active'
AND Transactions.CancelledDate IS NULL
AND Credits.CancelledDate IS NULL
GROUP BY Transactions.AccountId, Person.FullName, Person.Id
HAVING SUM(Credits.CreditAmount) > 20
This is what I came up with. It's an absolute pig... The SQL it generates must be awful.
var query = from p in Person
join t in Transactions
on p.AccountId equalas t.AccountId
join c in Credits
on t.TransactionId = c.TransactionId
where p.Status == "Active" &&
p.Type = "AccountHolder" &&
t.CancelledDate == null &&
c.CancelledDate == null
group new { c.CreditAmount, t.AccountId, p.FullName, p.Id } by new { t.AccountId, p.FullName, p.SSN } into grp
let sumC = grp.Select(x => x.CreditAmount).Sum()
select new
{
TotalCredit = sumC,
AccountId = grp.Key.AccountId,
FullName = grp.Key.FullName,
Id = grp.Key.Id
};
query.Where(p => p.TotalServiceCredit > 20);
The SQL query runs in approximately 3 seconds but I have yet to find the patience to let the Linq query finish. I was wondering if there is something different I should be doing to accomplish this "group, sum, having" query I'm trying to write? Is there something I can do to help Linq generate more performat SQL?
UPDATE
Turns out sgmoore had the right idea. The key to the performance issue was in his answer.
The difference between this
let sumC = grp.Select(x => x.CreditAmount).Sum()
and this
TotalCredit = grp.Sum(x => x.CreditAmount)
was the difference between a query that finishes and one that does not.
See my revised LINQ query below which completes in about the same time as the SQL (5.3 seconds for SQL vs 5.6 seconds for LINQ).
var query = from p in Person
join t in Transactions
on p.AccountId equalas t.AccountId
join c in Credits
on t.TransactionId = c.TransactionId
where p.Status == "Active" &&
p.Type = "AccountHolder" &&
t.CancelledDate == null &&
c.CancelledDate == null
group new { c.CreditAmount, t.AccountId, p.FullName, p.Id } by new { t.AccountId, p.FullName, p.SSN } into grp
select new
{
TotalCredit = grp.Sum(x => x.CreditAmount),
AccountId = grp.Key.AccountId,
FullName = grp.Key.FullName,
Id = grp.Key.Id
};
query.Where(p => p.TotalServiceCredit > 20);
Thanks for all your help!
I don't disagree with WEI_DBA's comment but if you need to do this, then you might find it easier to break this into several queries, eg
var query1 = from p in Person
join t in Transactions on p.AccountId equals t.AccountId
join c in Credits on t.TransactionId equals c.TransactionId
where p.Status == "Active" &&
p.Type = "AccountHolder" &&
t.CancelledDate == null &&
c.CancelledDate == null
select new { c.CreditAmount, t.AccountID, p.FullName, p.Id};
var query2 = (from p in query1
group p by new { p.AccountId, p.FullName, p.Id } into grp
select new
{
TotalCredit = grp.Sum(x => x.CreditAmount),
AccountId = grp.Key.AccountId,
FullName = grp.Key.FullName,
Id = grp.Key.Id
};
var query3 = (from p in query2 where p.TotalCredit > 20 select p);
Then you can let LINQ combine this into one sql command.
As always, it is a good idea to check and verify the actual TSQL generated.

Linq To entity no full join issue

I have the following SQL script that gives me the results i'm after , i'm having trouble replicating the full join and the count distinct on the activities count:
select ActivityCount,ActivityComplete, ImagePath, SubjectTitle from
(select
Count(Distinct(Act.[ActivityID])) as ActivityCount,
Sum(Case when (([OutcomeID] = 1 or [OutcomeID] = 3) and AOU.ActivityUserID=1 ) Then 1 Else 0 End) As ActivityComplete,
Sub.[SubjectImageName] as ImagePath,
Sub.SubjectTitle as SubjectTitle
from [dbo].[UserActivityOutcome] AOU
full join Activity Act on Act.[ActivityID] = AOU.[ActivityID]
left join Category Cat on Act.[CategoryID] = Cat.[CategoryID]
left join Subject Sub on Cat.[SubjectID] = Sub.[SubjectID]
group by Sub.SubjectTitle, Sub.[SubjectImageName]
) as x
results:
</head>
<body><div class="spacer"><table id="t1"><tr><td class="typeheader" colspan="4">Result Set (2 items)</td></tr><tr><th title="System.Int32">ActivityCount</th><th title="System.Int32">ActivityComplete</th><th title="System.String">ImagePath</th><th title="System.String">SubjectTitle</th></tr><tr><td class="n">25</td><td class="n">3</td><td>Subject 1.png</td><td>Subject 1</td></tr><tr><td class="n">1</td><td class="n">1</td><td>Subject 2.png</td><td>Subject 2</td></tr><tr><td title="Total=26
Average=13" class="columntotal">26</td><td title="Total=4
Average=2" class="columntotal">4</td><td title="Totals" class="columntotal"></td><td title="Totals" class="columntotal"></td></tr></table></div></body>
</html>
my linq looks like this:
from x in (
(from Act in Activities
join AOU in UserActivityOutcomes on Act.ActivityID equals AOU.ActivityID into AOU_join
from AOU in AOU_join.DefaultIfEmpty()
join ActNA in Activities on AOU.ActivityID equals ActNA.ActivityID into ActNA_join
from ActNA in ActNA_join.DefaultIfEmpty()
join Cat in Categories on AOU.Activity.CategoryID equals Cat.CategoryID into Cat_join
from Cat in Cat_join.DefaultIfEmpty()
join Sub in Subjects on Cat.SubjectID equals Sub.SubjectID into Sub_join
from Sub in Sub_join.DefaultIfEmpty()
group new {Sub, AOU, Act,ActNA} by new {
Sub.SubjectTitle,
Sub.SubjectImageName
} into g
select new {
ActivityCount = g.Distinct().Count(), //g.Count(),
ActivityComplete = g.Sum(p => (
(p.AOU.OutcomeID == 1 ||
p.AOU.OutcomeID == 3)&&
p.AOU.ActivityUserID == 23 ? 1 : 0)),
ImagePath = g.Key.SubjectImageName,
SubjectTitle = g.Key.SubjectTitle
}))
select new {
x.ActivityCount,
x.ActivityComplete,
x.ImagePath,
x.SubjectTitle
}
LINQ full outer join is tricky. The only way it could be simulated is by union of left outer join and right antijoin. Here is IMO the LINQ equivalent of your SQL query:
var query =
// AOU full join Act
(from e in (from AOU in UserActivityOutcomes
join Act in Activities on AOU.ActivityID equals Act.ActivityID into Act_join
from Act in Act_join.DefaultIfEmpty()
select new { AOU, Act })
.Concat
(from Act in Activities
join AOU in UserActivityOutcomes on Act.ActivityID equals AOU.ActivityID into AOU_join
from AOU in AOU_join.DefaultIfEmpty()
where AOU == null
select new { AOU, Act })
let AOU = e.AOU
let Act = e.Act
// left join Cat
join Cat in Categories on Act.CategoryID equals Cat.CategoryID into Cat_join
from Cat in Cat_join.DefaultIfEmpty()
// left join Sub
join Sub in Subjects on Cat.SubjectID equals Sub.SubjectID into Sub_join
from Sub in Sub_join.DefaultIfEmpty()
group new { Sub, AOU, Act } by new { Sub.SubjectTitle, Sub.SubjectImageName } into g
select new
{
ActivityCount = g.Where(e => e.Act != null).Select(e => e.Act.ActivityID).Distinct().Count(),
ActivityComplete = g.Sum(e => (e.AOU.OutcomeID == 1 || e.AOU.OutcomeID == 3) && e.AOU.ActivityUserID == 1 ? 1 : 0),
ImagePath = g.Key.SubjectImageName,
SubjectTitle = g.Key.SubjectTitle
};

Left outter join linq

How do i change the training events into a left outer join in training events im very basic at linq so excuse my ignorance its not retrieve records that don't have any trainnevent reference attached to it
var q = from need in pamsEntities.EmployeeLearningNeeds
join Employee e in pamsEntities.Employees on need.EmployeeId equals e.emp_no
join tevent in pamsEntities.TrainingEvents on need.TrainingEventId equals tevent.RecordId
where need.EmployeeId == employeeId
where need.TargetDate >= startdate
where need.TargetDate <= enddate
orderby need.TargetDat
It's best to use where in combination with DefaultIfEmpty.
See here: LEFT JOIN in LINQ to entities?
var query2 = (
from users in Repo.T_Benutzer
from mappings in Repo.T_Benutzer_Benutzergruppen.Where(mapping => mapping.BEBG_BE == users.BE_ID).DefaultIfEmpty()
from groups in Repo.T_Benutzergruppen.Where(gruppe => gruppe.ID == mappings.BEBG_BG).DefaultIfEmpty()
//where users.BE_Name.Contains(keyword)
// //|| mappings.BEBG_BE.Equals(666)
//|| mappings.BEBG_BE == 666
//|| groups.Name.Contains(keyword)
select new
{
UserId = users.BE_ID
,UserName = users.BE_User
,UserGroupId = mappings.BEBG_BG
,GroupName = groups.Name
}
);
var xy = (query2).ToList();
Which is equivalent to this select statement:
SELECT
T_Benutzer.BE_User
,T_Benutzer_Benutzergruppen.BEBG_BE
-- etc.
FROM T_Benutzer
LEFT JOIN T_Benutzer_Benutzergruppen
ON T_Benutzer_Benutzergruppen.BEBG_BE = T_Benutzer.BE_ID
LEFT JOIN T_Benutzergruppen
ON T_Benutzergruppen.ID = T_Benutzer_Benutzergruppen.BEBG_BG

Help with LINQ join

var q = (from Labels in dc.tblArtworkDataLabels select Labels).ToList();
But I need this to do the quivalent of:
SELECT d.ID, d.labelID, d.dataID, d.data, l.templateID
FROM tblArtworkDataLabels AS d INNER JOIN
tblArtworkData AS l ON d.dataID = l.ID
WHERE (l.templateID = 238)
How do I do this in LINQ?
Edit
Sorry! Missed the WHERE clause on original statmenet!
var result = dc.tblArtworkDataLabels
.Join(dc.tblArtworkData, l => l.ID, d => d.dataID, (l, d) => new {l, d})
.Select(o => new {
Id = o.d.ID,
LabelId = o.d.labelID,
DataId = o.d.dataID,
Data = o.d.data,
TemplateId = o.l.templateID,
})
.Where(o => o.l.templateID == 238);
If you have a correct foreign key on tblArtworkData to the primary key on the tblArtworkDataLabels and have imported them correctly into the DBML designer you can have LINQ2SQL implicitly creating the join:
from l in tblArtworkData
where l.templateID = 238
select new {
Id = l.tblArtworkDataLabel.ID,
LabelId = l.tblArtworkDataLabel.labelID,
DataId = l.tblArtworkDataLabel.dataID,
Data = l.tblArtworkDataLabel.data,
TemplateId = l.templateID,
}
See my answer on the question "LINQ to SQL: Multiple joins ON multiple Columns. Is this possible?" for how the implicit join translates to SQL.
Edit:
In the case I misunderstood your relations and you have many tblArtworkDataLabels to one tblArtworkData you have to turn the query the other way around
from d in tblArtworkDataLabels
where d.tblArtworkData.templateID = 238
select new {
Id = d.ID,
LabelId = d.labelID,
DataId = d.dataID,
Data = d.data,
TemplateId = d.tblArtworkData.templateID,
}
try
var q = (from Labels in dc.tblArtworkDataLabels
join data in dc.tblArtworkData on Labels.ID equals data.DataID select Labels).ToList();

Need Linq translation for the following SQL Query

select colId,
colTaskType,
MaxID
from tblTaskType
join (
select tblCheckList.colTaskTypeID,
max(colItemNumber) MaxID
from tblCheckList
group by colTaskTypeID
) x on coltaskTypeID = tblTaskType.colID
Assuming you are using linq-to-sql and have the two tables in a datacontext.
The more or less exact translation would be:
var maxChecks = from checks in DataContext.tblChecklist
group checks by checks.colTaskTypeID into g
select new { colTaskTypeID, max = g.Group.Max(x => x.colItemNumber) };
var result = from t in DataContext.tblTaskType
join c in maxChecks on t.colTaskTypeID equals c.colTaskTypeID
select new { t.colId, t.colTaskTypeID, c.max };
But you could try:
var result = from t in DataContext.tblTaskType
select new {
t.colId,
t.colTaskTypeID,
Max = (from c in DataContext.tblChecklist
where c.colTaskTypeID == t.colTaskTypeID
select c.colItemNumber).Max() };