Multiple Tables Group and substract sum of columns using linq sql - linq

Here i have two tables
Table One
+---------------+----------+------------+
| Raw Material | Size | Qty |
+---------------+----------+------------+
| A | 1 | 5 |
| A | 2 | 2 |
| A | 1 | 2 |
| B | 0 | 5 |
| B | 0 | 1 |
+---------------+----------+------------+
Table Two
+---------------+----------+------------+
| Raw Material | Size | Qty |
+---------------+----------+------------+
| A | 1 | 2 |
| A | 2 | 1 |
| A | 1 | 1 |
+---------------+----------+------------+
I want out put like
+---------------+----------+------------+
| Raw Material | Size | Qty |
+---------------+----------+------------+
| A | 1 | 4 |
| A | 2 | 1 |
| B | 0 | 6 |
+---------------+----------+------------+
Want to get substract first two tables sum of qty by grouping Rawmaterial and Size

Something like this should do the job
var result = tableA.Select(e => new { Item = e, Factor = 1 })
.Concat(tableB.Select(e => new { Item = e, Factor = -1 }))
.GroupBy(e => new { e.Item.RawMaterial, e.Item.Size }, (key, elements) => new
{
RawMaterial = key.RawMaterial,
Size = key.Size,
Qty = elements.Sum(e => e.Item.Qty * e.Factor)
}).ToList();
First we create a union of the two tables using Concat, including the information which one is additive (in Factor field), and then just do the normal grouping.
If you want the result to be List<YourTableElementType>, just replace the final anonymous type projection (new { ... }) with new YourTableElementType { ... }.

Related

How to get all unique records with the maximum value in column using LINQ

Learner | AssesmentId | Attempt
------------------------------------
Parker | 1 | 1
Parker | 1 | 2
Stark | 1 | 1
Rogers | 1 | 1
Rogers | 1 | 2
Parker | 1 | 3
Given this data, how do I get all the unique Student name with the highest Attempt?
I'm attempting to get this result:
Learner | AssesmentId | Attempt
------------------------------------
Parker | 1 | 3
Stark | 1 | 1
Rogers | 1 | 2
How can I do this in a single query in LINQ?
from la in _context.LearnerAssessments
where la.AssessmentId == assessmentId
&& learnerIds.Contains(la.LearnerId)
&& la.Attempt == {highest attempt}
var data = from list in _context.LearnerAssessments
group list by new
{
list.Learner,
list.AssesmentId
}
into g
select new
{
g.Key.Learner,
g.Key.AssesmentId,
Max= g.Max(x=>x.Attempt)
};
or using fluent API :
var data = _context.LearnerAssessments.GroupBy(l => new { l.Learner, l.AssesmentId }, (keys, item) => new
{
Key = keys,
MaxAttemp = item.Max(x => x.Attempt)
}).Select(x => new LearnerAssessment
{
Learner = x.Key.Learner,
Attempt = x.MaxAttemp,
AssesmentId = x.Key.AssesmentId
});

MDX - filter empty outside of selected range

Cube is populated with data divided into time dimension ( period ) which represents a month.
Following query:
select non empty {[Measures].[a], [Measures].[b], [Measures].[c]} on columns,
{[Period].[Period].ALLMEMEMBERS} on rows
from MyCube
returns:
+--------+----+---+--------+
| Period | a | b | c |
+--------+----+---+--------+
| 2 | 3 | 2 | (null) |
| 3 | 5 | 3 | 1 |
| 5 | 23 | 2 | 2 |
+--------+----+---+--------+
Removing non empty
select {[Measures].[a], [Measures].[b], [Measures].[c]} on columns,
{[Period].[Period].ALLMEMEMBERS} on rows
from MyCube
Renders:
+--------+--------+--------+--------+
| Period | a | b | c |
+--------+--------+--------+--------+
| 1 | (null) | (null) | (null) |
| 2 | 3 | 2 | (null) |
| 3 | 5 | 3 | 1 |
| 4 | (null) | (null) | (null) |
| 5 | 23 | 2 | 2 |
| 6 | (null) | (null) | (null) |
+--------+--------+--------+--------+
What i would like to get, is all records from period 2 to period 5, first occurance of values in measure "a" denotes start of range, last occurance - end of range.
This works - but i need this to be dynamically calculated during runtime by mdx:
select non empty {[Measures].[a], [Measures].[b], [Measures].[c]} on columns,
{[Period].[Period].&[2] :[Period].[Period].&[5]} on rows
from MyCube
desired output:
+--------+--------+--------+--------+
| Period | a | b | c |
+--------+--------+--------+--------+
| 2 | 3 | 2 | (null) |
| 3 | 5 | 3 | 1 |
| 4 | (null) | (null) | (null) |
| 5 | 23 | 2 | 2 |
+--------+--------+--------+--------+
I tried looking for first/last values but just couldn't compose them into the query properly. Anyone has this issue before ? This should be pretty common seeing as I want to get a continuous financial report without skipping months where nothing is going on. Thanks.
Maybe try playing with NonEmpty / Tail function in a WITH clause:
WITH
SET [First] AS
{HEAD(NONEMPTY([Period].[Period].MEMBERS, [Measures].[a]))}
SET [Last] AS
{TAIL(NONEMPTY([Period].[Period].MEMBERS, [Measures].[a]))}
SELECT
{
[Measures].[a]
, [Measures].[b]
, [Measures].[c]
} on columns,
[First].ITEM(0).ITEM(0)
:[Last].ITEM(0).ITEM(0) on rows
FROM MyCube;
to debug a custom set, to see what members it is returning you can do something like this:
WITH
SET [First] AS
{HEAD(NONEMPTY([Period].[Period].MEMBERS, [Measures].[a]))}
SELECT
{
[Measures].[a]
, [Measures].[b]
, [Measures].[c]
} on columns,
[First] on rows
FROM MyCube;
I think reading your comment about Children means that this is also an alternative - to add an extra [Period]:
WITH
SET [First] AS
{HEAD(NONEMPTY([Period].[Period].[Period].MEMBERS
, [Measures].[a]))}
SET [Last] AS
{TAIL(NONEMPTY([Period].[Period].[Period].MEMBERS
, [Measures].[a]))}
SELECT
{
[Measures].[a]
, [Measures].[b]
, [Measures].[c]
} on columns,
[First].ITEM(0).ITEM(0)
:[Last].ITEM(0).ITEM(0) on rows
FROM MyCube;

Best Practice for retrieving and iterating over one to many

I'm trying to produce a web page report of Orders Per Customer with line item details for each order.
So it looks like this:
Customer 1 | Order 1 | Item 1 |
| | == New Row == |
| | Item 2 |
| | == New Row == |
| | Item 3 |
| | == New Row == |
| ====== New Row ========= |
| Order 2 | Item 1 |
| | == New Row == |
| | Item 2 |
| | == New Row == |
| | Item 3 |
| | == New Row == |
| ====== New Row ========= |
| Order 3 | Item 1 |
| | Item 2 |
== New Row ==============================
Customer 2 | Order 1 | Item 1 |
| | == New Row == |
| | Item 2 |
| | == New Row == |
| | Item 3 |
| | == New Row == |
| | Item 4 |
| | == New Row == |
| | Item 5 |
| | == New Row == |
| ====== New Row ========= |
| Order 2 | Item 1 |
| | == New Row == |
| | Item 2 |
| | == New Row == |
| | Item 3 |
== New Row ==============================
Is it better to get an array list of data as follows:
1 | 1 | 1 |
1 | 1 | 2 |
1 | 1 | 3 |
1 | 2 | 1 |
1 | 2 | 2 |
1 | 2 | 3 |
1 | 3 | 1 |
1 | 3 | 2 |
2 | 1 | 1 |
2 | 1 | 2 |
2 | 1 | 3 |
2 | 1 | 4 |
2 | 1 | 5 |
2 | 2 | 1 |
2 | 2 | 2 |
2 | 2 | 3 |
and pass 1 JavaBean and use JSTL to determine the nesting as follows:
this is incomplete for brevity
<table><tr><th>Customer No</th><th>Orders</th></tr>
<c:forEach var="custOrderLineItem" items="${customerOrderLineItemList}">
<c:set var="currentOrder" value="custOrderLineItem.orderId">
<c:set var="currentCustomer" value="custOrderLineItem.customerId">
<c:if test="${currentOrder != custOrderLineItem.orderId}">
==New Row==
</c:if>
or is it better to use nested JavaBeans
Customer.setOrders<List>
Orders.setOrderDetails<List>
OrderDetails.setLineItem<List>
and then use JSTL as such
<c:forEach var="customer" items="${customerList}">
<c:forEach var="order" items="${customer.orderList}">
<c:forEach var="lineItem" items="${order.detailList}">
After spending the time writing this, I feel like the second method looks cleaner and easier. But the first method seems to have an easier SQL query. I'm not using JPA, just basic JDBC sql calls. So how does one populate nested JavaBeans, without JPA? Do you do something like this?
List<Customer> custList = getCustomerList();
ListIterator custListIter = custList.listIterator();
while (custListIter.hasNext()) {
customer = (Customer) custListIter.next();
List<Order> orderList = getOrderList(customer.getId());
ListIterator orderListIter = orderList.listIterator();
while (orderListIter.hasNext()) {
order = (Order) orderListIter.next();
List orderDetailsList<OrderDetail> = getOrderDetailList(order.getId);
order.setOrderDetails(orderDetailsList);
orderListIter.set(order);
}
customer.setOrderList(orderList);
custListIter.set(customer);
}
You could simply make the SQL query which returns everything, and build the graph of objects from the result set. Using maps to keep an association between IDs and the corresponding objects:
Map<Long, Customer> customersById = new HashMap<Long, Customer>();
Map<Long, Order> ordersById = new HashMap<Long, Order>();
Map<Long, Item> itemsById = new HashMap<Long, Item>();
while (rs.next()) {
Long customerId = rs.getLong(1);
Customer customer = customersById.get(customerId);
if (customer == null) {
customer = new Customer(customerId);
// populate other fields of customer
customersById.put(customerId, customer);
}
Long orderId = rs.getLong(5);
Order order = ordersById.get(orderId);
if (order == null) {
order = new Order(orderId);
customer.addOrder(order);
// populate other fields of order
ordersById.put(orderId, order);
}
// same for items
}
At the end of the loop, you have all the customers, each with their orders, each with their items.

Sum of the grouped distinct values

This is a bit hard to explain in words ... I'm trying to calculate a sum of grouped distinct values in a matrix. Let's say I have the following data returned by a SQL query:
------------------------------------------------
| Group | ParentID | ChildID | ParentProdCount |
| A | 1 | 1 | 2 |
| A | 1 | 2 | 2 |
| A | 1 | 3 | 2 |
| A | 1 | 4 | 2 |
| A | 2 | 5 | 3 |
| A | 2 | 6 | 3 |
| A | 2 | 7 | 3 |
| A | 2 | 8 | 3 |
| B | 3 | 9 | 1 |
| B | 3 | 10 | 1 |
| B | 3 | 11 | 1 |
------------------------------------------------
There's some other data in the query, but it's irrelevant. ParentProdCount is specific to the ParentID.
Now, I have a matrix in the MS Report Designer in which I'm trying to calculate a sum for ParentProdCount (grouped by "Group"). If I just add the expression
=Sum(Fields!ParentProdCount.Value)
I get a result 20 for Group A and 3 for Group B, which is incorrect. The correct values should be 5 for group A and 1 for group B. This wouldn't happen if there wasn't ChildID involved, but I have to use some other child-specific data in the same matrix.
I tried to nest FIRST() and SUM() aggregate functions but apparently it's not possible to have nested aggregation functions, even when they have scopes defined.
I'm pretty sure there is some way to calculate the grouped distinct sum without needing to create another SQL query. Anyone got an idea how to do that?
Ok I got this sorted out by adding a ROW_NUMBER() function my SQL query:
SELECT Group, ParentID, ROW_NUMBER() OVER (PARTITION BY ParentID ORDER BY ChildID ASC) AS Position, ChildID, ParentProdCount FROM Table
and then I replaced the SSRS SUM function with
=SUM(IIF(Position = 1, ParentProdCount.Value, 0))
Put a grouping over the ParentID and use a summation over that group,
eg:
if group over ParentID = "ParentIDGroup"
then
column sum of ParentPrdCount = SUM(Fields!ParentProdCount.Value,"ParentIDGroup")

Codeigniter Datamapper save as new id

I'm new to datamapper. I have a problem on trying to duplicate a result into a new id.
This is a simplified table for my database:
Job Table
| id | property_id | name | type |
| 1 | 1 | abc | i |
| 2 | 2 | def | ii |
Property Table
| id | job_id | size |
| 1 | 1 | 90 |
| 2 | 2 | 40 |
How can I automatically duplicate a new job based on job id 1 into new job/property id like
Job Table
| id | property_id | name | type |
| 1 | 1 | abc | i |
| 2 | 2 | def | ii |
| 3 | 3 | abc | i |
Property Table
| id | job_id | size |
| 1 | 1 | 90 |
| 2 | 2 | 40 |
| 3 | 3 | 90 |
Thanks for helping! :)
In the documentation for DataMapper Overzealous Edition: http://datamapper.wanwizard.eu/pages/clonecopy.html There's clone and copy, copy will clear the id. Here's their example, just skip the part of making changes:
// Let's save a new hosting plan
$p = new Plan();
$p->name = 'The 100GB Plan';
$p->storage = 1000;
$p->bandwidth = 2000;
$p->databases = 5;
$p->domains = 5;
$p->emails = 50;
$p->save();
// Now, lets make a copy of that saved plan and base a new one off of it
$p = $p->get_copy();
// Change only what we need to
$p->name = 'The Big 150GB Plan';
$p->storage = 1500;
$p->bandwidth = 2500;
// And now save a new record
$p->save();
You can also just modify the object you retrieve, and then use save_as_new() to save it as a new record.

Resources