I have two table A , B on SQL Server like bellow:
Columns of A: id1, id2, col_1, ... , col_100
Columns of B: id1, id2, ... (some columns)
I want to write the equivalent of below SQL query in the C# Linq:
select *, (select count(*) from B where A.id1 = B.id1 and A.id2 = b.id2) from A
I know that this can be done like below:
var lst = db.TableA.Select(a => new {
id1 = a.id1,
id2 = a.id2,
col_1 = a.col_1,
...
,
col_100 = a.col_100,
count = db.TableB.Where(b => b.id1 = a.id1 && b.id2 == a.id2).Count()
});
But in this format I have to mention all the columns of the table A, While I just want to add just one new column to the existing columns of the table A, Something like this: select *, count()
Can you help me?
How about this:
var result = dbContext.TableA.Select(a => new
{
A = a,
Count = dbContext.TableB.Where(b => b.id1 = a.id1 && b.id2 == a.id2)
.Count(),
});
So you select the complete row from tableA in property A, and you put the number of corresponding items in tableB in property Count
Related
Lets say I have the following EF6 Linq statement which counts number of items for 2 tables (Table1 has 10 items and Table2 has no items) :
var q = db.Table1.GroupBy(g => "Table1").Select(g => new { Name = g.Key, EntryCount = g.Count() })
.Union(db.Table2.GroupBy(g => "Table2").Select(g => new { Name = g.Key, EntryCount = g.Count() }));
var r = q.ToList();
The expected result should be something like
Name | EntryCount
---------------------
Table1 | 10
Table2 | 0
However because Table2 doesn't have any items it doesn't appear in the final result and I get the following:
Name | EntryCount
---------------------
Table1 | 10
How can I make sure Table 2 always appear in the final list even if its empty and doesn't have any records?
To give you a background on why I am doing this Linq statement , I am converting the following TSQL statement into a linq query:
CREATE FUNCTION [dbo].[ufnGetLookups] ()
RETURNS
#lookupsWithItemCounts TABLE
(
[Name] VARCHAR(100),
[EntryCount] INT
)
AS
BEGIN
INSERT INTO #lookupsWithItemCounts([Name],[EntryCount])
VALUES
('Table1', (SELECT COUNT(*) FROM Table1)),
('Table2', (SELECT COUNT(*) FROM Table2)),
('Table3', (SELECT COUNT(*) FROM Table3))
RETURN;
END
Also its very important to for this linq statement to run in one database trip, not multiple.
If you have a list of tables then table list can be used to union a row with EntryCount = 0 and then on final result set a GroupBy on Name and Sum of EntryCount will provide desired result.
//List of tables
var tableList = new string[] { "Table1", "Table2" };
var res = db.Table1
.GroupBy(t1 => "Table1")
.Select(gt1 => new { Name = gt1.Key, EntryCount = gt1.Count()})
.Union(db.Table2
.GroupBy(t2 => "Table2")
.Select(gt2 => new { Name = gt2.Key, EntryCount = gt2.Count()})
)
.Union(tableList
.GroupBy(s => s)
.Select(gs => new { Name = gs.Key, EntryCount = 0 })
)
.GroupBy(gg => gg.Name)
.Select(fg => new {Name = fg.Key, EntryCount=fg.Select(ee => ee.EntryCount).Sum()})
.ToList();
The result will have EntryCount for all tables in list.
You can do this by creating a default table with a single row in it.
var ans = Table1.GroupBy(u => "Table1")
.Select(ug => new { Name = ug.Key, EntryCount = ug.Count() })
.Union(Table2.GroupBy(l => "Table2")
.Select(lg => new { Name = lg.Key, EntryCount = lg.Count() })
.Union(OneRowTable.GroupBy(u2 => 1)
.Select(u2g => new { Name = "Table2", EntryCount = u2g.Count()-1 }) )
.OrderByDescending(cg => cg.EntryCount)
.Take(1)
);
This is evaluated in a single round trip to the database by LINQ to SQL. I can't easily test with LINQ to EF.
Note that in EF Core 3.0, the original query is translated in such a way as to return a 0 row for any empty tables.
This question already has answers here:
Create a Tuple in a Linq Select
(6 answers)
Closed 6 years ago.
I have 2 database tables.
In Table1 Calculate I have 1 row which is mapped via an Id to multiple rows in table2 CalculdateData.
Now I need to load the data from table1 Calculate with all relevant Details from Table2 CalculdateData.
How would i have the Details into a Tuple-List.?
So basically for CalculateData I have 4 columns per row which I Need to put into a Tuple. Meaning if I would have for example 4 rows i need to create 4 Tuples in a List.
IEnumerable<Storage> context = new MyEntities();
var Result = (from a in context.calculate
join b in context.CalculateData on a.Id equals b.CalcId into c
where a.SpecialID == 2023 && a.VersionId == 1
orderby a.InternalOrderNr ascending
select new Storage
{
myField1 = a.Field1;
myField2 = a.Field2;
myField3 = a.Field3;
< MISSING PART AND QUESTION >
}).ToList();
return Result;
public class Storage
{
public int myField1;
public int myField2;
public int myField3;
public List<Tuple<int, int, string, decimal>> myField4;
}
This should work:
var Result = (from a in calculate
join b in calculateData on a.Id equals b.CalcId into c
where a.SpecialID == 2023 && a.VersionId == 1
orderby a.InternalOrderNr ascending
select new Storage
{
myField1 = a.Field1,
myField2 = a.Field2,
myField3 = a.Field3,
myField4 = c.Select(d => new Tuple<int, int, string, decimal>
(d.Field1, d.Field2, d.Field3, d.Field4))
.ToList()
}).ToList();
return Result;
It also would be good thing to check that this query transforms in single sql request and you not making new sql request on each tuple list creation.
Edit: In case you will have problems with custom types in query (as #Toxantron pointed) this selection should work:
var queryResult = (from a in calculate
join b in calculateData on a.Id equals b.CalcId into c
where a.SpecialID == 2023 && a.VersionId == 1
orderby a.InternalOrderNr ascending
select new
{
a.Field1,
a.Field2,
a.Field3,
myField4 = c.Select(d => new {
d.Field1, d.Field2, d.Field3, d.Field4})
}).ToList();
result = queryResult.Select(r => new Storage
{
myField1 = r.Field1,
myField2 = r.Field2,
myField3 = r.Field3,
myField4 = r.myField4.Select(t => new Tuple<int,int,decimal,string>
(t.Field1, t.Field2, t.Field3, t.Field4))
.ToList()
})
return Result;
You could try something like this.This is not tested yet.
select new Storage
{
myField1 = a.Field1,
myField2 = a.Field2,
myField3 = a.Field3,
myField4 = c.Select(d => new Tuple<int, int, string, decimal>(d.Field1, d.Field2, d.Field3, d.Field4)).ToList()
}).ToList();
This is totally based on this This tutorial
I have a table:
DataTable store_temp = new DataTable();
store_temp.Columns.Add("patn");
store_temp.Columns.Add("rf");
store_temp.Columns.Add("name");
store_temp.Columns.Add("conv");
store_temp.Columns.Add("conv_type");
store_temp.Columns.Add("recorddate");
store_temp.Columns.Add("executiondate");
My C# code :
int i = 0;
var rowsgroups = (from row in store_temp.AsEnumerable().GroupBy(row =>
row.Field<string>("patn"))
.OrderBy((g => g.OrderByDescending(y => y.Field<string("executiondate")).ThenByDescending(y =>
y.Field<string>("rf"))))
select new
{
patn = row.ElementAt(i),
rf_num = ++i,
}).ToArray();
I want the lambda experession, which is equivalent to:
select patn, rf,
> row_number() over( partition by patn order by executiondate,rf )
as rf_num,
name, conv,conv_type, recorddate, executiondate
from store_temp2
But, lambda syntax ... var rowsgroups has just a one row..
I want to show all rows in store_temp.
What should I do to fix the query?
row_number() over(partition by patn order by executiondate, rf)
means in LINQ you need to group by patn, then order each group by executiondate, rf, then use the indexed Select overload to get row numbering inside the group, and finally flatten the result with SelectMany.
With that being said, the equivalent LINQ query could be something like this:
var result = store_temp.AsEnumerable()
.GroupBy(e => e.Field<string>("patn"), (key, elements) => elements
.OrderBy(e => e.Field<string>("executiondate"))
.ThenBy(e => e.Field<string>("rf"))
.Select((e, i) => new
{
patn = key,
rf = e.Field<string>("rf"),
rf_num = i + 1,
name = e.Field<string>("name"),
conv = e.Field<string>("conv"),
conv_type = e.Field<string>("conv_type"),
recorddate = e.Field<string>("recorddate"),
executiondate = e.Field<string>("executiondate")
}))
.SelectMany(elements => elements)
.ToArray();
Try something like this
select new
{
rowNum = store_temp.Rows.IndexOf(row),
patn = row.ElementAt(i),
rf_num = ++i,
}).ToArray();
I don't think you required any groupby as per your required sql
var i=0;
var rowsgroups = (from row in store_temp.AsEnumerable()
orderby row.Field<string>("executiondate") descending,
row.Field<string>("rf") descending
select new
{
patn = row.Field<string>("patn"),
rf_num = ++i,
name = row.Field<string>("name"),
conv = row.Field<string>("conv"),
conv_type = row.Field<string>("conv_type"),
recorddate = row.Field<string>("recorddate"),
executiondate = row.Field<string>("executiondate")
}).ToArray();
Suppose there is a table with more than 20 columns: (col1, col2, ... )
If I want to display the sum of col1 and col2 as well as the other columns,
In SQL I could:
SELECT (col1+col2), * FROM table1;
But in LINQ, I have to
from tb in table1
select new
{
sum = tb.col1 + tb.col2,
col1 = tb.col1,
col2 = tb.col2,
...
};
Is there another simpler ways? Thanks.
You can extend type of tb entity by sum property and write something like:
table1
.Select(tb => new
{
Tb = tb,
Sum = tb.col1 + tb.col2
})
.Select(x =>
{
x.Tb.sum = x.Sum;
return x.Tb;
});
I would like to convert a SQL statement to LINQ but i have some problems doing it.
The sql statement is :
SELECT
C.[Call_Date], CC.[Company], R.Resolution,
COUNT_BIG(*) AS CNT,
SUM([Duration]) AS Total
FROM
[Calls] C
Join
[CompanyCharges] CC on [Company_Charge] = [CompanyCharge]
Join
[Resolutions] R on C.Call_Resolution = R.Resolution
where
Call_Date >= '05/29/2013'
Group By
C.[Call_Date], CC.[Company], CC.[CompanyCharge], R.Resolution, R.Resolution_Order
I wrote something like :
var stats = from c in dbContext.Calls
join cc in dbContext.CompanyCharges on c.Company_Charge equals cc.CompanyCharge
join r in dbContext.Resolutions on c.Call_Resolution equals r.Resolution
where (c.Call_Date > "05/29/2013")
group new { c, cc, r } by new { c.Call_Date, cc.CompanyCharge, r.Resolution, r.Resolution_Order } into statsGroup
select new { Count = statsGroup.Count(), ??? };
I managed to count the elements, but i need a sum[duration] and some columns from different tables.
Please share your wisdome with me.
Assuming that the [Duration] field is part of the [Calls] table:
var stats = /* ... */
select new
{
Count = statsGroup.Count(),
Total = statsGroup.Sum(stat => stat.c.Duration),
};
You'd probably want to include your grouping fields in the new anonymous type as well:
var stats = /* ... */
select new
{
Call_Date = statsGroup.Key.c.Call_Date,
CompanyCharge = statsGroup.Key.cc.CompanyCharge,
Resolution = statsGroup.Key.r.Resolution,
Count = statsGroup.Count(),
Total = statsGroup.Sum(stat => stat.c.Duration),
};