Converting Stored Procedure PIVOT table to LINQ query - c#

My idea was to convert the currently stored procedure what I've defined before. The intention is, that I can't return the data from the database with the stored procedure. The reasons for this purpose was leaving in the query. I need to convert an existing table to a pivot table and after that I have to return the data via ASP.NET WebAPI. This pivot table is dynamically that means when the user adds a new article then it will be added in the pivot table as a column.
The normal table looks like as follows:
datum | rate | article
--------------------------------
2013-01-03 | 97,766..| DE011
2013-01-05 | 90.214..| DE090
2013-01-10 | 97,890..| DE011
2013-01-13 | 65,023..| DE220
2013-01-13 | 97,012..| DE300
2013-01-15 | 97,344..| DE300
....
the pivot table should looks like as follows:
rate | DE011 | ... | DE090 | ... | DE220 | ... | DE300
-------------------------------------------------------
100 | 0 | ... | 1 | ... | 0 | ... | 0
98 | 2 | ... | 0 | ... | 1 | ... | 0
97 | 0 | ... | 0 | ... | 0 | ... | 2
90 | 0 | ... | 1 | ... | 0 | ... | 4
...
the column datum is important for the pivot table because the user have to take some input in the angular view.. in this example the user is choosing dateFrom and dateTo inputs. The rate will round the numbers how you can see in the pivot column rate.The article descriptions are in the new table column titles and the rate will count for each article.
My stored procedure works fine in SQL Server. But after the SP was imported to the EDM Model the Entity Framework defined a return type INT and that is impossible for my purposes.
Here is the code of EF:
public virtual int getMonthIsin(Nullable<System.DateTime> fromDate, Nullable<System.DateTime> toDate)
{
var fromDateParameter = fromDate.HasValue ?
new ObjectParameter("fromDate", fromDate) :
new ObjectParameter("fromDate", typeof(System.DateTime));
var toDateParameter = toDate.HasValue ?
new ObjectParameter("toDate", toDate) :
new ObjectParameter("toDate", typeof(System.DateTime));
return ((IObjectContextAdapter)this).ObjectContext.ExecuteFunction("getMonthIsin", fromDateParameter, toDateParameter);
}
I also have tried the .SqlQuery() in my WebAPI-Controller as follows:
return db.Database.SqlQuery<IQueryable>("EXEC getMonthIsin #fromDate, #toDate", fromDate, toDate).AsQueryable();
But it doesn't work.
Well, now the idea is to try a LINQ query and get the return values. I don't have any idea to implement this :(
Currently I've tried approximately this LINQ query:
public IQueryable getDatas(DateTime fromDate, DateTime toDate)
{
var query = from t in db.table1
where t.datum >= fromDate && t.datum <= toDate
group t by t.article
into grp
select new
{
articles = grp.Key,
rate = grp.Select(g => g.rate),
total = grp.Select(g => g.rate).AsQueryable()
};
return query;
}
But this isn't really the correct return. It would be very helpful when anyone can help me!! I will upvoted each good answer!

Entity Framework is not suitable for fetching dynamic data structures. Dapper is the tool to use here. It basically is a collection of extension methods on IDbConnection, one of which is Query that returns an IEnumerable<dynamic>, where dynamic is an object that implements IDictionary<string, object>. Getting the data is really simple:
IEnumerable<IDictionary<string, object>> result;
using (var cnn = new SqlConnection(connectionString))
{
cnn.Open();
var p = new DynamicParameters();
p.Add(" #fromDate", fromDate, DbType.DateTime);
p.Add(" #toDate", toDate, DbType.DateTime);
result = (IEnumerable<IDictionary<string, object>>)
cnn.Query(sql: "getMonthIsin",
param: p,
commandType: CommandType.StoredProcedure);
}
Now you have an IEnumerable<IDictionary<string, object>> in which one item (IDictionary<string, object>) represents one row of key/value pairs from the stored procedure's result set:
Key Value
----- ----
rate 100
DE011 0
... ...
DE090 1
... ...
DE220 0
... ...
DE300 0
It's up to you how to go from here. You could, for instance, convert the result to a DataTable as shown here: Dictionary<string, object> to DataTable.
By the way, Dapper isn't only simple, it's blazingly fast too.

Related

Is it possible to fetch a link table without fetching all links?

Ok, so first of I would like to say that I'm using NHibernate for my project, and in this project we have (among other things) a sync function (to sync from a central MSSQL database to a local SQLite). Now I know that NHibernate was not made to sync databases, but I would like to do this anyways.
I have a medium large database model so I can't add it here, but the problem is that I have two datatables, and one link table to link them both.
Database model:
| Product | | ProductLinkProducer | | Producer |
|--------------------| |---------------------| |---------------------|
| Id | | LinkId | | Id |
| Name | | Product | | Name |
| ProductLinkProducer| | Producer | | ProductLinkProducer |
Database:
| Product | | ProductLinkProducer | | Producer |
|---------| |---------------------| |----------|
| Id | | LinkId | | Id |
| Name | | ProductId | | Name |
| | | ProducerId | | |
So during the sync, I first copy all data from the Product table, and then from the Producer table (basically var products = session.Query<Products>().ToList()). This is done by NHibernate in a single statement each:
select
product0_.id as id2_,
product0_.name as name2_
from
Product product0_
Now I have to evict all items from the first session (products.ForEach(x => session.Evict(x));)
And then the save (products.ForEach(x => syncSession.save(x));) is one insert per row (as expected).
So when saving the data in the link table I would have wished that there also would be just a single select. However that is not the case. Because first it makes a select ... as above. But now before every row to insert it does even more select for the Product and for the Producer.
So it will look something like:
Products:
select
insert (id 1)
insert (id 2)
Producer:
select
insert (id 101)
insert (id 102)
ProdLinkProducer:
select
select id 1 from Products
select id 1 from Products
select id 101 from Producer
select id 2 from Products
select id 2 from Products
select id 102 from Producer
select id 102 from Producer
insert
insert
So is there anyway avoiding this behavior?
EDIT
To better explain what I have done, I have created a small test project. It can be found here: https://github.com/tb2johm/NHibernateSync
(I would have preferred to add only a ghist, but I think that it might have left out to much data, sorry...)
EDIT2
I have found out one way to make it work, but I don't like it.
The way this solution works is to in the database model create a ProductLinkProducerSync table, that doesn't contain any links, but just the values, and avoid synchronizing the ordinary link tables, but just the "sync" tables. But as I said I don't like this idea, since if I change anything in the database, I have kind of the same data in two places that I need to update.
I was unable to find NHibernate out of the box way of doing what you are asking.
However I was able to get the desired behavior (I guess something is better than nothing:) by manually rebinding the FK references (proxy classes) to the new session:
var links = session.Query<ProductLinkProducer>().ToList();
links.ForEach(x => session.Evict(x));
foreach (var link in links)
{
link.Product = syncSession.Get<Product>(link.Product.Id);
link.Producer = syncSession.Get<Producer>(link.Producer.Id);
syncSession.Save(link);
}
syncSession.Flush();
Here is the generalized version using NHibernate metadata services:
static IEnumerable<Action<ISession, T>> GetRefBindActions<T>(ISessionFactory sessionFactory)
{
var classMeta = sessionFactory.GetClassMetadata(typeof(T));
var propertyNames = classMeta.PropertyNames;
var propertyTypes = classMeta.PropertyTypes;
for (int i = 0; i < propertyTypes.Length; i++)
{
var propertyType = propertyTypes[i];
if (propertyType.IsAssociationType && !propertyType.IsCollectionType)
{
var propertyName = propertyNames[i];
var propertyClass = propertyType.ReturnedClass;
var propertyClassMeta = sessionFactory.GetClassMetadata(propertyClass);
yield return (session, target) =>
{
var oldValue = classMeta.GetPropertyValue(target, propertyName, EntityMode.Poco);
var id = propertyClassMeta.GetIdentifier(oldValue, EntityMode.Poco);
var newValue = session.Get(propertyClass, id);
classMeta.SetPropertyValue(target, propertyName, newValue, EntityMode.Poco);
};
}
}
}
and applying it to your Sync method:
private static void Sync<T>(string tableName, ISession session, ISession syncSession)
{
Console.WriteLine("Fetching data for ####{0}####...", tableName);
var sqlLinks = session.Query<T>();
var links = sqlLinks.ToList();
Console.WriteLine("...Done");
Console.WriteLine("Evicting data...");
links.ForEach(x => session.Evict(x));
Console.WriteLine("...Done");
Console.WriteLine("Saving data...");
var bindRefs = GetRefBindActions<T>(syncSession.SessionFactory).ToList();
foreach (var link in links)
{
foreach (var action in bindRefs) action(syncSession, link);
syncSession.Save(link);
}
Console.WriteLine("...Flushing data...");
syncSession.Flush();
Console.WriteLine("...Done");
Console.WriteLine("\n\n\n");
}

How to do a Full Outer Join of 2 or more Datatables in C# with a common column

I need to merge/join datatables on C# with a common column.
I am aware of many examples and the questions on the same topic that have been asked. I have not found any that answer my question though.
Below is the code i am using.
The code only allows for one common column based on the data of on datatable. I need a common column, but it needs to consider any other "Account#" may be used in another datatable and add it into the common column.
Also the code only allows for merging of 2 datatables. i need to merge 31 datatables, to merge 1 datatable for each day of the month.
I have a datatable for each day of the month, dt_docDAY01, dt_docDAY02, dt_docDAY03 etc.
each datatable contains an account number "Account#" and a balance stored in a column refering to the day "DAY01", "DAY02", etc.
Can you tell me how to change the code so that i will include all accounts from all tables.
Also, how i would merge all the datatables in this code, so i dont have to run this same code 31 times.
'
string id = "Account#";
var tableJoinedDAY02 = dt_docDAY01_GROUPED.Clone(); // create columns from table1
// add columns from table2 except id
foreach (DataColumn column in dt_docDAY02_GROUPED.Columns)
{
if (column.ColumnName != id)
tableJoinedDAY02.Columns.Add(column.ColumnName, column.DataType);
}
tableJoinedDAY02.BeginLoadData();
foreach (DataRow row1 in dt_docDAY01_GROUPED.Rows)
{
foreach (DataRow row2 in dt_docDAY02_GROUPED.Rows)
{
if (row1.Field<string>(id) == row2.Field<string>(id))
{
var list = row1.ItemArray.ToList(); // items from table1
// add items from table2 except id
foreach (DataColumn column in dt_docDAY02_GROUPED.Columns)
if (column.ColumnName != id)
list.Add(row2[column]);
tableJoinedDAY02.Rows.Add(list.ToArray());
}
}
}
tableJoinedDAY02.EndLoadData();`
Table1
Account# | Day01
1234 | 11
4567 | 22
0909 | 33
Table2
Account# | Day02
1234 | 12
0909 | 34
5578 | 99
0065 | 34
Table3
Account# | Day03
1234 | 13
7777 | 44
Expected Outcome Merged Table
Table1
Account# | Day01 | Day02 | Day03
1234 | 11 | 12 | 13
4567 | 22 | 0 | 0
0909 | 33 | 34 | 0
5578 | 0 | 99 | 0
0065 | 0 | 34 | 0
7777 | 0 | 0 | 44
#Infost, you are trying to do what in SQL language is a full outer join. Searching that on SO pointed to this answer https://stackoverflow.com/a/16832096/97471 that I have adapted for more than 2 tables:
Starting from an MVCE like this one:
DataTable table1 = new DataTable();
table1.Columns.Add("Account", typeof(int));
table1.Columns.Add("Day01", typeof(decimal));
table1.Rows.Add(1234, 11);
table1.Rows.Add(4567, 22);
table1.Rows.Add(0909, 33);
DataTable table2 = new DataTable();
table2.Columns.Add("Account", typeof(int));
table2.Columns.Add("Day02", typeof(decimal));
table2.Rows.Add(1234, 12);
table2.Rows.Add(0909, 34);
table2.Rows.Add(5578, 99);
table2.Rows.Add(0065, 34);
DataTable table3 = new DataTable();
table3.Columns.Add("Account", typeof(int));
table3.Columns.Add("Day03", typeof(decimal));
table3.Rows.Add(1234, 13);
table3.Rows.Add(7777, 44);
You can join them calling the following function:
var table123 = FullOuterJoinDataTables(table1, table2, table3);
Here is the function source:
DataTable FullOuterJoinDataTables(params DataTable[] datatables) // supports as many datatables as you need.
{
DataTable result = datatables.First().Clone();
var commonColumns = result.Columns.OfType<DataColumn>();
foreach (var dt in datatables.Skip(1))
{
commonColumns = commonColumns.Intersect(dt.Columns.OfType<DataColumn>(), new DataColumnComparer());
}
result.PrimaryKey = commonColumns.ToArray();
foreach (var dt in datatables)
{
result.Merge(dt, false, MissingSchemaAction.AddWithKey);
}
return result;
}
/* also create this class */
public class DataColumnComparer : IEqualityComparer<DataColumn>
{
public bool Equals(DataColumn x, DataColumn y) { return x.Caption == y.Caption; }
public int GetHashCode(DataColumn obj) { return obj.Caption.GetHashCode(); }
}
The output is
Account Day01 Day02 Day03
1234 11 12 13
4567 22
909 33 34
5578 99
65 34
7777 44
This need to handled as follows, all the tables cannot be joined together by magic, let's take a smaller sample set:
Table1 (dt1) - Account# | Day01
Table2 (dt2) - Account# | Day02
Table3 (dt3) - Account# | Day03
Table4 (dt4) - Account# | Day04
dt1.AsEnumerable()
.Join(dt2.AsEnumerable(), d1 => (int)d1["Account#"], d2 =>
(int)d2["Account#"],
(d1,d2) => new {Account = (int)d1["Account#"],Day01 =
d1["Day01"],Day02 = d2["Day02"]})
.Join(dt3.AsEnumerable(), d12 => d12.Account, d3 => (int)d3["Account#"],
(d12,d3) => new {d12.Account,d12.Day01,d12.Day02,Day03=d3["Day03"]})
.Join(dt4.AsEnumerable(), dAll => dAll.Account, d4 =>
(int)d4["Account#"],
(dAll,d4) => new
{dAll.Account,dAll.Day01,dAll.Day02,dAll.Day03,Day04=d4["Day04"]})
Result of the above operation would be IEnumerable<AnonymousType>, where as of now Anonymous type consists of the Properties Account,Day01,Day02,Day03,Day04, similarly you can add up to Day31. Also note how post first join we start using the AnonymousType generated as part of the last Join statement
This needs conversion to DataTable, which would be something similar to the code posted in the following thread:
Convert IEnumerable to DataTable
Also check this as its for conversion to DataTable using IEnumerable of Anonymous type, using a Nuget utility Fastmember
C# how to convert IEnumerable anonymous lists into data table

Linq query to return country as column and cities as rows and count in cells

I have this table in db :
| Buyer Country| City |...
|:-------------|-------:|...
| Uk | Lisbon |...
| Portugal | London |...
| France | Madrid |...
| France | Lisbon |...
| France | Madrid |...
| France | Madrid |...
And i would like to get a linq query that could give me this information so that i can bind it with a data grid like this :
| Buyer Country| Madrid | Lisbon | London |
|:-------------|-------:|-------:|-------:|
| Uk | 0 | 1 | 0 |
| Portugal | 1 | 0 | 1 |
| France | 3 | 1 | 0 |
Countries and cities are dynamic to i cannot create them manually.
Basically i want to get the list of all buyers country in the first row and the number of times that country bought in that city in the form of columns by city.
The best i got was this :
var query = from purchase in db.TotalPurchases
group purchase by new { purchase.Country, purchase.City } into grp
select new
{
country = grp.Key.Country,
city = grp.Key.City,
count = grp.Count(),
};
But this is not quite what i want :/
If you want to create a "pivot table" from your database table to bind to a grid view, the easiest way I know of is to use a DataTable as the binding source and use a LINQ query to get the information to populate it.
So we basically need to turn the unique row values for the "City" column into columns of the new pivot data table. We can do this in a few ways, but each of them requires a number of steps. The sample below shows a way that only requires a single query to the database to retrieve the data that we need.
// 1. Query for all data we need
var pivotInput = db.TotalPurchases
.GroupBy(p => p.Country)
.Select(cp => new {
Country = cp.Key,
PurchasesByCity = cp.GroupBy(x => x.City).Select(c => new {
City = c.Key,
NumberOfPurchases = c.Count()
})
}).ToList();
// 2. Get all distinct cities
var allCities = pivotInput
.SelectMany(x => x.PurchasesByCity.Select(c => c.City))
.Distinct().ToList();
// 3. Build the pivot data table "schema".
var pivotTable = new DataTable("purchasesByCity");
pivotTable.Columns.Add(new DataColumn("Country", typeof(string)));
allCities.ForEach(c => pivotTable.Columns.Add(new DataColumn(c, typeof(int))));
// 4. Add as rows to the datatable
foreach (var c in pivotInput)
{
var row = pivotTable.NewRow();
row["Country"] = c.Country;
foreach (var city in c.PurchasesByCity)
row[city.City] = city.NumberOfPurchases;
pivotTable.Rows.Add(row);
}

Linq to SQL / filter duplicates

i have a view in my sql server 2012 with a couple of duplicates and i want to sort them by the newest and filter all others - can anyone help me?
My viewin my SQL Server 2012:
GUID (as primary key), number, datetime and name
+-----+----------+--------------------------------+-----
| guid | number| datetime | name
+-----+----------+--------------------------------+------
| b105..| 1234567|2014-07-07T16:32:20.854+02:00:00|Name1
| s1b5..| 1111222|2014-07-06T16:30:21.854+02:00:00|Name2
| b17a..| 1234567|2014-07-06T15:22:17.854+02:00:00|Name1
| f205..| 1233333|2014-07-07T17:40:20.854+02:00:00|Name3
| b11t..| 1233333|2014-07-04T11:12:15.854+02:00:00|Name3
| rt85..| 1111222|2014-07-07T21:55:52.854+02:00:00|Name2
+-------+--------+--------------------------------+-----
the name is every time the same if the number is the same. for e.g. number 1234567 is always name 1.
I want to filter my table that i have only the newest number without duplicates
so the result should be:
+-----+----------+--------------------------------+-----
| guid | number| datetime | name
+-----+----------+--------------------------------+------
| b105..| 1234567|2014-07-07T16:32:20.854+02:00:00|Name1
| f205..| 1233333|2014-07-07T17:40:20.854+02:00:00|Name3
| rt85..| 1111222|2014-07-07T21:55:52.854+02:00:00|Name2
+-------+--------+--------------------------------+-----
How can i do this in Linq? "Distinct" is not working because of the guid and the datetime
You can do it by grouping your elements by 2 columns. (number and name). Then access the grouped data. You can do it somehow like that:
var query =
from col in viewData
group col by new
{
col.name,
col.number,
} into groupedCol
select new viewData()
{
number = groupedCol.Key.number,
name = groupedCol.Key.name,
datetime = groupedCol.OrderBy( dateCol => dateCol.datetime).First()
};
var res = list.GroupBy(c => c.name).Select(group => group.OrderBy( c1 => c1.datetime).First()).ToList();
This should work as long as datetime is stored as an instance of DateTime, instead of string.

Selecting records with max version

I have a table as follows:
ConfigID | VersionNo | ObjectType
ConfigID and VersionNo constitute the unique key.
I want to be able to select the record with the highest VersionNo for each configID based on an object type.
I have tried
configs = (from config in configRepository.FindBy(x => x.ObjectType.Equals(typeof(Node).ToString(), StringComparison.InvariantCultureIgnoreCase))
group config by config.ConfigID into orderedConfigs
select orderedConfigs.OrderBy(x => x.ConfigID).ThenByDescending(x => x.VersionNo).First());
EDIT: I must add that the FindBy is basically just a where clause.
But I am getting no results. Please help me with this.
EDIT:
The data in the table could look like:
3fa1e32a-e341-46fd-885d-8f06ad0caf2e | 1 | Sybrin10.Common.DTO.Node
3fa1e32a-e341-46fd-885d-8f06ad0caf2e | 2 | Sybrin10.Common.DTO.Node
51d2a6c7-292d-42fc-ae64-acd238d26ccf | 3 | Sybrin10.Common.DTO.Node
51d2a6c7-292d-42fc-ae64-acd238d26ccf | 4 | Sybrin10.Common.DTO.Node
8dbf7a33-441f-40bc-b594-e34c5a2c3f51 | 1 | Some Other Type
91413e73-4997-4643-b7d2-e4c208163c0d | 1 | Some Other Type
From this I would only want to retrieve the second and fourth records as they have the highest version numbers for the configID and are of the required type.
Not sure if 100% works because writing out of VS :) but idea should be good
var configs = configRepository.Where(x=>x.ObjectType==typeof(Node).ToString());
var grouped = configs.GroupBy(x=>x.ConfigId);
var a = grouped.select(x=>x.OrderByDescending(y=>y.VersionNo).First());
It looks LINQ sql to but in pure SQL i can write the query like this
SELECT ConfigID ,MAX(VersionNo ) FROM CUSTOMIZE_COLUMNS_DETAIL WHERE
ObjectType = 'objectType' GROUP BY ConfigID
I have tried to replicate the scenario in sql , might by useful to you, THanks

Categories

Resources