So I am trying to write something like this:
SELECT s.CompanyID,
s.ShareDate,
s.OutstandingShares,
s.ControlBlock
FROM (
SELECT MAX(ShareDate) AS Sharedate,
CompanyID
FROM ShareInfo
WHERE (ShareDate <= #filter_date)
GROUP BY CompanyID
) AS si
INNER JOIN
tblShareInfo AS s ON s.ShareDate = si.Sharedate AND s.CompanyID = si.CompanyID
Essentially this is trying to return the most recent Share Information, we keep a running history. Now I am trying to write something similar to this in LINQ.
Here was my closest attempt:
From a _
In db_context.ShareInfos _
Where a.ShareDate <= filter_date _
Group a By a.CompanyID Into Group _
Select CompanyID, MostRecentShareDate = Group.Max(Function(a) a.ShareDate) _
Join b In db_context.ShareInfos On b.CompanyID Equals a.CompanyID _
Select b.CompanyID, b.ShareDate, b.OS, b.CB()
Unfortunately this does not compile. Obviously I'm not understanding the LINQ syntax somehow. Can anyone steer me in the right direction?
Thanks.
with your last select statement you should use
select new {
CompanyID = b.CompanyID,
ShareDate = b.ShareDate,
OS = b.OS,
CB = b.CB
};
that's a start...
Okay so looks like this needs to be done using two statements:
Dim MostRecentShareDates = _
From s2 In query_collection.DBContext.ShareInfos _
Where s2.ShareDate <= filter_date _
Group s2 By s2.CompanyID Into Group _
Select New With { _
.CompanyID = CompanyID, _
.MostRecentShareDate = Group.Max(Function(s3) s3.ShareDate) _
}
Return From s In query_collection.DBContext.ShareInfos _
Join s1 In MostRecentShareDates On s.CompanyID Equals s1.CompanyID And s.ShareDate Equals s1.MostRecentShareDate _
Select New With { _
.CompanyID = s.CompanyID, _
.ShareDate = s.ShareDate, _
.OS = s.OS, _
.CB = s.CB _
}
I tried using the 'Let' keyword to embed the first statement into the second, but that would not compile either. Now the nice thing about this is the Linq has delayed execution, so until you traverse the collection returned by the second statement, no SQL gets generated. Linq is then smart enough to combine the two code fragments into one SQL statement, essentially exactly the same statement as I wrote in my original SQL above.
Related
I have this SQL statement and it joining 2 tables...
SELECT TRIDENT.Maintenance.RCFAs.*, TRIDENT.Maintenance.Equipment.EquipmentName
FROM TRIDENT.Maintenance.RCFAs
LEFT JOIN TRIDENT.Maintenance.Equipment
ON TRIDENT.Maintenance.Equipment.EquipmentId = TRIDENT.Maintenance.RCFAs.EquipmentId
WHERE 1 = 1
Now I have to reference a third table because I have a dropdown by the name of components that we will use to lookup stuff on the table named TRIDENT.Maintenance.RCFAXrefComponents. So if "bolts" is selected from the dropdown it will look up the YEAR and RCFAID which makes a unqiue key for both RCFAXrefComponents and RCFAs and checks if there are any rows in RCFAs that had "bolts" in the component column when looking up that YEAR and RCFAId. A coworker suggested I use an IN clause to check if the YEAR and RCFAId is in that RCFAXrefComponents. He said it might be tricky, but I'm a little lost on how to do this.
Below is the RCFAs table that I want to grab the YEAR and RCFAId values together and lookup on the other to see if that bolt is also there. Then it should bring up the line item on RCFAs table to output to user
this is RCFAXrefComponents table below
Public Shared Function GetRCFAList(rcfaNumber As Integer?, description As String,
failureType As String, equipmentDescription As String, componentSelection As String) As List(Of RCFA)
Dim sql = "SELECT r.*, e.EquipmentName FROM TRIDENT.Maintenance.RCFAs AS r LEFT JOIN TRIDENT.Maintenance.Equipment AS e ON e.EquipmentId = r.EquipmentId JOIN TRIDENT. Maintenance.RCFAXrefComponents AS c ON c.Year = r.Year AND c.RCFAId = r.RCFAId WHERE"
If failureType <> "" Then
sql += " AND RCFAs.FailureType = '" & failureType.ToUpper & "'"
End If
If description <> "" Then
sql += " AND RCFAs.ShortDesc LIKE '%" & description.ToUpper & "%'"
End If
If equipmentDescription <> "" Then
sql += " AND Equipment.EquipmentName LIKE '%" & equipmentDescription.ToUpper & "%'"
End If
If componentSelection <> "" Then
sql += " c.ComponentId = '%" & componentSelection.ToUpper & "%'"
End If
Dim dbConn As New Trident.Core.DBConnection
Dim ds = dbConn.FillDataSet(sql)
Dim tmpList As New List(Of RCFA)
For Each dr As DataRow In ds.Tables(0).Rows
tmpList.Add(New RCFA(New Trident.Objects.Maintenance.RCFA.RCFA(dr)))
Next
Return tmpList
End Function
Just use a simple JOIN with the RFCAXrefComponents table, and a WHERE clause that filters the component ID.
SELECT r.*, e.EquipmentName
FROM TRIDENT.Maintenance.RCFAs AS r
LEFT JOIN TRIDENT.Maintenance.Equipment AS e
ON e.EquipmentId = r.EquipmentId
JOIN TRIDENT.Maintenance.RCFAXrefComponents AS c
ON c.Year = r.Year
AND c.RCFAId = r.RCFAId
WHERE c.ComponentID = 'BOLTS'
I may be confused but it sounds like you need to use EXISTS.
SELECT r.*,
e.EquipmentName
FROM TRIDENT.Maintenance.RCFAs r
LEFT JOIN TRIDENT.Maintenance.Equipment e ON e.EquipmentId = r.EquipmentId
WHERE EXISTS ( SELECT 1
FROM TRIDENT.Maintenance.RCFAXrefComponents c
WHERE c.Year = r.Year
AND c.RCFAId = r.RCFAId
AND c.ComponentId LIKE '%BOLTS%' )
you may not need the c.ComponentId LIKE '%BOLTS%' you might just want c.ComponentId = 'BOLTS'
I'm trying to run the following query :
Dim lst = (From t In context.MyObj1 where t1.id>6 Select New With { _
.Parent = t, _
.sash = t.child1.AsQueryable.Where(Function(t2) t2.tp=2).Sum(Function(t3) t3.quantity), _
.vlh = t.child1.AsQueryable.Where(Function(t3) t3.tp=2).Sum(Function(t3) t3.value) _
}).ToList
( in this query .quantity and .value have Decimal type.)
but I'm getting this error on runtime :
An unhandled exception of type 'System.InvalidOperationException' occurred in EntityFramework.dll
Additional information: The cast to value type 'System.Decimal' failed because the materialized value is null.
Either the result type's genericparameter or the query must use a nullable type.
It's sure that the collection child1 has items that have .tp=2.
What's wrong ?
Thank you !
Updated :
these are the tables on database :
MyObj1:
Id name
2 name1
7 name7
8 name8
Child1:
ID ParentID TP Quantity Value
1 2 2 7 9
2 7 2 20 10
3 7 2 8 11
( ParentID is the forign key for child1 related to ID field on MyObj )
Also , I try the query like this :
Dim lst = (From t In context.MyObj1 where t1.id>6 Select New With { _
.Parent = t, _
.sash = t.child1.AsQueryable.Where(Function(t2) t2.tp=2).Count(Function(t3) t3.quantity), _
.vlh = t.child1.AsQueryable.Where(Function(t3) t3.tp=2).Count(Function(t3) t3.value) _
}).ToList
and has no problem. so I think maybe the problem is the SUM function.
Update :
This is working without errors :
Dim lst = (From t In context.MyObj1 where t1.id>6 Select New With { _
.Parent = t, _
.sash = t.child1.AsQueryable.Where(Function(t2) t2.tp=2).Sum(Function(t3) Ctype(t3.quantity,System.Nullable(of Decimal)), _
.vlh = t.child1.AsQueryable.Where(Function(t3) t3.tp=2).Sum(Function(t3) Ctype(t3.value,System.Nullable(of Decimal)) _
}).ToList
But I have problems because this method doesn't return any value on the Sums for those parent's items that doesn't have any child in Child1 collection , for example For the Item on Myobj1 with id=8 there's no child1's item , but in this case I want to return a 0 as a sum.
What can I do ?
Thank you !
Try this:
Dim lst = (From t In context.MyObj1
Where t.id > 6
Where Not (t.child1 Is Nothing)
Select New With {}).ToList
Hard to tell with just the code you've posted, but it appears something before you get into the LINQ statements is already null (i.e., Nothing).
EDIT
Sorry, just couldn't hack it in VB anymore ... switching to C# - hoping this is what you're looking for (because it's EF, I don't have an actual DB, and don't have time to set up an in-memory data store, it's not tested with your actual data):
(from t in context.MyObj1s
where t.Id > 6
from c in context.Child1s
where c.ParentId == t.Id
where c.Tp == 2
group new { Quantity = c.Quantity, Value = c.Value } by t into g
select new
{
Parent = g.Key,
Sash = g.Sum(x => x.Quantity),
Vlh = g.Sum(x => x.Value),
}).ToList();
This avoids passing the child1 navigation property on MyObj1 into a context where it's trying to convert IQueryables into SQL, which child1 is not (directly).
The cast to nullable decimals is necessary because of the null values.
If you want zeros in stead of null values you have to add DefaultIfEmpty:
Dim lst = (From t In context.MyObj1 _
where t1.id>6 Select New With { _
.Parent = t, _
.sash = t.child1.Where(Function(t2) t2.tp=2) _
.Select(Function(t3) t3.quantity), _
.DefaultIfEmpty().Sum(), _
.vlh = t.child1.Where(Function(t3) t3.tp=2) _
.Select(Function(t3) t3.value) _
.DefaultIfEmpty().Sum() _
}).ToList
This return an IEnumerable with a 0 value when there are no results in the subqueries.
For some reason I am getting a syntax error on the below code. What I am trying to achieve is
A LEFT JOIN with MULTIPLE JOIN CLAUSES. The syntax error occurs at the Keyword INTO foobar. VS 2012 says unexpected Token. Any help would be great, thank you !
Dim results = From f In foo _
Join b In bar On new with {f.Type,f.ID} Equals New With {"Test",b.ID} into fooBar _
from x in foobar.DefaultEmpty() _
Where foo.id = 1
You want a Group Join:
Dim results = From f In foo _
Group Join b In bar On
New With {f.Type,f.ID} Equals New With {"Test",b.ID} _
Into fooBar = Group _
from x in foobar.DefaultEmpty() _
Where foo.id = 1
Try to cast the f.ID or b.ID
Dim results = From f In foo _
Join b In bar On new with {f.Type, CInt(f.ID)} Equals New With {"Test", CInt(b.ID)} into fooBar _
from x in foobar.DefaultEmpty() _
Where foo.id = 1
I want to declare a variable to hold the query result from LINQ to SQL like to the following:
Dim query As IQueryable(Of Student)
If isNoUserName Then
query = From st In db.Students _
Order By st.AssignedId Ascending _
Where (st.studentId = studentId _
Select st
Else
'' error here
query = From st In db.Students _
Order By st.firstName Ascending _
Join user In db.MSUsers On user.UserId Equals st.UserId _
Where st.studentId = studentId _
Select st.firstName, st.lastName, user.userName
End If
Return query
Error : conversions from 'System.Linq.IQueryable(Of )' to 'System.Linq.IQueryable(Of Student)'.
How do I declare variable "query" to hold both data from "Student" and "User" tables?
Thank you.
query is declared as being IQueryable<Student>, but it you look at the second query, you aren't actually returning students - you are returning a compiler-generated type composed of firstName, lastName and userName. Try changing the second query to end with:
Select st;
Edit:
Re needing data other than st, then you might try a trick to give query a type; I'll use C# for my illustration, as my VB is read-only:
var query = Enumerable.Repeat(new {firstName="",lastName="",userName=""}, 0);
...
if(isNoUserName) {
query = ...
select new {st.firstName, st.lastName, userName = ""};
} else {
query = ...
select new {st.firstName, st.lastName, user.userName };
}
What I really want is to select these two tables in to an anon type like in Scott Gu's blog: here However, I would settle for this created type "ActiveLots" I am joining two tables together and want to be able to reference columns from each in my result set.
I don't seem to be getting the syntax correctly.
Dim pi = From p In dc.Inventories Join i In dc.InventoryItems On p.InventoryItemID _
Equals i.InventoryItemID Where p.LotNumber <> "" _
Select New ActiveLots LotNumber = p.LotNumber, Quantity = p.Quantity, Item = i.Item, Uom = i.UnitofMeasure, Description = i.Description
Have a look at Daniel Moth's blog entry. I suspect you want:
Dim pi = From p In dc.Inventories _
Join i In dc.InventoryItems
On p.InventoryItemID Equals i.InventoryItemID _
Where p.LotNumber <> "" _
Select New With { .LotNumber = p.LotNumber, .Quantity = p.Quantity, _
.Item = i.Item, .Uom = i.UnitofMeasure, _
.Description = i.Description }
That's using an anonymous type - to use a concrete type, you'd use New ActiveLots With { ... (where ActiveLots has to have a parameterless constructor).