use left join in LINQ method synthax - vb.net

I have two tables: t1 and t3 and I have following sql query:
select t1.*, t3.* from t1 left join t3 on t1.f1=t3.f1
t1 contains 30k records, t3 has only 10. The matching field is f1.
t1 has 5 fields, t3 has 2 fields.
With the above query I get the 30k records + 10 field values also like this:
t1.f1, t1.f2, t1.f3, t1.f4, t1.f5, t3.f1, t3.f2
The records are so, that there is a matching only in case of the first 10 records. So after that, t3.f1 and t3.f2 are empty values.
Now, I would like to write a query in LINQ, wich produces this result:
Dim JoinedResult = LeftTable.GroupJoin(RightTable, Function(LeftTableRow) LeftTableRow(FieldIndexInLeftTable), _
Function(RightTableRow) RightTableRow(FieldIndexInRightTable), _
Function(LeftTableRow, RightTableRow) New With {LeftTableRow, RightTableRow.DefaultIfEmpty(Function(Item) IIf(Item Is Nothing, Nothing, Item))})
As you see, I'm trying to tell LINQ, that if any item of the RightTableRow is empty, then I need the field value in this case too, with an empty value.
But vb says: Anonymous type member name can be inferred only from a simple or qualified name with no arguments.
It is important, that the fields of table t3 can be various, so defining an empty value for t3.f1 and t3.f2 is not an option. I need to define an empty value (null) for all fields in t3, if there is no matching record.
It can be, that in t3 there is more matching record to a row in t1, so I must use SelectMany too, but how?
Thanks.
EDIT:
Dim JoinedResult = LeftTable.GroupJoin(RightTable, Function(LeftTableRow) LeftTableRow(FieldIndexInLeftTable), _
Function(RightTableRow) RightTableRow(FieldIndexInRightTable), _
Function(LeftTableRow, RightTableRow) _
New With {LeftTableRow, RightTableRow}). _
SelectMany(Function(Row) _
Row.RightTableRow.DefaultIfEmpty().Select(Function(RightT) _
New With {Row.LeftTableRow, _
Key .rt = RightT.Select(Function(Item) IIf(Item Is Nothing, String.Empty, Item)).ToArray}))
with this the error message disappear, but I get a null exception at runtime, which is obv. results from the fact, that RighT is - except the first 10 records - null.

You should look at this link :
http://msdn.microsoft.com/en-us/library/vstudio/bb397895.aspx
Your code should look to follow :
var query =
from left in LeftTable
join right in RightTable on right.FieldIndexInRightTable equals left.FieldIndexInLeftTable
into gj
from subpet in gj.DefaultIfEmpty()
select new {Your fields };

Related

Adding a WHERE clause to a Group Join

I am working in VB.NET. I have two datatables (table & table2), which are identical. The two columns in question are:
id | voided_id
1 | null
2 | null
3 | 2
table1 is the list of items, and table2 is the list of voided items. so, if an item has voided an earlier item, I want to exclude the voided item. In this example id 2 would be excluded because it was voided by id 3.
Here is what I have so far:
Dim compareResults = From table In resultOds
Group Join table2 In voidOds
On table.Field(Of Int64)("id") Equals table2.Field(Of Int64?)("voided_loan_id")
Into tablesJoin = Group From tableJoin In tablesJoin.Where(Function(x) x.Field(Of Int64?)("voided_loan_id") Is Nothing).DefaultIfEmpty()
Select table
right now, I get everything. The WHERE clause inside the group join isn't working. Any suggestions?
Many articles I found said .DefaultIfEmpty() should provide the functionality of the WHERE but this returns everything as well:
Dim compareResults2 = From table In resultOds
Group Join table2 In voidOds
On table.Field(Of Int64)("id") Equals table2.Field(Of Int64?)("voided_loan_id") Into tablesJoin = Group
From table2 In tablesJoin.DefaultIfEmpty()
Select table
based on some off-line input I got, I rewrote this as a subquery. Still returns everything.
Dim compareResults2 = From r In resultOds
Where Not (From v In voidOds Where v.Field(Of Int64?)("voided_loan_id") IsNot Nothing Select v.Field(Of Int64?)("voided_loan_id")).Contains(r.Field(Of Int64?)("id"))
Select r
Try below
C#
var s =ItemTable.Where(i=>!VoidedItem.Any(v=>v.id==i.id))?.ToList();
VB
Dim s = ItemTable.Where(Function(i) Not VoidedItem.Any(Function(v) v.id = i.id))?.ToList()
I didn't get it working so I defaulted to executing a SQL string. thanks for the feedback.

Linq group join failing, if the result has empty rows

I'm quite new to using Linq queries, and I'm trying to do a group join between two data tables ( to simulate a left join in sql ), which is failing if some of the rows miss values.
I've tried the query below:
(From date_amotiq
In amotiq_data_dt.AsEnumerable
Group Join v05_va33_data In v05_join_va33_zalvunloading.AsEnumerable
On date_amotiq("MATNR").ToString Equals v05_va33_data("ALV_PN").ToString
And date_amotiq("KDMAT").ToString Equals v05_va33_data("CUST_PN").ToString
And date_amotiq("UIP_Formatted").ToString
Equals v05_va33_data("Unloading_Point").ToString Into main_sht_data = Group
From v05_va33_data in main_sht_data.DefaultIfEmpty()
Select main_sheet_dt.Clone.LoadDataRow(New Object()
{"RO1W",
date_amotiq("MATNR"), date_amotiq("KDMAT"),date_amotiq("UIP_Formatted"),
date_amotiq("RDATUM"), "",
main_sht_data.FirstOrDefault().Item(0)
},False))
.CopyToDataTable
I know that "main_sht_data" is generated as an enumerable and I've tried to get the values with FirstOrDefault and ElementOrDefault, which return a datarow, but it fails as soon as I hit an empty row.
Could you please assist?
I managed to get it working by using if(v05_va33_data is nothing,"",v05_va33_data("Sales_Doc_Type"), and so on, for all fields which could be empty.

VB.NET LINQ to XML Left Join with ambiguous column name

I need to write a LINQ TO XML query, which queries two XML files exported from Access database tables. The original Access DB query looks like this:
SELECT
(
[TableB].[Code] Is Null,[TableA].[Code],
LCase(Left([TableA].[Code],1)) & ":" & [TableB].[code]
) AS Code,
Trim
(
[TableB].[Description] & " " & [TableA].[Description]
) AS Description
FROM TableA LEFT JOIN TableB
ON TableA.Code = TableB.SubProduct;
When I convert it to LINQ to XML, I have the problem of the right part of the left join is not available. My LINQ look like this:
Dim results = _
From a In TableA.Descendants("Product")
Group Join b In TableB.Descendants("Product")
On a.Element("Code").Value Equals b.Element("SubProduct").Value Into leftJoinGroup Group
From p In leftJoinGroup.DefaultIfEmpty
Select New With
{
I DON KNOW HOW TO WRITE IT
}
Both tables have the column named "Code". However, the variable TableB seems to be unavailable inside my Selectclause. I only have a and p available so I can't get the Code from TableB (b). How should I do that?
I just started using linq myself and ran into this issue last week. This was very helpful for me http://msdn.microsoft.com/en-us/vstudio/bb688088.aspx but here is an example of how to perform a left outer join in vb.net http://msdn.microsoft.com/en-us/vstudio/bb737909#lojoin.
In your example tableB is being stored into leftJoinGroup which your selecting from using p. To get values from tableB you will need to select from p and since your trying to concatenate the columns from both tableA and tableB, I would check if tableB record is null.
Dim results = From a In TableA.Descendants("Product") Group Join b In TableB.Descendants("Product") _
On a.Element("Code").Value Equals b.Element("SubProduct").Value Into leftJoinGroup = Group _
From p In leftJoinGroup.DefaultIfEmpty() _
Select New With { _
.Code = If(p Is Nothing, a.Element("Code").Value, String.Format("{0}:{1}", Left(a.Element("Code").Value.ToLower(), 1), p.Element("Code").Value)), _
.Description = If(p Is Nothing, a.Element("Description").Value, String.Format("{0} {1}", p.Element("Description").Value, a.Element("Description").Value))}
Here is an example of your code above, I didn't tested it. I've used this code when joining datatables not xdocuments. Sorry if this isn't clear this my first post here.

Improved way for multi-table SQL (MySQL) query?

Hoping you can help. I have three tables and would like to create a conditional query to make a subset based on a row's presence in one table then excluding the row from the results, then query a final, 3rd table. I thought this would be simple enough, but I'm not well practiced in SQL and after researching/testing for 6 hours on left joins, correlated sub-queries etc, it has helped, but I still can't hit the correct result set. So here's the setup:
T1
arn_mkt_stn
A00001_177_JOHN_FM
A00001_177_BILL_FM
A00001_174_DAVE_FM
A00002_177_JOHN_FM
A00006_177_BILL_FM
A00010_177_JOHN_FM - note: the name's relationship to the 3 digit prefix (e.g. _177) and the FM part always is consistent: '_177_JOHN_FM' only the A000XX changes
T2
arn_mkt
A00001_105
A00001_177
A00001_188
A00001_246
A00002_177
A00003_177
A00004_026
A00004_135
A00004_177
A00006_177
A00010_177
Example: So if _177_JOHN_FM is a substring of arn_mkt_stn rows in T1, exclude it when getting arn_mkts with a substring of 177 from T2 - in this case, the desired result set would be:
A00003_177
A00004_177
A00006_177
Similarly, _177_BILL_FM would return:
A00002_177
A00003_177
A00004_177
A00010_177
Then I would like to use this result set to pull records from a third table based on the 'A00003' etc
T3
arn
A00001
A00002
A00003
A00004
A00005
A00006
...
I've tried a number of methods [where here $stn_code = JOHN_FM and $stn_mkt = 177]
"SELECT * FROM T2, T1 WHERE arn != SUBSTRING(T1.arn_mkt_stn, 1,6)
AND SUBSTRING(T1.arn_mkt_stn, 12,7) = '$stn_code'
AND SUBSTRING(arn_mkt, 8,3) = '$stn_mkt' (then use this result to query T3..)
Also a left join and a subquery, but I'm clearly missing something!
Any pointers gratefully received, thanks,
Rich.
[EDIT: Thanks for helping out sgeddes. I'll expand on my logic above... first, the result set desired is always in connection with one name only per query, e.g. from T1, lets use JOHN_FM. In T1, JOHN_FM is currently associated with 'arn's (within the arn_mkt_stn): A00001, A00002 & A00010'. The next step in T2 is to find all the 'arn's (within arn_mkt)' that have JOHN_FM's 3 digit prefix (177), then exclude those that are in T1. Note: A00006 remains because it is not connected to JOHN_FM in T1. The same query for BILL_FM gives slightly different results, excluding A00001 & A00006 as it has this assoc in T1.. Thanks, R]
You can use a LEFT JOIN to remove the records from T2 that match those in T1. However, I'm not sure I'm understanding your logic.
You say A00001_177_JOHN_FM should return:
A00003_177
A00004_177
A00006_177
However, wouldn't A00006_177_BILL_FM exclude A00006_177 from the above results?
This query should be close (wasn't completely sure which fields you needed returned) to what you're looking for if I'm understanding you correctly:
SELECT T2.arn_mkt, T3.arn
FROM T2
LEFT JOIN T1 ON
T1.arn_mkt_stn LIKE CONCAT(T2.arn_mkt,'%')
INNER JOIN T3 ON
T2.arn_mkt LIKE CONCAT(T3.arn,'%')
WHERE T1.arn_mkt_stn IS NULL
Sample Fiddle Demo
--EDIT--
Reviewing the comments, this should be what you're looking for:
SELECT *
FROM T2
LEFT JOIN T1 ON
T1.arn_mkt_stn LIKE CONCAT(LEFT(T2.arn_mkt,LOCATE('_',T2.arn_mkt)),'%') AND T1.arn_mkt_stn LIKE '%JOHN_FM'
INNER JOIN T3 ON
T2.arn_mkt LIKE CONCAT(T3.arn,'%')
WHERE T1.arn_mkt_stn IS NULL
And here is the updated Fiddle: http://sqlfiddle.com/#!2/3c293/13

Multiple columns in Linq left outer join

Could somebody help me complete this linq left outer join query so that the second column of the right table (tempData) is contained in the result set even though it may be null?
sortedData = From cs In mbCustomSort
Order By cs(0)
Group Join entry In tempData On cs(joinColumn) Equals entry(0) Into Group
From last In Group.DefaultIfEmpty _
Select New With {.groupField = cs(joinColumn)}
Sorry, I write in C#, but how about:
var sortedData = from cs in mbCustomSort orderby cs.JoinColumn
join entry in tempData on cs.JoinColumn equals entry.OtherJoinColumn into Group
from subentry in Group.DefaultIfEmpty()
select new { groupField = cs.JoinColumn };
LINQPad's default processing doesn't allow for item(fieldno), so I used actual field names in data repurposed from here, including adding a new row in Mbc with no corresponding OrderId in Mbtd. This works for me:
From cs In Mbcs _
Order By cs.Catalogid _
Group Join entry In mbtds On cs.OrderId Equals entry.OrderId Into Group _
From last In Group.DefaultIfEmpty _
Select cs.OrderId, last.Ocardtype
And by "works" I mean the row I added appears with a null Ocardtype (as well as another row where Ocardtype was already null).
(Edited)
The From last In Group turns the outer join into an inner join. This does not happen when you continue with the Group variable:
sortedData = From cs In mbCustomSort
Order By cs(0)
Group Join entry In tempData On cs(joinColumn) Equals entry(0) Into Group
Select New With {.groupField = cs(joinColumn),
.col2 = Group(1).RightColumn }
Change RightColumn by the property of "In-memory Query" object.