How to achieve string concatentation of entries in column having same id in Oracle Analytics Cloud Professional Edition? - oracle

I have a dataset in which one column is Branch-ID and other one is Branch Manager and it looks as follows in the given url.
dataset
I want to combine the branch managers into one single column based on the branch-id. For example if Bob and Sandra are two different branch-managers but have the same branch id which is branch-id=1, then we should concatenate them together as Bob-Sandra and place them in a separately created column.
I have attached the expected output for the above dataset. expected_output_dataset
I am currently using Oracle Analytics Cloud Professional Version.

I don't know Oracle Analytics, but - if it has anything to do with an Oracle database and its capabilities, then listagg helps.
Sample data in lines #1 - 10; query you might be interested in begins at line #11.
SQL> with test (account_id, branch_id, branch_manager) as
2 (select 1, 123, 'Sandra' from dual union all
3 select 3, 124, 'Martha' from dual union all
4 select 4, 125, 'John' from dual union all
5 select 6, 126, 'Andrew' from dual union all
6 select 7, 126, 'Mathew' from dual union all
7 select 2, 123, 'Michael' from dual union all
8 select 5, 125, 'David' from dual union all
9 select 8, 126, 'Mark' from dual
10 )
11 select a.account_id, a.branch_id, a.branch_manager,
12 b.concatenated_column
13 from test a join (select branch_id,
14 listagg(branch_manager, '-') within group (order by null) concatenated_column
15 from test
16 group by branch_id
17 ) b on b.branch_id = a.branch_id;
ACCOUNT_ID BRANCH_ID BRANCH_ CONCATENATED_COLUMN
---------- ---------- ------- -------------------------
1 123 Sandra Michael-Sandra
3 124 Martha Martha
4 125 John David-John
6 126 Andrew Andrew-Mark-Mathew
7 126 Mathew Andrew-Mark-Mathew
2 123 Michael Michael-Sandra
5 125 David David-John
8 126 Mark Andrew-Mark-Mathew
8 rows selected.
SQL>

Related

How to select the first 5 dates from each group and put them in a single column separated by comma in Oracle?

I have a table like this:
Division
Region
Date of Last Visit
1
2
11/20/2021
1
2
11/18/2021
1
7
10/18/2021
1
7
11/19/2021
2
2
11/17/2021
2
3
09/20/2021
2
3
10/20/2021
I want to write a query that groups by the division and region columns and gives me the last 5 dates for each group separated by commas in a single column. Something like this:
Division
Region
Date of Last Visit
Today
Days since last visit
1
2
11/20/2021, 11/18/2021
sysdate
sysdate - max(date of last visit)
1
7
10/18/2021, 11/19/2021
sysdate
sysdate - max(date of last visit)
2
2
11/17/2021
sysdate
sysdate - max(date of last visit)
2
3
9/20/2021, 10/20/2021
sysdate
sysdate - max(date of last visit)
The last two columns are custom calculated columns that I also need for the final output table. Any help would be greatly appreciated as I have tried a lot of things but I keep getting errors about it not being grouped properly, possibly because of the two extra columns at the end. But even without that, I am not sure how to fetch only the last 5 dates per group in oracle.
Thanks!
You want to filter the greatest-n-per-group using the ROW_NUMBER analytic function and then aggregate:
SELECT division,
region,
LISTAGG(TO_CHAR(date_of_last_visit, 'DD/MM/YYYY'), ',')
WITHIN GROUP (ORDER BY date_of_last_visit DESC)
AS date_of_last_visit,
SYSDATE AS today,
TRUNC(SYSDATE - MAX(date_of_last_visit)) AS days_since_last_visit
FROM (
SELECT t.*,
ROW_NUMBER() OVER (PARTITION BY division, region
ORDER BY date_of_last_visit DESC) AS rn
FROM table_name t
)
WHERE rn <= 5
GROUP BY
division,
region
Which, for the sample data:
CREATE TABLE table_name (division, region, date_of_last_visit) as
select 1, 2, date '2021-11-20' from dual union all
select 1, 2, date '2021-11-18' from dual union all
select 1, 7, date '2021-10-18' from dual union all
select 1, 7, date '2021-11-19' from dual union all
select 2, 2, date '2021-11-17' from dual union all
select 2, 3, date '2021-09-20' from dual union all
select 2, 3, date '2021-10-20' from dual;
Outputs:
DIVISION
REGION
DATE_OF_LAST_VISIT
TODAY
DAYS_SINCE_LAST_VISIT
1
2
20/11/2021,18/11/2021
30-NOV-21
10
1
7
19/11/2021,18/10/2021
30-NOV-21
11
2
2
17/11/2021
30-NOV-21
13
2
3
20/10/2021,20/09/2021
30-NOV-21
41
db<>fiddle here
Here you go; read comments within code.
SQL> with test (division, region, datum) as
2 -- sample data
3 (select 1, 2, date '2021-11-20' from dual union all
4 select 1, 2, date '2021-11-18' from dual union all
5 select 1, 7, date '2021-10-18' from dual union all
6 select 1, 7, date '2021-11-19' from dual union all
7 select 2, 2, date '2021-11-17' from dual union all
8 select 2, 3, date '2021-09-20' from dual union all
9 select 2, 3, date '2021-10-20' from dual
10 ),
11 temp as
12 -- rank rows per division/region, sorted by date column in descending order
13 (select t.*,
14 rank() over (partition by division, region order by datum desc) rnk
15 from test t
16 )
17 -- select up to 5 last rows per division/region
18 select division, region,
19 listagg(datum, ', ') within group (order by datum) dates,
20 trunc(sysdate) today,
21 --
22 (select trunc(sysdate) - a.datum
23 from temp a
24 where a.division = t.division
25 and a.region = t.region
26 and a.rnk = 1) days_since
27 from temp t
28 where rnk <= 5
29 group by division, region
30 order by division, region;
DIVISION REGION DATES TODAY DAYS_SINCE
---------- ---------- ------------------------------ ---------- ----------
1 2 11/18/2021, 11/20/2021 11/30/2021 10
1 7 10/18/2021, 11/19/2021 11/30/2021 11
2 2 11/17/2021 11/30/2021 13
2 3 09/20/2021, 10/20/2021 11/30/2021 41
SQL>

how to write query to display two rows in one row from same table with different alias column names using oracle

i need to display two rows from same table with different column names. one as Current columns and other as previous columns. How to display it in one row.
SELECT BILL_ID CUR_BILL_ID, BILL_START_DT CUR_BILL_START_DT, BILL_END_DT CUR_BILL_END_DT, BILL_STATUS_CD CUR_BILL_STATUS_CD, BILL_APPROVED_BY CUR_BILL_APPROVED_BY, BILL_APPROVED_DT CUR_BILL_APPROVED_DT FROM FPM_CB_BILL_DETAILS WHERE BILL_ID = (select max(BILL_ID) from FPM_CB_BILL_DETAILS)
SELECT BILL_ID PRV_BILL_ID, BILL_START_DT PRV_BILL_START_DT, BILL_END_DT PRV_BILL_END_DT, BILL_STATUS_CD PRV_BILL_STATUS_CD, BILL_APPROVED_BY PRV_BILL_APPROVED_BY, BILL_APPROVED_DT PRV_BILL_APPROVED_DT FROM FPM_CB_BILL_DETAILS WHERE BILL_ID = (select max(BILL_ID) from FPM_CB_BILL_DETAILS) - 1
Looks like selfjoin might help.
SQL> with fpm_cb_bill_Details (bill_id, bill_status_cd) as
2 (select 100, 'status A' from dual union all
3 select 101, 'status B' from dual union all
4 select 102, 'statuc C' from dual union all
5 select 103, 'status D' from dual
6 )
7 select a.bill_id curr_bill_id,
8 a.bill_status_cd curr_status,
9 b.bill_id prev_bill_id,
10 b.bill_status_cd prev_status
11 from fpm_cb_bill_details a join fpm_cb_bill_details b on b.bill_id = a.bill_id - 1
12 where a.bill_id = (select max(bill_id) from fpm_cb_bill_details);
CURR_BILL_ID CURR_STA PREV_BILL_ID PREV_STA
------------ -------- ------------ --------
103 status D 102 statuc C
SQL>
You can use lag() analytic function to see the previous row's column values, ordering by bill_id as follows:
FSITJA#db01> with fpm_cb_bill_Details (bill_id, bill_status_cd) as
2 (select 100, 'status A' from dual union all
3 select 101, 'status B' from dual union all
4 select 102, 'statuc C' from dual union all
5 select 103, 'status D' from dual
6 )
7 select d.bill_id curr_bill_id,
8 d.bill_status_cd curr_status,
9 lag(d.bill_id) over (order by d.bill_id) prev_bill_id,
10 lag(d.bill_status_cd) over (order by d.bill_id) prev_status
11 from fpm_cb_bill_Details d;
CURR_BILL_ID CURR_STA PREV_BILL_ID PREV_STA
------------ -------- ------------ --------
100 status A
101 status B 100 status A
102 statuc C 101 status B
103 status D 102 statuc C
Thanks #Littlefoot for the sample table data.

PL SQL Group By Issue

I have two tables T1 and T2.
T1 has an ID column that is generated as a sequence.
It also has two columns first name and Last name.
The table T2 is connected to table T1 via the ID column (referential).
T2 table has a salary column, that is revised every few years.
I want to get all the first name, last name, salary , and salary date if the salary has changed.
I am not able to get this information using the ID.
A second ID is generated for the same FN and LN pair if the employee comes up for review.
For Example :-
ID FN LN
1 John Doe
2 John Doe
ID SALARY DATE
1 $1 2015
2 $2 2018
I am trying something like this
SELECT T.FN ||' '|| T.LN AS NAME, COUNT(*) AS CT,
S.SALARY, S.DATE
SALARYTABLE S, EMP T
WHERE S.ID=T.ID
HAVING COUNT(*) > 1
GROUP BY (T.FN ||' '|| T.LN);
I have solved this by using a Java program. I have to store all the ID's and loop through all the records and check if the FN and LN matches and then extract the Date and Salary. This is inefficient and I want to do it within PL/SQL.
Please help. Thank you.
Well, your data model is kind of wrong; you shouldn't rely on distinguishing people on their names. What if yet another "John Doe" gets employed?
Anyway: would something like this do?
CTEs T1 and T2 simulate your tables. I added some more rows, just to make sure that the following query doesn't fail too obviously
INTER CTE joins those two tables and calculates employee's previous salary (using the LAG function)
the final query select rows (from INTER) whose current and previous salary are different
As you already have those tables, you'd use lines 16 onwards.
SQL> with
2 t1 (id, fn, ln) as
3 (select 1, 'John', 'Doe' from dual union all
4 select 2, 'John', 'Doe' from dual union all
5 select 3, 'John', 'Doe' from dual union all
6 select 5, 'Billy', 'Jean' from dual union all
7 select 6, 'Billy', 'Jean' from dual
8 ),
9 t2 (id, salary, c_date) as
10 (select 1, 1, 2015 from dual union all
11 select 2, 2, 2018 from dual union all
12 select 3, 2, 2019 from dual union all
13 select 5, 3, 2016 from dual union all
14 select 6, 3, 2017 from dual
15 ),
16 inter as
17 (select
18 t1.id, t1.fn, t1.ln,
19 t2.id, t2.salary, t2.c_date,
20 lag(t2.salary) over (partition by t1.fn, t1.ln
21 order by c_date) prev_salary
22 from t1 join t2 on t1.id = t2.id
23 )
24 select i.fn, i.ln, i.salary, i.c_date
25 from inter i
26 where i.salary <> nvl(i.prev_salary, i.salary)
27 order by i.ln, i.c_date;
FN LN SALARY C_DATE
----- ---- ---------- ----------
John Doe 2 2018
SQL>

Getting Results in Horizontal way in oracle

I wrote query
select s_id from emp
where s_inv=12
i got results in this manner
1
2
3
4
5
but i want it in this format
1 2 3 4 5
If you need your result in a single column, you can use LISTAGG:
with emp(s_id, s_inv) as
(
select 1, 12 from dual union all
select 2, 12 from dual union all
select 3, 12 from dual union all
select 4, 12 from dual union all
select 5, 12 from dual
)
select listagg(s_id, ' ') within group (order by s_id)
from emp
where s_inv = 12
If you need to build many columns on the same row, you should first define how many columns will your result have

how to count rows based on column values null and not null

May be this question is a duplicate of another, I already explored couple of similar questions here but I didn't find similar one. Please suggest if you find a link to similar question.
My problem is, I have a table say CLIENTS as below
BRANCH CLNTID ACCNT FACID
------ ---------- ---------- ----------
201 10001 123400 110021
201 10001 123401
201 10001 123402 110023
201 10001 123403
201 10001 123404 110025
201 10001 123405
201 10001 123406 110027
201 10001 123407 110028
so on... many rows.
Now I want to write a query to give output like this
Branch clntid facid_null facid_not_null
201 10001 3 5
I want to find facid colmun count for facid=null and facid !=null for each branch and each clntid.
I wrote the below query but its fetching only one count either facid is null or facid is not null.
select branch,clntid,count(*)
from clnt
where facid is null
group by branch, clntid;
Please help me to find both counts in a single query using GROUP BY and as well as OVER (PARTITION BY ) clauses.
Thanks in advance.
Vivek.
select branch
,clntid
,count(*) as num_rows
,count(facid) as not_nulls
,count(*) - count(facid) as nulls
from clnt
group
by branch
,clntid;
The aggregate function COUNT counts all not null occurrences, so you can simply use count(facid) to count the facid_not_null column and you can use a similar technique and first swap the null and not null for the facid_null column. Here is a working example:
SQL> create table clnt (branch,clntid,accnt,facid)
2 as
3 select 201, 10001, 123400, 110021 from dual union all
4 select 201, 10001, 123401, null from dual union all
5 select 201, 10001, 123402, 110023 from dual union all
6 select 201, 10001, 123403, null from dual union all
7 select 201, 10001, 123404, 110025 from dual union all
8 select 201, 10001, 123405, null from dual union all
9 select 201, 10001, 123406, 110027 from dual union all
10 select 201, 10001, 123407, 110028 from dual
11 /
Table created.
SQL> select branch
2 , clntid
3 , count(case when facid is null then 1 end) facid_null
4 , count(facid) facid_not_null
5 from clnt
6 group by branch
7 , clntid
8 /
BRANCH CLNTID FACID_NULL FACID_NOT_NULL
---------- ---------- ---------- --------------
201 10001 3 5
1 row selected.

Resources