Jasper report: two detail sections? - reporting

I've got a jasper report to do, with data like this:
Item | Quantity | Color
------+-----------+--------
A001 | 1 | Red
A001 | 1 | Green
B002 | 3 | Red
B002 | 3 | Purple
The report is grouped by Item/Quantity, e.g.
Item: A001, Qty: 1, Colors: Red,Green
Item: B002, Qty: 3, Colors: Red,Purple
Now I've got this Jasper report that already groups as such - i.e. shows a heading with the item and quantity, with a list of colors underneath.
The question now is, underneath this group I need to display a number of horizontal lines (for someone to write something in), equal to the qty of the item. e.g. underneath the A001 group I need to display one line, and under the B002 group I need to display three lines, like so:
Item A001, Qty 1, Colors Red, Green
_________________________
Item B002, Qty 3, Colors Red, Purple
_________________________
_________________________
_________________________
I've tried looking at jasper scripts, but it seems they can only manipulate report parameters/variables.
Does anyone have an idea of how I could do this?

Hmm, interesting.
Here is what you could do:
Using this source data (MySQL):
create table items (
item varchar(4),
quantity number,
color varchar(10),
);
(insert data...)
create table numbers (i integer)
(insert data 0, 1, 2 .... MySQL 5.1 has stored procedures that could do it, earlier versions would need an external script to populate it. Go from 0 to the largest quantity you'd have).
Then, the trick is to craft the right sort of query. I came up with this:
select i.*, n.i from
(
select concat(i.item, ' ', i.quantity) as grouping, i.item, i.quantity,group_concat(distinct color) as colors
from items i
GROUP BY item, quantity
) i
cross join numbers n
where quantity > n.i;
E.g. If I populate my numbers table, and the populate the items table with your example data, and then run the query I get:
+----------+------+----------+------------+------+
| grouping | item | quantity | colors | i |
+----------+------+----------+------------+------+
| A001 1 | A001 | 1 | Red,Greem | 0 |
| B002 3 | B002 | 3 | Red,Purple | 0 |
| A001 1 | A001 | 1 | Red,Greem | 1 |
| B002 3 | B002 | 3 | Red,Purple | 1 |
| A001 1 | A001 | 1 | Red,Greem | 2 |
| B002 3 | B002 | 3 | Red,Purple | 2 |
| A001 1 | A001 | 1 | Red,Greem | 3 |
| B002 3 | B002 | 3 | Red,Purple | 3 |
+----------+------+----------+------------+------+
Then in your Jasper Report, the trick is to create a group/band that works off the 'grouping' column, and put your heading in that:
Item A001, Qty 1, Colors Red, Green
And then, in the detail section just have a line as the only thing in the detail.
Doing this generates the report you want for me.
Note that the numbers table is a little silly, but is a standard data warehousing technique, though I suspect some database (e.g. Oracle) would have clever recursive procedures or other functions that would exclude the need for it.

OK, in the end, this was done using a subdataset and a crosstab in the detail footer section. Works nicely - thanks for everyone who contributed :)

Related

ArrayFormula - If cell contains match, combine other cells with TEXTJOIN

I have a Google Sheet that contains names of characters, together with corresponding values for the group name, "selected" and attack power. It looks like this:
Sheet1
| NAME | GROUP NAME | SELECTED | ATTACK POWER |
|:---------|:-----------|----------:|-------------:|
| guile | Team Red | 1 | 333 |
|----------|------------|-----------|--------------|
| blanka | Team Red | 1 | 50 |
|----------|------------|-----------|--------------|
| sagat | Team Red | | 500 |
|----------|------------|-----------|--------------|
| ruy | Team Blue | 1 | 450 |
|----------|------------|-----------|--------------|
| vega | Team Blue | 2 | 150 |
Sheet2
In my second sheet, I have two columns. Group name, which contains names of each team from Sheet1 and names, which contains my current ArrayFormula:
=ARRAYFORMULA(TEXTJOIN(CHAR(10); 1;
REPT('Sheet1'!A:A; 1*('Sheet1'!B:B=A2))))
Using this formula I can combine all characters into one cell (with textjoin, repeated with row breaks) based on the value in Group name. The result looks like the following:
| GROUP NAME | NAME |
|:-----------|:--------------------------|
| Team Red | guile |
| | blanka |
| | sagat |
|------------|---------------------------|
| Team Blue | ruy |
| | vega |
|------------|---------------------------|
The problem is that I only want to combine the characters with having a selected value of 1. End-result should instead look like this:
| GROUP NAME | NAME |
|:-----------|:--------------------------|
| Team Red | guile |
| | blanka |
|------------|---------------------------|
| Team Blue | ruy |
|------------|---------------------------|
I tried the following setup using a IF-statement, but it just returns a string of FALSE:
=ARRAYFORMULA(TEXTJOIN(CHAR(10); 1;
REPT(IF('Sheet1'!C:C="1";'Sheet1'!A:A); 1*('Sheet1'!B:B=A2))))
Can this be one?
paste in F2 cell:
=UNIQUE(FILTER(B:B, C:C=1))
paste in G2 cell and drag down:
=TEXTJOIN(CHAR(10), 1, FILTER(A:A, B:B=F2, C:C=1))
or G2 cell be like:
=ARRAYFORMULA(TEXTJOIN(CHAR(10), 1,
REPT(FILTER(Sheet1!A:A, Sheet1!C:C=1), 1*(FILTER(Sheet1!B:B, Sheet1!C:C=1)=F2))))

MySQL - Top 5 rank best seller plans or courses

I sell subscriptions of my online course, as well as the courses in retail.
I would bring the "top 5" of best selling plans / courses. For this, I have a table called "subscriptionPlan", which stores the purchased plan ID, or in the case of a course, the course ID, and the amount spent on this transaction. Example:
table subscriptionPlan
sbpId | subId | plaId | couId | sbpAmount
1 | 1 | 1 | 1 | 499.99
2 | 2 | 1 | 2 | 499.99
3 | 3 | 2 | 0 | 899.99
4 | 4 | 1 | 1 | 499.99
Just for educational purposes, plaId = 1 is a plan called "Single Sale" that I created, to maintain the integrity of the DB. When the couId isn't empty, you also have bought a separate course, not a plan where you can attend any course.
My need is: List the top 5 sales. If it is a plan, display the plan name (plan table, column plaTitle). If it is a course, display its name (table course, colna couTitle). This logic that I can't code. I was able to rank a top 5 of PLANS, but it groups the courses, since the GROUP BY is by the ID of the plan. I believe the prank is here, maybe creating an IF / ELSE in this GROUPBY, but I don't know how to do this.
The query that i code, to rank my top 5 plans is:
SELECT sp.plaId, sp.couId, p.plaTitle, p.plaPermanent, c.couTitle, SUM(sbpAmount) AS sbpTotalAmount
FROM subscriptionPlan sp
LEFT JOIN plan p ON sp.plaId = p.plaId
LEFT JOIN course c ON sp.couId = c.couId
GROUP BY sp.plaId
ORDER BY sbpTotalAmount DESC
LIMIT 5
The result that i expected was:
plaId | couId | plaTitle | couTitle | plaPermanent | sbpTotalAmount
1 | 1 | Venda avulsa | Curso 01 | true | 999.98
2 | 0 | Acesso total | null | false | 899.99
3 | 2 | Venda avulsa | Curso 02 | true | 499.99
How could I get into this query formula?
When grouping you can use:
Simple columns, or
Any [complex] expression.
In your case, it seems you need to group by an expression, such as:
GROUP BY CASE WHEN sp.plaId = 1 THEN -1 ELSE sp.couId END
In this case I chose -1 as the grouping for the "Single Plan". You can replace the value for any other that doesn't match any couId.

Showing Complete Set of Data with Filter and parameter - Tableau 10

I have a set of data below.
| AdviserName | PolicyNumber | Product | Status | Duration |
|-------------|--------------|---------|--------|----------|
| Andy | LIF123 | ANZ | New | 2 |
| Andy | X224 | AXA | Lapsed | 3 |
| George | KL1122 | TAL | New | 0 |
| George | OLK43 | AXA | Lapsed | 5 |
| Ben | LIF98 | ANZ | New | 0 |
| Ben | KL343 | TAL | Lapsed | 1 |
I want to get the set of data where the status = 'Lapsed' duration between 1 to 3. However I still want to show correspond status = 'New' for each adviser. Below is the expected result that I want to see.
| AdviserName | PolicyNumber | Product | Status | Duration |
|-------------|--------------|---------|--------|----------|
| Andy | LIF123 | ANZ | New | 2 |
| Andy | X224 | AXA | Lapsed | 3 |
| Ben | LIF98 | ANZ | New | 0 |
| Ben | KL343 | TAL | Lapsed | 1 |
I created 2 parameters :
MinYear with the value set to 1
MaxYear with the value set to 3
I created 2 calculated fields :
MinInvYear which is iif([Duration]>=[Min Year] and [Status] = 'Lapsed', 1, 0)
MaxInvYear which is iif([Duration]<=[Max Year] and [Status] = 'Lapsed', 1, 0)
Last step, I put both calculated fields into the filters and tick the filter = 1 only. Unfortunately what I got is the table below. I need to show the status 'New' as well for Andy and Ben.
| AdviserName | PolicyNumber | Product | Status | Duration |
|-------------|--------------|---------|--------|----------|
| Andy | X224 | AXA | Lapsed | 3 |
| Ben | KL343 | TAL | Lapsed | 1 |
I tried to modify the calculated field using Level of Detail :
MinInvYear which is {include [Status] = 'New Business' : iif([Duration]>=[Min Year] and [Status] = 'Lapsed', 1, 0)} and I got error message 'Result of a level of detail expression must be aggregate.'
What am I missing?
FYI, I'm using Tableau 10.
Thanks all for your help.
Instead of the 2 calculated fields MinInvYear and MaxInvYear, try adding a calculated field (let's say Filter for Status and Duration) which returns a boolean value based on your condition
IF ([Status] == 'Lapsed' OR ([MinYear] < [Duration] AND [Duration] < [MaxYear])) THEN
TRUE
ELSE
FALSE
END
Now, add a filter for this calculated field to show only records which result in True.
try this, Bit lengthy but works for sure:
You are manipulating on only one aspect (Lapsed) of the field status, hence you are applying the parameter values on only that value. So here you need to independently work on that column to get the required output. Follow the below process:
Create 2 sets for each value in status column:
Set 1: Lapsed
Here create a set and just select checkbox `Lapsed`.
Set 2: New
Here create a set and just select checkbox `New`.
Create two calculated fields to extract the required data separately for Lapsed and New
Calculated Field 1: Lapsed_Status
IF [Duration] >= [Min] and [Duration] <= [Max]
THEN [Lapsed] //Set
END
Calculated Field 2: New Status
IF [New]=[New]
THEN [New] //Set
END
Create two more sets out of 2 calculated fields (created in step 2) to just select only those values that are needed for display on sheet:
Set 3: Display_Lapsed //From Calculated Field 1: New Status
Select checkbox True
Set 4: Display_New //From Calculated Field 2: Lapsed_Status
Select checkbox True
Now we have our required data in 2 sets, Create one more calculated field and use in filter
Calculated Field 'Display_sheet'
Display_Lapsed <> Display_New //Both are sets
Use the calculated field Display_sheet in filter and select true
Use the required fields on the sheet to display data.
Let me know how it goes
Below trick will get you moving
Duplicate the data source (right click on the data source in 'Data' panel -> 'Duplicate') having above data
Drag AdviserName from the original data source in 'Rows' view
Click on the duplicated data source in 'Data' panel. There you'll find 'link symbol' next to every dimension (in grey color). Click on the 'link symbol' next to AdviserName and it'll turn 'red' (if it turns grey don't worry. Click again and it'll turn red). Be sure that only one 'link symbol' is red (which is next to AdviserName).
Create a calculated field filter_cond in this duplicated data source as
IF [Status]='Lapsed' and [Duration]<=3 and [Duration]>=1
then 'Y'
ELSE 'N'
END
Drag filter_cond in 'Filters' view and select 'Y'
Now drag PolicyNumber, Product & Status from the original datasource in 'Rows' view. You are done!
Voila!

1 item, many configuration options

Example item
___________________________________________________
| ITEM | COLOR | SIZE | QTY | PRICE | SKU |
===================================================
| Shirt | Black | S | 35 | $8.00 | ShirtBS |
| Shirt | Black | M | 52 | $8.00 | ShirtBM |
| Shirt | Black | L | 19 | $8.00 | ShirtBL |
| Shirt | Black | XL | 27 | $8.00 | ShirtBXL |
| Shirt | Black | XXL | 16 | $10.00 | ShirtBXXL |
| Shirt | White | S | 17 | $8.00 | ShirtWS |
| Shirt | White | M | 20 | $8.00 | ShirtWM |
| Shirt | White | XXL | 9 | $10.00 | ShirtWXXL |
---------------------------------------------------
Currently I have my items set up as
Item 1 - $8 - Qty 16
Shirt (Black)
- select box (size) - options S|M|L|XL|XXL (+$2) { SKU starting ShirtB- }
Item 2 - $8 - Qty 9
Shirt (White)
- select box (size) - options S|M|XXL (+$2) { SKU starting ShirtW- }
Is there a configuration where my customer can view it as 1 item, where they select color and size options will change based on color they choose, and SKU will follow with proper selection?
Even better if quantities follow through, currently I am setting base quantity on whatever the lowest number is (so Shirt (White) lists 9 available and Shirt (Black) lists 16; using my example above)
Item - $8
- select box (color) - options Black|White
+ selected 'Black'
- select box (size) - options S|M|L|XL|XXL (+$2) {SKU starting ShirtB- }
+ selected 'White'
- select box (size) - options S|M|XXL (+$2) { SKU starting ShirtW- }
I am willing to accept paid add-on's through Magento store if necessary (and not prohibitive in cost), although I would hope that this functionality is built in and I am just not able to find it
My import is through MAGMI, so special bonus points if it can be easily accomplished this way
The behaviour you describe is typically handled by a standard "configurable product" in magento.
see this tutorial
Magmi is able to process configurable products through its "configurable" plugin.
Depending on the pricing model that suits you the most , you may also try "Simple Configurable Products" extension.

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")

Resources