Distinct SUM in recursive tablix - sql

I need to report the sum of sales by user and include recursive totals. The problem is, each account has 2 account managers, and the account managers can come from different teams. At each parent level, I want return the distinct sum of each relevant account at the group level.
For example:
Name LEVEL AccountNum Sales
James 1 A1 1000
A2 2000
A3 5000
Mike 2 A1 1000
A2 2000
Sally 3 A1 1000
John 3 A1 1000
Mary 3 A2 2000
Matt 2 A2 2000
A3 5000
Andy 3 A2 2000
A3 5000
Bob 3 A3 5000
I want to return the totals only and not show the account details:
Name Sales
James 8000
Mike 3000
Sally 1000
John 1000
Mary 2000
Matt 7000
Andy 7000
Bob 5000
I'm a relative newbie to SSRS, so would massively appreciate any advice on how to achieve the above group totals! Thanks!
I tried using recursive queries in SQL, but the totals only work up to Level 2, because of the cross-team accounts. Using partition by ranking also only works up to Level 2. (e.g.
{row_number() over (partition by AccountNum, TeamLeaderKey ORDER BY AccountNum ASC))}) in order to exclude any with rank > 1. This falls down at level 1 when there are account with account managers from different teams at level 2.

In case anyone else ever comes across this problem, this is how I solved it. I introduced two new fields in my dataset:
IntraTeamRank:
Case When ROW_NUMBER() OVER (PARTITION BY AccountNum, TeamLeaderKey ORDER BY AccountNum ASC) > 1 Then 0 Else 1 End
InterTeamRank:
Case When ROW_NUMBER() OVER (PARTITION BY AccountNum ORDER BY AccountNum ASC) > 1 Then 0 Else 1 End
I have a parameter set up in SSRS to return the max level.
I then used the following expression in SSRS:
IIf( Level() < (Parameters!MaxLevel.Value - 1),
SUM((Fields!InterTeamRank.Value * Fields!Amount.Value), "StaffName", Recursive),
IIf( Level() = (Parameters!MaxLevel.Value - 1),
SUM((Fields!IntraTeamRank.Value * Fields!Amount.Value), "StaffName", Recursive),
SUM(Fields!Amount.Value), "StaffName")
)
)
Maybe not the most elegant solution, but it worked for my purposes. It would break if an account were shared between regions, as opposed to just teams, but it is unlikely in my scenario. I'd still be interested to know if anyone has a more robust solution.

Related

Row number in query result

I have query to get firms by theirs sales last year.
select
Name,
Sale
from Sales
order by
Sale DESC
and I get
Firm 2 | 200 000
Firm 1 | 190 000
Firm 3 | 100 000
And I would like to get index of row in result. For Firm 2 I would like to get 0 (or 1), for Firm 3 1 (or 2) and etc. Is this possible? Or at least create some sort of autoincrement column. I can use even stored procedure if it is needed.
Firebird 3.0 supports row_number() which is the better way to do this.
However for Firebird 2.5, you can get what you want with a correlated subquery:
select s.Name, s.Sale,
(select count(*) from Sales s2 where s2.sale >= s.sale) as seqnum
from Sales s
order by s.Sale DESC;

How to Sum the 1st record of one column with the 2nd record of another column?

I am trying the Sum the 2nd record of one column with the 1st record of another column and store the result in a new column
Here is the example SQL Server table
Emp_Code Emp_Name Month Opening_Balance
G101 Sam 1 1000
G102 James 2 -2500
G103 David 3 3000
G104 Paul 4 1800
G105 Tom 5 -1500
I am trying to get the output as below on the new Reserve column
Emp_Code Emp_Name Month Opening_Balance Reserve
G101 Sam 1 1000 1000
G102 James 2 -2500 -1500
G103 David 3 3000 1500
G104 Paul 4 1800 3300
G105 Tom 5 -1500 1800
Actually the rule for calculating the Reserve column is that
For Month-1 it's the same as Opening Balance
For rest of the months its Reserve for Month-2 = Reserve for Month-1 + Opening Balance for Month-2
You seem to want a cumulative sum. In SQL Server 2012+, you would do:
select t.*,
sum(opening_balance) over (order by [Month]) as Reserve
from t;
In earlier versions, you would do this with a correlated subquery or apply:
select t.*,
(select sum(t2.opening_balance) from t t2 where t2.[Month] <= t.[Month]) as reserve
from t;
You can do a self join.
SELECT t.Emp_Code, t.Emp_Name, t.Month, t.Opening_Balance, t.Opening_Balance + n.Reserve
FROM Table1 t
JOIN Table2 n
ON t.Month = n.Month - 1

SQL - count without group by? I need to use two ids for a join

I thought I could count a column and add it as a column as I can with a sum but I get an error about having to group by / having. An example of what I want...
Initial table...
Global ID Local ID Name Role
100 1 Andy Manager
100 2 Andy Manager
100 1 John Co-Manager
200 1 Andy Co-Manager
200 2 John Manager
200 2 Mike Manager
I then want to add a column that counts the number of Manager in each group / local pairing...
Global ID Local ID Name Role Manager Count
100 1 Andy Manager 1
100 2 Andy Manager 1
100 1 John Co-Manager 0
200 1 Andy Co-Manager 0
200 2 John Manager 2
200 2 Mike Manager 2
I tried just joining the two tables on the global / local ID and then adding a column for count of the Role. The problem is that I get an error about grouping / having by but I don't want to group anything. I just want to add the column and still have the same number of rows. Any way around this?
FYI - for the last two rows, the last column has 2 because John and Mike are on the same group / local ID
It looks like your definition of a group is a unique combination of [global_id, local_id]. If that is the case you do want to group by those two values, and do a count, where the role is Manager. But because you want other columns from the original table, you must do that count within an inline view, and then join back to the original table, like so:
select t.*, v.mgr_in_grp
from tbl t
left join (select global_id, local_id, count(*) as mgr_in_grp
from tbl
where role = 'Manager'
group by global_id, local_id) v
on t.global_id = v.global_id
and t.local_id = v.local_id
Fiddle: http://sqlfiddle.com/#!2/fb3ace/2/0
Notice that there is a difference on row 3, as compared to your expected output.
John, Co-manager at global_id and local_id 100 and 1, respectively, belongs to a pair that has a manager in that combination (Andy, your first row). So the count appears should be 1.

Retrieve highest value from sql table

How can retrieve that data:
Name Title Profit
Peter CEO 2
Robert A.D 3
Michael Vice 5
Peter CEO 4
Robert Admin 5
Robert CEO 13
Adrin Promotion 8
Michael Vice 21
Peter CEO 3
Robert Admin 15
to get this:
Peter........4
Robert.......15
Michael......21
Adrin........8
I want to get the highest profit value from each name.
If there are multiple equal names always take the highest value.
select name,max(profit) from table group by name
Since this type of request almost always follows with "now can I include the title?" - here is a query that gets the highest profit for each name but can include all the other columns without grouping or applying arbitrary aggregates to those other columns:
;WITH x AS
(
SELECT Name, Title, Profit, rn = ROW_NUMBER()
OVER (PARTITION BY Name ORDER BY Profit DESC)
FROM dbo.table
)
SELECT Name, Title, Profit
FROM x
WHERE rn = 1;

selecting top N rows for each group in a table

I am facing a very common issue regarding "Selecting top N rows for each group in a table".
Consider a table with id, name, hair_colour, score columns.
I want a resultset such that, for each hair colour, get me top 3 scorer names.
To solve this i got exactly what i need on Rick Osborne's blogpost "sql-getting-top-n-rows-for-a-grouped-query"
That solution doesn't work as expected when my scores are equal.
In above example the result as follow.
id name hair score ranknum
---------------------------------
12 Kit Blonde 10 1
9 Becca Blonde 9 2
8 Katie Blonde 8 3
3 Sarah Brunette 10 1
4 Deborah Brunette 9 2 - ------- - - > if
1 Kim Brunette 8 3
Consider the row 4 Deborah Brunette 9 2. If this also has same score (10) same as Sarah, then ranknum will be 2,2,3 for "Brunette" type of hair.
What's the solution to this?
If you're using SQL Server 2005 or newer, you can use the ranking functions and a CTE to achieve this:
;WITH HairColors AS
(SELECT id, name, hair, score,
ROW_NUMBER() OVER(PARTITION BY hair ORDER BY score DESC) as 'RowNum'
)
SELECT id, name, hair, score
FROM HairColors
WHERE RowNum <= 3
This CTE will "partition" your data by the value of the hair column, and each partition is then order by score (descending) and gets a row number; the highest score for each partition is 1, then 2 etc.
So if you want to the TOP 3 of each group, select only those rows from the CTE that have a RowNum of 3 or less (1, 2, 3) --> there you go!
The way the algorithm comes up with the rank, is to count the number of rows in the cross-product with a score equal to or greater than the girl in question, in order to generate rank. Hence in the problem case you're talking about, Sarah's grid would look like
a.name | a.score | b.name | b.score
-------+---------+---------+--------
Sarah | 9 | Sarah | 9
Sarah | 9 | Deborah | 9
and similarly for Deborah, which is why both girls get a rank of 2 here.
The problem is that when there's a tie, all girls take the lowest value in the tied range due to this count, when you'd want them to take the highest value instead. I think a simple change can fix this:
Instead of a greater-than-or-equal comparison, use a strict greater-than comparison to count the number of girls who are strictly better. Then, add one to that and you have your rank (which will deal with ties as appropriate). So the inner select would be:
SELECT a.id, COUNT(*) + 1 AS ranknum
FROM girl AS a
INNER JOIN girl AS b ON (a.hair = b.hair) AND (a.score < b.score)
GROUP BY a.id
HAVING COUNT(*) <= 3
Can anyone see any problems with this approach that have escaped my notice?
Use this compound select which handles OP problem properly
SELECT g.* FROM girls as g
WHERE g.score > IFNULL( (SELECT g2.score FROM girls as g2
WHERE g.hair=g2.hair ORDER BY g2.score DESC LIMIT 3,1), 0)
Note that you need to use IFNULL here to handle case when table girls has less rows for some type of hair then we want to see in sql answer (in OP case it is 3 items).