1
votes

Comment sélectionner des lignes "produit" au lieu de lignes "vente"

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)
})


0 commentaires

6 Réponses :


1
votes

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.


0 commentaires

1
votes

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>


0 commentaires

3
votes
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

5 commentaires

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



1
votes

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)
    };


2 commentaires

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.



0
votes

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();


0 commentaires

1
votes

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))
            };

Fiddle a>


0 commentaires