SSRS Adjacent grouping in matrix report - reporting-services

I have a question related to SSRS matrix report. I have data captured in the following format in the table:
Category Name Month Cost
C1 N1 M1 10
C1 N1 M2 20
C1 N1 M3 30
C1 N2 M1 40
C1 N2 M2 50
C1 N2 M3 60
C2 N3 M1 70
C2 N3 M2 80
C2 N3 M3 90
So basically it captures the category, the product name and costs for various months.
What I want to have in my report is something like this:
Category Name M1 M2 M3
C1 N1 10 20 30
C1 N2 40 50 60
C1 N3 70 80 90
So this is similar to pivot on months with Category and Name as static columns. When I try to create two groups, Category and Name, in the matrix report, the Category becomes the parent group and the report groups all the products in common categories. For example I am getting something like this:
Category Name M1 M2 M3
C1 N1 10 20 30
N2 40 50 60
C1 N3 70 80 90
I don’t want above grouping. I want category c1 to be repeated for N1 and N2.
I am kind of novice in matrix report and maybe I missing some obvious group settings. Can someone please help me in this?
EDIT: I am using SSRS 2008 R2
Thanks & Regards
AK

When you create a matrix style Tablix in SSRS it will look like this be default:
The reason C1 is displayed like this is that by default group headers are not added to the Tablix body area and are only shown once per group.
Note that Category and Name are to the left of the dotted lines:
See Understanding the Tablix Data Region.
Delete the two left columns. When prompted, choose Delete columns only:
Now, add two columns like below:
The tablix now looks like this:
Note there is no longer dotted lines between Name and Month, i.e. everything is in the Tablix body region.
Now the end result looks like your requirement:

Related

Join table A into table B multiple times

I have 2 tables in a mysql db (simplified example data):
Table 1: 3 names (unique)
Table 2: names (only from table 1), 3 dates, info
For each date there are sometimes all 3 names in table 2, sometimes less then 3.
Example for table 2:
d1 n1 info
d1 n2 info
d1 n3 info
d2 n1 info
d2 n3 info
d3 n3 info
Date 1 has got all 3 names, date 2 has got 2 names, date 3 hast got 1 name.
Goal: I need each "date" to have all 3 names. I can already filter 1 date from table 2 and join the tables successfully to have the desired outcome for one date, but how can I "add" all names to each date?
d1 n1 info
d1 n2 info
d1 n3 info
d2 n1 info
d2 n2 (added by the join, "info" is empty)
d2 n3 info
d3 n1 (added by the join, "info" is empty)
d3 n2 (added by the join, "info" is empty)
d3 n3 info
My real data has got much more names and dates, which makes individual joins impractical. It feels like there should be an easy solution to this, but I could not find any.
I also could do this with code ("for each date add missing names") , but I wonder if it can be done with sql
Working on MySQL 10.1.37
Use CROSS JOIN to create all possible combinations of names and dates, followed by a LEFT JOIN:
SELECT name_table.name, datelist.date, info_table.info
FROM name_table
CROSS JOIN (SELECT DISTINCT date FROM info_table) AS datelist
LEFT JOIN info_table ON name_table.name = info_table.name AND datelist.date = info_table.date

How to add up unique values from table in SQL

I have a table in SQL that looks like this
Person brand brand_spend category category_spend
0 p1 b1 20 c1 100
1 p1 b2 50 c1 100
2 p2 b1 25 c2 40
3 p1 b3 30 c1 100
4 p1 b2 15 c2 70
I need to tag every customer based on percentage spend he has made at a brand based on total category spend where that brand is present.
So essentially I would want to tag Person p1 for brand b1' as percentage spend at 'b1 which should be calculated as 45/ 140
How this can be achieved. If I roll up on brand level to find total category spend then I think duplicates rows would add up.
I just want to find customer's Spend at a brand based on total spend at Brand for all categories where that brand is present.
You need grouping on two levels, as shown here:
select person, brand, sum(brand_spend) personspend, spendbrandallcats,
round(sum(brand_spend)/spendbrandallcats,3) pbratio from tbl t
inner join ( -- brand statistics: sum of all spends per brand in all categories
select brand br, sum(casp) spendbrandallcats from tbl
inner join ( -- category statistics: total category sums
select category ca, sum(brand_spend) casp from tbl group by category
) catspend ON ca=category
group by brand
) brandstats on br=brand
group by person,brand
These are the results:
person brand personspend spendbrandallcats pbratio
p1 b1 20 140 0.143
p1 b2 65 140 0.464
p1 b3 30 100 0.3
p2 b1 25 140 0.179
See the little demo here: https://rextester.com/SVNH27609
Sometimes things are not as complicated as it seems and a simple query does the trick.
select person , brand , sum(prsn_brand_spend) ,
sum(category_spend) , (sum(prsn_brand_spend)/sum(category_spend)) as perc_spend
from tbl group by person , brand
The Results are
person brand brand_spend category_spend perc
p1 b1 20 100 0.200000
p1 b2 65 170 0.382353
p1 b3 30 100 0.300000
p2 b1 25 40 0.625000

Obtaining groups and counts at the same time

Suppose my table looks like this:
Name City Salary
n1 c1 10
n1 c1 20
n1 c1 30
n2 c2 20
n2 c2 50
n3 c2 70
And I am interested in the following output:
Name Count
n1 3
n2 2
n3 1
I am fairly new to SQL and have been doing similar commands in SAS (e.g. in this case I would have used PROC FREQ) - but I am now required to write the same code only in the SQL language (I am using Aginity Netezza).
If its MySQL, or any fairly standard SQL:
SELECT Name, Count(*)
FROM table
GROUP BY Name

Sql, print only rows with a value that appear at least n times

I have two tables created in sql.
players(code(PK),surname,name)
and
errors(playererrorcode(PK),date,money,code(FK to players))
So two tables looks like:
Players:
A1 Surname1 Name1
A2 Surname2 Name2
And errors for example:
E1 2015/7/10 10$ A1
E2 2015/2/20 50$ A1
E3 2015/1/30 40$ A2
E4 2015/5/20 30$ A1
Well, now, i have to print(select in this language) ONLY players that have at least 3 errors! In this case i want to print only the row that include: surname and name of who have at least 3 errors, then it have to print only surname1 and name1.
How to do that?
In SQL Server you can use the Count aggregation to get the number of errors per player and use Having to specify the number of error required -- like this:
SELECT
p.surname,
p.name,
FROM players as p
INNER JOIN errors as e
on p.code=e.code
GROUP BY
p.surname,
p.name
HAVING
COUNT(e.playererrorcode)>=3

Get N number of records from child table for each parent record in a MySQL View

I tried finding answer to this question in SO , but could not find any. Any links will be of great help.
I have a parent table and a child table with one to many relationship between the parent and child table. The child table contains around 1 million records and I want to create a view with 1st 10 records in child table for each parent record.
Example-
Parent_Table - Fields -- ID, Name
ID Name
---- -----
1 A
2 B
3 C
Child_Table - Fields -- ID, ParentID, Date, Data
ID ParentID Date Data
--------------------------
1 1 04/10 A1
2 1 04/11 A2
3 1 04/11 A3
4 1 04/12 A4
5 1 04/12 A5
6 2 04/10 B1
7 2 04/11 B2
8 2 04/12 B3
9 2 04/12 B4
10 2 04/13 B5
11 2 04/13 B6
Now, I want to create a view with 1st 4 records for each parent record sorted by date.
Output Expected
ID ParentID Date Data
--------------------------
1 1 04/10 A1
2 1 04/11 A2
3 1 04/11 A3
4 1 04/12 A4
6 2 04/10 B1
7 2 04/11 B2
8 2 04/12 B3
9 2 04/12 B4
Any links or guide to the solution will be appreciated. Thanks in advance!
In case you need any clarification, please post a comment.
If you need to create a VIEW, you could use something like this:
CREATE VIEW First_Four AS
SELECT c1.*
FROM
Child_Table c1 LEFT JOIN Child_Table c2
ON c1.ParentID = c2.ParentID
AND (STR_TO_DATE(c1.`date`, '%m/%Y')>STR_TO_DATE(c2.`date`, '%m/%Y')
OR (STR_TO_DATE(c1.`date`, '%m/%Y')=STR_TO_DATE(c2.`date`, '%m/%Y')
AND c1.ID>c2.ID)
)
GROUP BY
c1.ID, c1.ParentID, c1.`Date`, c1.Data
HAVING
COUNT(c2.ID)<4
I'm considering the field data as a VARCHAR column, so we need to use STR_TO_DATE, if it is not we can just compare c1.date with c2.date directly.
Please see fiddle here.
I tried this one my computer and it displayed based on your requirements using your own data. I changed some field name though like ID of Child to ChildID, Date to ChildDate, Data to ChildData. Here it is:
SELECT * FROM
(SELECT ParentID, ChildID, ChildDate, ChildData, #ChildRank:= CASE WHEN #Parent <> ParentID THEN 1 ELSE #ChildRank+1 END as ChildRanking, #Parent := ParentID as Parent FROM
(SELECT #ChildRank:=0) CR, (SELECT #Parent:=1) P, (SELECT * FROM Child_Table ORDER BY
ParentID, ChildID) MainTable) AllTable WHERE ChildRanking <=4;
I use only the Child Table only but anyway you could INNER JOIN this with Parent_Table if you like.
A little explanation:
1) ChildRank will starts with Rank 0 (i.e. SELECT #ChildRank:0) but because of #ChildRank+1 it will start with Rank 1
2) When new ParentID (i.e. #Parent<> ParentID) then starts with Rank 1 right away.
3) AllTable is the alias for everything so that you could now reference the ChildRanking field.
If you don't want to display the ChildRanking field then you have to specify the fields you want to dispaly.