Oracle sql query to convert common rows to columns - oracle

I had a requirement to build a survey like application. Did this using Oracle APEX 18.2. I'm done designing the table structures and their relationships. I'm now on the part where I need to build a report (can be Interactive Report/Classic Report) where the columns will depend on what questions are available.
So basically, each question is supposed to have a column in the report. I can build the report and add these questions as column on design time. But I was wondering if there is a way to do this at runtime as well? More questions can be added in the future and I would like to report to be flexible enough to accommodate this. For now I would like to try via SQL statement.
Here's my table design for refence.
create table persons (
id number primary key
, name varchar2(100) not null
)
/
insert into persons values ( 1, 'Bruce' )
/
insert into persons values ( 2, 'Jennifer' )
/
create table questions (
id number primary key
, question varchar2(100) not null
)
/
insert into questions values ( 1, 'Gender' )
/
insert into questions values ( 2, 'Currently Employed' )
/
create table choices (
id number primary key
, choice varchar2(100) not null
)
/
insert into choices values ( 1, 'Male' )
/
insert into choices values ( 2, 'Female' )
/
insert into choices values ( 3, 'Yes' )
/
insert into choices values ( 4, 'No' )
/
create table question_choices (
id number primary key
, question_id number
, choice_id number
)
/
insert into question_choices values ( 1, 1, 1 )
/
insert into question_choices values ( 2, 1, 2 )
/
insert into question_choices values ( 3, 2, 3 )
/
insert into question_choices values ( 4, 2, 4 )
/
create table survey (
id number primary key
, name varchar2(100) not null
)
/
insert into survey values ( 1, 'Survey 1' )
/
create table survey_questions (
id number primary key
, survey_id number
, question_id number
)
/
insert into survey_questions values ( 1, 1, 1 )
/
insert into survey_questions values ( 2, 1, 2 )
/
create table survey_responses (
id number primary key
, survey_id number
, person_id number
, question_id number
, choice_id number
)
/
insert into survey_responses values ( 1, 1, 1, 1, 1 )
/
insert into survey_responses values ( 2, 1, 1, 2, 4 )
/
insert into survey_responses values ( 3, 1, 2, 1, 2 )
/
insert into survey_responses values ( 4, 1, 2, 2, 3 )
/
commit
/
I did a bit or research and found out about the pivot command. However, this approach also requires that the columns be defined at design time.
select *
from (
select s.name survey
, p.name respondent
, q.question
, c.choice
, row_number() over (partition by person_id, question order by choice ) rn
from persons p
, questions q
, choices c
, question_choices qc
, survey s
, survey_questions sq
, survey_responses sr
where p.id = sr.person_id
and q.id = sr.question_id
and c.id = qc.choice_id
and s.id = sq.survey_id
and sq.question_id = q.id
and sr.choice_id = c.id
)
pivot (max(question) question, max(choice) for question in ( 'Gender','Currently Employed' ))
Appreciate any comments/suggestions.

Related

How do you repeat rows in a table multiple time

I created a table a DAX Studio and want to repeat all the rows (with all the columns) in a new table multiple times; to my choosing.
//create temp table of Historic Facilities for all annual groups 1 to 3
TABLE HistFac =
ADDCOLUMNS ( FacSchHist,
"Rank", RANKX ( FacSchHist, [WtAvg] ),
"Annual Group", (RANKX ( FacSchHist, [WtAvg] ) - FLOOR((RANKX ( FacSchHist, [WtAvg] ) - 1) /3 * 3,3)))
EVALUATE
NonHistFac
ORDER BY [Annual Group],
[WtAvg] DESC
I want the maintain the original order in the repeated list. There are obviously many ways to construct a table (see code above); however, how does one repeat the list of rows?
Thanks you for any suggestions.
Try to remove the filters that are affecting the measure inside RANKX because of the context transition coming from the table provide in the first argument of GENERATE
DEFINE
TABLE HistFac =
GENERATE (
{ 1, 2, 3 },
ADDCOLUMNS (
ADDCOLUMNS (
FacSchHist,
"#Rank",
RANKX (
FacSchHist,
CALCULATE ( [WtAvg], REMOVEFILTERS ( TableSupliedInTheFirstArgument ) )
)
),
"Annual Group",
[#Rank]
- FLOOR ( ( [#Rank] - 1 ) / 3 * 3, 3 )
)
)
EVALUATE
NonHistFac
ORDER BY
[Annual Group],
[WtAvg] DESC
Or simply use variables to compute in a different filter context.
DEFINE
TABLE HistFac =
VAR RankedTable =
ADDCOLUMNS ( FacSchHist, "#Rank", RANKX ( FacSchHist, [WtAvg] ) )
VAR SomeCalculationOnRankedTable =
ADDCOLUMNS (
RankedTable,
"Annual Group",
[#Rank]
- FLOOR ( ( [#Rank] - 1 ) / 3 * 3, 3 )
)
RETURN
GENERATE ( { 1, 2, 3 }, SomeCalculationOnRankedTable )
EVALUATE
NonHistFac
ORDER BY
[Annual Group],
[WtAvg] DESC
You can use GENERATE for repeating the rows:
DEFINE
TABLE HistFac =
GENERATE (
{ 1, 2, 3 },
ADDCOLUMNS (
FacSchHist,
"Rank", RANKX ( FacSchHist, [WtAvg] ),
"Annual Group",
(
RANKX ( FacSchHist, [WtAvg] )
- FLOOR ( ( RANKX ( FacSchHist, [WtAvg] ) - 1 ) / 3 * 3, 3 )
)
)
)
EVALUATE
NonHistFac
ORDER BY
[Annual Group],
[WtAvg] DESC
A better way to write this code will be to use nested ADDCOLUMNS:
DEFINE
TABLE HistFac =
GENERATE (
{ 1, 2, 3 },
ADDCOLUMNS (
ADDCOLUMNS ( FacSchHist, "#Rank", RANKX ( FacSchHist, [WtAvg] ) ),
"Annual Group",
[#Rank]
- FLOOR ( ( [#Rank] - 1 ) / 3 * 3, 3 )
)
)
EVALUATE
NonHistFac
ORDER BY
[Annual Group],
[WtAvg] DESC
AntrikshSharma - I will give those a try now , appreciate your guidance.
Okay, the code worked fine; but uncovered a problem. After the initial ranking of the list, and before the replication - I do not want to continue ranking.
In other words, I want the initial ranked-list to be reproduced 3x without any further ranking; just have each duplicated occurrence appear after the initial list in the exact order.
If the initial list was 12345, then 12345,12345,12345 - thank you for the more efficient code as well.
AntrikshSharma, your comment and code, "Or simply use variables to compute in a different filter context" is genius. The code performed as expected. The problem I'm experiencing is with the UNION of the two tables "NonHistFac" and "HistFac". I will provide an image to explain then the most current code.
15yr cycle of Hist & NonHist Facilities
The image shows the goal. combine the NonHistFac (all surveyed every 5yrs) with the HistFac (all surveyed every 3yrs). The original ranking must be maintained (this is currently met.)
Here is the problem. The numbers within the image are the Values as produced by the GENERATE function. When the tables are combined and the NonHistFac are mixed with the HistFac, they are not evenly distributed throughout the 1 to 5 Values.
What appears in Values 4 and 5 is obviously the remaining HistFac. I need to have the entire combination distributed from 1 to 5; or as shown, over the 15 year cycle.
Thank you.
// produce non-historic table with additional columns and replicate all rows 3x
TABLE NonHistFac =
VAR NonHistRankedTable =
ADDCOLUMNS ( FacSchNonHist, "#Rank", RANKX ( FacSchNonHist, [WtAvg] ) )
VAR NonHistAnnualizedRankedTable =
ADDCOLUMNS (
NonHistRankedTable,
"Annual Group",
[#Rank] - FLOOR ( ( [#Rank] - 1 ) / 5 * 5, 5 )
)
RETURN
GENERATE ( { 1, 2, 3 }, NonHistAnnualizedRankedTable )
// from the FLOOR & GENERATE functions, 5 x 3 = 15 (this is a 15 year cycle)
//produce historic facility table with additional columns and replicate all rows 5x
TABLE HistFac =
VAR HistRankedTable =
ADDCOLUMNS ( FacSchHist, "#Rank", RANKX ( FacSchHist, [WtAvg] ) )
VAR HistAnnualizedRankedTable =
ADDCOLUMNS (
HistRankedTable,
"Annual Group",
[#Rank] - FLOOR ( ( [#Rank] - 1 ) / 3 * 3, 3 )
)
RETURN
GENERATE ( { 1, 2, 3, 4, 5 }, HistAnnualizedRankedTable )
// from the FLOOR & GENERATE functions, 3 x 5 = 15 (this is a 15 year cycle)
// combine the tables NonHistFac & HistFac to create one-table representing a 15 yr cycle
VAR FacSchUnion =
UNION(
NonHistFac,
HistFac
)
VAR FacSch15yr =
DISTINCT(FacSchUnion)
EVALUATE
FacSch15yr
ORDER BY [Value],
[Annual Group],
[WtAvg] DESC

select x,y from SDO_ORDINATES collection for simple point sdo geometry

I want to select the x, y values from a sdo geometry table. The gtype is 2001, so they are points. The sdo geometry column name is geometry.
I have tried:
select geometry.sdo_point.x, geometry.sdo_point.y from el_pole_test
This returns null values.
I have also tried by index:
select p.geometry.SDO_ORDINATES[0] as point_x, p.geometry.SDO_ORDINATES[1] as point_y from el_pole_test
This errors out.
If I look inside of a collection for a given point I see an array like this:
1661473.42619016
2630277.19731551
1
0
The first two values in the array are the x and y. I am not sure what role the 1 and 0 play.
Table create:
create table EL_POLE_TEST
(
entity_id VARCHAR2(39) not null,
geometry MDSYS.SDO_GEOMETRY
)
Sample rows:
INSERT INTO el_pole_test
VALUES ('00001D212',
mdsys.Sdo_geometry(2001, NULL, NULL,
mdsys.Sdo_elem_info_array(1, 1, 1, 3, 1, 0),
mdsys.Sdo_ordinate_array(1661473.42619016, 2630277.19731551, 1, 0)));
INSERT INTO el_pole_test
VALUES ('00000D212',
mdsys.Sdo_geometry(2001, NULL, NULL,
mdsys.Sdo_elem_info_array(1, 1, 1, 3, 1, 0),
mdsys.Sdo_ordinate_array(1667630.11795338, 2640351.11795338, 1, 0)));
You need a surprisingly large number of SQL tricks to get the X and Y coordinates out of that table: unnest the collection elements by cross joining the TABLE operator (but you must use a table alias - this is the only time when an alias is required), use an analytic function to order the elements for each ENTITY_ID, and then use MAX/CASE to aggregate the results and get one X and one Y per row:
select
entity_id,
max(case when rownumber = 1 then column_value else null end) x,
max(case when rownumber = 2 then column_value else null end) y
from
(
select
entity_id,
column_value,
row_number() over (partition by entity_id order by rownum) rownumber
from el_pole_test e
cross join table(e.geometry.sdo_ordinates)
)
group by entity_id;
ENTITY_ID X Y
--------- ---------------- ----------------
00000D212 1667630.11795338 2640351.11795338
00001D212 1661473.42619016 2630277.19731551

How to insert row numbers while inserting temp table in column - oracle

CREATE GLOBAL TEMPORARY TABLE tempTable(
rowseq NUMBER,
types NUMBER(18,0)
) ON COMMIT PRESERVE ROWS;
INSERT INTO tempTable( types ) SELECT
regexp_substr( inputString , '[^,]+', 1, level ) inputs
FROM
dual
CONNECT BY
regexp_substr( inputString, '[^,]+', 1, level) IS NOT NULL;
using the above insert into query , suppose the
inputString = '10','30','40'
I am creating temp table like
rowSeq types
null 10
null 30
null 40
What edit should be done in insert query , so that table can belike this
rowSeq types
1 10
2 30
3 40

select values from table and create oracle objects

i want to insert values to oracle Object type by selecting values from other table
And the tables and insert statement looks like this.
CREATE TYPE Test_obj AS OBJECT (
attr1 VARCHAR2(20),
attr2 VARCHAR2(20),
attr3 VARCHAR2(25) );
/
CREATE TABLE resultrow_obj (
resultrow Test_obj ,
RESULTTABLEID NUMBER(20,0),
ROWNUMBER NUMBER(20,0) );
/
INSERT INTO resultrow_obj VALUES (
Test_obj (select col1,col2,col3 from Table2 where rownum<=1),
1,123 );
/
You've got it nearly right:
SQL> INSERT INTO resultrow_obj
2 VALUES((SELECT Test_obj('A', 'B', 'C')
3 FROM dual WHERE rownum <= 1),
4 1, 123);
1 row inserted

How do i turn this tables into an object-oriented table (object-relation) in Oracle

I have this table below in the picture that i want to create as an object-oriented table. I dont want the usual create table with relationships.... I just want to learn how to turn this table into an object-oriented table. Below is a picture of my tables and how are they connected:
CREATE TYPE A_TYPE AS OBJECT(
id INT,
col1 INT
);
/
CREATE TYPE A_REF_TABLE_TYPE AS TABLE OF REF A_TYPE;
/
CREATE TYPE B_TYPE AS OBJECT(
id INT,
col1 INT
);
/
CREATE TYPE B_REF_TABLE_TYPE AS TABLE OF REF B_TYPE;
/
CREATE TYPE C_TYPE AS OBJECT(
id INT,
a_list A_REF_TABLE_TYPE,
b_list B_REF_TABLE_TYPE,
col1 INT
);
/
CREATE TABLE A_TAB OF A_TYPE(
ID PRIMARY KEY
);
CREATE TABLE B_TAB OF B_TYPE(
ID PRIMARY KEY
);
CREATE TABLE C_TAB OF C_TYPE(
ID PRIMARY KEY
)
NESTED TABLE a_list STORE AS c_a_lists
NESTED TABLE b_list STORE AS c_b_lists;
INSERT INTO A_TAB VALUES( A_TYPE( 1, 3 ) );
INSERT INTO A_TAB VALUES( 2, 4 );
INSERT INTO B_TAB VALUES ( B_TYPE( 1, 7 ) );
INSERT INTO B_TAB VALUES ( 2, 2 );
INSERT INTO B_TAB VALUES ( 3, 10 );
INSERT INTO C_TAB VALUES (
1,
A_REF_TABLE_TYPE(
( SELECT REF(a) FROM A_TAB a WHERE ID = 2 ) -- Single value
),
( -- Multiple values
SELECT CAST( COLLECT( REF(b) ) AS B_REF_TABLE_TYPE )
FROM TAB_B b
WHERE ID IN ( 1, 3 )
),
42
);
INSERT INTO C_TAB VALUES (
2,
NULL, -- Unknown
B_REF_TABLE_TYPE(), -- No values
54
);
Output:
SELECT * FROM C_TAB;
ID A_LIST B_LIST COL1
-- ------------------------------- --------------------------------------------- ----
1 A_REF_TABLE_TYPE( A_TYPE(2,4) ) B_REF_TABLE_TYPE( B_TYPE(1,7), B_TYPE(3,10) ) 42
2 (null) B_REF_TABLE_TYPE() 54

Resources