sum of multiple columns in ORACLE - oracle

I have a table that has 97 columns, I want to sum 96 columns.
select sum(col1+col2+col3+.....+col96)
from tableA where meter_id=x;
I do not want to give all 96 column names, what is the best way to do it?
Regards,
RR

There is no way to avoid writing each column name. All you can do is curse the stupid data modeller and get busy with cut'n'paste.

In the case where there are a significant number of columns, I would look at using the data dictionary tables to help create the query by using a query like the one below:
Select column_name || '+' as column_name_list
From user_tab_columns
Where table_name = 'TABLEA'
Order by column_id
It doesn't change the world but does simplify writing one query.

You could create a virtual column that adds up your 96 columns, something like:
alter table TABLEA add (MY_TOTAL_COL NUMBER GENERATED ALWAYS AS (col1+col2+col3...) VIRTUAL);
Then your query can simply do sum(my_total_col).

You might be best to sum the columns and then put the result in Excel to do the sum of sums. Otherwise this query should do what you need:
SELECT SUM(TOTAL_SUM) FROM (
SELECT SUM(column1) AS TOTAL_SUM FROM your_table
UNION
SELECT SUM(column2) AS TOTAL_SUM FROM your_table
UNION
SELECT SUM(column3) AS TOTAL_SUM FROM your_table
);

SELECT A.consol_key,
A.amt_lcy,
B.amt_lcy,
C.amt_lcy
FROM categ A,
spec B,
stmt C;
SELECT Sum(total_sum)
FROM (SELECT Sum(amt_lcy) AS TOTAL_SUM
FROM categ
UNION
SELECT Sum(amt_lcy) AS TOTAL_SUM
FROM spec
UNION
SELECT Sum(amt_lcy) AS TOTAL_SUM
FROM stmt)
WHERE table_id NOT IN (SELECT table_id
FROM categ
WHERE txn_code = 'COR'
AND system_id <> 'AA');

It could be possible:
Using Can an SQL procedure return a table?
and the answer of Mike Meyers you could write a stored procedure using dynamic sql
sumcolumns(columnfilter,tablename,whereclause)
and use it something like
select *
from table(sumcolumns('column_name <> ''col97''','tableA','meter_id=x'))

Try using UNPIVOT as per example below (still need to specify the column list as others have noted):
with tableA as /* prototype tableA just for example */
(
select 1 meter_id, 101 col1, 10 col2, 20 col3, 30 col4, NULL col5, 101 col11, 10 col12, 20 col13, 30 col14, NULL col15, 101 col21, 10 col22, 20 col23, 30 col24, NULL col25 from dual union
select 2, 102, 40, NULL, 50, NULL, 102, 40, NULL, 50, NULL, 102, 40, NULL, 50, NULL from dual union
select 3, 103, 60, 70, 80, 90, 103, 60, 70, 80, 90, 103, 60, 70, 80, 90 from dual union
select 4, 104, 100, NULL, NULL, NULL, 104, 100, NULL, NULL, NULL, 104, 100, NULL, NULL, NULL from dual
)
, unpivoted_tableA as /* UNPIVOT tableA columns into rows */
(
select *
from tableA
unpivot include nulls
(
col_value for col_ in
(COL1,COL2,COL3,COL4,COL5,COL11,COL12,COL13,COL14,COL15,COL21,COL22,COL23,COL24,COL25)
)
)
/* main query - Sum of all columns that were unpivoted to rows */
select meter_id, sum(col_value)
from unpivoted_tableA
group by meter_id
;

Related

Make Column Data as Column Header whole year in Oracle

Hi im working on a query on oracle with the sample data like this
[
expected Result but the whole year
will also display other month even if there's no data for that month
i've search the internet for this but cant find any related to my problem
here's what i have tried but i think im in a wrong way
select * from(
select fd.empid,fd.employeename,to_char(fd.payrolldate,'Month') as Month,fd.for_deduction15th,d.DEDUCTED15TH,fd.for_deduction30th,d.DEDUCTED30TH from
VW_MTS_FOR_DEDUCTION FD
full outer join VW_MTS_DEDUCTED D
on FD.EMPID = D.EMPID
and FD.PAYROLLDATE = D.PAYROLLDATE
)
pivot(max(Month) for Month in ('March' ,'April','February' ))
Your PIVOT was close, but you need to use the "deduction" columns in your pivot. Below is a sample query with assumed data since the actual data in the tables was not shared.
--Setup of sample data
WITH
VW_MTS_FOR_DEDUCTION (empid,
employeename,
payrolldate,
for_deduction15th,
for_deduction30th)
AS
(SELECT 233, 'HERNANDEZ, CECILIA ALVANO', DATE '2021-3-1', NULL, 2 FROM DUAL
UNION ALL
SELECT 233, 'HERNANDEZ, CECILIA ALVANO', DATE '2021-4-1', NULL, 1778 FROM DUAL
UNION ALL
SELECT 233, 'HERNANDEZ, CECILIA ALVANO', DATE '2021-4-1', 1, NULL FROM DUAL
UNION ALL
SELECT 40079, 'ALLASAS, DERRICK AMANTE', DATE '2021-4-1', NULL, 1 FROM DUAL),
VW_MTS_DEDUCTED (empid,
payrolldate,
DEDUCTED15TH,
DEDUCTED30TH)
AS
(SELECT 233, DATE '2021-3-1', NULL, 1 FROM DUAL
UNION ALL
SELECT 233, DATE '2021-4-1', NULL, 1777 FROM DUAL
UNION ALL
SELECT 233, DATE '2021-4-1', 1, NULL FROM DUAL
UNION ALL
SELECT 40079, DATE '2021-4-1', NULL, 1 FROM DUAL)
--Actual query
SELECT *
FROM (SELECT fd.empid,
fd.employeename,
TRIM (TO_CHAR (fd.payrolldate, 'Month')) AS mon,
fd.for_deduction15th,
d.DEDUCTED15TH,
fd.for_deduction30th,
d.DEDUCTED30TH as
FROM VW_MTS_FOR_DEDUCTION FD
LEFT JOIN VW_MTS_DEDUCTED D ON FD.EMPID = D.EMPID AND FD.PAYROLLDATE = D.PAYROLLDATE)
PIVOT (MAX (for_deduction15th) AS for_deduction15th,
MAX (DEDUCTED15TH) AS DEDUCTED15TH,
MAX (for_deduction30th) AS for_deduction30th,
MAX (DEDUCTED30TH) AS DEDUCTED30TH
FOR mon
IN ('March' AS march, 'April' AS april))
ORDER BY empid;
EMPID EMPLOYEENAME MARCH_FOR_DEDUCTION15TH MARCH_DEDUCTED15TH MARCH_FOR_DEDUCTION30TH MARCH_DEDUCTED30TH APRIL_FOR_DEDUCTION15TH APRIL_DEDUCTED15TH APRIL_FOR_DEDUCTION30TH APRIL_DEDUCTED30TH
________ ____________________________ __________________________ _____________________ __________________________ _____________________ __________________________ _____________________ __________________________ _____________________
233 HERNANDEZ, CECILIA ALVANO 2 1 1 1 1778 1777
40079 ALLASAS, DERRICK AMANTE 1 1

Oracle - Connect By Prior

I know I have to use CONNECT BY PRIOR in this query, but I'm not sure how to implement it.
We have customers who purchase monthly subscriptions, and those get auto-renewed each month. We have a log table which can show what your current order ID is, and what your previous order ID is. So, table records could like like this:
CUSTOMER ID: 1 ORDER ID: 123 PREV_ORDER_ID: STATUS: Complete
CUSTOMER ID: 1 ORDER ID: 456 PREV_ORDER_ID: 123 STATUS: Complete
CUSTOMER ID: 1 ORDER ID: 789 PREV_ORDER_ID: 456 STATUS: Complete
CUSTOMER ID: 1 ORDER ID: 888 PREV_ORDER_ID: 789 STATUS: Complete
CUSTOMER ID: 1 ORDER ID: 999 PREV_ORDER_ID: 888 STATUS: Active
I am looking to count how many customers have had at least 13 months of consecutive subscriptions, with no gaps with the most recent subscription will have an "Active" status. If there is a break in subscriptions, the PREV_ORDER_ID will be NULL.
Hoping to do this in a query, and not having to write an anonymous block for it.
Many thanks!
You could do something like this (using your actual table and column names instead of the with clause from the query, which you should delete). Note that the hierarchical recursion starts from the end (from 'Active' status) and proceeds backwards; in my query I stop it at level 4 since I didn't feel like writing enough rows to get to level 13. Of course, you will have to replace 4 with 13 in the where clause.
with
test_data (customer_id, order_id, prev_order_id, status) as (
select 1, 123, null, 'Complete' from dual union all
select 1, 456, 123, 'Complete' from dual union all
select 1, 789, 456, 'Complete' from dual union all
select 1, 888, 789, 'Complete' from dual union all
select 1, 999, 888, 'Active' from dual union all
select 2, 100, null, 'Complete' from dual union all
select 2, 200, 100, 'Active' from dual union all
select 5, 105, null, 'Complete' from dual union all
select 5, 106, 105, 'Complete' from dual union all
select 5, 205, null, 'Complete' from dual union all
select 5, 206, 205, 'Active' from dual
)
select customer_id
from test_data
where level = 4
start with status = 'Active'
connect by customer_id = prior customer_id and order_id = prior prev_order_id
;
CUSTOMER_ID
-----------
1
More data and tests are needed.
Maybe it will help you
CREATE TABLE CUSTOMER_LOG
(
CUSTOMER_ID number(5),
ORDER_ID number(5),
PREV_ORDER_ID number(5),
STATUS VARCHAR(50)
);
INSERT INTO CUSTOMER_LOG(CUSTOMER_ID,ORDER_ID,PREV_ORDER_ID,STATUS) values (1,123,NULL, 'Complete');
INSERT INTO CUSTOMER_LOG(CUSTOMER_ID,ORDER_ID,PREV_ORDER_ID,STATUS) values (1,456,123, 'Complete');
INSERT INTO CUSTOMER_LOG(CUSTOMER_ID,ORDER_ID,PREV_ORDER_ID,STATUS) values (1,789,456, 'Complete');
INSERT INTO CUSTOMER_LOG(CUSTOMER_ID,ORDER_ID,PREV_ORDER_ID,STATUS) values (1,888,789, 'Complete');
INSERT INTO CUSTOMER_LOG(CUSTOMER_ID,ORDER_ID,PREV_ORDER_ID,STATUS) values (1,999,888, 'Active');
Select
l.*,
(
select count(*)
from CUSTOMER_LOG s
where s.customer_id=1
start with s.ORDER_ID=l.ORDER_ID
connect by s.ORDER_ID= prior s.PREV_ORDER_ID
) QTDE
from CUSTOMER_LOG l
where l.status='Active'

subtract values selecting by timestamp self join

I have the following problem:
Table1:
id, timestamp, value1,value2,value3
1, 12.01.2017 09:00:01, 234, 345, 456
2, 12.01.2017 09:00:05, 567, 678, 789
3, 12.01.2017 09:00:25, 777, 888, 999
the values are absolut values. now i need to insert this data into a new table, but as incremental values.
that means i have to find the previous record for each record and subtract the values.
but unfortunately im not getting there...
I tried a self join like this:
select
se1.timestamp,
se1.value1,
se1.value2,
se1.value3
from
table1 se1,
table1 se2
where
se1.id = se2.id
and se1.timestamp < (select max(timestamp) from table1)
order by
timestamp desc
fetch first 100 rows only;
it would be great if anyone could help me with this...
The output should look something like this:
timestamp, value1, value2, value3
12.01.2017 09:00:05, 333, 333, 333 (record from 09:00:01 subtracted)
12.01.2017 09:00:25, 210, 210, 210 (record from 09:00:05 subtracted)
I hope anyone can understand this ;)
If I understand well, you need to compute for every value (value1, value2 and value3) the difference between the value in the current row and the value in the preceding row, ordered by timestamp.
If so, you may need:
with test(id, timestamp, value1, value2, value3) as (
select 1, to_timestamp('12.01.2017 09:00:01', 'dd.mm.yyyy hh24:mi:ss'), 234, 345, 456 from dual union all
select 2, to_timestamp('12.01.2017 09:00:05', 'dd.mm.yyyy hh24:mi:ss'), 567, 678, 789 from dual union all
select 3, to_timestamp('12.01.2017 09:00:25', 'dd.mm.yyyy hh24:mi:ss'), 777, 888, 999 from dual
)
select timestamp,
value1 - lag(value1, 1, 0) over ( order by timestamp) increment1,
value2 - lag(value2, 1, 0) over ( order by timestamp) increment2,
value3 - lag(value3, 1, 0) over ( order by timestamp) increment3
from test
This uses LAG to evaluate the preceding row and make the difference with the value in the current row, thus giving:
TIMESTAMP INCREMENT1 INCREMENT2 INCREMENT3
12/01/2017 09:00:01,000000000 234 345 456
12/01/2017 09:00:05,000000000 333 333 333
12/01/2017 09:00:25,000000000 210 210 210
Notice that timestamp is an Oracle type, so it would be better not to use it as the name of a column.
Also, notice the comments by Boneist: in the first version of my answer I used something like NVL(LAG(..), 0) to handle the case where LAG(..) is null (the first row). Boneist's comment made me notice that LAG already handles a default value in case the needed preceding row does not exist, thus allowing me to avoid the NVL.

how to combine multiple rows into one row in table in oracle and store result in other table

sample data: table consists of 96000 rows and i want to combine them into 16000 rows
row 1:1255467861 40825825 IDF+0149502016010615210300000396000026+0000651+ 00000000000000+|
row 2:000000000+0000000+0000000+000000677+02 YY 0444410100000 00 0001000000054+10001EB4200002+00000+0000052+0000000+0000000+|
row 3:00000 00000+00000+0000000+0000000+0000000+00000 00000+00000+0000000+0000000+0000000+00000 00000+00000+0000000+0000000+|
row 4:0000000+00000 00000+00000+0000000+0000000+0000000+00 004 1 000000000000 0000000000 M5|
row 5: 00000000 +00000000000000000000000000000000000000+00000000001011 Y 000000000+|
row 6:0000000+0000000+0000000+AB0002210000000000FIABMM81 15067195 0000000000009403228870|
Assuming that you know what the criteria (column1, column2 in the example) for grouping your rows is, I suggest you to use aggregate functions functions such as SUM(), MAX(), and COUNT() to aggregate data, Group by the criteria, and insert into the target table.
E.G.
insert into my_target_table
Select column1, column2, sum(column3), max(column4), count(1) as column5
from my_source_table
group by column_1, column2
Assuming "row 1, row 2, row 3" has some meaning, you must have another column somewhere that shows the order. In my setup below, I assume you have an "id" (not necessarily consecutive numbers starting from 1; it can be number, or it can be date, etc. - for illustration I use some random numbers, they can also be fractional, or negative etc. - I build "row numbers" form that). If you don't have ANY kind of ordering, in the "order by" clause for the row_number() function you can simply "order by 1". However, I assume you also want to capture the minimum value of the "id" from each group of six (again, the id may be a date or timestamp or whatever).
The strategy is to create "row numbers". It works better if everything starts from 0, so I subtract 1 from the row_number() values. Then group by trunc(rn/6) and order by mod(rn, 6).
To concatenate the strings use listagg. I assume you wanted the strings concatenated with NO separator between them; if you do want them separated, for example, by #, then change the second argument to listagg from '' to '#'.
Query (including setup data, I shortened the input strings and I added a few more to show how everything works):
with test_data(id, input_str) as (
select 1, '12554861 40825825 IDF+0140000000+|' from dual union all
select 2, '0000000+00052+0000000+0000000+|' from dual union all
select 4, '00000 000+00000+0+00000+000+0000000+|' from dual union all
select 9, '00000+0000000+000000000000 M5|' from dual union all
select 14, '000000 +000000000011 Y 000000000+|' from dual union all
select 23, '000000IABMM81 15067195 0000003228870|' from dual union all
select 31, '125508825 IDF+0143333300000+|' from dual union all
select 32, '0000+052+0000AXZC000+0000000+|' from dual union all
select 37, '000MMDOQ000+0+00000+0000000+0000000+|' from dual union all
select 45, '000000 +00000000001011 Y 000000000+|' from dual union all
select 46, '00000+0000FIAB1 15067195 000CCV70|' from dual
),
has_rn (id, input_str, rn) as (
select id, input_str, row_number() over (order by id) - 1
from test_data
)
select min(id) as id,
listagg(input_str, '') within group (order by mod(rn, 6)) as output_str
from has_rn
group by trunc(rn/6);
Output (using the sample inputs in the test_data cte):
ID OUTPUT_STR
-- -------------------------------------------------------------------------------------------------------------------
1 12554861 40825825 IDF+0140000000+|0000000+00052+0000000+0000000+|00000 000+00000+0+00000+000+0000000+|00000+0000000+000000000000 M5|000000 +000000000011 Y 000000000+|000000IABMM81 15067195 0000003228870|
31 125508825 IDF+0143333300000+|0000+052+0000AXZC000+0000000+|000MMDOQ000+0+00000+0000000+0000000+|000000 +00000000001011 Y 000000000+|00000+0000FIAB1 15067195 000CCV70|

duplicating entries in listagg function

i have a table in which two fields are id, controlflag.It looks like
Id CntrlFlag
121 SSSSSRNNNSSRSSNNR
122 SSSNNRRSSNNRSSSSS
123 RRSSSNNSSSSSSSSSSSSSSS
I have to get output in the following form( the occurences of R)
Id Flag
121 6,12,17
122 6,7,12
123 1,2
I tried oracle query( as i obtained from this forum):
select mtr_id,listagg(str,',') within group (order by lvl) as flags from
( select mtr_id, instr(mtr_ctrl_flags,'R', 1, level) as str, level as lvl
from mer_trans_reject
connect by level <= regexp_count(mtr_ctrl_flags, 'R'))group by mtr_id;
it gives the result but 2nd and 3rd occurrences(not 1st one) are duplicated a no. of times.
it looks like
id Flag
123 6,12,12,12,12,17,17,17,17,17.
Can anybody know what's wrong here?
It could be avoided by select distinct keyword.Is there any other way?
Yes, there is, but this one is a little bit heavier(distinct will cost you less):
with t1(Id1, CntrlFlag) as(
select 121, 'SSSSSRNNNSSRSSNNR' from dual union all
select 122, 'SSSNNRRSSNNRSSSSS' from dual union all
select 123, 'RRSSSNNSSSSSSSSSSSSSSS' from dual
)
select w.id1
, listagg(w.r_pos, ',') within group(order by w.id1) as R_Positions
from (select q.id1
, regexp_instr(q.CntrlFlag,'R', 1, t.rn) as r_pos
from t1 q
cross join (select rownum rn
from(select max (regexp_count(CntrlFlag, 'R')) ml
from t1
)
connect by level <= ml
) t
) w
where w.r_pos > 0
group by w.id1
Result:
ID1 R_POSITIONS
---------- -----------
121 12,17,6
122 12,6,7
123 1,2

Resources