How to join linq in c#? - sql

How to convert sql to system.linq?
Select top 100 percent s.a,s.b,s.c,s.d
From table a as s, table b as x
Where
s.a=x.a and s.b=x.b and s.c=x.c
Group by
s.a,s.b,s.c,s.d

As per my understanding of your question; seems like you want to fetch the data in c# and do joining. if so, then you may do as following:
public class tabData
{
public string a {get;set;}
public string b {get;set;}
public string c {get;set;}
public string d {get;set;}
}
List<tabData> tabA = {data of your table a}
List<tabData> tabB = {data of your table b}
var result = from r1 in tabA
join r2 in tabB on new {T1 = r1.a, T2 = r1.b, T3 = r1.c} equals new {T1 = r2.a, T2 = r2.b, T3 = r2.c}
group r1 by new
{
aa = r1.a,
bb = r1.b,
cc = r1.c,
dd = r1.d
} into g
select new
{
a = g.key.aa,
b = g.key.bb,
c = g.key.cc,
d = g.key.dd
}

I think you are asking how to join in linq as you would in sql, if so, please see below:
var query =
from abc in tbl1
join def in tbl2 on tbl1.PK equals tbl2.FK
select new { ABC = abc, DEF = def };

Related

Linq to SQL use case and Lag equivalent

I have this SQL which I am trying to convert to LINQ , how can this be converted?
Is there an equivalent of Lag at all?
I see there is a case statement not sure how to use it
SELECT
ah.AuthHist_ID,
ah.F_ID,
CASE WHEN ah.AuthPIFlg = 1 OR ah.AuthPINVFlg = 1 THEN 'True' ELSE 'False' end AS chkReqPI,
lag(CASE WHEN ah.AuthPIFlg = 1 OR ah.AuthPINVFlg = 1 THEN 'True' ELSE 'False' end, 1, null) OVER (ORDER BY ah.f_id, ah.AuthHist_ID) AS prevChkReqPI,
ah.Cr8Dt,
lag(ah.Cr8Dt, 1, null) OVER (ORDER BY ah.f_id, ah.AuthHist_ID) AS prevCr8Dt,
cu.UserName AS Cr8UserName,
lag(cu.UserName, 1, null) OVER (ORDER BY ah.f_id, ah.AuthHist_ID) AS prevCr8UserName,
fh.UpdtDt,
FROM AuthHist Ah
LEFT JOIN User cu
ON Ah.Cr8User_ID = cu.User_ID
WHERE Ah.F_ID = #fid
return (from a in DbContext.AuthHist
join c DbContext.User on a.UpdtUserId equals c.UserId
where a.FId == fId
select new AuthHistEntity()
{
FId = a.FId,
checkReqPI = a.AuthPIflg = 1 || a.AuthPINVflg = 1 :
});
I have this SQL which I am trying to convert to LINQ , how can this be
converted?
You can use linq to do this, but it will be a bit more complicated.
For the case when in linq, you can directly use the ternary operator(?:) instead.
As for the Lag() function, we need to replace this function with another sql writing as follow:
(This is just to facilitate the understanding of the linq statement below, you do not need to change your sql statement)
select kh.AuthHist_ID,kh.F_ID,
CASE WHEN kh.AuthPIFlg = 1 OR kh.AuthPINVFlg = 1 THEN 'True' ELSE 'False' end AS chkReqPI,
CASE WHEN (ch.AuthPIFlg is null or ch.AuthPINVFlg is null) then Null when (ch.AuthPIFlg = 1 OR ch.AuthPINVFlg = 1) THEN 'True' ELSE 'False' end AS prevChkReqPI ,
kh.Cr8Dt,
ch.Cr8Dt AS prevCr8Dt ,
kh.UserName AS Cr8UserName,
cu.UserName AS prevCr8UserName
from
( select -1+row_number() over (order by ah.f_id,ah.AuthHist_ID) as row1, * from
(select * from AuthHist a left join User u on a.Cr8User_ID = u.User_ID) Ah) kh
left join (select * from AuthHist a left join User u on a.Cr8User_ID = u.User_ID) cu ON kh.row1 = cu.AuthHist_ID
left join AuthHist ch on kh.row1 = ch.AuthHist_ID where kh.F_ID =#fid
Because the types of some fields are uncertain, I have create the AuthHistEntity class as follow, you can modify some details according to your needs.
public class AuthHistEntity
{
public int AuthHist_ID { get; set; }
public int F_ID { get; set; }
public string chkReqPI { get; set; }
public string prevChkReqPI { get; set; }
public string Cr8Dt { get; set; }
public string prevCr8Dt { get; set; }
public string Cr8UserName { get; set; }
public string prevCr8UserName { get; set; }
}
Here is the linq writing, you can try it:
var newUser = (from a in DbContext.AuthHist
join u in DbContext.User on a.Cr8User_ID equals u.User_ID
select new { a, u }).ToList();
var newAuthList = (from t in newUser
select new
{
row_1 = t.a.AuthHist_ID - 1,
data = t
}).ToList();
int fid = 1;
var result = (from dt in
(from ch in newAuthList
join ah in DbContext.AuthHist on ch.row_1 equals ah.AuthHist_ID
into ot from otnew in ot.DefaultIfEmpty()
select new { T1 = ch, T2 = otnew == null ? new AuthHist() : otnew
}).ToList()
join nu in newUser on dt.T1.row_1 equals nu.a.AuthHist_ID
into yG from otnew in yG.DefaultIfEmpty()
where dt.T1.data.a.F_ID == fid
select new AuthHistEntity()
{
AuthHist_ID = dt.T1.data.a.AuthHist_ID,
F_ID = dt.T1.data.a.F_ID,
chkReqPI = (dt.T1.data.a.AuthPIFlg == 1 || dt.T1.data.a.AuthPINVFlg == 1) ? "True" : "False",
prevChkReqPI = (dt.T2.AuthPIFlg == null || dt.T2.AuthPINVFlg == null) ? null : ((dt.T2.AuthPIFlg == 1 || dt.T2.AuthPINVFlg == 1) ? "True" : "Flase"),
Cr8Dt = dt.T1.data.a.Cr8Dt,
prevCr8Dt = dt.T2.Cr8Dt == null ? null : dt.T2.Cr8Dt,
Cr8UserName = dt.T1.data.u.UserName,
prevCr8UserName = otnew == null ? null : otnew.u.UserName
}).ToList();

LINQ and Lambda, query based on an array

I have this model and I want to write a where clause that query specific results based on and array, for example I want to show only the song that has an id in the array [1, 3, 7, 8]:
I wrote the expression below but I don't know how to write the where statement:
var model = from c in _db.Categories
from co in _db.Composers
from k in _db.Keys
from p in _db.Poets
from si in _db.Singers
from t in _db.Types
join s in _db.Songs on
new
{
Catid = c.id,
Comid = co.id,
Keyid = k.id,
Poetid = p.id,
Singerid = si.id,
Typeid = t.id
}
equals
new
{
Catid = s.CategoryId,
Comid = s.ComposerId,
Keyid = s.KeymId,
Poetid = s.PoetId,
Singerid = s.SingerId,
Typeid = s.TypeId
}
where
............
select new SongViewModel
{
id = s.id,
Name = s.Name,
Lyric = s.Lyric,
Chord = s.Chord,
Note = s.Note,
Audio = s.Audio,
Lycho = s.Lycho,
Likes = s.Likes,
Dislikes = s.Dislikes,
Category = c.Name,
Composer = co.Name,
Keym = k.Name,
Poet = p.Name,
Singer = si.Name,
Type = t.Name
};
Try this:
var ids = new List<int> {1,3,7,8};
...
where ids.Contains(s.Id)
select
...

Empty result set when Joining two table with Non-match Foreign key

I am joining two tables using a foreign key. TABLE_1 might have a row with a null for the foreign key. Which means that when I join the two tables based on that foreign key I won't get results for it. My problem is that when I use JOIN two tables in LINQ, I get an empty result set.
I want to be able to get the row in TABLE_1 even if the JOIN result with no match with TABLE_2.
I tried to use DefaultIfEmpty in the join of TABLE_2, but I still get an empty result set. How can I join two tables and still get a result even if TABLE_1 contains a null in the foreign key which I use to JOIN the two table?
Thanks
Hi try left join from Table2 to Table1
class Program
{
static void Main(string[] args)
{
List<Table1> Table_1 = new List<Table1>();
Table_1.Add(new Table1() { Id = 1, Name = "Lion" });
Table_1.Add(new Table1() { Id = 2, Name = "Elephant" });
List<Table2> Table_2 = new List<Table2>();
Table_2.Add(new Table2() { Id = 1, Class = "Carnivorous" });
Table_2.Add(new Table2() { Id = 2, Class = "Herbivorous" });
Table_2.Add(new Table2() { Id = 3, Class = "Mammal" });
Table_2.Add(new Table2() { Id = 4, Class = "Aquarious" });
var result = (from a in Table_2
join b in Table_1
on a.Id equals b.Id into leftJoin
from c in leftJoin.DefaultIfEmpty()
select new { Id = a.Id, Name = c == null ? string.Empty : c.Name, Class = a.Class }
).ToList();
var abc = result;
}
}
public class Table1
{
public int Id;
public string Name;
}
public class Table2
{
public int Id;
public string Class;
}
Try RIGHT JOIN if LEFT JOIN doesn't work.

Need Linq equivalent

Help in find the Linq equivalent on the below sql query:
select sum(weight) from (
select weight from pers p
join list l on l.persindex= p.persindex
group by p.persindex,weight) a
from p in context.pers
join l in context.list on l.persindex equals p.persindex
group by new
{ p.persindex,
l.weight
} into myGroup
select new()
{ Key = myGroup.Key,
GroupSum = myGroup.sum(x=>x.weight)
}
I guess that's what you need:
public int CalcWeight(IEnumerable<Person> pers, IEnumerable<Person> list)
{
return
pers
.Join(list, p=>p.PersIndex, l=>l.PersIndex, (p, l) => new {p.PersIndex, l.Weight})
.GroupBy( a => new {a.PersIndex, a.Weight})
.Sum(group=>group.Key.Weight);
}
Data class Person is decalerd like this:
public class Person
{
public int PersIndex;
public int Weight;
}
VB.NET version
Dim customerList = From p In pers
Group Join l In list On
p.persindex Equals l.persindex
Into joineddata = Group,
TotalOweight = Sum(p.weight)
Select p.persindex, TotalOweight
Try : Linquer SQL to LINQ convertion tool :
from p in pers
joins l in list on l.persindex equals p.persindex
group by new {p.persindex,l.weight } into grp
select new { sum = grp.sum(x=>x.weight)}

selecting a distinct combination of 2 columns in SQL

When i run a select after a number of joins on my table I have an output of 2 columns and I want to select a distinct combination of col1 and col2 for the rowset returned.
the query that i run will be smthing like this:
select a.Col1,b.Col2 from a inner join b on b.Col4=a.Col3
now the output will be somewhat like this
Col1 Col2
1 z
2 z
2 x
2 y
3 x
3 x
3 y
4 a
4 b
5 b
5 b
6 c
6 c
6 d
now I want the output should be something like follows
1 z
2 y
3 x
4 a
5 b
6 d
its ok if I pick the second column randomly as my query output is like a million rows and I really dnt think there will be a case where I will get Col1 and Col2 output to be same even if that is the case I can edit the value..
Can you please help me with the same.. I think basically the col3 needs to be a row number i guess and then i need to selct two cols bases on a random row number.. I dont know how do i transalte this to SQL
consider the case 1a 1b 1c 1d 1e 2a 2b 2c 2d 2e now group by will give me all these results where as i want 1a and 2d or 1a and 2b. any such combination.
OK let me explain what im expecting:
with rs as(
select a.Col1,b.Col2,rownumber() as rowNumber from a inner join b on b.Col4=a.Col3)
select rs.Col1,rs.Col2 from rs where rs.rowNumber=Round( Rand() *100)
now I am not sure how do i get the rownumber or the random working correctly!!
Thanks in advance.
If you simply don't care what col2 value is returned
select a.Col1,MAX(b.Col2) AS Col2
from a inner join b on b.Col4=a.Col3
GROUP BY a.Col1
If you do want a random value you could use the approach below.
;WITH T
AS (SELECT a.Col1,
b.Col2
ROW_NUMBER() OVER (PARTITION BY a.Col1 ORDER BY (SELECT NEWID())
) AS RN
FROM a
INNER JOIN b
ON b.Col4 = a.Col3)
SELECT Col1,
Col2
FROM T
WHERE RN = 1
Or alternatively use a CLR Aggregate function. This approach has the advantage that it eliminates the requirement to sort by partition, newid() an example implementation is below.
using System;
using System.Data.SqlTypes;
using System.IO;
using System.Security.Cryptography;
using Microsoft.SqlServer.Server;
[Serializable]
[SqlUserDefinedAggregate(Format.UserDefined, MaxByteSize = 8000)]
public struct Random : IBinarySerialize
{
private MaxSoFar _maxSoFar;
public void Init()
{
}
public void Accumulate(SqlString value)
{
int rnd = GetRandom();
if (!_maxSoFar.Initialised || (rnd > _maxSoFar.Rand))
_maxSoFar = new MaxSoFar(value, rnd) {Rand = rnd, Value = value};
}
public void Merge(Random group)
{
if (_maxSoFar.Rand > group._maxSoFar.Rand)
{
_maxSoFar = group._maxSoFar;
}
}
private static int GetRandom()
{
var buffer = new byte[4];
new RNGCryptoServiceProvider().GetBytes(buffer);
return BitConverter.ToInt32(buffer, 0);
}
public SqlString Terminate()
{
return _maxSoFar.Value;
}
#region Nested type: MaxSoFar
private struct MaxSoFar
{
private SqlString _value;
public MaxSoFar(SqlString value, int rand) : this()
{
Value = value;
Rand = rand;
Initialised = true;
}
public SqlString Value
{
get { return _value; }
set
{
_value = value;
IsNull = value.IsNull;
}
}
public int Rand { get; set; }
public bool Initialised { get; set; }
public bool IsNull { get; set; }
}
#endregion
#region IBinarySerialize Members
public void Read(BinaryReader r)
{
_maxSoFar.Rand = r.ReadInt32();
_maxSoFar.Initialised = r.ReadBoolean();
_maxSoFar.IsNull = r.ReadBoolean();
if (_maxSoFar.Initialised && !_maxSoFar.IsNull)
_maxSoFar.Value = r.ReadString();
}
public void Write(BinaryWriter w)
{
w.Write(_maxSoFar.Rand);
w.Write(_maxSoFar.Initialised);
w.Write(_maxSoFar.IsNull);
if (!_maxSoFar.IsNull)
w.Write(_maxSoFar.Value.Value);
}
#endregion
}
You need to group by a.Col1 to get distinct by only a.Col1, then since b.Col2 is not included in the group you need to find a suitable aggregate function to reduce all values in the group to just one, MIN is good enough if you just want one of the values.
select a.Col1, MIN(b.Col2) as c2
from a
inner join b on b.Col4=a.Col3
group by a.Col1
If I understand you correctly, you want to have one line for each combination in column 1 and 2. That can easily be done by using GROUP BY or DISTINCT
for instance:
SELECT col1, col2
FROM Your Join
GROUP BY col1, col2
You must use a group by clause :
select a.Col1,b.Col2
from a
inner join b on b.Col4=a.Col3
group by a.Col1