Related
My goal is to make a function to select a data in Tableau with vertical and horizontal coordinates. How to make my function body?
The function should select column dynamically according to function parameter I_HORIZENT, which I have no idea how to handle with.
For example, if vertical coordinate is 'E1' and horizontal coordinate is 'RP'
then the function should return '12'. Vertical coordinate is the one column K1.
Horizontal coordinate is the one row with column NUM = 0.
NUM K1 K2 K3 K4
--- --- ---- ---- ----
0 RK RP RN
1 E1 10 12 11
2 E2 21 23 19
Table and My Function are as below
CREATE TABLE DATAA (
NUM VARCHAR2(15),
K1 VARCHAR2(15),
K2 VARCHAR2(15),
K3 VARCHAR2(15),
K4 VARCHAR2(15)
);
INSERT INTO DATAA VALUES('0',NULL,'RK','RP','RN');
INSERT INTO DATAA VALUES('1','E1','10','12','11');
INSERT INTO DATAA VALUES('2','E2','21','23','19');
SELECT * FROM DATAA;
CREATE OR REPLACE FUNCTION MYFUN
(
I_VERTICAL IN VARCHAR2.
I_HORIZENT IN VARCHAR2
)RETURN VARCHAR2 AS V_VALUE VARCHAR2;
BEGIN
---
? --How to make my function body?
---
RETURN V_VALUE;
END;
You don't need dynamic SQL for this; you can use UNPIVOT:
WITH unpivoted ( num, col_name, col_index, value ) AS (
SELECT *
FROM DATAA
UNPIVOT ( value FOR col_index IN ( K2 AS 2, K3 AS 3, K4 AS 4 ) )
)
SELECT r.value
FROM ( SELECT * FROM unpivoted WHERE value = 'RP' AND num = '0' ) c
INNER JOIN
( SELECT * FROM unpivoted WHERE col_name = 'E1' ) r
ON ( r.col_index = c.col_index )
Outputs:
| VALUE |
| :---- |
| 12 |
db<>fiddle here
If you want to wrap it into a function then:
CREATE OR REPLACE FUNCTION MYFUN
(
I_VERTICAL IN VARCHAR2,
I_HORIZONTAL IN VARCHAR2
) RETURN VARCHAR2
AS
V_VALUE VARCHAR2(15);
BEGIN
WITH unpivoted ( num, col_name, col_index, value ) AS (
SELECT *
FROM DATAA
UNPIVOT ( value FOR col_index IN ( K2 AS 2, K3 AS 3, K4 AS 4 ) )
)
SELECT r.value
INTO V_VALUE
FROM ( SELECT * FROM unpivoted WHERE value = I_HORIZONTAL AND num = '0' ) c
INNER JOIN
( SELECT * FROM unpivoted WHERE col_name = I_VERTICAL ) r
ON ( r.col_index = c.col_index );
RETURN V_VALUE;
EXCEPTION
WHEN NO_DATA_FOUND THEN
RETURN NULL;
END;
/
and
SELECT MYFUN( 'E1', 'RP' ) FROM DUAL;
outputs:
| MYFUN('E1','RP') |
| :--------------- |
| 12 |
db<>fiddle here
I would like to distribute a set of data into a table shown below:
RANGE(days) No of days. Amount
0-30 - 0
31-180 1 4,185.78
181-365 2 74,056.86
366 and above 6 587,198.35
TOTAL 9 665,440.99
SOURCE Data
S/N START DATE Details Tran Amt END DATE
1 22/05/2015 A 448,749.14 30/06/2018
2 22/09/2015 B 4,883.02 30/06/2018
3 04/11/2015 C 45,646.27 30/06/2018
4 26/04/2016 D 42,861.99 30/06/2018
5 16/06/2016 E 23,144.23 30/06/2018
6 27/07/2016 F 21,913.70 30/06/2018
7 11/08/2017 G 61,396.94 30/06/2018
8 30/11/2017 H 12,659.92 30/06/2018
9 19/03/2018 I 4,185.78 30/06/2018
TOTAL 665,440.99
Thanks
SQL Fiddle
Oracle 11g R2 Schema Setup:
CREATE TABLE data ( SN, START_DATE, Details, TranAmt ) AS
SELECT 1, DATE '2015-05-22', 'A', 448749.14 FROM DUAL UNION ALL
SELECT 2, DATE '2015-09-22', 'B', 4883.02 FROM DUAL UNION ALL
SELECT 3, DATE '2015-11-04', 'C', 45646.27 FROM DUAL UNION ALL
SELECT 4, DATE '2016-04-26', 'D', 42861.99 FROM DUAL UNION ALL
SELECT 5, DATE '2016-06-16', 'E', 23144.23 FROM DUAL UNION ALL
SELECT 6, DATE '2016-07-27', 'F', 21913.70 FROM DUAL UNION ALL
SELECT 7, DATE '2017-08-11', 'G', 61396.94 FROM DUAL UNION ALL
SELECT 8, DATE '2017-11-30', 'H', 12659.92 FROM DUAL UNION ALL
SELECT 9, DATE '2018-03-19', 'I', 4185.78 FROM DUAL;
Query 1:
WITH end_date ( end_date ) AS (
SELECT DATE '2018-06-30' FROM DUAL
),
ranges ( first, last ) AS (
SELECT 0, 30 FROM DUAL UNION ALL
SELECT 31, 180 FROM DUAL UNION ALL
SELECT 181, 365 FROM DUAL UNION ALL
SELECT 366, NULL FROM DUAL
)
SELECT NVL2(
r.first,
r.first || NVL2(
MAX( r.last ),
' - ' || MAX( r.last ),
' and above'
),
'Total'
) AS range,
COUNT( d.sn ) AS "No of days",
COALESCE( SUM( d.TranAmt ), 0 ) AS Amount
FROM data d
CROSS JOIN end_date e
RIGHT OUTER JOIN ranges r
ON ( d.start_date < e.end_date + 1 - r.first
AND ( r.last IS NULL
OR d.start_date >= e.end_date - r.last ) )
GROUP BY ROLLUP( r.first )
ORDER BY r.first
Results:
| RANGE | No of days | AMOUNT |
|---------------|------------|-----------|
| 0 - 30 | 0 | 0 |
| 31 - 180 | 1 | 4185.78 |
| 181 - 365 | 2 | 74056.86 |
| 366 and above | 6 | 587198.35 |
| Total | 9 | 665440.99 |
I have the following requirement.
Do we have direct functions available in oracle 12c to accomplish this.
create table t1(input_name varchar2(500),input_values varchar2(500));
insert into t1 values('a,b,c,d,','1,2,3,4');
insert into t1 values('e,f,g,','5,6,7');
insert into t1 values('a1,b1,c1,d1,','11,12,13,14');
insert into t1 values('d,c,b,a,','100,200,300,400');
commit;
select * from t1;
INPUT_NAME INPUT_VALUES
------------------------------ ----------------
a,b,c,d, 1,2,3,4
e,f,g, 5,6,7
a1,b1,c1,d1, 11,12,13,14
d,c,b,a, 100,200,300,400
output:
a b c d e f g a1 b1 c1 d1
1 2 3 4 5 6 7 11 12 13 14
400 300 200 100
Thanks,
Rahmat Ali
Yes... if you have a known set of input names. But you would be better reorganising your data so that you are not storing correlated pairs of comma-separated lists.
SQL Fiddle
Oracle 11g R2 Schema Setup:
create table t1(input_name,input_values) AS
SELECT 'a,b,c,d,','1,2,3,4' FROM DUAL UNION ALL
SELECT 'e,f,g,','5,6,7' FROM DUAL UNION ALL
SELECT 'a1,b1,c1,d1,','11,12,13,14' FROM DUAL UNION ALL
SELECT 'd,c,b,a,','100,200,300,400' FROM DUAL
/
CREATE TYPE pair IS OBJECT(
name VARCHAR2(20),
value VARCHAR2(20)
)
/
CREATE TYPE pair_table IS TABLE OF PAIR
/
Query 1:
SELECT MAX( CASE name WHEN 'a' THEN value END ) AS a,
MAX( CASE name WHEN 'b' THEN value END ) AS b,
MAX( CASE name WHEN 'c' THEN value END ) AS c,
MAX( CASE name WHEN 'd' THEN value END ) AS d,
MAX( CASE name WHEN 'e' THEN value END ) AS e,
MAX( CASE name WHEN 'f' THEN value END ) AS f,
MAX( CASE name WHEN 'g' THEN value END ) AS g,
MAX( CASE name WHEN 'a1' THEN value END ) AS a1,
MAX( CASE name WHEN 'b1' THEN value END ) AS b1,
MAX( CASE name WHEN 'c1' THEN value END ) AS c1,
MAX( CASE name WHEN 'd1' THEN value END ) AS d1
FROM (
SELECT v.name,
v.value,
ROW_NUMBER() OVER ( PARTITION BY v.name ORDER BY ROWNUM ) AS rn
FROM t1 t
CROSS JOIN
TABLE(
CAST(
MULTISET(
SELECT pair(
REGEXP_SUBSTR( t.input_name, '([^,]+)(,|$)', 1, LEVEL, NULL, 1 ),
REGEXP_SUBSTR( t.input_values, '([^,]+)(,|$)', 1, LEVEL, NULL, 1 )
)
FROM DUAL
CONNECT BY level <= REGEXP_COUNT( t.input_name, '([^,]+)(,|$)' )
) AS pair_table
)
) v
)
GROUP BY rn
Results:
| A | B | C | D | E | F | G | A1 | B1 | C1 | D1 |
|-----|-----|-----|-----|--------|--------|--------|--------|--------|--------|--------|
| 1 | 2 | 3 | 4 | 5 | 6 | 7 | 11 | 12 | 13 | 14 |
| 400 | 300 | 200 | 100 | (null) | (null) | (null) | (null) | (null) | (null) | (null) |
You can also use a PIVOT statement rather than multiple MAX( CASE ... END ) statements.
If you do not have a static set of input names then you will need to search for dynamic pivots.
Update:
Is there a way where I can avoid using types?
You can avoid creating types and just use a built-in VARRAY or collection like SYS.ODCIVARCHAR2LIST but then you will need two lists and it becomes complicated to correlate between the two.
WITH input_names ( rid, idx, name ) AS (
SELECT t.ROWID,
ROW_NUMBER() OVER ( PARTITION BY t.ROWID ORDER BY ROWNUM ) AS rn,
v.COLUMN_VALUE
FROM t1 t
CROSS JOIN
TABLE(
CAST(
MULTISET(
SELECT REGEXP_SUBSTR( t.input_name, '([^,]+)(,|$)', 1, LEVEL, NULL, 1 )
FROM DUAL
CONNECT BY level <= REGEXP_COUNT( t.input_name, '([^,]+)(,|$)' )
) AS SYS.ODCIVARCHAR2LIST
)
) v
),
input_values ( rid, idx, value ) AS (
SELECT t.ROWID,
ROW_NUMBER() OVER ( PARTITION BY t.ROWID ORDER BY ROWNUM ) AS rn,
v.COLUMN_VALUE
FROM t1 t
CROSS JOIN
TABLE(
CAST(
MULTISET(
SELECT REGEXP_SUBSTR( t.input_values, '([^,]+)(,|$)', 1, LEVEL, NULL, 1 )
FROM DUAL
CONNECT BY level <= REGEXP_COUNT( t.input_values, '([^,]+)(,|$)' )
) AS SYS.ODCIVARCHAR2LIST
)
) v
),
correlated ( name, value, rn ) AS (
SELECT n.name,
v.value,
ROW_NUMBER() OVER ( PARTITION BY n.name
ORDER BY ROWNUM )
FROM input_names n
INNER JOIN
input_values v
ON ( n.rid = v.rid AND n.idx = v.idx )
)
SELECT MAX( CASE name WHEN 'a' THEN value END ) AS a,
MAX( CASE name WHEN 'b' THEN value END ) AS b,
MAX( CASE name WHEN 'c' THEN value END ) AS c,
MAX( CASE name WHEN 'd' THEN value END ) AS d,
MAX( CASE name WHEN 'e' THEN value END ) AS e,
MAX( CASE name WHEN 'f' THEN value END ) AS f,
MAX( CASE name WHEN 'g' THEN value END ) AS g,
MAX( CASE name WHEN 'a1' THEN value END ) AS a1,
MAX( CASE name WHEN 'b1' THEN value END ) AS b1,
MAX( CASE name WHEN 'c1' THEN value END ) AS c1,
MAX( CASE name WHEN 'd1' THEN value END ) AS d1
FROM correlated
GROUP BY rn;
I've source data Like this
Childid | Parent ID
------- | ---------
1 | NULL
2 | 1
3 | 1
4 | 2
5 | 4
6 | 5
7 | 6
I need an oracle query to show the out put like this.
Child | L1Parent | l2Parent | L3Parent | L4Parent | L5Parent
----- | -------- | -------- | -------- | -------- | --------
1 | NULL
2 | 1
3 | 1
4 | 1 | 2
5 | 1 | 2 | 4
6 | 1 | 2 | 4 | 5
7 | 1 | 2 | 4 | 5 | 6
This seems to me easier:
SELECT childId,
trim(SYS_CONNECT_BY_PATH(decode(level,2,parentId,''), ' ')) AS L1,
trim(SYS_CONNECT_BY_PATH(decode(level,3,parentId,''), ' ')) AS L2,
trim(SYS_CONNECT_BY_PATH(decode(level,4,parentId,''), ' ')) AS L3,
trim(SYS_CONNECT_BY_PATH(decode(level,5,parentId,''), ' ')) AS L4,
trim(SYS_CONNECT_BY_PATH(decode(level,6,parentId,''), ' ')) AS L5,
trim(SYS_CONNECT_BY_PATH(decode(level,7,parentId,''), ' ')) AS L6,
trim(SYS_CONNECT_BY_PATH(decode(level,8,parentId,''), ' ')) AS L7
FROM table_name
START WITH parentId is null
CONNECT BY PRIOR childId = parentId;
Oracle Setup:
CREATE TABLE table_name ( childId, parentId ) AS
SELECT 1, NULL FROM DUAL UNION ALL
SELECT 2, 1 FROM DUAL UNION ALL
SELECT 3, 1 FROM DUAL UNION ALL
SELECT 4, 2 FROM DUAL UNION ALL
SELECT 5, 4 FROM DUAL UNION ALL
SELECT 6, 5 FROM DUAL UNION ALL
SELECT 7, 6 FROM DUAL;
Query:
SELECT childId,
CASE WHEN p02 = 1 THEN NULL WHEN p03 = 1 THEN SUBSTR( path, p02 ) ELSE SUBSTR( path, p02, p03 - p02 - 1 ) END AS Lp1,
CASE WHEN p03 = 1 THEN NULL WHEN p04 = 1 THEN SUBSTR( path, p03 ) ELSE SUBSTR( path, p03, p04 - p03 - 1 ) END AS lp2,
CASE WHEN p04 = 1 THEN NULL WHEN p05 = 1 THEN SUBSTR( path, p04 ) ELSE SUBSTR( path, p04, p05 - p04 - 1 ) END AS lp3,
CASE WHEN p05 = 1 THEN NULL WHEN p06 = 1 THEN SUBSTR( path, p05 ) ELSE SUBSTR( path, p05, p06 - p05 - 1 ) END AS lp4,
CASE WHEN p06 = 1 THEN NULL WHEN p07 = 1 THEN SUBSTR( path, p06 ) ELSE SUBSTR( path, p06, p07 - p06 - 1 ) END AS lp5,
CASE WHEN p07 = 1 THEN NULL WHEN p08 = 1 THEN SUBSTR( path, p07 ) ELSE SUBSTR( path, p07, p08 - p07 - 1 ) END AS lp6
FROM (
SELECT childId,
path,
INSTR( path, '/', 1, 2 ) + 1 AS p02,
INSTR( path, '/', 1, 3 ) + 1 AS p03,
INSTR( path, '/', 1, 4 ) + 1 AS p04,
INSTR( path, '/', 1, 5 ) + 1 AS p05,
INSTR( path, '/', 1, 6 ) + 1 AS p06,
INSTR( path, '/', 1, 7 ) + 1 AS p07,
INSTR( path, '/', 1, 8 ) + 1 AS p08
FROM (
SELECT childId,
SYS_CONNECT_BY_PATH( parentId, '/') AS path
FROM table_name
START WITH parentId IS NULL
CONNECT BY PRIOR childId = parentId
)
);
Output:
CHILDID LP1 LP2 LP3 LP4 LP5 LP6
---------- --- --- --- --- --- ---
1
2 1
4 1 2
5 1 2 4
6 1 2 4 5
7 1 2 4 5 6
3 1
Query 2 - Regular Expressions:
SELECT childId,
TO_NUMBER( REGEXP_SUBSTR( path, '/([^/]*)', 1, 2, NULL, 1 ) ) AS lp1,
TO_NUMBER( REGEXP_SUBSTR( path, '/([^/]*)', 1, 3, NULL, 1 ) ) AS lp2,
TO_NUMBER( REGEXP_SUBSTR( path, '/([^/]*)', 1, 4, NULL, 1 ) ) AS lp3,
TO_NUMBER( REGEXP_SUBSTR( path, '/([^/]*)', 1, 5, NULL, 1 ) ) AS lp4,
TO_NUMBER( REGEXP_SUBSTR( path, '/([^/]*)', 1, 6, NULL, 1 ) ) AS lp5,
TO_NUMBER( REGEXP_SUBSTR( path, '/([^/]*)', 1, 7, NULL, 1 ) ) AS lp6,
TO_NUMBER( REGEXP_SUBSTR( path, '/([^/]*)', 1, 8, NULL, 1 ) ) AS lp7
FROM (
SELECT childId,
SYS_CONNECT_BY_PATH( parentId, '/') AS path
FROM table_name
START WITH parentId IS NULL
CONNECT BY PRIOR childId = parentId
);
I have a problem in converting a date value stored in a blob field in Oracle 11g sql command. When i execute the sql:
select dump(HIGH_VALUE) from all_tab_columns where COLUMN_NAME='TARIH'
i receive the following result;
Typ=23 Len=7: 120,116,3,6,1,1,1
I know that these numbers represent a date (not datetime), but i don't know how to extract the date from this result.
Thanks in advance,
Alper
Oracle stores dates in tables as 7-bytes
byte 1 - century + 100
byte 2 - (year MOD 100 ) + 100
byte 3 - month
byte 4 - day
byte 5 - hour + 1
byte 6 - minute + 1
byte 7 - seconds+ 1
So 120,116,3,6,1,1,1 converts to:
byte 1 - century = 120 - 100 = 20
byte 2 - year = 116 - 100 = 16
byte 3 - month = 3
byte 4 - day = 6
byte 5 - hour = 1 - 1 = 0
byte 6 - minute = 1 - 1 = 0
byte 7 - seconds = 1 - 1 = 0
So 2016-03-06T00:00:00
Oracle Setup:
CREATE TABLE file_upload ( file_blob BLOB );
INSERT INTO file_upload VALUES (
utl_raw.cast_to_raw(
CHR(120) || CHR(116) || CHR(3) || CHR(6) || CHR(1) || CHR(1) || CHR(1)
)
);
Query:
SELECT DUMP( DBMS_LOB.SUBSTR( file_blob, 7, 1 ) ) AS dmp,
TO_DATE(
TO_CHAR(
( ASCII( SUBSTR( chars, 1, 1 ) ) - 100 ) * 100
+ ASCII( SUBSTR( chars, 2, 1 ) ) - 100,
'0000'
)
|| TO_CHAR( ASCII( SUBSTR( chars, 3, 1 ) ), '00' )
|| TO_CHAR( ASCII( SUBSTR( chars, 4, 1 ) ), '00' )
|| TO_CHAR( ASCII( SUBSTR( chars, 5, 1 ) ) - 1, '00' )
|| TO_CHAR( ASCII( SUBSTR( chars, 6, 1 ) ) - 1, '00' )
|| TO_CHAR( ASCII( SUBSTR( chars, 7, 1 ) ) - 1, '00' ),
'YYYYMMDDHH24MISS'
) AS converted_date
FROM (
SELECT file_blob,
UTL_RAW.CAST_TO_VARCHAR2(DBMS_LOB.SUBSTR( file_blob, 7, 1 ) ) AS chars
FROM file_upload
);
Output:
DMP CONVERTED_DATE
------------------------------- -------------------
Typ=23 Len=7: 120,116,3,6,1,1,1 2016-03-06 00:00:00