Get number of records n a group in LINQ - c#

I wrote a LINQ query that it' result similar to this :
var res = from a in tbl
group a by {a.StateCode,a.CityCode} into grp
//where grp.Count() == 1
select ...
StateCode CityCode ......
------------------------------------------
01 001 ......
01 002 ......
02 001 ......
03 001 ......
03 002 ......
03 003 ......
indeed I want to get just 02 001 record because it has one result row in the final result.If I add commented code It returns count of records correspond to each group not count of own group.I know I can do this with two group by but is there a better way to get row that has (for example) one row in the result?

You are grouping by StateCode and CityCode so each group has only one element. You must group by StateCode only and inlcude where grp.Count() == 1 again.
Each group contains the StateCode as its key and an enumeration of the elements. Since you already filtered for groups with only a a single element you can just return the first of each group:
var res = from a in tbl
group a by a.StateCode into grp
where grp.Count() == 1
select grp.FirstOrDefault();

How about this:
(from b in Tbls
where (from a in Tbls
where a.StateCode == b.StateCode
group a by a.StateCode into grp
where grp.Count () == 1
select grp).Count() > 0
select b).Dump();
Here's the SQL I used to test it with:
create table tbl (StateCode varchar(5), CityCode varchar(5))
insert tbl values ('01', '001')
insert tbl values ('01', '002')
insert tbl values ('02', '001')
insert tbl values ('03', '001')
insert tbl values ('03', '002')
insert tbl values ('03', '003')

Related

LINQ group by on a single column

I have a database table that looks like this:
ID USERID DATE NAME
1 1 01-01-2001 aaaa
1 2 01-02-2001 aaaa
1 3 01-03-2001 aaaa
2 5 02-02-2002 bbbb
2 6 02-02-2002 bbbb
2 7 02-02-2002 bbbb
So I want to group everything by ID, so it has multiple USERIDs for each ID. However DATE and NAME are all the same. So how do get this as a linq query?
So far I thought of doing this, and this works for the users
from table in context.table
group table.USERID by table.ID into grp
orderby grp.Key descending
select new
{
ID = grp.Key,
Users = grp.ToList()
}
But this does not work if I add other items like DATE because DATE is sometimes different. However if it would select the first date that would be just fine.
EDIT what I would like as result is:
ID Users DATE NAME
1 1,2,3 01-01-2001 aaaa
2 5,6,7 02-02-2002 bbbb
You group by multiple columns:
from table in context.table
group new{table.UserID,table.Date} by new{table.ID,table.Name} into grp
orderby grp.Key.ID descending
select new
{
ID = grp.Key.ID,
Date=grp.FirstOrDefault().Date,
Name=grp.Key.Name,
Users = grp.Select(e=>e.UserID).ToList()
}

How to retrieve all columns from table1 and matching columns from table2(Left outer join) using Linq

I have to retrieve all the columns from table1 and matching columns from table2. I have a stored procedure as :
alter Procedure [dbo].[usp_Property]
#UserId bigint =null
As
Begin
select P.PID, P.PropertyName, P.SBUArea, P.ListedOn,
P.Availability, P.Price, F.UserID, F.PID as FavProjId
from dbo.Property P left outer join dbo.Favorite F
on (F.PID=P.PID And F.UserID=#UserId)
I want to get Linq query for the same. So far I tried with something like
//User Id comes from session..
//var userId
var result=(from p in Properties
join f in Favorites
on p.PID equals f.PID into r
from r1 in r.DefaultIfEmpty()
where r1.UserID==userId
select new
{
p.PID,
p.PropertyName,
p.SBUArea, p.ListedOn,
r1.UserId
});
Can anyone please correct me. I want to use left outer join or any other alternate thing here.
If I beautify your SP's code, I get this:
DECLARE #UserId int
SET #UserId = 12435
SELECT
P.PID
,P.PropertyName
,P.SBUArea
,P.ListedOn
,P.Availability
,P.Price
,F.UserID
,F.PID AS FavProjId
FROM Property AS P
LEFT JOIN Favorite AS F
ON (F.PID=P.PID AND F.UserID = #UserId)
Now I wonder if you need that UserId in the WHERE clause of the SQL, or really in the join.
But anyway, here the LINQ-equivalent of exactly that SQL:
System.Int64 __UserId = 12435;
var query = (
from P in Repo.Property
from F in Repo.Favorite
.Where(fav=> fav.PID == P.PID && fav.UserID == __UserId)
.DefaultIfEmpty() // <== makes join left join
select new
{
PID = P.PID
,PropertyName = P.PropertyName
,SBUArea = P.SBUArea
,ListenOn = P.ListedOn
,Availabiity = P.Availability
,Price = P.Price
,UserId = F.UserID
,FavProjId = F.PID
}
);
var data = (query).ToList();
Use anonymous objects in your selection
var result = from t in table1
join x in table2
on t.id equals x.id
select new { id = t.id, col1 = t.col1, col2 = x.col2 }
If you will put the where clause after join you may get null reference exception because DefaultIfEmpty returns default value for non matching rows. You can filter the records before joining itself like this:-
var result=(from p in Properties
join f in Favorites.Where(x => x.UserID == userId)
on p.PID equals f.PID into r
from r1 in r.DefaultIfEmpty()
select new
{
p.PID,
p.PropertyName,
p.SBUArea,
p.ListedOn,
r1.UserId
});
Please note you need to access properties of Favorites using r1.
Update:
As far as I have understood you need all records from Property table and only matching rows from Favorite table. But you have a filter on your Favorite table so the ultimate data source will differ. Let me make my point clear by this example:-
Suppose you have following data in Property table:-
PID PropertyName Availability Price
1 aaa true 20
2 bbb false 10
3 ccc true 50
4 ddd false 80
5 eee true 55
6 fff false 70
and Favorite table like this:-
FID PID UserId
1 4 1001
2 2 1005
3 5 1007
And let's say you want all records for UserId 1005, then the result should contain all the property Id's from 1 till 6 even if UserId 1005 doesn't match for property Id's 4 & 2 right? So the query above is as per this understanding. Check this Fiddle with same example and output.

Join and Update DataTable

I have two Table1 and Table2 whose columns with the values are given below:
These are the two tables:
I want to update "PaidAmount" in Table1. Join based on "InvoiceNo" column in both the tables.
Table1:
InvoiceNo Vendor InvoiceValue InvoiceDate PaidAmount
--------- ------ ------------- ----------- ----------
1 AA 15000 01 Dec 2013 0
2 BB 20000 10 Dec 2013 0
3 CC 10000 18 Dec 2013 0
Table2:
VoucherNo Date InvoiceNo Amount
------- ----------- ---------- ------
001 01 Nov 2013 1 5000
002 10 Nov 2013 2 6000
003 20 Nov 2013 3 7000
001 02 Nov 2013 1 2000
My desired datatable should be like this:
DesiredTable:
InvoiceNo Vendor InvoiceValue InvoiceDate PaidAmount
--------- ------ ------------- ----------- ----------
1 AA 15000 01 Dec 2013 7000
2 BB 20000 10 Dec 2013 6000
3 CC 10000 18 Dec 2013 7000
How to achieve this result?
I have tried the below one.
Table1.AsEnumerable().Join(Table2.AsEnumerable(),
dt1_Row => dt1_Row.ItemArray[0],
dt2_Row => dt2_Row.ItemArray[2],
dt1_Row, dt2_Row) => new { dt1_Row, dt2_Row }
).ToList()
.ForEach(o => o.dt1_Row.SetField(4, o.dt2_Row.ItemArray[3]));
But it gives the result as
InvoiceNo Vendor InvoiceValue InvoiceDate PaidAmount
--------- ------ ------------- ----------- ----------
1 AA 15000 01 Dec 2013 2000
2 BB 20000 10 Dec 2013 6000
3 CC 10000 18 Dec 2013 7000
How to get my desired table?
Your join give you a list of multiple row1, row2 couple.
So you are looping throught every couple, the first time for invoiceNo1, row1.PaidAmount = 5000, then your loop continue, and the second time, row1.PaidAmount = 2000, so your result.
You want to sum the Amount values of row2, so after joining, you have to group the datas by InvoiceValue, then perform a sum:
foreach(var grp in Table1.AsEnumerable()
.Join(Table2.AsEnumerable(),
dt1_Row => dt1_Row.ItemArray[0],
dt2_Row => dt2_Row.ItemArray[2],
dt1_Row, dt2_Row) => new { dt1_Row, dt2_Row }
)
.GroupBy(o => o.dt1_Row.ItemArray[0]))
{
var row1 = grp.First().dt1_Row;
var sum = grp.Sum(t => Convert.ToDecimal(t.dt2_Row.ItemArray[3]));
row1.SetField(4, sum)
}
For more readibility, try to avoid using ForEach of List, it doesn't really improve your code.
It looks like you're iterating through your results and overwriting as opposed to adding to the pre-existing result from other rows. You would likely want to do something like this:
Table1.AsEnumerable().Join(Table2.AsEnumerable(),
dt1_Row => dt1_Row.ItemArray[0],
dt2_Row => dt2_Row.ItemArray[2],
dt1_Row, dt2_Row) => new { dt1_Row, dt2_Row }
).ToList()
.ForEach(o => o.dt1_Row.SetField(4, o.dt1_Row.ItemArray[4] + o.dt2_Row.ItemArray[3]));
Here's one approach. You could use GroupBy to create invoice-groups and ToDictionary to create an efficient InvoiceNo to PaidAmount lookup. Then you just need a loop to update the rows:
var invoiceGroups = from r1 in Table1.AsEnumerable()
join r2 in Table2.AsEnumerable()
on r1.Field<int>("InvoiceNo") equals r2.Field<int>("InvoiceNo")
group r2 by r2.Field<int>("InvoiceNo") into InvoiceGroup
select InvoiceGroup;
Dictionary<int, int> invoiceSumLookup = invoiceGroups
.ToDictionary(ig => ig.Key, ig => ig.Sum(r => r.Field<int>("Amount")));
foreach(DataRow row in Table1.Rows)
row.SetField("PaidAmount", invoiceSumLookup[row.Field<int>("InvoiceNo")]);
Note that this does not delete duplicate InvoiceNo rows from Table1 if that's possible and not desired. It just groups the second table by that column and sums all Amounts.
It modifies the original table and does not need to create a new one.

How to get records with the same specific column content from a DataTable using LINQ

I have a table with personal data which looks something like this:
Identifier Name Phone Address
.............................
1 aa 23 abc
2 bb 22 abd
2 cc 11 aaa
3 dd 44 amd
4 fa 33 agd
2 ds 14 dad
3 as 55 fgg
I want to get the records with the same identifier, using LINQ, to get something like this
Identifier Name Phone Address
.............................
2 bb 22 abd
2 cc 11 aaa
2 ds 14 dad
3 dd 44 amd
3 as 55 fgg
I could order by Identifier and copy to a new DataTable, then parse it and get the records with the same identifier, but that would be expensive i guess. Is there a shorter way ?
Thank you !
Something like the code below would filter and extract the duplicates to a new DataTable with the same schema. Code assumes Identifier is an int. Replace with the appropriate names and types, as applicable.
var extractedDuplicates = (from row in table.AsEnumerable()
group row by row.Field<int>("Identifier") into rows
where rows.Count() > 1
from row in rows
select row).CopyToDataTable();
Give it a try and see how far that gets you. If there is any chance there aren't any duplicates, you will want to split this into multiple statements, as CopyToDataTable() will throw if there are no rows to copy.
var duplicateRows = from row in table.AsEnumerable()
group row by row.Field<int>("Identifier") into rows
where rows.Count() > 1
from row in rows
select row;
DataTable extractedDuplicates;
if (duplicateRows.Any())
extractedDuplicates = duplicateRows.CopyToDataTable();
else
extractedDuplicates = table.Clone();
And, of course, if you do not need a new DataTable, omit the second portion of this code entirely and just work with duplicateRows.
select * from YourTable a where a.Identifier in
(
select aa.Identifier from YourTable aa group by aa.Identifier
having (count(aa.Identifier ) > 1)
)

SQL query to get the sum and the latest enries

I have a table called machineStatus:
ID Successfiles totaldata Backupsessiontime
> 1 3 988 1256637314
> 2 21 323 1256551419
> 3 8 23 1256642968
> 4 94 424 1256642968
> 1 42 324 1256810937
> 1 0 433 1256642968
Now here i want to group by ID where the successfiles and total data gets summed up, but only display the latest Backupsessiontime.
I can do this seperately but not together.
Any suggestions????
For doing this seperately:
to get the sum:
select ID, sum(NumOfSuccessFiles), sum(TotalData)
from MachineStat
group by ID;
to get latest:
With idT as (
select ID
from MachineStat
group by ID
)
select applyT.*
from idT p
CROSS APPLY (
select top 1 ID,BackupSessionTime from MachineStat where eID=p.ID
order by MachineID desc
) as applyT
It looks like you want to do
select ID, sum(NumOfSuccessFiles), sum(TotalData), max(Backupsessiontime)
from MachineStat
group by ID;
You can use an aggregate function that would suit your needs:
select
ID,
sum(NumOfSuccessFiles) TotalNumOfSuccessFiles,
sum(TotalData) TotalData,
max(Backupsessiontime) LastBackupsessiontime
from
MachineStat ms
group by
ID
Or you can use a sub-query in your primary query, if you need more complicated sorting logic.
select
ID,
sum(NumOfSuccessFiles) TotalNumOfSuccessFiles,
sum(TotalData) TotalData,
(select top 1 Backupsessiontime from MachineStat where ID = ms.ID order by ...) LastBackupsessiontime
from
MachineStat ms
group by
ID
Max(BackupSessionTime) ?

Categories