Filter Columns based the condition of a Row - sql-server

I have the following Matrix in SSRS 2008 R2:
What I would like to do is only show columns where the "FTE" row has a value. (Marked with red circles). I've tried the filter and show/hide options based on expressions in the Column Properties, but I can't figure out how to only reference the rows when "category"="FTE". My data looks like this:
tblPhysicians
employee_id last_name ...
102341145 Smith
123252252 Jones
tblPhysiciansMetrics
id fy period division_name category_name employee_id
123 2014 1 Allergy Overhead 123456
124 2014 1 Allergy Salary 125223
125 2014 1 Allergy FTE 1.0
query
SELECT * FROM
tblPhysicians
INNER JOIN tblPhysicianMetrics
ON tblPhysicians.employee_id = tblPhysicianMetrics.employee_id
WHERE
tblPhysicianMetrics.division_name = #division_name
AND tblPhysicianMetrics.fy = #fy
AND tblPhysicianMetrics.period = #period
Notice that the rows in my Matrix is just the category_name, so I can't just hide when category_name = "FTE", that's not really what I want. What I really need is some way of saying, "For rows where category_name = "FTE", if the value is not set, don't show that column". Is this possible?
An alternative would be to not even get those in the query, but as with the filtering of the matrix, if I simply add "AND tblPhysiciansMetrix.category_name = 'FTE'" to the WHERE clause, my entire data set is reduced to only those records where category_name is FTE.
Any help is much appreciated!
Update: added definition of Matrix to help:

You need to set the column visibility with an expression that checks all underlying data in the column for the FTE category.
I have a simple dataset:
And a simple matrix based on this:
Which looks exactly as you expect:
So in the above example, we want the Brown column to be hidden. To do this, use an expression like this for the Column Visibility:
=IIf(Sum(IIf(Fields!category_name.Value = "FTE", 1, 0), "last_name") > 0
, False
, True)
This is effectively counting all the fields in the column (determined by the last_name scope parameter in the Sum expression) - if this is > 0 show the column. Works as required:

CAVEAT I'm not 100% sure how you are grouping this data in the matrix. It isn't 100% clear from your description. Let me know why this doesn't work (if it doesn't and I'll update my answer accordingly.
I've replaced the column employee_id with the name value for my answer, to keep my explanation simple.
Add this nested IIF() to the visibility property of your Matrix's column.
=IIF(Fields!category_Name.Value="FTE" ,IIF( Fields!value.Value >= 0, True,False),false)
It will check both the value of the the category and the 'FTE' in that row's cell.

Related

Report Builder Multiple Group Matrix Calculation

I have a Matrix created using Matrix Wizard in Report Builder 3.0(2014), having 2 row groups ,1 column groups and 2 values. After I create the matrix (included total and subtotal), I have a matrix that look just nice. But now I want to add one more cell for each columns groups (one row), to store the below value.
Value = Total of 1st row group + Total of 2nd row group - Total of 3rd row group ...
The matrix built just show me the subtotal of each row group which I don't need.
I want to ask how do I retrieve the result of total calculated by matrix itself and how do I identify them based on their row group value using expression? And also, how do I do this for every column groups which have different data?
I tried to look at the expression in design view of the matrix, it just shows [SUM(MyField)] for every cell in the Matrix (total & subtotal).
Or should I do it at another dataset using another query? If so, what query should I use and how do I put two dataset into one matrix?
My Matrix looks something like this :
Column Group
ROW GROUP 1 | ROW GROUP 2 | VALUE 1 | VALUE 2
Row Group 1 | Row Group 2 | [Sum(MyField)] | [Sum(MyField)]
| TOTAL OF ROW GROUP 1 | [Value] | [Value]
| ROW PLAN TO ADD | [Value(0)+Value | [Value(0)+Value
| (1)-Value(2)] | (1)-Value(2)]
CAPITALS : Column name, constant
[sqrbrkted] : Calculated Value
Normal : data inside table
I am new to Report Builder, sorry if I made any mistake. In case I didn't make myself clear, please do comment and let me know. Thank you in advance.
EDIT: I have figured out an approach to achieve my purpose at the answer section below. If anyone have other solution, please feel free to answer it. Thanks.
I solved this problem by changing my SQL query to make another dummy column which shows negative result if its respective row group is meant to deduct the subtotal (Value(2)), and place it inside the matrix. And inside the expression, I have another IIF statement to convert it back to positive for display purpose.
SQL:
SELECT *, (CASE WHEN COL_B = 'VAL_2' THEN -COL_A ELSE COL_A END)AS DMY_COL_A FROM TABLE
Expression:
=IIF(Sum(Fields!DMY_COL_A.Value)<0,-Sum(Fields!DMY_COL_A.Value),Sum(Fields!DMY_COL_A.Value))

How to find the most frequently occurring value in SSRS tablix dataset?

Consider following dataset that is displayed in tablix in SSRS report:
GroupID | ProductID
---------------------
Group 1 | Product1
Group 2 | Product10
Group 1 | Product2
Group 3 | Product27
Group 2 | Product12
Group 2 | Product14
I added new row via Insert Row/Outside Group - Below.
On this row I display total number of rows - achieved via CountRows(), number of distinct Groups - achieved via =CountDistinct(Fields!GroupID.Value)
I also want to display the name of the group with the most number of rows, in this case it would be "Group 2" (in case if there is more than one group with the same number of rows I only need to display one of them).
How can this be achieved? I think I should use some of aggregate or lookup functions but so far can't figure out how.
PS This report is being ported from Crystal Reports to SSRS. In Crystal Reports this is easily achieved via "Nth most frequent" summary with N=1 but there is nothing like this in SSRS as far as I can tell.
Add a tablix and set GroupId as Row Group.
For Rows Count use:
=Count(Fields!GroupID.Value)
Right click GroupID group in the Row Groups pane and go to group properties, in the Filters tab use the following settings:
For Expression use:
=Count(Fields!GroupID.Value)
It will filter the top 1 group with the greatest Rows count. The result is something like this:
UPDATE: The previous solution doesn't work if there is more than one group with the same number of occurencies.
Add a tablix, delete the details (default group) and add the GroupID field in the first column.
For the Rows Count column use the following expression replacing DataSetName by the actual name of your dataset:
=LookupSet(
Fields!GroupID.Value,
Fields!GroupID.Value,
Fields!GroupID.Value,
"DataSetName"
).Length
Right click your tablix and go to tablix properties, in the Sorting tab select Z to A order and use the previous expression in the Sort By textbox.
It should show the only one group even if a second group with same number of occurencies is present.
Let me know if this helps.

SQL Shift Table Column Down 1

I have a table of +15 million rows and 36 columns, there are two rows of data for every object to which the table refers. I need to:
Move one Column 0 down one space so that the useful information from that column appears in the row below.
Here is a sample of the data with less columns:
Table name = ekd0310
I want to shift Column 0 down 1
Column 0 Column 1 Column 2 Column 3
B02100AA.CZE
B02100AA.CZF I MIGA0027 SUBDIREC.019
B02100AA.CZG
B02100AA.CZH I MIGA0027 SUBDIREC.019
B02100AA.CZI
B02100AA.CZJ I MIGA0027 SUBDIREC.019
B02100AA.CZK '
THe function that you are looking for is probably lead(). You can use this if you assume that there is a column that specifies the ordering. An example:
select e.*, lead(col) over (order by id) as nextcol
from ekd0310 e;
Although this is an ANSI standard function, not all databases support it (yet). You can do something similar with correlated subqueries. Similarly, the above returns the information, but it is possible to do this as an update as well.

Column and Row grouping in SQL Server Reporting Services 2008

This is the desired result I need to populate as a report, where xx is number of people.
I have a table which has fields like:
----------
table1
----------
id
state
year(as Quarter)
gender
I need to determine the count from id and populate as a report. The year is like 20081, 20082..20084 (in quarter).
I have created a dataset using this query:
SELECT STATE,GENDER,YEAR,COUNT(*)
FROM TABLE 1
GROUP BY STATE,GENDER,YEAR
From this query I could populate the result
ex: ca, m , 20081,3
ny, f , 20091,4
From the above query I could populate the count and using group by(row) state(in ssrs).
I need to group by (column). From the gender I get and by year.
How do I take the column gender and make it has Male and Female column?
Do I need to create multiple dataset like passing
where gender = 'M' or gender = 'F'
so that I could have two datasets, one for Male and One for Female? Otherwise, is there any way I could group from the Gender field just like pivot?
Should I populate result separately like creating multiple dataset for Male 2008, Female 2009 or is there any way I could group by with the single dataset using SSRS Matrix table and column grouping?
Should I resolve it at my Query level or is there any Features in SSRS which could solve this problem?
Any help would be appreciated.
Your SQL query looks good, but I would remove the quarter with a left statement:
select state, gender, left(year,4) as [Year], count(ID) as N
from table1
group by state, gender, left([year],4)
Then you have a classic case for a Matrix. Create a new report with the Report Wizard, choose "Matrix", then drag the fields across:
Rows: State
Columns: Year, Gender
Details: N
This should give you the required Matrix. Then replace the expression of the textbox with the Gender from
=Fields!gender.Value
to
=IIF(Fields!gender.Value="M", "Male", "Female")
Good luck.

MS Access row number, specify an index

Is there a way in MS access to return a dataset between a specific index?
So lets say my dataset is:
rank | first_name | age
1 Max 23
2 Bob 40
3 Sid 25
4 Billy 18
5 Sally 19
But I only want to return those records between 'rank' 2 and 4, so my results set is Bob, Sid and Billy? However, Rank is not part of the table, and this should be generated when the query is run. Why don't I use an autogenerated number, because if a record is deleted, this will be inconsistent, and what if I wanted the results in reverse!
This obviously very simple, and the reason I ask is because I am working on a product catalogue and I am looking for a more efficient way of paging through the returned dataset, so if I only return 1 page worth of data from the database this is obviously going to be quicker then return a complete set of 3000 records and then having to subselect from that set!
Thanks R.
Original suggestion:
SELECT * from table where rank BETWEEN 2 and 4;
Modified after comment, that rank is not existing in structure:
Select top 100 * from table;
And if you want to choose subsequent results, you can choose the ID of the last record from the first query, say it was ID 101, and use a WHERE clause to get the next 100;
Select top 100 * from table where ID > 100;
But these won't give you what you're looking for either, I bet.
How are you calculating rank? I assume you are basing it on some data in another dataset somewhere. If so, create a function, do a table join, or do something that can calculate rank based on values in other table(s), then you can do queries based on the rank() function.
For example:
select *
from table
where rank() between 2 and 4
If you are not calculating rank based on some data somewhere, there really isn't a way to write this query, and you might as well be returning three random rows from the table.
I think you need to use a correlated subquery to calculate the rank on the fly e.g. I'm guessing the rank is based on name:
SELECT T1.first_name, T1.age,
(
SELECT COUNT(*) + 1
FROM MyTable AS T2
WHERE T1.first_name > T2.first_name
) AS rank
FROM MyTable AS T1;
The bad news is the Access data engine is poorly optimized for this kind of query; in my experience, performace will start to noticeably degrade beyond a few hundred rows.
If it is not possible to maintain the rank on the db side of the house (e.g. high insertion environment) consider doing the paging on the client side. For example, an ADO classic recordset object has properties to support paging (PageCount, PageSize, AbsolutePage, etc), something for which DAO recordsets (being of an older vintage) have no support.
As always, you'll have to perform your own timings but I suspect that when there are, say, 10K rows you will find it faster to take on the overhead of fetching all the rows to an ADO recordset then finding the page (then perhaps fabricate smaller ADO recordset consisting of just that page's worth of rows) than it is to perform a correlated subquery to only fetch the number of rows for the page.
Unfortunately the LIMIT keyword isn't available in MS Access -- that's what is used in MySQL for a multi-page presentation. If you can write an order key into the results table, then you can use it something like this:
SELECT TOP 25 MyOrder, Etc FROM Table1 WHERE MyOrder in
(SELECT TOP 55 MyOrder FROM Table1 ORDER BY MyOrder DESC)
ORDER BY MyOrder ASCENDING
If I understand you correctly, there is ionly first_name and age columns in your table. If this is the case, then there is no way to return Bob, Sid, and Billy with a single query. Unless you do something like
SELECT * FROM Table
WHERE FirstName = 'Bob'
OR FirstName = 'Sid'
OR FirstName = 'Billy'
But I think that this is not what you are looking for.
This is because SQL databases make no guarantee as to the order that the data will come out of the database unless you specify an ORDER BY clause. It will usually come out in the same order it was added, but there are no guarantees, and once you get a lot of rows in your table, there's a reasonably high probability that they won't come out in the order you put them in.
As a side note, you should probably add a "rank" column (this column is usually called id) to your table, and make it an auto incrementing integer (see Access documentation), so that you can do the query mentioned by Sev. It's also important to have a primary key so that you can be certain which rows are being updated when you are running an update query, or which rows are being deleted when you run a delete query. For example, if you had 2 people named Max, and they were both 23, how you delete 1 row without deleting the other. If you had another auto incrementing unique column in there, you could specify the unique ID in your query to delete only one.
[ADDITION]
Upon reading your comment, If you add an autoincrement field, and want to read 3 rows, and you know the ID of the first row you want to read, then you can use "TOP" to read 3 rows.
Assuming your data looks like this
ID | first_name | age
1 Max 23
2 Bob 40
6 Sid 25
8 Billy 18
15 Sally 19
You can wuery Bob, Sid and Billy with the following QUERY.
SELECT TOP 3 FirstName, Age
From Table
WHERE ID >= 2
ORDER BY ID

Resources