How do I sort by a rolled up group? - sorting

Let's say I have a table in SQL Server 2008 R2 called Purchase with two columns: Purchaser and Expenditure.
Let's say that the table has the following rows:
Purchaser Expenditure
--------- -----------
Alex 200
Alex 300
Alex 500
Bob 300
Bob 400
Charlie 200
Charlie 600
Derek 100
Derek 300
Now I have this query:
SELECT Purchaser, Expenditure, SUM(Expenditure) AS SumExpenditure FROM Purchase GROUP BY Purchaser, Expenditure WITH ROLLUP
This returns the following:
Purchaser Expenditure SumExpenditure
--------- ----------- --------------
Alex 200 200
Alex 300 300
Alex 500 500
--------------------------------
Alex NULL 1000
--------------------------------
Bob 300 300
Bob 400 400
--------------------------------
Bob NULL 700
--------------------------------
Charlie 200 200
Charlie 600 600
--------------------------------
Charlie NULL 800
--------------------------------
Derek 100 100
Derek 300 300
--------------------------------
Derek NULL 400
--------------------------------
(Lines added to emphasise the rolled up amounts.)
What I would like would be to be able to sort the groups by the grouped amounts so that I end up with a result set like this:
Purchaser Expenditure SumExpenditure
--------- ----------- --------------
Derek 100 100
Derek 300 300
--------------------------------
Derek NULL 400
--------------------------------
Bob 300 300
Bob 400 400
--------------------------------
Bob NULL 700
--------------------------------
Charlie 200 200
Charlie 600 600
--------------------------------
Charlie NULL 800
--------------------------------
Alex 200 200
Alex 300 300
Alex 500 500
--------------------------------
Alex NULL 1000
--------------------------------
In other words, I am sorting the groups, using the 400, 700, 800 and 1000 in the group rows by ascending order.
Can anyone suggest what query would return this result set?

;WITH x AS
(
SELECT Purchaser, Expenditure, s = SUM(Expenditure)
FROM dbo.Purchase
GROUP BY Purchaser, Expenditure WITH ROLLUP
),
y AS
(
SELECT Purchaser, s FROM x
WHERE Expenditure IS NULL
AND Purchaser IS NOT NULL
),
z AS
(
SELECT Purchaser, s, rn = ROW_NUMBER() OVER (ORDER BY s)
FROM y
)
SELECT x.Purchaser, x.Expenditure, x.s FROM x
INNER JOIN z ON x.Purchaser = z.Purchaser
ORDER BY z.rn, CASE WHEN z.s IS NULL THEN 2 ELSE 1 END;

Related

PowerPivot DAX - Creating column based on most common value in another column

I have a table in my Power Pivot model that includes customer IDs, account IDs, and sales for a bunch of transactions. The problem is that some account IDs are missing.
For any record with a missing account ID, I want to populate it with the most common account ID for the customer (based on sales).
Cust_ID
Acct_ID
Sales
225
ABC
10
225
ABC
50
225
DEF
0
225
10
225
20
588
XYZ
500
So for Customer 225, the most common Account ID (based on sales) is ABC. I'd want to add a column like this.
Cust_ID
Acct_ID
Sales
Final_Acct_ID
225
ABC
10
ABC
225
ABC
50
ABC
225
DEF
0
ABC
225
10
ABC
225
20
ABC
588
XYZ
500
XYZ
Final_Acct_ID =
VAR tbl =
CALCULATETABLE(
FILTER(
ADDCOLUMNS(
SUMMARIZE( 'Table1', 'Table1'[Cust_ID], 'Table1'[Acct_ID]),
"#count",
CALCULATE( COUNT('Table1'[Acct_ID]))
),
'Table1'[Acct_ID] <> BLANK()
),
ALLEXCEPT('Table1','Table1'[Cust_ID]))
RETURN
MAXX(TOPN(1,tbl,[#count]), 'Table1'[Acct_ID])

Retrieving profiles from a table based on specific logic

I have a requirement to extract profiles from a table for the below criteria:
There are 5 IDs in total. 100, 200, 300, 400 and 500
The profiles to have atleast one ID of each value 100, 300 and 400. IDs with value 200 and 500 should not be present even once.
Profile can have multiple number of IDs of value 300 and 400 but will have only one ID of value 100.
The profiles will have IDs 300 and 400 equally. i.e For every ID will the value 300, there will be an ID of value 400.
Eg:
TABLE A:
-------------------------
PROFILE_ID ID
-------------------------
12345 100
12345 300
12345 400
23456 100
23456 300
23456 400
23456 300
23456 400
34567 100
34567 200
-------------------------
The result should fetch PROFILE_IDs 12345 and 23456 and not 34567.
I am pretty stuck and blank in getting a clear idea on how to frame a query for this. Please help.
For sample data you posted:
SQL> select * From test;
PROFILE_ID ID
---------- ----------
12345 100
12345 300
12345 400
23456 100
23456 300
23456 400
23456 300
23456 400
34567 100
34567 200
10 rows selected.
one option is to do it rule-by-rule, each of CTEs retrieving data which satisfy certain rule. The final result is intersection of these PROFILE_IDs.
SQL> with
2 rule2 as -- don't allow IDs 200 nor 500
3 (select profile_id
4 from test
5 where profile_id not in (select profile_id
6 from test
7 where id in (200, 500)
8 )
9 ),
10 rule3 as -- there can be only one ID = 100 for each PROFILE_ID
11 (select profile_id
12 from (select profile_id,
13 sum(case when id = 100 then 1 else 0 end) cnt_100
14 from test
15 group by profile_id
16 )
17 where cnt_100 = 1
18 ),
19 rule4 as -- number of IDs 300 and 400 has to be equal and greater than 0
20 (select profile_id
21 from (select profile_id,
22 sum(case when id = 300 then 1 else 0 end) cnt_300,
23 sum(case when id = 400 then 1 else 0 end) cnt_400
24 from test
25 group by profile_id
26 )
27 where cnt_300 = cnt_400
28 and cnt_300 > 0
29 )
30 select profile_id from rule2
31 intersect
32 select profile_id from rule3
33 intersect
34 select profile_id from rule4;
PROFILE_ID
----------
12345
23456
SQL>
Personally, since all your rules rely on counting the different ids, I'd do it more like this. Some of this is redundant, but it makes explicit which part comes from which of your criteria.
with rules as (
select profile_id,
sum(case when id = 100 then 1 else 0 end) as cnt_100,
sum(case when id = 200 then 1 else 0 end) as cnt_200,
sum(case when id = 300 then 1 else 0 end) as cnt_300,
sum(case when id = 400 then 1 else 0 end) as cnt_400,
sum(case when id = 500 then 1 else 0 end) as cnt_500
from table1
group by profile_id
)
select profile_id
from rules
-- rule 1
where cnt_100 > 0
and cnt_300 > 0
and cnt_400 > 0
-- rule 2
and cnt_200 = 0
and cnt_500 = 0
-- rule 3
and cnt_100 = 1
-- rule 4
and cnt_300 = cnt_400
SQL Fiddle

Sum of consecutive salaries in the following output

I have a table of salaries I want to find sum of all the salaries consecutively in a different column based on the manager_id. Suppose I have a table like this
Manager_id
Salary
100
1200
100
1400
100
1600
103
1800
104
1900
And I want output like this
Manager_id
Salary
Salary_Sum
100
1200
1200
100
1400
2600
100
1600
4200
103
1800
1800
104
1900
1900
SUM analytical function can fulfil your requirement but there must some other column also to order your rows apart from Manager_id -
SELECT
Manager_id,
Salary,
SUM(Salary) OVER(PARTITION BY Manager_id ORDER BY <other_column_for_ordering>) Salary_Sum
FROM salaries
ORDER BY Manager_id, <other_column_for_ordering>;

PIVOT table in Oracle

Can you help me to pivot the details in my Oracle table PAY_DETAILS
PAY_NO NOT NULL NUMBER
EMP_NO NOT NULL VARCHAR2(10)
EMP_ERN_DDCT_NO NOT NULL VARCHAR2(21)
ERN_DDCT_CATNO NOT NULL VARCHAR2(10)
ERN_DDCT_CATNAME NOT NULL VARCHAR2(1000)
PAY_MONTH NOT NULL DATE
AMOUNT NOT NULL NUMBER(10,2)
EARN_DEDUCT NOT NULL VARCHAR2(2)
select EMP_NO,EMP_ERN_DDCT_NO,AMOUNT,EARN_DEDUCT, ERN_DDCT_CATNO from pay_details
EMP_NO EMP_ERN_DDCT_NO AMOUNT EA ERN_DDCT_C
---------- --------------------- ---------- -- ----------
219 10 175 A 001
219 1 5000 A 002
794 7 50000 A 001
769 6 35000 A 001
465 4 5000 A 002
289 2 5000 A 002
435 3 5000 A 002
816 38 5 D 201
737 30 5 D 201
Is it possible to make this output into a cross tab?
So, lets assume you want to pivot your salary data for each month. You can use the following query.
SELECT * FROM
(
SELECT emp_no,
emp_ern_ddct_no,
pay_month,
amount
FROM pay_details
)
PIVOT
(
SUM(amount)
FOR pay_month IN ('01/01/2016', '02/01/2016', '03/01/2016','04/01/2016','05/01/2016','06/01/2016','07/01/2016','08/01/2016','09/01/2016','10/01/2016','11/01/2016','12/01/2016')
)
ORDER BY emp_no;
This is just an example, you can PIVOT your data based on different columns. For more details refer to the following link.
http://www.techonthenet.com/oracle/pivot.php
http://www.oracle-developer.net/display.php?id=506
Since you are on Oracle 10g PIVOT wont work. Try using something similar to the below query.
SELECT emp_no,
SUM(CASE WHEN pay_month ='01/01/2016' THEN AMOUNT ELSE 0 END) jan_pay,
SUM(CASE WHEN pay_month ='02/01/2016' THEN AMOUNT ELSE 0 END) feb_pay,
SUM(CASE WHEN pay_month ='03/01/2016' THEN AMOUNT ELSE 0 END) march_pay
.........
FROM pay_details
GROUP BY emp_no;

How do I get both the child and one upper level parent information by using oracle connect by prior?

I want to get both the child and one upper level parent information by using oracle connect by prior?
For example the folowing query retrieve child info and parent id,
SELECT last_name, employee_id, manager_id, LEVEL
FROM employees
START WITH employee_id = 100
CONNECT BY PRIOR employee_id = manager_id
but I want to get parent info also like
LAST_NAME EMPLOYEE_ID MANAGER_ID LEVEL MANAGER_NAME
------------------------- ----------- ---------- ----------------------------
King 100 1 ?
Cambrault 148 100 2 ?
Bates 172 148 3 ?
Bloom 169 148 3 .
Fox 170 148 3 .
How can I handle this problem, when I applied left join after selecting childs by connect by prior,The objects order is mixing.
You can refer to prior values in the select list:
SELECT last_name, employee_id, manager_id, LEVEL, prior last_name as manager_name
FROM employees
START WITH employee_id = 100
CONNECT BY PRIOR employee_id = manager_id;
LAST_NAME EMPLOYEE_ID MANAGER_ID LEVEL MANAGER_NAME
------------------------- ----------- ---------- ---------- -------------------------
King 100 1
Kochhar 101 100 2 King
Greenberg 108 101 3 Kochhar
Faviet 109 108 4 Greenberg
...
Cambrault 148 100 2 King
Bates 172 148 3 Cambrault
Bloom 169 148 3 Cambrault
Fox 170 148 3 Cambrault
...

Resources