Home > front end >  Making changes on a pivot table
Making changes on a pivot table

Time:12-06

I have the following query:


SELECT CASE GROUPING_ID(status, CASE WHEN status IN (2, 4, 5) THEN 1 ELSE 0 END)
       WHEN 0
       THEN TO_CHAR(status)
       WHEN 2
       THEN 'subtotal'
       ELSE 'total'
       END AS status,
       COUNT(CASE user_type WHEN 1 THEN 1 END) AS "1",
       COUNT(CASE user_type WHEN 2 THEN 1 END) AS "2",
       COUNT(CASE user_type WHEN 3 THEN 1 END) AS "3",
       COUNT(CASE user_type WHEN 5 THEN 1 END) AS "5",
       COUNT(*) AS total
FROM   (Select STATUS,USER_TYPE
FROM TRANSACTIONS tr  join TRANSACTION_STATUS_CODES sc on sc.id = tr.user_type join
TRANSACTION_USER_TYPES ut on ut.id=tr.user_type  
WHERE tr.status!=1 AND Tr.Update_Date BETWEEN     TO_DATE('2022-01-01 00:00:00', 'yyyy-mm-dd HH24:MI:SS')     AND TO_DATE('2022-11-13 23:59:59', 'yyyy-mm-dd HH24:MI:SS'))
GROUP BY CUBE(status,CASE WHEN status IN (2, 4, 5) THEN 1 ELSE 0 END)
HAVING GROUPING_ID(status, CASE WHEN status IN (2, 4, 5) THEN 1 ELSE 0 END) IN (0, 3)
OR     (   GROUPING_ID(status, CASE WHEN status IN (2, 4, 5) THEN 1 ELSE 0 END) = 2
       AND CASE WHEN status IN (2, 4, 5) THEN 1 ELSE 0 END = 1 );

which displays:

[table

with inner query giving ( thats 10 out of 36rows):

inner

anyways.. I've been asked to change the inner query to a better organized one, so I came up with the following pivot table ( which I want it to be my new inner query ):

SELECT p.*
FROM   (
  SELECT user_type,
         status 
  FROM   transactions
  WHERE  status !=1
  AND    Update_Date >= DATE '2022-01-01'
  AND    Update_Date <  DATE '2022-11-14' 
) PIVOT (
  COUNT(*)
  FOR user_type IN (1,2,3,5)
) p
ORDER BY status asc;

which gives:

enter image description here

what I need to try and achieve is the sum of subtotal/total of rows aswell as total of column( as I have in the first table ), and when I try to make it an inner query the outer query doesnt recognize anything..

so basically to add these:

enter image description here

enter image description here

is that achievable without changing the entire query? or else, what is the best approach to achieve that?

thanks in advance!

CodePudding user response:

CUBE does what you want in a single query and was covered in my previous answer.

You cannot use PIVOT to do what you want in a single query; instead you would need to use multiple queries joined together with UNION ALL to calculate the status, sub-total and total parts (which is likely to be much less efficient):

SELECT TO_CHAR(status) AS status,
       "1",
       "2",
       "3",
       "5",
       "1"   "2"   "3"   "5" AS total
FROM   table_name
PIVOT ( COUNT(*) FOR user_type IN (1,2,3,5) )
UNION ALL
SELECT 'SUB_TOTAL',
       "1",
       "2",
       "3",
       "5",
       "1"   "2"   "3"   "5" AS total
FROM   (SELECT user_type FROM table_name WHERE status IN (2,4,5))
PIVOT ( COUNT(*) FOR user_type IN (1,2,3,5) )
UNION ALL
SELECT 'TOTAL',
       "1",
       "2",
       "3",
       "5",
       "1"   "2"   "3"   "5" AS total
FROM   (SELECT user_type FROM table_name)
PIVOT ( COUNT(*) FOR user_type IN (1,2,3,5) )

or:

SELECT TO_CHAR(status) AS status,
       COUNT(CASE user_type WHEN 1 THEN 1 END) AS "1",
       COUNT(CASE user_type WHEN 2 THEN 1 END) AS "2",
       COUNT(CASE user_type WHEN 3 THEN 1 END) AS "3",
       COUNT(CASE user_type WHEN 5 THEN 1 END) AS "5",
       COUNT(*) AS total
FROM   table_name
GROUP BY status
UNION ALL
SELECT 'SUB_TOTAL',
       COUNT(CASE user_type WHEN 1 THEN 1 END) AS "1",
       COUNT(CASE user_type WHEN 2 THEN 1 END) AS "2",
       COUNT(CASE user_type WHEN 3 THEN 1 END) AS "3",
       COUNT(CASE user_type WHEN 5 THEN 1 END) AS "5",
       COUNT(*) AS total
FROM   table_name
WHERE  status IN (2,4,5)
UNION ALL
SELECT 'TOTAL',
       COUNT(CASE user_type WHEN 1 THEN 1 END) AS "1",
       COUNT(CASE user_type WHEN 2 THEN 1 END) AS "2",
       COUNT(CASE user_type WHEN 3 THEN 1 END) AS "3",
       COUNT(CASE user_type WHEN 5 THEN 1 END) AS "5",
       COUNT(*) AS total
FROM   table_name

Which, for the sample data, (also from my previous answer):

CREATE TABLE table_name (status, user_type) AS
  SELECT 2, 1 FROM DUAL CONNECT BY LEVEL <=  3 UNION ALL
  SELECT 4, 1 FROM DUAL CONNECT BY LEVEL <= 13 UNION ALL
  SELECT 5, 1 FROM DUAL CONNECT BY LEVEL <=  1 UNION ALL
  SELECT 3, 1 FROM DUAL CONNECT BY LEVEL <=  5 UNION ALL
  SELECT 3, 5 FROM DUAL CONNECT BY LEVEL <=  1 UNION ALL
  SELECT 0, 1 FROM DUAL CONNECT BY LEVEL <=  4 UNION ALL
  SELECT 0, 5 FROM DUAL CONNECT BY LEVEL <=  8;

Both output the same as using a single query with CUBE:

STATUS 1 2 3 5 TOTAL
2 3 0 0 0 3
4 13 0 0 0 13
0 4 0 0 8 12
5 1 0 0 0 1
3 5 0 0 1 6
SUB_TOTAL 17 0 0 0 17
TOTAL 26 0 0 9 35

If you did want to do it without CUBE then you can generate extra rows before pivoting (however, I think CUBE will still be more efficient):

SELECT title AS status,
       "1",
       "2",
       "3",
       "5",
       "1"   "2"   "3"   "5" AS total
FROM   (
  SELECT l.*, t.user_type
  FROM   table_name t
         CROSS JOIN LATERAL (
           SELECT t.status, TO_CHAR(t.status) AS title, 1 AS priority FROM DUAL UNION ALL 
           SELECT NULL,     'SUB_TOTAL',                2 FROM DUAL WHERE t.status IN (2,4,5) UNION ALL
           SELECT NULL,     'TOTAL',                    3 FROM DUAL
         ) l
)
PIVOT ( COUNT(*) FOR user_type IN (1,2,3,5) )
ORDER BY priority, status

Which produces the same output.

fiddle

  • Related