How to display row data into different column ORACLE - sql

I'm new to SQL and currently this is what I'm trying to do:
Display multiple rows of data into different columns within the same row
I have a table like this:
CREATE TABLE TRIPLEG(
T# NUMBER(10) NOT NULL,
LEG# NUMBER(2) NOT NULL,
DEPARTURE VARCHAR(30) NOT NULL,
DESTINATION VARCHAR(30) NOT NULL,
CONSTRAINT TRIPLEG_PKEY PRIMARY KEY (T#, LEG#),
CONSTRAINT TRIPLEG_UNIQUE UNIQUE(T#, DEPARTURE, DESTINATION),
CONSTRAINT TRIPLEG_FKEY1 FOREIGN KEY (T#) REFERENCES TRIP(T#) );
INSERT INTO TRIPLEG VALUES( 1, 1, 'Sydney', 'Melbourne');
INSERT INTO TRIPLEG VALUES( 1, 2, 'Melbourne', 'Adelaide');
The result should be something like this:
T# | ORIGIN | DESTINATION1 | DESTINATION2
1 | SYDNEY | MELBORUNE | ADELAIDE
The origin is the DEPARTURE.
DESTINATION1 could either be DEPARTURE OR DESTINATION.
DESTINATION2 is the DESTINATION.
The query should include the COUNT(T#) < 3 since I only need to display the records less than 3. How can i do this with 2 relational view to achieve this result?

Try this:
select t1.T#,
(select t2.departure from tripleg t2 where t1.T# = t2.T# and t2.LEG# = 1) Origin,
(select t2.destination from tripleg t2 where t1.T# = t2.T# and t2.LEG# = 1) Destination1,
(select t2.destination from tripleg t2 where t1.T# = t2.T# and t2.LEG# = 2) Destination2
from tripleg t1
group by t1.T#
having count(1) < 3

You can make use of hierarchical queries.
Data Setup:
CREATE TABLE TRIPLEG ( T# NUMBER ( 10 ) NOT NULL,
LEG# NUMBER ( 2 ) NOT NULL,
DEPARTURE VARCHAR ( 30 ) NOT NULL,
DESTINATION VARCHAR ( 30 ) NOT NULL );
INSERT INTO
TRIPLEG
VALUES
( 1,
1,
'Sydney',
'Melbourne' );
INSERT INTO
TRIPLEG
VALUES
( 1,
2,
'Melbourne',
'Adelaide' );
INSERT INTO
TRIPLEG
VALUES
( 2,
1,
'A',
'B' );
INSERT INTO
TRIPLEG
VALUES
( 2,
2,
'B',
'C' );
INSERT INTO
TRIPLEG
VALUES
( 2,
3,
'C',
'D' );
INSERT INTO
TRIPLEG
VALUES
( 3,
1,
'A',
'B' );
COMMIT;
Query:
SELECT
T#,
CONNECT_BY_ROOT DEPARTURE
|| SYS_CONNECT_BY_PATH ( DESTINATION,
' ~ ' )
AS ROUTE
FROM
TRIPLEG
WHERE
LEVEL = 2
CONNECT BY
NOCYCLE DEPARTURE = PRIOR DESTINATION
START WITH
T# = '1';
Explanation:
Connect rows based on the relationship between Departure and Destination.
Filter for rows satisfying a transit (Strictly)
LEG# is not used.

Kindly try this
select t#,regexp_substr(tree,'[^:]+',2) origin,
regexp_substr(tree,'[^:]+',2,2) destination1,
regexp_substr(tree,'[^:]+',2,3) destination2
from
(
select a.*,
sys_connect_by_path(departure,':') tree,
level lvl
from tripleg a
connect by nocycle prior destination = departure and prior t# = t#
start with leg# = 1
and level < = 3
)
where lvl = 3;

Related

SQL inner join condition by actual date

I need to join actual document to people. Documents has date issued (passport, for example).
SQL Fiddle: http://sqlfiddle.com/#!9/3a8118/2/0
Table structure:
CREATE TABLE people
(
p_id INT NOT NULL AUTO_INCREMENT,
p_name VARCHAR(50) NOT NULL,
PRIMARY KEY(p_id)
);
INSERT INTO people (p_id, p_name)
VALUES (1, 'Name_1'),
(2, 'Name_2');
CREATE TABLE documents
(
d_id INT NOT NULL AUTO_INCREMENT,
d_people INT(10) NOT NULL,
d_date VARCHAR(10) NOT NULL,
PRIMARY KEY(d_id)
);
INSERT INTO documents (d_id, d_people, d_date)
VALUES (1, 1, '01.01.2022'),
(2, 2, '01.12.2021'),
(3, 1, '05.02.2022'),
(4, 1, '10.02.2022'),
(5, 2, '04.01.2022'),
(6, 1, '20.01.2022');
Query: condition is select actual document when date is 21.01.2022, it must return d_id = 6:
SELECT *
FROM people
INNER JOIN documents ON d_people = p_id
WHERE p_id = 1 AND ??? d_date 21.01.2022 ???
;
I need to do an inner join to return only this row:
use this query:
Fiddle
SELECT * FROM people
INNER JOIN documents ON d_people = p_id
WHERE p_id = 1 and d_date='20.01.2022';

Join operations in SQL

I should retrieve the IDs and names of ingredients that are not contained by any ice cream and then
sort the output rows in ascending order by ingredient ID. I don't quite understand how JOIN -operation works in this exercise.
I tried e.g. the following but it gives too many rows in the output.
SELECT ingredient.ingredient_id, ingredient.ingredient_name
FROM ingredient
LEFT JOIN contains ON ingredient.ingredient_id != contains.ingredient_id
LEFT JOIN ice_cream ON ice_cream.ice_cream_id != contains.ice_cream_id
ORDER BY ingredient.ingredient_id;
Output columns should be as follows:
ingredient_id ingredient_name
----------------------------
6 Dark chocolate
and here are the tables:
CREATE TABLE manufacturer (
manufacturer_id INT,
manufacturer_name VARCHAR(30) NOT NULL,
country VARCHAR(30) NOT NULL,
PRIMARY KEY (manufacturer_id),
UNIQUE (manufacturer_name)
);
-- jäätelöitä
-- ice creams
CREATE TABLE ice_cream (
ice_cream_id INT,
ice_cream_name VARCHAR(30) NOT NULL,
manufacturer_id INT NOT NULL,
manufacturing_cost NUMERIC(4,2),
PRIMARY KEY (ice_cream_id),
UNIQUE (ice_cream_name),
FOREIGN KEY (manufacturer_id) REFERENCES manufacturer
);
-- aineksia
-- ingredients
-- plant_based arvo 0 merkitse ei ja arvo 1 merkitsee kyllä
-- plant_based value 0 means no and value 1 means yes
CREATE TABLE ingredient (
ingredient_id INT,
ingredient_name VARCHAR(30) NOT NULL,
kcal INT,
protein NUMERIC(3,1),
plant_based INT,
PRIMARY KEY (ingredient_id),
UNIQUE (ingredient_name)
);
-- jäätelöt sisältävät aineksia
-- ice creams contain ingredients
CREATE TABLE contains(
ice_cream_id INT NOT NULL,
ingredient_id INT NOT NULL,
quantity INT,
PRIMARY KEY (ice_cream_id, ingredient_id),
FOREIGN KEY (ice_cream_id) REFERENCES ice_cream,
FOREIGN KEY (ingredient_id) REFERENCES ingredient
);
--Ice cream manufacturers
INSERT INTO manufacturer VALUES (
1, 'Jen & Berry', 'Canada'
);
INSERT INTO manufacturer VALUES (
2, '4 Friends', 'Finland'
);
INSERT INTO manufacturer VALUES (
3, 'Gelatron', 'Italy'
);
--Ice cream
INSERT INTO ice_cream VALUES (
1, 'Plain Vanilla', 1, 1.00
);
INSERT INTO ice_cream VALUES (
2, 'Vegan Vanilla', 2, 0.89
);
INSERT INTO ice_cream VALUES (
3, 'Super Strawberry', 2, 1.44
);
INSERT INTO ice_cream VALUES (
4, 'Very plain', 2, 1.20
);
--Ingredients
INSERT INTO ingredient VALUES (
1, 'Cream', 400, 3, 0
);
INSERT INTO ingredient VALUES (
2, 'Coconut cream', 230, 2.3, 1
);
INSERT INTO ingredient VALUES (
3, 'Sugar', 387, 0, 1
);
INSERT INTO ingredient VALUES (
4, 'Vanilla extract', 12, 0, 1
);
INSERT INTO ingredient VALUES (
5, 'Strawberry', 33, 0.7, 1
);
INSERT INTO ingredient VALUES (
6, 'Dark chocolate', 535, 8, 1
);
--Contains
INSERT INTO contains VALUES (
1, 1, 70
);
INSERT INTO contains VALUES (
1, 3, 27
);
INSERT INTO contains VALUES (
1, 4, 3
);
INSERT INTO contains VALUES (
2, 2, 74
);
INSERT INTO contains VALUES (
2, 3, 21
);
INSERT INTO contains VALUES (
2, 4, 5
);
INSERT INTO contains VALUES (
3, 1, 60
);
INSERT INTO contains VALUES (
3, 3, 10
);
INSERT INTO contains VALUES (
3, 5, 30
);
INSERT INTO contains VALUES (
4, 2, 95
);
INSERT INTO contains VALUES (
4, 4, 5
);
You can join the "contains" table with the "ingredient" table using a LEFT JOIN and filter all values that have NULL in the "contains.ice_cream_id" field:
SELECT i.ingredient_id,
i.ingredient_name
FROM ingredient i
LEFT JOIN contains c
ON i.ingredient_id = c.ingredient_id
WHERE c.ice_cream_id IS NULL
It appears you are asking to find where somethig does not exist, so it would make sense to express that using not exists
So you just need to find the ingredients that don't exist in the list of contents:
select ingredient_id, ingredient_name
from ingredient i
where not exists (
select * from contains c
where c.ingredient_id = i.ingredient_id
);
Demo Fiddle

When I am inserting records into the table it is inserting 0 records. Though there are no errors

CREATE TABLE new_details_staging (
e_id NUMBER(10),
e_name VARCHAR2(30),
portal_desc VARCHAR2(50),
CONSTRAINT pk_new_details_staging PRIMARY KEY ( e_id )
);
INSERT INTO new_details_staging VALUES (
11,
'A',
'AA'
);
INSERT INTO new_details_staging VALUES (
22,
'B',
'BB'
);
CREATE TABLE lookup_ref (
ref_id NUMBER(10),
ref_typ VARCHAR2(30),
ref_typ_desc NUMBER(20),
CONSTRAINT pk_lookup_ref PRIMARY KEY ( ref_id )
);
INSERT INTO lookup_ref VALUES (
181,
'portal',
'AA'
);
INSERT INTO lookup_ref VALUES (
182,
'portal',
'BB'
);
CREATE TABLE new_details_main (
e_id NUMBER(10),
e_name VARCHAR2(30),
portal NUMBER(20),
CONSTRAINT pk_new_details_main PRIMARY KEY ( e_id )
);
When I am inserting records into the table it is inserting 0 records. Can someone help with this why no rows are getting inserted into the table? Basically, I want to populate the lookup id from the lookup_ref table based on the staging table column portal_desc.
Insert query :
insert
INTO new_details_main (
e_id,
e_name,
portal
)
SELECT
n.e_id,
n.e_name,
case
WHEN n.portal_desc = 'AA' THEN (select l.ref_id from lookup_ref where l.ref_typ='portal' and l.ref_typ_desc = 'AA')
when n.portal_desc = 'BB' then (select l.ref_id from lookup_ref where l.ref_typ= 'portal' and l.ref_typ_desc = 'BB') end
FROM new_details_staging n cross join lookup_ref l;
Expected Output
Aside from the mis-declaration of LOOKUP_REF.REF_TYP_DESC, which should be changed to VARCHAR2(20) as pointed out by #thatjeffsmith, it appears to me that you should not be doing a cross join of LOOKUP_REF. Instead, I suggest doing an inner join so that you only join the rows you care about:
INSERT INTO new_details_main
(e_id, e_name, portal)
SELECT n.e_id,
n.e_name,
l.ref_id AS PORTAL
FROM new_details_staging n
INNER JOIN lookup_ref l
ON l.REF_TYP = 'portal' AND
l.REF_TYP_DESC = n.PORTAL_DESC
This produces your desired results.
db<>fiddle here
You have a couple of bad INSERTs
You have defined LOOKUP_REF.REF_TYPE_DESC as NUMBER(20), but then you attempt to insert a values of 'AA' and 'BB' into it.
That fails with
ORA-01722: invalid number
Then for your query -
SELECT
n.e_id,
n.e_name,
case
WHEN n.portal_desc = 'AA' THEN (select l.ref_id from lookup_ref where l.ref_typ='portal' and l.ref_typ_desc = 'AA')
when n.portal_desc = 'BB' then (select l.ref_id from lookup_ref where l.ref_typ= 'portal' and l.ref_typ_desc = 'BB') end
FROM new_details_staging n cross join lookup_ref l;
Those rows with AA and BB aren't there to be 'joined' with, so the resultset is empty.
Let's try and fix this.
ALTER TABLE LOOKUP_REF
MODIFY ( REF_TYP_DESC varchar2(20) )
Table ADMIN.LOOKUP_REF altered.
Run the INSERTs again.
INSERT INTO lookup_ref VALUES (
181,
'portal',
'AA'
);
INSERT INTO lookup_ref VALUES (
182,
'portal',
'BB'
);
1 row inserted.
Elapsed: 00:00:00.009
Copy to clipboard
Show info
1 row inserted.
Elapsed: 00:00:00.005
Now run the query again -
Closer. You now get
ORA-01427: single-row subquery returns more than one row
You say:
I want to populate the lookup id from the lookup_ref table based on
the staging table column portal_desc
This very much sounds like there should be a foreign key constraint. You would then use the FK value in the 'child' table to 'look up' the details in the 'parent' table based on the matching 'ID.' That column needs a unique or primary key constraint to ensure there can be, only one matching value.

SQL combine foreign key values of all parents into child

In an informix database, I have a hierarchical tree structure, where a child entry can have any level of parents (parent, grandparent, etc). via relation to its parent entry.
Every entry has a collection of attribute names and values.
The tables are modeled is as follows:
node:
+-------------+----------------------+--------+
| id | parn_id | name |
+-------------+----------------------+--------+
| int | int | string |
| primary key | existing id, or null | |
+-------------+----------------------+--------+
vals:
+-----------------------+-------------+---------+
| id | atr_id | atr_val |
+-----------------------+-------------+---------+
| int | int | string |
| foreign key from node | primary key | |
+-----------------------+-------------+---------+
look:
+-----------------------+--------+
| atr_id | name |
+-----------------------+--------+
| int | string |
| foreign key from vals | |
+-----------------------+--------+
I need a sql query which returns all of the parent's (vals, look) pairs when asking for a child.
For example, if I have
Parent: (valP, nameP), (valP2, nameP2)
*
* * * Child (valC, nameC)
*
* * * GrandChild (valGC, nameGC)
And I query for the GrandChild, I want it to return:
GrandChild (valP, nameP), (valP2, nameP2), (valC, nameC), (valGC, nameGC)
Using a recent Informix version ( I am using Informix 14.10.FC1 ) you can use the CONNECT BY clause to work with hierarchical queries.
The setup, based on your description:
CREATE TABLE node
(
id INTEGER PRIMARY KEY,
parn_id INTEGER,
name CHAR( 20 ) NOT NULL
);
INSERT INTO node VALUES ( 1, NULL, 'Node_A' );
INSERT INTO node VALUES ( 2, NULL, 'Node_B' );
INSERT INTO node VALUES ( 3, NULL, 'Node_C' );
INSERT INTO node VALUES ( 4, 2, 'Node_D' );
INSERT INTO node VALUES ( 5, 3, 'Node_E' );
INSERT INTO node VALUES ( 6, 3, 'Node_F' );
INSERT INTO node VALUES ( 7, 4, 'Node_G' );
CREATE TABLE vals
(
id INTEGER NOT NULL REFERENCES node( id ),
atr_id INTEGER PRIMARY KEY,
atr_val CHAR( 20 ) NOT NULL
);
INSERT INTO vals VALUES ( 1, 1, 'Value_A_1' );
INSERT INTO vals VALUES ( 2, 2, 'Value_B_1' );
INSERT INTO vals VALUES ( 2, 3, 'Value_B_2' );
INSERT INTO vals VALUES ( 3, 4, 'Value_C_1' );
INSERT INTO vals VALUES ( 3, 5, 'Value_C_2' );
INSERT INTO vals VALUES ( 4, 6, 'Value_D_1' );
INSERT INTO vals VALUES ( 5, 7, 'Value_E_1' );
INSERT INTO vals VALUES ( 5, 8, 'Value_E_2' );
INSERT INTO vals VALUES ( 6, 9, 'Value_F_1' );
INSERT INTO vals VALUES ( 7, 10, 'Value_G_1' );
CREATE TABLE look
(
atr_id INTEGER NOT NULL REFERENCES vals( atr_id ),
name CHAR( 20 ) NOT NULL
);
INSERT INTO look VALUES ( 1, 'Look_A_1' );
INSERT INTO look VALUES ( 2, 'Look_B_1' );
INSERT INTO look VALUES ( 3, 'Look_B_2' );
INSERT INTO look VALUES ( 4, 'Look_C_1' );
INSERT INTO look VALUES ( 5, 'Look_C_2' );
INSERT INTO look VALUES ( 6, 'Look_D_1' );
INSERT INTO look VALUES ( 7, 'Look_E_1' );
INSERT INTO look VALUES ( 8, 'Look_E_2' );
INSERT INTO look VALUES ( 9, 'Look_F_1' );
INSERT INTO look VALUES ( 10, 'Look_G_1' );
we can use the CONNECT BY to find the child parents, for example:
-- Starting from 'Node_G'
SELECT
n.id,
n.parn_id,
n.name,
CONNECT_BY_ROOT n.name AS starting_node
FROM
node AS n
START WITH n.name = 'Node_G'
CONNECT BY PRIOR n.parn_id = n.id
ORDER BY
n.name
;
-- RESULTS:
id parn_id name starting_node
2 Node_B Node_G
4 2 Node_D Node_G
7 4 Node_G Node_G
And then we can join with the attribute tables:
SELECT
vt1.starting_node,
v.atr_val,
l.name
FROM
(
SELECT
n.id,
n.parn_id,
n.name,
CONNECT_BY_ROOT n.name AS starting_node
FROM
node AS n
START WITH n.name = 'Node_G'
CONNECT BY PRIOR n.parn_id = n.id
) AS vt1
INNER JOIN vals AS v
ON
v.id = vt1.id
INNER JOIN look AS l
ON
l.atr_id = v.atr_id
ORDER BY
vt1.starting_node, v.atr_val
;
-- RESULTS:
starting_node atr_val name
Node_G Value_B_1 Look_B_1
Node_G Value_B_2 Look_B_2
Node_G Value_D_1 Look_D_1
Node_G Value_G_1 Look_G_1
If we remove the START WITH clause, we get the hierarchical results for each node:
SELECT
vt1.starting_node,
v.atr_val,
l.name
FROM
(
SELECT
n.id,
n.parn_id,
n.name,
CONNECT_BY_ROOT n.name AS starting_node
FROM
node AS n
CONNECT BY PRIOR n.parn_id = n.id
) AS vt1
INNER JOIN vals AS v
ON
v.id = vt1.id
INNER JOIN look AS l
ON
l.atr_id = v.atr_id
ORDER BY
vt1.starting_node, v.atr_val
;
-- RESULTS:
starting_node atr_val name
Node_A Value_A_1 Look_A_1
Node_B Value_B_1 Look_B_1
Node_B Value_B_2 Look_B_2
Node_C Value_C_1 Look_C_1
Node_C Value_C_2 Look_C_2
Node_D Value_B_1 Look_B_1
Node_D Value_B_2 Look_B_2
Node_D Value_D_1 Look_D_1
Node_E Value_C_1 Look_C_1
Node_E Value_C_2 Look_C_2
Node_E Value_E_1 Look_E_1
Node_E Value_E_2 Look_E_2
Node_F Value_C_1 Look_C_1
Node_F Value_C_2 Look_C_2
Node_F Value_F_1 Look_F_1
Node_G Value_B_1 Look_B_1
Node_G Value_B_2 Look_B_2
Node_G Value_D_1 Look_D_1
Node_G Value_G_1 Look_G_1

How to count null columns in a row?

How to count all the columns in a table that have a null value?
The table having a large number of columns and the method should iterate over the columns in a dynamic manner.
In any given row (selected by an identifier), count the null cells.
Select count(number of null value cells) where id=1 from table
e.g:
I have a table consisting of 200 columns I want to know how many null
cells does the row with id=1 have
Basically, you need to check every column in a selected row if it's null or not.
Since you have 200+ columns in your table, manual approach seems tedious, so you can automate it a little bit and construct the query dynamically by querying user_tab_columns:
-- set up
create table t1(
rid number primary key,
c1 varchar2(17),
c2 date,
c3 timestamp,
c4 number
);
insert into t1
valueS(1, 'string', null, systimestamp, null);
commit ;
-- going to use DECODE function - doesnt require type consistency.
select 'decode('||column_name||', null, 1, 0)+' as res
from user_tab_columns
where table_name = 'T1'
Result:
RES
------------------------------
decode(RID, null, 1, 0)+
decode(C1, null, 1, 0)+
decode(C2, null, 1, 0)+
decode(C3, null, 1, 0)+
decode(C4, null, 1, 0)+
And final query:
select decode(C4, null, 1, 0)+
decode(C3, null, 1, 0)+
decode(C2, null, 1, 0)+
decode(C1, null, 1, 0)+
decode(RID, null, 1, 0) as num_of_nulls
from t1
where rid = 1
Result:
NUM_OF_NULLS
--------------
2
Try this. You can pass any ID and get the number of columns with NULL value.
CREATE TABLE TEST (ID NUMBER, B VARCHAR2(20), C NUMBER, D VARCHAR2(200));
INSERT INTO TEST VALUES (1,NULL,NULL,'XX');
SELECT COUNT(NULL_COLS)
FROM (
SELECT
to_number(extractvalue(xmltype(dbms_xmlgen.getxml('SELECT CASE WHEN '||COLUMN_NAME||' IS NULL THEN 0 ELSE NULL END COL_VAL FROM '||TABLE_NAME||' WHERE ID=&VALUE')),'/ROWSET/ROW/COL_VAL')) NULL_COLS
FROM USER_TAB_COLUMNS
WHERE TABLE_NAME='TEST');