ClosedXML Outline - closedxml

I am trying to create a Group in the exported Excel Workbook using OpenXML.
My source data table looks like this:
Row State Product Sales
1 NY A 100
2 NY A 200
3 NY B 300
4 CA A 100
5 CA A 200
6 CA B 300
I would like to create an outline by State and then Product with a subtotal on each group
I tried
ws.Outline.SummaryVLocation = XLOutlineSummaryVLocation.Top;
ws.Rows(1, 3).Group(); // Create an outline (level 2) for rows 1-4
ws.Rows(4, 6).Group();
But it's not giving me what I want, and I don't see an option to add the subtotals.
How can I achieve this?

The code example in the documentation which you use is either outdated or just wrong.
If you want to group rows 2 to 4 you need to use the code ws.Rows(3, 4).Group(); (see picture). This is consistent with Excel itself, there you have to select only rows 3 and 4 before clicking the group button to get the same result.
When you try to group rows 1 to 3 like in your code you group them all under row 0 which leads to errors since there is no row 0.
You can control this behaviour to some extend with the XLOutlineSummaryVLocation property. If you use Bottom instead of top you use the top two rows to group rows 2 to 4: ws.Rows(2, 3).Group();
With all this said two more points:
You need to use Excel row numbers not the numbers in your column "Row".
All this grouping and collapsing is only for display purposes. To sum up the sales numbers you have to use the subtotal functions in Excel (which I find rather confusing and unhelpful in this case) or add columns and results directly in C#.
Using this code should lead to your desired result (see picture below):
ws.Outline.SummaryVLocation = XLOutlineSummaryVLocation.Top;
ws.Cell(1, 5).SetValue("Product subtotals");
ws.Cell(1, 6).SetValue("State subtotals");
ws.Rows(3, 4).Group(); // group rows 2 to 4 (state NY), outline level 1
ws.Cell(2, 6).SetFormulaA1("=SUM(D2:D4)"); // subtotal for all NY sales
ws.Row(3).Group(); // group rows 2 and 3 (product A), outline level 2
ws.Cell(2, 5).SetFormulaA1("=SUM(D2:D3)"); // subtotal for all NY, product A sales
ws.Cell(4, 5).SetFormulaA1("=SUM(D4)"); // subtotal for all NY, product B sales
ws.Rows(6, 7).Group(); // group rows 5 to 7 (state CA), outline level 1
ws.Row(6).Group(); // group rows 5 and 6 (product A), outline level 2
ws.CollapseRows(2); // collapse group level 2 (products)

Related

How do I create a running sum for the count of the number of items associated with a value in a query? [duplicate]

This question already has answers here:
SQL Server: create an incremental counter for records in the same year?
(3 answers)
Closed 1 year ago.
I am trying to use a query to create a Bill of Materials list that gives an item number to a material. I have a column for Material and BOMPart. The Material column gives a list of item numbers that repeat for each item in the Bill of Materials. The BOMPart column lists each part of the item in Material. So Material 1 is created using items a, b, and c. I would like the third column, ItemNo, to start at 1 for each Material and count each BOMPart associated to the material. It should then reset to 1 for the next Material. Any suggestions? I am still pretty new to Access. Here is what I would like the columns to look like, I replaced my actual material numbers for simplicity.
Material
BOMPart
ItemNo
1
a
1
1
b
2
1
c
3
2
a
1
2
f
2
3
g
1
3
h
2
3
i
3
4
k
1
4
m
2
You want to use row_number for this. I am not certain this works in Access.
select Material
, BOMPart
, Row_Number() over (partition by Material order by BOMPart)
from xxxx

How to sum only one of repeated values from joined data in RDLC

I'm not sure if SSRS is dumb, or I am (I'm leaning towards both).
I have a dataset that (as a result of joins etc) has some columns with the same values duplicated across every row (fairly standard database stuff):
rid cnt bid flg1 flg2
-------------------------------
4 2882 1 17 3
5 2784 1 17 3
6 1293 1 17 3
18 9288 2 4 9
20 762 2 4 9
Reporting based on cnt is straightforward enough. I can also make a tablix that shows the following:
bid flg1 flg2
------------------
1 17 3
2 4 9
(Where the tablix is grouped by Fields!bid.Value and the columns are just Fields!flg1.Value and Fields!flg2.Value respectively.)
What I can't figure out is how to display the sum of these values -- specifically I want to show that the sum of flg1 is 21 and the sum of flg2 is 12 -- not the sum of every row in the dataset (counting each value more than once).
(Note that I'm not looking for a sum of distinct values, as they may not be unique. I want a sum of one value from each bid group, because it's from a table join so they will always have the same value.)
If possible, I'd also like to be able to do a similar calculation at the top level of the report (not in any tablix); although I'd settle for hiding the detail row if that's the only way.
Obviously, Sum(Fields!flg1.Value) isn't the answer, as this either returns 51 (if on the first row inside the group) or 59 (if outside it).
I also tried Sum(Fields!flg1.Value, "bid") but this wasn't considered a valid scope.
I also tried Sum(First(Fields!flg1.Value, "bid")) but apparently you're not allowed to sum first values for some weird reason (and may have had the same scope problem anyway).
Using Sum(Max(Fields!flg1.Value, "bid")) does work, but feels wrong. Is there a better way to do this?
(Related: is there a good way to save the result of that calculation so that I can later also show a Sum of those totals without an even hairier expression?)
There are two basic ways to do this.
Do what you have already done (Sum(Max(Fields!flg1.Value, "bid")))
Sum the rendered values. To do this check the name of the cell containing the data you want (check it's properties) and then use something like =SUM(ReportItems!flg1.Value) where flg1 is the name of the textbox, which is not necessarily always the same name as the field.

aggregate of the group columns along with the group in a table

I am very new to Microsoft reporting. I have the following table in my database:
CategoryName Id
Normal 1
High 2
Normal 3
Low 4
Normal 5
Normal 6
Normal 7
Normal 8
Low 9
Low 10
Low 11
High 12
I want to group by Category and also show the count of each category. Here is what I did:
I inserted a two column table and I grouped by the categoryName in the first column and in the second column, I tried doing
=CountDistinct(Fields!CategoryName.Value)
This is what I am seeing in the report
High 1
1
Normal 1
1
1
1
1
1
1
Low 1
1
1
1
I want to see something like this:
Category Count
Normal 6
Low 4
High 2
any help will be highly appreciated
Delete the Detail row and put the Count epxression in the Group row.
You'd probably be better off doing this in the query. Easier and leaves less room for error. Something like the following should work.
SELECT categoryName, COUNT(*)
FROM your table
GROUP BY categoryName

Spotfire intersect first 'n' periods

Is there a way to use an Over and Intersect function to get the average sales for the first 3 periods (not always consecutive months, sometimes a month is skipped) for each Employee?
For example:
EmpID 1 is 71.67 ((80 + 60 + 75)/3) despite skipping "3/1/2007"
EmpID 3 is 250 ((350 + 250 + 150)/3).
I'm not sure how EmpID 2 would work because there are just two data points.
I've used a work-around by calculated column using DenseRank over Date, "asc", EmpID and then used another Boolean calculated column where DenseRank column name is <= 3, then used Over functions over the Boolean=TRUE column but I want to figure the correct way to do this.
There are Last 'n' Period functions but I haven't seen anything resembling a First 'n' Period function.
EmpID Date Sales
1 1/1/2007 80
1 2/1/2007 60
1 4/1/2007 75
1 5/1/2007 30
1 9/1/2007 100
2 2/1/2007 200
2 3/1/2007 100
3 12/1/2006 350
3 1/1/2007 250
3 3/1/2007 150
3 4/1/2007 275
3 8/1/2007 375
3 9/1/2007 475
3 10/1/2007 300
3 12/1/2007 200
I suppose the solution depends on where you want this data represented, but here is one example
If((Rank([Date],"asc",[EmpID])<=3) and (Max(Rank([Date],"asc",[EmpID])) OVER ([EmpID])>=3),Avg([Sales]) over ([EmpID]))
You can insert this as a calculated column and it will give you what you want (assuming your data is sorted by date when imported).
You may want to see the row numbering, and in that case insert this as a calculated column as well and name it RN
Rank([Date],"asc",[EmpID])
Explanation
Rank([Date],"asc",[EmpID])
This part of the function is basically applying a row number (labeled as RN in the results below) to each EmpID grouping.
Rank([Date],"asc",[EmpID])<=3
This is how we are taking the top 3 rows regardless if Months are skipped. If your data isn't sorted, we'd have to create one additional calculated column but the same logic applies.
(Max(Rank([Date],"asc",[EmpID])) OVER ([EmpID])>=3)
This is where we are basically ignoring EmpID = 2, or any EmpID who doesn't have at least 3 rows. Removing this would give you the average (dynamically) for each EmpID based on their first 1, 2, or 3 months respectively.
Avg([Sales]) over ([EmpID])
Now that our data is limited to the rows we care about, just take the average for each EmpID.
#Chris- Here is the solution I came up with
Step 1: Inserted a calculated column 'rank' with the expression below
DenseRank([Date],"asc",[EmpID])
Step 2: Created a cross table visualization from the data table and limited data with the expression below

Summing Values in Visible Rows Only

I have a report with one group of which the visibility is toggled based on if the [total] is greater than 5. I want to make another row, that adds up only the visible information from each column. The first column is the owner name, The 3rd column [total] gets divided by the second column to get the 4th [avg] column. Right now it's counting everything, including the hidden rows.
Also, columns 2-4 are calculated based on expressions
Example of what I want it to look like
Owner Count Total AVG
Bob 3 12 4
Jane 1 9 9
Marcos 2 24 12
TOTAL: 6 45 7.5
I also need to count the visible Owners total, and divide that by the total amount of active owners, which I pull from a second dataset.
Example if we had 12 owners total
Total listed owners: 3
% of owners: 25%
For the total displayed owners, just SUM on the expression you use to hide the row:
=SUM(IIF(Fields!Total.Value > 5, 1, 0))
then to get the percent, divide this by the COUNT of the total rows, including the hidden ones (which you already have).