fyasir
fyasir

Reputation: 2970

Linq advanced queries with multiple columns

In my ASP.NET Project I have a data table named BrowserStats which contains different browser list for different users. I want to group the users by their id and shows the number of access with specific browsers(Chrome,Firefox,IE) using linq.
My Data Table(BrowserStats) is as following:

UserId      |   Browser
----------------------------
1           |   Chrome-32.0
1           |   Chrome-30.0
1           |   Chrome-33.0
1           |   Firefox-20.0
1           |   Firefox-26.0
1           |   Safari 
1           |   IE-9
1           |   IE-10
2           |   Chrome-31.0
2           |   Chrome-32.0
2           |   IE-10
2           |   Firefox-22.0
2           |   Firefox-26.0

My Output Table should be :

UserId      | Chrome |  Firefox |  IE | Others
-----------------------------------------------
1           |   3    |      2   |   2 |     1
2           |   2    |      2   |   1 |     0
  1. How would be the query in linq for this output?
  2. Is linq a faster way or I should write a stored procedure with this query in database and call it from C#?
  3. Is there any good tutorial on Advanced linq queries?

Upvotes: 2

Views: 1309

Answers (3)

pradeep varma
pradeep varma

Reputation: 136

if you want to go with LINQ. it dynamic add column and row to datatable. output is datatable format

DataTable dt = new DataTable();
        dt.Columns.AddRange(new DataColumn[] { new DataColumn("Browser", Type.GetType("System.String")), new DataColumn("userid", Type.GetType("System.String")) });
        dt.Rows.Add(new object[] {"Chrome-32.0","1" });
        dt.Rows.Add(new object[] { "Chrome-32.0", "1" });
        dt.Rows.Add(new object[] { "Firefox-20.0", "1" });
        dt.Rows.Add(new object[] { "Firefox-26.0", "1" });
        dt.Rows.Add(new object[] { "Safari", "1" });
        dt.Rows.Add(new object[] { "IE-9", "1" });
        dt.Rows.Add(new object[] { "IE-10", "1" });
        dt.Rows.Add(new object[] { "Chrome-31.0", "2" });
        dt.Rows.Add(new object[] { "Chrome-32.0", "1" });
        dt.Rows.Add(new object[] { "IE-10", "1" });
        dt.Rows.Add(new object[] { "Firefox-22.0", "2" });

        DataTable dtOutPut = new DataTable();
        dtOutPut.Columns.Add(new DataColumn("UserID", Type.GetType("System.String")));
        var tableColumnName = dt.AsEnumerable().Select(s => s.Field<string>("Browser").Trim().ToLower().Split('-')[0].Trim()).Distinct();
        foreach (var item in tableColumnName)
        {
            dtOutPut.Columns.Add(new DataColumn(item, Type.GetType("System.String")));
        }

        var usrid = dt.AsEnumerable().Select(s => s.Field<string>("userid").Trim()).Distinct();

        Parallel.ForEach(usrid, (s) => {

            DataRow rec = dtOutPut.NewRow();
            rec["UserID"] = s;
            foreach (var item in tableColumnName)
            {
                rec[item] = dt.AsEnumerable().Where(s1 => s1.Field<string>("Browser").Trim().ToLower().Contains(item) && s1.Field<string>("userid") == s).Count();

            }

            dtOutPut.Rows.Add(rec);
        });

output:

enter image description here

Upvotes: 2

Rahul Singh
Rahul Singh

Reputation: 21805

You can use Nested group in LINQ:-

var query = from browser in browesers
                        group browser by browser.UserID into UserGroup
                        from countGroup in
                            (from brow in UserGroup
                             group brow by new
                                 {
                                     Chrome = brow.Browser.Contains("Chrome"),
                                     Firefox = brow.Browser.Contains("Firefox"),
                                     IE = brow.Browser.Contains("IE")
                                 } into test
                                     select new
                                     {
                                         ChromeCount = test.Key.Chrome ? test.Count() : 0,
                                         FirefoxCount = test.Key.Firefox ? test.Count() : 0,
                                         IECount = test.Key.IE ? test.Count() : 0,
                                         OthersCount = (!test.Key.Chrome && !test.Key.Firefox && !test.Key.IE) ? test.Count() : 0
                                     }
                                 )
                        group countGroup by UserGroup.Key;

I have Used the following Type:-

public class BrowserInfo
    {
        public int UserID { get; set; }
        public string Browser { get; set; }
    }

Here is the working Fiddle.

Upvotes: 3

RadioSpace
RadioSpace

Reputation: 952

first off, I would consider LINQ slow. LINQ is pretty much a clean way to nest array iteration logic. Only use when the CPU must manipulate static data. or data is CPU generated and there is no backing storage.

that's how I think of it anyways. now for the answer:

I have built a database in SQLServer 2012 Express for Demon-Striation purposes (<- Xanth reference). I used your browsers and made imaginary users. the pivot code should be a stored procedure you call from c#. if you are using VS I can edit for a pure VS(2012) solution because I would prefer to use datasets and add a query to the TableAdapter for using a stored procedure. but this should get you 2/3rds the way there

the User Table::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::

user table

the Browser Table:::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::

browser table

the Usage Table Sample (324 row total):::::::::::::::::::::::::::::::::::::::::::::::::::

usage table

the Diagram:::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::

SQL diagram

The PIVOT (I added this as a stored procedure):::::::::::::::::::::::::::::::::::::::::::

the pivot result

VS 2012 / WPF implementation::::::::::::::::::

after adding the stored procedure, connecting VS to your database, and adding a Dataset to your project. Drag and drop the stored procedure into your dataset. you can also use the stored procedure without the typed dataset generator. See Here

vs dataset

WPF XAML::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::

<Window x:Class="WPFPIVOT.MainWindow"
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    Title="MainWindow" Height="350" Width="525">
<Grid>
    <DataGrid Name="datagrid" ItemsSource="{Binding}"/>
</Grid>
</Window>

.cs::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Data;

namespace WPFPIVOT
{
/// <summary>
/// Interaction logic for MainWindow.xaml
/// </summary>
public partial class MainWindow : Window
{
    Pivot pivoted;
    PivotTableAdapters.GetUsageTableAdapter adapter;


    public MainWindow()
    {
        InitializeComponent();

        //this code is the same for WPF and FORMs
        pivoted = new Pivot();
        adapter = new PivotTableAdapters.GetUsageTableAdapter();
        adapter.Fill(pivoted.GetUsage);
        ///////////////////////////////////////////////////////////////


        datagrid.DataContext = pivoted;
    }


}
}

WPF WINDOW:::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::

(the UserID column defaults to the right most column the select statement could be select p1.UserID,p1[1],... p.[n])

the wpf window

Upvotes: 2

Related Questions