Home > other >  How can I group with multiple columns and count?
How can I group with multiple columns and count?

Time:10-09

Firstly, I just cannot explain my question in the title properly, but I don't think its a duplicate. I have a List of model which id like to group by multiple fields, then count them into new field, and keep all the other field in the result. I am sorry for the bad explanation, I've been researching how to do this for hours and I just cannot figure it out.. I would like to get from this :

SaleID Name Product Address Phone PrintType
112 Joe Apple New street 12 11223344 PrintType1
112 Joe Apple New street 12 11223344 PrintType2
112 Joe Apple New street 12 11223344 PrintType2
112 Joe Apple New street 12 11223344 PrintType2
112 Joe Apple New street 12 11223344 PrintType3
113 Joe Kiwi New street 12 11223344 PrintType3
114 Jane Orange New street 19 72754722 PrintType1
115 John Orange New street 11 99236527 PrintType2
115 John Orange New street 11 99236527 PrintType2

Grouped by SaleID and PrintType into Something like this :

SaleID Name Product Address Phone NoOfPrintType1 NoOfPrintType2 NoOfPrintType3
112 Joe Apple New street 12 11223344 1 3 1
113 Joe Kiwi New street 12 11223344 0 0 1
114 Jane Orange New street 19 72754722 1 0 0
115 John Orange New street 11 99236527 0 2 0

Preferably id like to use LINQ, but id be good with SQL too, id just like to avoid using a for loop if possible. Edit.: There are a set number of printtypes so it wouldn't need to be dynamic.

CodePudding user response:

In order to aggregate the data for a set number of PrintTypes, you can group by SaleId as the additional data like address, phone and product are the same for a SaleId (based on your sample data).

var aggregated = from x in GenerateSales() 
    group x by x.SaleId into g // Assume that SaleId is the key that you want to group by
    select new Aggregate() 
    { 
        SaleId = g.Key, 
        // Additional data that are the same for all rows with the same SaleId
        Name = g.First().Name, 
        Product = g.First().Product, 
        Address = g.First().Address, 
        Phone = g.First().Phone, 
        // Calculate counts of Print Types
        NoOfPrintType1 = g.Where(x => x.PrintType == "PrintType1").Count(),
        NoOfPrintType2 = g.Where(x => x.PrintType == "PrintType2").Count(),
        NoOfPrintType3 = g.Where(x => x.PrintType == "PrintType3").Count(),
    };

The Linq statement first groups by SaleId and then creates an object for each SaleId, that comprises

  • the SaleId
  • the additional data like address, phone...
  • the count for each known PrintType (calculated by filtering the items of the group and then counting the rows)

Below you can find a sample that generates test data and outputs the result.

Result

112 | Joe | Apple | New street 12 | 11223344 | 1 | 3 | 1
113 | Joe | Kiwi | New street 12 | 11223344 | 0 | 0 | 1
114 | Jane | Orange | New street 19 | 72754722 | 1 | 0 | 0
115 | John | Orange | New street 11 | 99236527 | 0 | 2 | 0

Sample code

using System;
using System.Collections.Generic;
using System.Linq;

namespace LinqTest
{
    class Sale 
    {
        public string SaleId { get; set; }
        public string Name { get; set; }
        public string Product { get; set; }
        public string Address { get; set; }
        public string Phone { get; set; }
        public string PrintType { get; set; }
    }

    class Aggregate 
    {
        public string SaleId { get; set; }
        public string Name { get; set; }
        public string Product { get; set; }
        public string Address { get; set; }
        public string Phone { get; set; }
        public int NoOfPrintType1 { get; set; }
        public int NoOfPrintType2 { get; set; }
        public int NoOfPrintType3 { get; set; }

        public override string ToString() 
        {
            return $"{SaleId} | {Name} | {Product} | {Address} | {Phone} | {NoOfPrintType1} | {NoOfPrintType2} | {NoOfPrintType3}";
        }
    }

    class Program
    {
        static void Main(string[] args)
        {
            var aggregated = from x in GenerateSales() 
                group x by x.SaleId into g // Assume that SaleId is the key that you want to group by
                select new Aggregate() 
                { 
                    SaleId = g.Key, 
                    // Additional data that are the same for all rows with the same SaleId
                    Name = g.First().Name, 
                    Product = g.First().Product, 
                    Address = g.First().Address, 
                    Phone = g.First().Phone, 
                    // Calculate counts of Print Types
                    NoOfPrintType1 = g.Where(x => x.PrintType == "PrintType1").Count(),
                    NoOfPrintType2 = g.Where(x => x.PrintType == "PrintType2").Count(),
                    NoOfPrintType3 = g.Where(x => x.PrintType == "PrintType3").Count(),
                };
            foreach(var a in aggregated)
            {
                Console.WriteLine(a.ToString());
            }
        }

        static IEnumerable<Sale> GenerateSales()
        {
            yield return new Sale() { SaleId = "112", Name = "Joe", Product = "Apple", Address = "New street 12", Phone = "11223344", PrintType = "PrintType1" };
            yield return new Sale() { SaleId = "112", Name = "Joe", Product = "Apple", Address = "New street 12", Phone = "11223344", PrintType = "PrintType2" };
            yield return new Sale() { SaleId = "112", Name = "Joe", Product = "Apple", Address = "New street 12", Phone = "11223344", PrintType = "PrintType2" };
            yield return new Sale() { SaleId = "112", Name = "Joe", Product = "Apple", Address = "New street 12", Phone = "11223344", PrintType = "PrintType2" };
            yield return new Sale() { SaleId = "112", Name = "Joe", Product = "Apple", Address = "New street 12", Phone = "11223344", PrintType = "PrintType3" };
            yield return new Sale() { SaleId = "113", Name = "Joe", Product = "Kiwi", Address = "New street 12", Phone = "11223344", PrintType = "PrintType3" };
            yield return new Sale() { SaleId = "114", Name = "Jane", Product = "Orange", Address = "New street 19", Phone = "72754722", PrintType = "PrintType1" };
            yield return new Sale() { SaleId = "115", Name = "John", Product = "Orange", Address = "New street 11", Phone = "99236527", PrintType = "PrintType2" };
            yield return new Sale() { SaleId = "115", Name = "John", Product = "Orange", Address = "New street 11", Phone = "99236527", PrintType = "PrintType2" };
        }
    }
}

CodePudding user response:

you can use this SQL query

with x as (
   SELECT  [SaleID],[Name],[Product],[Address],[Phone],[PrintType]
          ,case when [PrintType]='PrintType1' then count(*) else 0 end NoOfPrintType1
          ,case when [PrintType]='PrintType2' then count(*) else 0 end NoOfPrintType2
          ,case when [PrintType]='PrintType3' then count(*) else 0 end NoOfPrintType3
      FROM [Table_1]
      group by [SaleID] ,[Name],[Product],[Address],[Phone] ,[PrintType])
    
    select [SaleID],[Name],[Product],[Address],[Phone]
          ,sum(NoOfPrintType1)NoOfPrintType1
          ,sum(NoOfPrintType2)NoOfPrintType2
          ,sum(NoOfPrintType3)NoOfPrintType3 from x
   group by [SaleID]
          ,[Name],[Product],[Address],[Phone]
  • Related