Dans ma requête LINQ ci-dessous, je souhaite sélectionner les lignes "produit" et y ajouter les données des lignes "vente", mais l'inverse se produit, il s'agit de sélectionner les lignes "vente" et d'ajouter les lignes "produit"
+-------+------------+ | Id | TotalSales | +-------+------------+ | mlk3 | 210 | | snck2 | 78 | +-------+------------+
Exemple de tableau des produits
+-----+-----------+--------+ | Id | ProductId | Amount | +-----+-----------+--------+ | 120 | mlk3 | 55 | | 121 | mlk3 | 15 | | 122 | snck2 | 12 | | 123 | mlk3 | 5 | | 124 | mlk3 | 10 | | 125 | snck2 | 2 | | 126 | mlk3 | 115 | | 127 | snck2 | 6 | | 128 | snck2 | 34 | +-----+-----------+--------+
Exemple de tableau des ventes
+-------+------------+---------+-----------------+-------+------------+ | Id | CategoryId | BrandId | Name | Price | TotalSales | +-------+------------+---------+-----------------+-------+------------+ | mlk3 | MLK | BRND1 | Creamy Milk | 5 | 10 | | snck2 | SNCK | BRND2 | Chocolate Snack | 2 | 24 | +-------+------------+---------+-----------------+-------+------------+
Résultat souhaité
var query = (from product in SampleProductTable
from sale in SampleSalesTable
where (sale.ProductId == product.Id)
select new
{
Id = product.Id,
TotalSales = product.TotalSales + ((product.Id == sale.ProductId) ? sale.Amount : 0)
})
6 Réponses :
Dans votre exemple, vous joignez deux collections de la façon dont le résultat aura autant de lignes qu'il y a d'enregistrements enfants distincts (ventes dans ce cas) et créez un nouvel objet pour chaque enregistrement (similaire à INNER JOIN). C'est pourquoi le résultat est "basé sur les ventes".
Si je comprends bien votre intention, je l'aborderai comme:
SampleProductTable.Select(p => new
{
Id = p.Id,
TotalSales = p.Sales.Sum(s => s.Amount)
}
veuillez noter que pour cette approche, vous besoin de mapper la collection «Ventes» sur un produit.
Tout d'abord, il serait préférable d'utiliser l'instruction join à la place, puis il semble que vous deviez regrouper vos tables Sales en fonction de ProductId :
var query = (from product in SampleProductTable
join sale in SampleSalesTable.GroupBy(c => c.ProductId)
on product.Id equals sale.Key
select new
{
Id = product.Id,
TotalSales = product.TotalSales + sale.Sum(c=>c.Amount)
}).ToList();
Notez également: puisque vous avez utilisé une instruction where dans votre code, vous n'avez pas besoin d'utiliser cette condition ( product.Id == sale.ProductId)? dans votre select plus. Identique à la mienne, car j'ai utilisé l'instruction de jointure avec le mot clé on , il n'est pas nécessaire d'utiliser la condition dans la zone de sélection.
Vous pouvez voir le résultat souhaité dans ce qui suit lien:
https://dotnetfiddle.net/RFTtrv
p>
select p.Id, p.TotalSales + sum(coalesce(s.Amount, 0)) TotalSales from SampleProductTable p left outer join SampleSalesTable s on p.Id = s.ProductId group by p.Id, p.TotalSales
Merci. Je vais tester cela dès que possible.
J'obtiens une erreur dans nouvelle vente de groupe par nouveau . J'essaierai de convertir votre sql en linq et verrai si cela fonctionne quand j'ai le temps. Merci
@klent, désolé, c'est une faute de frappe essayez vente de groupe par nouveau au lieu de nouvelle vente de groupe par nouveau
Je reçois toujours les lignes de vente
@klent, remplacez vente par subSale
En termes généraux LINQ, la forme de requête que vous recherchez est appelée jointure groupée :
La jointure de groupe est utile pour produire des structures de données hiérarchiques. Il associe chaque élément de la première collection à un ensemble d'éléments corrélés de la deuxième collection.
Dans votre cas, il produira une collection de Ventes corrélées pour chaque Produit . Il vous suffit ensuite d'appliquer l'agrégat ( Sum ) à l'intérieur de la projection finale ( select ):
var query = db.Products
.Select(product => new
{
Id = product.Id,
TotalSales = product.TotalSales + product.Sales.Sum(sale => sale.Amount)
});
Mais depuis dans certains des commentaires que vous avez mentionnés lors de la conversion en SQL, vous utilisez très probablement des ORM comme LinqToSQL, EF ou EF Core. Dans ce cas, les choses sont encore plus simples. Ces ORM prennent en charge ce que l'on appelle les propriétés de navigation qui représentent les relations, et lorsqu'elles sont utilisées à l'intérieur des requêtes, elles sont traduites en SQL avec toutes les jointures nécessaires, vous n'avez donc pas besoin de vous soucier de ces détails et pouvez vous concentrer sur la logique nécessaire pour produire le résultat souhaité.
Si tel est le cas, la classe Product aurait normalement quelque chose comme
public ICollection<Sale> Sales { get; set; }
et la requête en question serait simple Sélectionnez comme ceci:
var query =
from product in SampleProductTable
join sale in SampleSalesTable on product.Id equals sale.ProductId into productSales
select new
{
Id = product.Id,
TotalSales = product.TotalSales + productSales.Sum(sale => sale.Amount)
};
J'obtiens cette erreur (Impossible de créer une valeur constante de type 'type anonyme'. Seuls les types primitifs ou les types d'énumération sont pris en charge dans ce contexte.)
Cela ressemble à une erreur EF6 :) Mais ne peut provenir d'aucune des deux requêtes assez simples ci-dessus. Vous faites probablement autre chose dans le code réel qui est à l'origine de cette exception.
Dans la syntaxe de requête, la solution de Slava devrait renvoyer le résultat que vous recherchez, c'est-à-dire
var methodSyntax = (SampleProductTable
.GroupJoin(SampleSalesTable, product => product.Id, sale => sale.ProductId,
(product, sales) => new {product, sales})
.SelectMany(s => s.sales.DefaultIfEmpty(), (s, subSales) => new {s, subSales})
.GroupBy(ss => new {ss.s.product.Id, ss.s.product.TotalSales}, ss => ss.subSales)
.Select(grp => new {grp.Key.Id, TotalSales = grp.Sum(s => s.Amount) + grp.Key.TotalSales})).ToList();
Si vous avez un désir ardent d'utiliser la syntaxe de méthode pour une raison quelconque, cette requête LINQ équivalente sera fonctionnent également:
var querySyntax = (from product in SampleProductTable
join sale in SampleSalesTable on product.Id equals sale.ProductId into sales
from subSales in sales.DefaultIfEmpty()
group subSales by new { product.Id, product.TotalSales }
into grp
select new
{
grp.Key.Id,
TotalSales = grp.Sum(s => s.Amount) + grp.Key.TotalSales
}).ToList();
LEFT JOIN avec regroupement ressemble à
TotalSales = product.TotalSales + (lj?.Sum(c => c.Amount) ?? 0)
Left join peut retourner null, donc vérifiez le groupe potentiel, lj avant d'essayer de le additionner. Pour les versions c # ultérieures, la vérification nulle pourrait être abrégée en
var query =
from product in SampleProductTable
join sale in SampleSalesTable.GroupBy(c => c.ProductId)
on product.Id equals sale.Key into join1
from lj in join1.DefaultIfEmpty() // left join
select new
{
Id = product.Id,
TotalSales = product.TotalSales + (lj == null ? 0 : lj.Sum(c => c.Amount))
};