I'm using Entity Framework in ASP.NET. I have something like a table like this:
+----+--------------------------+-------+-------+------------+
| id | content | type1 | type2 | date |
+----+--------------------------+-------+-------+------------+
| 0 | Some text | TypeA | TypeB | 2013-04-01 |
| 1 | Some older text | TypeB | TypeA | 2012-03-01 |
| 2 | Some even older texttext | TypeB | TypeC | 2011-01-01 |
| 3 | A dog | TypeC | TypeB | 2013-04-01 |
| 4 | And older dog | TypeC | TypeB | 2012-03-01 |
| 5 | An even older dog | TypeA | TypeC | 2011-01-01 |
| 6 | More text | TypeA | TypeB | 2013-03-01 |
+----+--------------------------+-------+-------+------------+
I already can obtain the most recent occurrence of type 1 or type 2, but I want to query the database to obtain the most recent occurrence of the combination of two types:
+----+--------------------------+-------+-------+------------+
| id | content | type1 | type2 | date |
+----+--------------------------+-------+-------+------------+
| 0 | Some text | TypeA | TypeB | 2013-04-01 |
| 3 | A dog | TypeC | TypeB | 2013-04-01 |
| 5 | An even older dog | TypeA | TypeC | 2011-01-01 |
+----+--------------------------+-------+-------+------------+
Thanks!
EDIT: The columns type1 or type2 are basically the same, so if Type1=A and Type2=B it's the same as Type1=B and Type2=A.
This would do the trick, except for one little issue, which I'll describe after the code sample.
var grouped = datacontext.Table
.GroupBy(
item => new { item.Type1, item.Type2 },
(key, groupData) => groupData.OrderBy(x => x.TheDate).First())
.ToArray();
The problem is that this way the grouping is done on the combination of Type1 and Type2, in that order. So what you are saying in the comments, which is that Type1=A AND Type2=B is equal to Type1=B AND Type2=A is a problem.
I really have no idea if EF (or L2S) can compile this, but it is worth a try.
var grouped = datacontext.Table
.Select(x => new Data // TODO: Update to the correct typename
{
x.Id,
x.Content,
Type1 = x.Type1.CompareTo(x.Type2) > 0 ? x.Type2 : x.Type1,
Type2 = x.Type1.CompareTo(x.Type2) > 0 ? x.Type1 : x.Type2,
x.TheDate
})
.GroupBy(
item => new { item.Type1, item.Type2 },
(key, groupData) => groupData.OrderBy(x => x.TheDate).First())
.ToArray();
If the above doesn't compile, then an alternative option is to correct the Type1/Type2 values after the database-grouping, and perform the grouping again in-memory. Yes, this means it will group twice (once by the database, once in-memory), but it will work, and won't require to import the complete table into memory.
var grouped = datacontext.Table
.GroupBy(
item => new { item.Type1, item.Type2 },
(key, groupData) => groupData.OrderBy(x => x.TheDate).First())
.AsEnumerable()
.GroupBy(
item => new {
// Group by the possible-swapped-values of Type1 and Type2
Type1 = x.Type1.CompareTo(x.Type2) > 0 ? x.Type2 : x.Type1
Type2 = x.Type1.CompareTo(x.Type2) > 0 ? x.Type1 : x.Type2
},
(key, groupData) => groupData.OrderBy(x => x.TheDate).First())
.ToArray();
I think I like the last option the best, since:
it can be translated to sql (no special features like string.CompareTo)
at most it will be twice the amount of records that the database will yield, not anymore (which in my opinion is acceptable without knowing the amount of total data we're talking about)
Related
I'm pretty new to LINQ and trying to figure it out. I have the following statement:
Context.dataset1
.Join(
Context.dataset2,
r => r.ID, o => o.ID,
(r, o) => new { PartID = r.PartID, Quantity = r.Quantity1 - r.Quantity2, Date = o.Date })
.GroupBy(
column => new { column.Date },
(key, group) => new {Date = key.Date, Quantity = group.Sum(g => g.Quantity) })
.Where(x => x.Quantity > 0);
the return data set looks like this
| Date | Quantity |
| ------------- | ---------|
| 2022-01-01 | 333 |
| 2022-01-02 | 444 |
| 2022-03-03 | 444 |
what i want it to look like is
| PartID | Date | Quantity |
|--------| ------------- | ---------|
|1 | 2022-01-01 | 333 |
|1 | 2022-01-02 | 444 |
|2 | 2022-03-03 | 444 |
Basically it seems that when I do the groupby I lose access to the PartId column since i'm no specifying it inside the groupby. I'm not sure how to make it appear without grouping by it which I don't want to do.
Any help would be great. Thanks.
What if two different part ids exist for the same date? What part id would it show? If you really want the part id, then you need to include the part id in your group by. For example:
column => new { column.PartID, column.Date }
This will mean that if you have multiple part ids for the same date, you will have as many rows for that date as you have distinct part ids. Based on your comments, this seems like what you're after.
I have two DataTables:
DataTable dtCatalogFromMySql;
DataTable dtDataForExistingProducts;
dtCatalogFromMySql
Id | productname | barcode | pricesell| type
---+--------------+----------+----------+------
1 | Abz | 123 | 2.01 | RS // different product name
2 | Abd | 122 | 8.90 | RS // different price
3 | Abx | 125 | 21.00 | WS // both different
4 | Abf | 124 | 2.11 | RS
5 | Abg | 126 | 8.01 | WS
6 | Abh | 127 | 60.23 | RS
7 | Abi | 128 | 9.10 | RS
dtDataForExistingProducts
Id | productname | barcode | pricesell| type
---+--------------+----------+----------+------
1 | Abc | 123 | 2.01 | RS
2 | Abd | 122 | 3.90 | RS
3 | Abe | 125 | 23.00 | WS
4 | Abf | 124 | 2.11 | RS
5 | Abg | 126 | 8.01 | WS
6 | Abh | 127 | 60.23 | RS
7 | Abi | 128 | 9.10 | RS
I need return only rows which are different as in first table
I need select all data where Prod_No equals to baracode and Descript not equals to productname and Retail_PRC also not equals to pricesell.
I am not getting results with this code
List<DataRow> matchingRows = dtCatalogFromMySql.AsEnumerable()
.Where(a => dtDataForExistingProducts.AsEnumerable()
.Select(b => b.Field<string>("Prod_No"))
.Contains(a.Field<string>("barcode")))
.Where(a => !dtDataForExistingProducts.AsEnumerable()
.Select(b => b.Field<string>("Descript"))
.Equals(a.Field<string>("productname")))
.Where(a => !dtDataForExistingProducts.AsEnumerable()
.Select(b => b.Field<decimal>("Retail_PRC"))
.Equals(Convert.ToDecimal(a.Field<double>("pricesell"))))
.ToList();
I suppose, Contains() will also fetch the data if barcode = 123456 and Prod_No = 1234, it is right? If I am right what is right way to compare string exactly same
You may want to consider a clearer syntax such as:
var results = from t1 in dtCatalogFromMySql.AsEnumerable()
join t2 in dtDataForExistingProducts.AsEnumerable() on
(string)t1["barcode"] equals (string)t2["Prod_No"]
where (string)t1["productname"] != (string)t2["descript"] &&
Convert.ToDecimal((double)t1["pricesell"]) !=
(decimal)t2["Retail_PRC"]
select t2;
The Join is definitely the way to go. You can modify the select according to your required result set.
trighati makes a good point about using OR instead of AND. This is assuming that you want all of the data where at least one of your values changed where Prod_no and barcode are equal. This would change the query to be:
var results = from t1 in dtCatalogFromMySql.AsEnumerable()
join t2 in dtDataForExistingProducts.AsEnumerable() on
(string)t1["barcode"] equals (string)t2["Prod_No"]
where (string)t1["productname"] != (string)t2["descript"] ||
Convert.ToDecimal((double)t1["pricesell"]) !=
(decimal)t2["Retail_PRC"]
select t2;
Use Join to combine them into one result set, then filter the result set:
var combined = dtDataForExistingProducts.AsEnumerable()
.Join(dtCatalogFromMySql.AsEnumerable(),
ep => ep.Field<string>("Prod_No")
ce => ce.Field<string>("barcode"),
(ep, ce) => new {ExistingProduct = ep, CatalogEntry = ce})
.Where(m => !m.ExistingProduct.Field("Descript")
.Equals(m.CatalogEntry.Field("productname")))
.Where(m => decimal.Parse(m.ExistingProduct.Field("Retail_PRC").ToString())
!= decimal.Parse(m.CatalogEntry.Field("pricesell").ToString()))
.ToList()
;
In my application, there is a ListView that contains the Location name and the Picking Priority (Lowest is chosen first) of all the locations that products are kept in. As well as this, I am also creating temporary List's that contain only the locations an individual product is stored in. For example:-
LISTVIEW List<String>
-------- ------------
__________________________ __________________________
|Location |Picking Priority| | Location |
|---------|----------------| |--------------------------|
| A | 100 | | A |
| B | 50 | | C |
| C | 500 | | D |
| D | 150 | |__________________________|
|_________|________________|
What I want to happen is the List to be ordered based on the Picking Priority of that location in the ListView, lowest to highest.
In effect, this would mean the List above would now look like this:-
__________________________
| Location |
|--------------------------|
| A |
| D |
| C |
|__________________________|
How would this be possible to do?
You can order the items based on the listitems, it's ugly, it's slow (slower than using a class with both properties) but it can work.
I assume ListViewItem.Text contains the name of the location and ListViewItem.Subitems[1].Text is the column with the priority (in integer format):
var items = theListView.Items.Cast<ListViewItem>();
var sortedList = theStringList.OrderBy(d => items.Where(i => i.Text == d).Select(i => int.Parse(i.Subitems[1].Text)).First()).ToList();
everyone!
I've just faced a problem with timing out in my LINQ query.
I have 3 tables: Work, Projects and Subprojects.
Projects:
+--------+
| Id |<--+<--+
| Name | | |
+--------+ | |
SubProjects: | |
+--------+ | |
+->| Id | | |
| | Name | | |
| | ProjId |---+ |
| +--------+ |
| Work: |
| +------------+ |
| | Id | |
| | Type | |
| | ProjId |---+
+--| SubProjId | (nullable)
+------------+
I need to create a report based on Subprojects:
Group by subproject Id,
if subproject Id is null -> group by project Id
I've solved it by making two queries and then merging them, but when sometimes it times out.
I was doing it with
result1.AddRange(result2);
because
var temp = result1.Concat(result2);
is throwing an Exception:
Internal .NET Framework Data Provider error 1004, 0, Unresolvable Var used in Command: VarType=Computed, Id=2090.
Can somebody help me with creating it in one query?
I'm not sure what your code looks like so this might not be perfect but you could try something like this:
var result = from work in works
group work by work.SubProjId ?? work.ProjId into groupedWorks
select groupedWorks.ToList();
or
var result = works.GroupBy(work => work.SubProjId ?? work.ProjId).ToList();
try this query
var itemlist =contex.Work.where(x=>x.SubProjId !=null).Groupby(x=>x.SubProjId).Concat(Contex.Work.where(x=>x.SubProjId ==null).Groupby(x=>x.ProjId)).ToList();
I'm guessing this is what you need:
var groups = from work in ctx.Works // the work table
group work // we want to group whole work "rows"
// we are grouping by project id and subproject id
by new { ProjId = work.ProjId, SubProjId = work.SubProjId }
into g // and we are calling the grouping 'g'
select g; // select the group
// example of doing something with the groupings
foreach (var group in groups)
{
var key = group.Key; // gets a { ProjId, SubProjId } tuple
foreach (var work in group)
{
// each work is a row in the Work-table
}
}
First Table
+--------+------------+-------+
| type | variety | price |
+--------+------------+-------+
| apple | gala | 2.79 |
| apple | fuji | 0.24 |
| apple | limbertwig | 2.87 |
| orange | valencia | 3.59 |
| orange | navel | 9.36 |
| pear | bradford | 6.05 |
| pear | bartlett | 2.14 |
| cherry | bing | 2.55 |
| cherry | chelan | 6.33 |
+--------+------------+-------+
Second Table
+--------+----------+
| type | minprice |
+--------+----------+
| apple | 0.24 |
| cherry | 2.55 |
| orange | 3.59 |
| pear | 2.14 |
+--------+----------+
select type, min(price) as minprice
from fruits
group by type;
The first table is and example of the data that I have and the second table is what I want to get from the first.
I am using GenericRepository/UnitOfwork to get the data from repository.
repository.fruitRepository.Get().GroupBy(m => m.type);
But I can only get the type field but I want to get more fields.
Do I need to use a select clause before groupby? If yes, how can I select more fields?
The GroupBy method returns more data, but it's returned as an enumerable... you may be able to pull what you want out of it with a Select after the GroupBy...
repository.fruitRepository.Get()
.GroupBy(m => m.type)
.Select(m => new { type = m.Key, minPrice = m.Min(f => f.Price) });
Or if you prefer a LINQ statement:
var result = from x in repository.fruitRepository.Get()
group x by x.type into typeGroup
select new
{
type = typeGroup.Key,
minPrice = typeGroup.Min(item => item.Price)
};