SSRS List formatting - reporting-services

Need to make a list in SSRS that has a numbered range from 0-30 in the rows and allows for null values to be entered in as a dash. i.e if I have 8 players who scored 10 points the list would show an 8 in the row value next to the row value 10 but for the other 29 numbers it would show a dash(-)?

You may have to make a couple of adjustments, but this should get you most of the way there. There wasn't a lot of information to go by to determine exactly what you want.
Adjust your dataset query in SSRS to the following, replacing the subquery Z with your current query that provides the points and player count. I inserted dummy data in there for now so I would have data for this example (8 = 1, 13 = 1, 17 = 2).
With X as
(
select top (30) n = ROW_NUMBER() OVER (ORDER BY m1.number)
from master.dbo.spt_values as m1
CROSS JOIN master.dbo.spt_values as m2
)
,Y as
(
Select Points, PlayerCount, ROW_NUMBER() OVER (Order by PlayerCount) RowNum
from (
--replace this with your query to return the data
--with your 2 columns for points and player count
Select 8 as Points, 1 as PlayerCount
UNION
Select 17 as Points, 2 as PlayerCount
UNION
Select 13 as Points, 1 as PlayerCount) Z
)
Select x.n as Points, /*isnull(Y.Points,0),*/ Isnull(Y.PlayerCount,0) PlayerCount
from X
left join Y on X.n = Y.Points;
The CTE labeled X is what creates the 30 spots. If you want it to be 31 spots (0 - 30 inclusive), change the query in X to be
select top (31) n = ROW_NUMBER() OVER (ORDER BY m1.number)-1
from master.dbo.spt_values as m1
CROSS JOIN master.dbo.spt_values as m2
You end up with a data set with 2 columns: Points and PlayerCount.
Now create your list in SSRS.
Insert a list. In that list, insert 2 columns to the right. Then delete the left most (original) column.
Set the expression for the left textbox in the list to the points field.
Set the expression for the right textbox in the list to the PlayerCount field.
Add a row outside the group above. Type in column headers for each column. I used Points and Player Count.
In the Group Properties for the Details row group, go to Sorting and set the Sort By column to points. The order should be A to Z.
Adjust the height of the rows to whatever looks best to you. I used .25 for the header and detail rows.
In the Tablix properties, check the box next to Keep together on one page if possible.
On the text box containing the Player Count field (bottom right), go to text box properties, Number category. Set it to 0 decimal places. Check the box next to Show zero as:. Make sure - is selected.
That gives you a list that looks like this:
If for some reason you want to see only the numbers that have > 0 players with that amount of points at the top and then the rest, you can do this with a calculated column.
Right click on your data set and add a calculated field. Set the Name to PointsCountSort. The the expression to =iif(Fields!PlayerCount.Value = 0, 2, 1)
. Click OK.
In the Sort Order of the Details group. Change the sort order to go by PointsCountSort A to Z then Points A to Z.
That makes the list sorted like this:

Related

How to combine rows and take highs and lows from those rows

I am trying to generate a 5M OHLC chart (Open, High, Low, Close). I'm currently reading in data by the minute, but I want to make a 5 minute data chart as well which I can do pretty easy by simply doing
SELECT * FROM intraday_data.intraday WHERE intraday.id mod 5 = 0;
However, this doesn't accurately represent the data because for the OHLC chart to be accurate it would need to also have the open from the very first row and close from the very last row, and it would have to have the highest high from all 5 and the lower low from all 5 if that makes sense, I would also like to implement where it is able to add up all of the volumes.
Here is the current schema:
As you can see the open in highlighted would need to be pulled out in the final row, the highlighted value in the 5th row which is the highest high as well as the remaining rows and the total volume, so essentially after running the function the row presented should be:
id 5: open 4402.75: high 4403: low 4402.5: volume : 12+24+37+32+29
Obviously, I would need to iterate over all of the rows and return every 5th row with the data combined from the last 5 rows,
Current Updated Query:
select open_close_t.cross_id,open_close_t.open_val,open_close_t.close_val,high_low_t.high,high_low_t.low,high_low_t.total_volume from (
select open_close.max_id as cross_id,open_t.open as open_val,close_t.close as close_val from
(select max(id) as max_id,min(id) as min_id from intraday group by FLOOR(id/5)) as open_close
inner join intraday as open_t on (open_t.id=open_close.min_id)
inner join intraday as close_t on (close_t.id=open_close.min_id)
) as open_close_t
left join (
select max(id) as cross_id,max(high_val) as high,min(low_val) as low,sum(volume) as total_volume
from (select id,GREATEST(open,high,low,close) as high_val,GREATEST(open,high,low,close) as low_val,volume from intraday_data.intraday) as _t
group by FLOOR(id/5)
) as high_low_t on (open_close_t.cross_id=high_low_t.cross_id)
Current Updated Results:
we can seperate this question with two main query.
get the the open and close value
get max and min id of every 5th row
use that max_id and min id to get close and open value
get highest and lowest value
first we need get max and min cross open,high,low,close per row
group by 5th rows the previous generated and get highest and lowest values
join two previous generated table by max_id(cross_id in the sql) of each 5th rows
select open_close_t.cross_id,open_close_t.open_val,open_close_t.close_val,high_low_t.high,high_low_t.low,high_low_t.total_volume from (
select open_close.max_id as cross_id,open_t.open as open_val,close_t.close as close_val from
(select max(id) as max_id,min(id) as min_id from intraday group by CEILING(id/5)) as open_close
inner join intraday as open_t on (open_t.id=open_close.min_id)
inner join intraday as close_t on (close_t.id=open_close.max_id)
) as open_close_t
left join (
select max(id) as cross_id,max(high_val) as high,min(low_val) as low,sum(volume) as total_volume
from (select id,greatest(open,high,low,close) as high_val,least(open,high,low,close) as low_val,volume from intraday) as _t
group by CEILING(id/5)
) as high_low_t on (open_close_t.cross_id=high_low_t.cross_id)
fix every 4 instead every 5 bug,because I should use ceiling not floor
the open_close temp table join problem from close_t.id=open_close.min_id to close_t.id=open_close.max_id
the lowest value using least not greatest
I made a db-fiddle example,if has further problem we can test on db-fiddle

How can I make empty groups of a bar chart visible in SSRS?

I have a bar chart that displays # of Work Orders on the X axis and groups them into 3 categories on the Y axis (Overdue, 0 to 7 days, 8 to 28 days). The report is setup for the user to select a parameter (in this case an NYC boro) in order to run the report. In some cases, the borough selected does not return values for all 3 of the groups on the Y axis. How do I force SSRS to display all of the categories on the Y axis even when those groups are empty. In the example image included, the "0 to 7 Days" category is not showing up.
You need to create a table with a list of your categories in and then left join your current query to that. Without seeing your current query I can;t give the best answer as you may already have all the categories in a table that you could already use but this will still work...
DECLARE #cat TABLE(CategoryName varchar(20))
INSERT INTO #cat VALUES
('Overdue'),
('8 to 28 days'),
('0 to 7 days')
SELECT
c.CategoryName, q.*
FROM #cat c
LEFT JOIN (SELECT * FROM YourOriginalQuery) q
ON c.CategoryName = q.myOriginalCategoryName
As you are now left joining from a list of categories, each category name will be present in your dataset (unless of course your WHERE clause filters it out).
If you edit your question showing your current query, there may be a much better way to achieve the same result.

SSRS - grouping with ceiling

I'm creating a SSRS-report where I need to group my values and do another grouping based on the grouped values. Then I also want to limit the records on each row.
Now my table look like this (but with maybe 50 values):
A
A
A
B
C
C
D
E
E
F
(ignore the bullets, it was the only way to get the values vertical)
I want my table to fit in one page and become horizontal and be grouped.
The result I'm after look like this:
A, B, C,
D, E, F
I writing this in MDX because I need to have data direct from the cube.
I would have a great solution if i didn't have to group the values together.
It's was to use the ceiling-function (ceiling(rownumber(nothing) mod 6)) in ColumnGroup and (=ceiling(rownumber(nothing) / 6)) in RowGroup.
Has someone a solution, maybe a nested expression to both group the values and then do the ceiling trick?
Perhaps you can add a calculated field to the dataset, GroupID, with a value based on the position in the alphabet and your paging requirement.
For example :
Letter GroupID
A-F 1
G-L 2
M-Q 3
Next you can group similar to below.
Column Group 1 Expression = GroupID
Column Group 2 Expression = Letter
You may wish to place a page break after group for Group1 to force repeats on a new page.

why top 5 with ties not giving complete record sets

My query result should give top 5 with ties records but the query mentioned below is giving 130,130,120,120,120,120,120,120,120,120
but I want the result as
130,130,120,120,120,120,120,120,120,120,110,100
select top 5 with ties b.quantity
from dbo.Products as a
inner join dbo.[Order Details] as b
on a.productid = b.productid
inner join dbo.Suppliers as c
on c.supplierid = a.supplierid
order by quantity desc
I am not able to understand why the above query is not including 110 and 100 numbers even after including Top 5 with ties.
According to MSDN about TOP WITH TIES,
WITH TIES may cause more rows to be returned than the value specified
in expression. For example, if expression is set to 5 but 2 additional
rows match the values of the ORDER BY columns in row 5, the result set
will contain 7 rows.
In your example ORDER BY quantity descgives the results as,
130
130
120
120
120
etc
You have specified TOP 5. Which means that the tied values in fifth row will be retrieved by the SELECT query. Here 5th row is 120, so all the 120 will be retrieved.
SQL Fiddle
Ordered list is cut at the 5th row. But all rows with the same "rank" come in too, otherwise it would be rather random, which of them is included and which is not.

Hive Query that returns distinct value that each User has

I have a mysql table-
User Value
A 1
A 12
A 3
B 4
B 3
B 1
C 1
C 1
C 8
D 34
D 1
E 1
F 1
G 56
G 1
H 1
H 3
C 3
F 3
E 3
G 3
I need to run a query which returns 2nd distinct value that each user has.
Means if any 2 values are accessed by each user , then based on the occurrence, pick the 2nd distinct value.
So as above 1 & 3 is being accessed by each User. Occurrence of 1 is
more than 3 , so 2nd distinct will be 3
So I thought first I will get all distinct user.
create table temp AS Select distinct user from table;
Then I will have an outer query-
Select value from table where value in (...)
In programmatically way , I can iterate through each of the value user contains like Map but in Hive query I just couldn't write that.
This will return the second most frequented value from your list that spans all users. There isn't one of these values in the table which I expect is a typo in the data. In real data you will likely have muliple ties that you need to figure out how to handle.
Select value as second_distinct from
(select value, rank() over (order by occurrences desc) as rank
from
(SELECT value, unique_users, max(count_users) as count_users, count(value) as occurrences
from
(select value, size(collect_set(user) over (partition by value))
as count_users from my_table
) t
left outer join
(select count(distinct user) as unique_users from my_table
) t2 on (1=1)
where unique_users=count_users
group by value, unique_users
) t3
) t4
where rank = 2;
This works. It returns NULL because there is only value that visited every user (value of 1). Value 3 is not a solution because not every user has seen that value in your data. I expect you intended that three should be returned but again it doesn't span all the users (user D did not see value 3).
Not sure how #invoketheshell's answer was marked correct; it doesn't run and it needs 6 MR jobs. This will get you there in 4 and is less code.
Query:
select value
from (
select value, value_count, rank() over (order by value_count desc) rank
from (
select value, count(value) value_count
from (
select value, num_users, max(num_users) over () max_users
from (
select value
, size(collect_set(user) over (partition by value)) num_users
from db.table ) x ) y
where num_users = max_users
group by value ) z ) f
where rank = 2
Output:
3
EDIT: Let me clarify my solution as there seems to be some confusion. The OP's example says
"So as above 1 & 3 is being accessed by each User ... "
As my comment below the question suggests, in the example given, user D never accesses value 3. I made the assumption that this was a typo and added this to the dataset and then added another 1 as well to make there be more 1's than 3's. So my code correctly returns 3, which was the desired output. If you run this script on the actual dataset it will also produce the correct output which is nothing because there isn't a "2nd Distinct". The only time it could produce an incorrect value, is if there was no one specific number that was accessed by all users, which illustrates the point I was trying to make to #invoketheshell: if there is no single number that every user has accessed, running a query with 6 map-reduce jobs is an absurd way to find that out. Since we are using Hive I believe it would be fair to assume that if this problem were a "real-world" problem, it would most likely be executed on at least 100's of TBs of data (probably more). I the interest of preserving time and resources, it would behoove an individual to at least check that one number had been accessed by all users before running a massive query whose analysis hinges on that assumption being true.