Oracle Order by array index - oracle

I have a collection which is a table of numbers containing primary keys and I want to sort a select statement - containing those keys - by the index of the number collection.
For example:
TYPE "NUMBERCOLLECTION" AS TABLE OF NUMBER;
...
myNumberCollection numberCollection := numberCollection(45, 7799, 2187);
...
select nr
, columnA
, columnB
from myTable
where myTable.nr member of myNumberCollection
order by index of myNumberCollection
will result in
NR COLUMNA COLUMNB
-----------------------
45 xyz abc
7799 xyz abc
2187 xyz abc

SELECT nr,
columnA,
columnB
FROM myTable m
INNER JOIN
( SELECT ROWNUM AS id, COLUMN_VALUE FROM TABLE( myNumberCollection ) ) c
ON ( m.nr = c.COLUMN_VALUE )
ORDER BY c.id;

Related

Oracle - Replace null values for pivot columns with join from another table

I've a pivot table output and now I want to check for the values from the pivot columns and replace the values if null from another column from another table.
Invoice_No
Column
value
111
A
One
111
B
Two
111
C
Three
111
E
Five
(SELECT Invoice_No, new_value, Column_Name FROM table_name)
PIVOT(max(new_value)
FOR Column_Name IN ('A','B','C','D','E'))
This returned the following table
Invoice_No
'A'
'B'
'C'
'D'
'E'
111
One
Two
Three
null
Five
Now, I want to replace the null value from column D with a value from another table that matches the Invoice_no.
with temp as
(SELECT Invoice_No, new_value, Column_Name FROM table_name)
PIVOT(max(new_value)
FOR Column_Name IN ('A','B','C','D','E'))
select nvl(temp.D,bckup.D)
from
(select A,B,C,D,E from Backup_table) bckup
join
temp
on
temp.Invoice_No = bckup = Invoice_No
Now, I'm getting the error saying D Column does not exist.
Pivot will rename your column as 'D' not D only. So, You need a simple update in your query as -
WITH temp AS(SELECT *
FROM(SELECT Invoice_No, new_value, Column_Name
FROM table_name)
PIVOT(max(new_value) FOR Column_Name IN ('A' AS A,'B' AS B,'C' AS C,'D' AS D,'E' AS E)
)
SELECT NVL(temp.D,bckup.D)
FROM(SELECT A,B,C,D,E
FROM Backup_table) bckup
JOIN temp ON temp.Invoice_No = bckup.Invoice_No

Getting rows that are having distinct id with bulk collect - Oracle

I want to get the records that are having distinct member id using bulk collect.
My query is like below:
...
type members_nt is table of table2%rowtype;
members_bc members_nt;
...
select t2.* bulk collect
into members_bc
from table1 t1
,table2 t2
where t1.isn= t2.isn
and t1.customer= customer
and t1.insert_date between ... and ... );
t2 table has a column named member_id and what I want to get into members_bc is the rows having distinct member_id.
For example if my t2 table has something like this:
name | surname | account | member_id
john alby abc 123
john alby def 123
mary hale lkj 234
I want to bulk collect only
name | surname | account | member_id
john alby abc 123
mary hale lkj 234
or
name | surname | account | member_id
john alby def 123
mary hale lkj 234
It does not matter which one. But memberid must be unique in the members_bc.
How can I achieve this? Thank you.
Use the ROW_NUMBER() analytic function to give each row a number per member_id and then filter to only get the first row.
DECLARE
TYPE members_nt IS TABLE OF table2%ROWTYPE;
members_bc members_nt;
BEGIN
SELECT t2.name, t2.surname, t2.account, t2.member_id
BULK COLLECT INTO members_bc
FROM (
SELECT t2.*,
ROW_NUMBER() OVER ( PARTITION BY member_id ORDER BY ROWNUM ) AS rn
FROM table2 t2
) t2
WHERE rn = 1;
FOR i IN 1 .. members_bc.COUNT LOOP
DBMS_OUTPUT.PUT_LINE( members_bc(i).member_id || ' ' || members_bc(i).name );
END LOOP;
END;
/
outputs:
123 john
234 mary
db<>fiddle here
You can avoid "select *" and just enter the desired columns by creating a cursor for your select and then creating a %rowtype of the cursor.
Using #MT0 answer as baseline template.)
declare
cursor members_cur is
select t2.name, t2.surname, t2.account, t2.member_id
from ( select t2.name, t2.surname, t2.account, t2.member_id
, row_number() over ( partition by member_id order by rownum ) as rn
from table2 t2
) t2
where rn = 1;
type members_tt is table of members_cur%rowtype;
members_bc members_tt;
begin
open members_cur;
fetch members_cur
bulk collect into members_bc;
close members_cur;
for i in 1 .. members_bc.count
loop
dbms_output.put_line(members_bc(i).member_id || ' ' ||members_bc(i).name);
end loop;
end;
A simple option is to use aggregation, e.g.
select name,
surname,
min(account) as account, --> this
member_id
from ...
group by name, surname, member_id --> and this

Concat Results of 2 Select Queries into 1 Column (oracle)

Im trying to insert a record into my table. But there is 1 column in which I want to get concatenated results of 2 select statements. Like the 2 statements will fetch their records and concatenate to form 1 value so that it can be inserted into the column.
insert into ABC (Name,City,Age)
Values ('John',(
(Select City from TableA where ID=1)concat(Select City from TableA where ID=2)),'22')
Or it can be comma separated but I am not getting what to use here.
Try this one:
INSERT INTO ABC (Name, City, Age)
VALUES ('John',
(
(SELECT City FROM TableA WHERE ID = 1) ||
(SELECT City FROM TableA WHERE ID = 2)
),
'22');
But ensure ... WHERE ID = 1 and ....WHERE ID = 2 return one row.
Using a cross join to select from the two tables produces a nice clear statement:
insert into ABC (Name,City,Age)
select 'John', concat(t1.city, t2.city), 22
from TableA t1
cross join TableA t2
where t1.ID = 1
and t2.ID = 2
/
Use CONCAT() or CONCAT_WS() functions for this (reference)
insert into ABC (Name,City,Age) Values (
'John',
CONCAT_WS(' ', (Select City from TableA where ID=1), (Select City from TableA where ID=2)),
'22'
)

How to put 2 rows returned from a query into into 2 columns of a single row in Oracle sql

I run a query that returns 2 rows
SELECT a FROM TABLE-A WHERE condition=something;
row1 Value1
row2 Value2
Now, I want to put it in a new table in 2 columns of a single row
SELECT column1, column2 FROM TABLE-B WHERE condition=something;
row1 column1 column2
value1 value2
Can you please help me with this?
It is unclear how you want to pick which row goes into which column - here are a couple of options:
SELECT MIN( a ) AS minimum_value,
MAX( a ) AS maximum_value
FROM table_a
WHERE 1=1;
or
SELECT MAX( a ) KEEP ( DENSE_RANK FIRST ORDER BY ROWNUM ) AS first_value,
MAX( a ) KEEP ( DENSE_RANK LAST ORDER BY ROWNUM ) AS last_value
FROM table_a
WHERE 1=1;
or
SELECT *
FROM ( SELECT a, ROWNUM AS rn FROM table_a WHERE 1=1 )
PIVOT ( MAX(a) FOR rn IN ( 1 AS first_value, 2 AS second_value ) );

Oracle: Get column names of Top n values over multiple columns

My question is similar to
https://community.oracle.com/message/4418327
in my query i need the MAX value of 3 different columns.
example: column 1 = 10, column 2 = 20, column 3 = 30 > output should
be 30. i need this MAX value to sort the list by it.
However instead of the actually value I need the column name and ideally not just the max one but top 3 as example.
The desired output would then be
ID first second third
-------------------------------
1 column 3 column 2 column1
There is no built-in function for what you are asking. So user1578653's answer is good, straight-forward and fast. Another way would be to write a function with PL/SQL.
In case you want to use pure SQL, but want it easier to add columns to the comparision, then if you are satisfied with just two columns (id and column names string), you can do this:
select id, listagg(colname, ', ') within group (order by value desc) as columns
from
(
select id, 'column1' as colname, col1 as value from mytable
union all
select id, 'column2' as colname, col2 as value from mytable
union all
select id, 'column3' as colname, col3 as value from mytable
-- add more columns here, if you wish
)
group by id;
Be aware this is slower than user1578653's SQL statement, as the table is being read three times. (I also treat equal values differently here, same values lead to random order.)
You can use PIVOT/UNPIVOT for this.
Fisrt UNPIVOT your table and assign rank with values ordered in descending order.
create table sample(
id number,
col1 number,
col2 number,
col3 number
);
insert into sample values(1,10,20,30);
insert into sample values(2,10,20,15);
select id, col_name, val,
row_number() over (partition by id order by val desc) r
from sample
unpivot(val for col_name in (
col1 AS 'col1', col2 AS 'col2', col3 AS 'col3')
);
Output:
| ID | COL_NAME | VAL | R |
|----|----------|-----|---|
| 1 | col3 | 30 | 1 |
| 1 | col2 | 20 | 2 |
| 1 | col1 | 10 | 3 |
| 2 | col2 | 20 | 1 |
| 2 | col3 | 15 | 2 |
| 2 | col1 | 10 | 3 |
sqlfiddle.
Next PIVOT the col_name based on the rank column.
with x(id, col_name, val,r) as (
select id, col_name, val,
row_number() over (partition by id order by val desc)
from sample
unpivot(val for col_name in (
col1 AS 'col1', col2 AS 'col2', col3 AS 'col3')
)
)
select * from (
select id, col_name, r
from x
)
pivot(max(col_name) for r in (
1 as first, 2 as second, 3 as third)
);
Output:
| ID | FIRST | SECOND | THIRD |
|----|-------|--------|-------|
| 1 | col3 | col2 | col1 |
| 2 | col2 | col3 | col1 |
sqlfiddle.
Not sure if there is a better way of doing this, but here's a possible solution using the CASE statement. Here's the table structure I've tested it on:
CREATE TABLE MAX_COL(
"ID" INT,
COLUMN_1 INT,
COLUMN_2 INT,
COLUMN_3 INT
);
And here's the SQL I used:
SELECT
"ID",
CASE
WHEN COLUMN_1 > COLUMN_2 AND COLUMN_1 > COLUMN_3 THEN 'COLUMN_1'
WHEN COLUMN_2 > COLUMN_1 AND COLUMN_2 > COLUMN_3 THEN 'COLUMN_2'
WHEN COLUMN_3 > COLUMN_2 AND COLUMN_3 > COLUMN_1 THEN 'COLUMN_3'
ELSE 'NONE'
END AS "FIRST",
CASE
WHEN COLUMN_1 > COLUMN_2 AND COLUMN_1 < COLUMN_3 THEN 'COLUMN_1'
WHEN COLUMN_2 > COLUMN_1 AND COLUMN_2 < COLUMN_3 THEN 'COLUMN_2'
WHEN COLUMN_3 > COLUMN_2 AND COLUMN_3 < COLUMN_1 THEN 'COLUMN_3'
ELSE 'NONE'
END AS "SECOND",
CASE
WHEN COLUMN_1 < COLUMN_2 AND COLUMN_1 < COLUMN_3 THEN 'COLUMN_1'
WHEN COLUMN_2 < COLUMN_1 AND COLUMN_2 < COLUMN_3 THEN 'COLUMN_2'
WHEN COLUMN_3 < COLUMN_2 AND COLUMN_3 < COLUMN_1 THEN 'COLUMN_3'
ELSE 'NONE'
END AS "THIRD"
FROM
MAX_COL;
Please note that you will need to deal with situations where one or more columns have the same value. In this case I am just returning 'NONE' if this occurs, but you may want to do something else.
UPDATE
You could also use PLSQL functions to achieve this, which may be easier to use for many columns. Here's an example:
CREATE OR REPLACE
FUNCTION COLUMN_POSITION (IDVAL INT, POSITION INT) RETURN VARCHAR2 AS
TYPE COLUMN_VALUE_TYPE IS TABLE OF NUMBER INDEX BY VARCHAR2(64);
COLS COLUMN_VALUE_TYPE;
COL_KEY VARCHAR2(64);
QUERY_STR VARCHAR2(1000 CHAR) := 'SELECT COL FROM(SELECT T.*, DENSE_RANK() OVER (ORDER BY VAL DESC) AS RANK FROM (';
COL_NAME VARCHAR2(64);
BEGIN
COLS('COLUMN_1') := NULL;
COLS('COLUMN_2') := NULL;
COLS('COLUMN_3') := NULL;
--ADD MORE COLUMN NAMES HERE...
COL_KEY := COLS.FIRST;
LOOP
EXIT WHEN COL_KEY IS NULL;
QUERY_STR := QUERY_STR || 'SELECT ' || COL_KEY || ' AS VAL, '''|| COL_KEY ||''' AS COL FROM MAX_COL WHERE ID = '|| IDVAL ||' UNION ALL ';
COL_KEY := COLS.NEXT(COL_KEY);
END LOOP;
QUERY_STR := SUBSTR(QUERY_STR, 0, LENGTH(QUERY_STR) - 11);
QUERY_STR := QUERY_STR || ') T ) WHERE RANK = ' || POSITION;
EXECUTE IMMEDIATE QUERY_STR INTO COL_NAME;
RETURN COL_NAME;
END;
This example has only 3 columns but you could easily add more. You can then use this in a query like this:
SELECT
MAX_COL.*,
COLUMN_POSITION(ID, 1) AS "FIRST",
COLUMN_POSITION(ID, 2) AS "SECOND",
COLUMN_POSITION(ID, 3) AS "THIRD"
FROM MAX_COL
SELECT
(CASE
WHEN A > B AND A > C THEN 'A'
WHEN B > A AND B > C THEN 'B'
WHEN C > B AND C > A THEN 'C'
END) AS "MaxValue_Column_Name",
greatest(A,B,C) AS Max_Value FROM max_search ;
--table name =max_search and column_name are A,B and C

Resources