How to pass value as parameter to join tables in Oracle SQL - sql

We have situation like each project has separate / unique table. and each table has unique column name.
For ex Project AAA is having table A1_table, in this table the column name will be A1_APP, A1_DOCUMENT, A1_Pages and so on.
Similarly for Project BBB will have table B1_Table and this table will have column name like B1_APP, B1_DOCUMENT, B1_Pages.
I am trying to join the table by passing the column name value as parameter. Since it will be difficult to change the column name for each project
Since we have different column name i could not able to join the table.
Kindly advise
Note :
The table is already created by vendor. i am just trying to extract data for all studies. so it will be difficult for me to rename the column one by one
Sql Script :
DECLARE
V_IMG_DOC_ID INT := '12345';
V_SHORT_DESC NVARCHAR2(100) := 'B18' ;
v_sql VARCHAR2(5000);
BEGIN
Select C.PROJECT "PROJECT", D.SUBJECT "SUBJECT_NO",D.SITE_NUMBER, E.IMAGE_ID,E.IMG_DOC_ID,F.
DTYPE_DESC "DOCUMENT_TYPE",E.IMG_FILENAME,E.IMG_NAT_FILE_ORG "FILE_LOCATION"
from APPLICATION A
inner join B18_DOCUMENT B on A. APP_ID = B.||V_SHORT_DESC||_APP_ID
inner join PROJECT_IMAGE E on E.IMG_DOC_ID = B.||V_SHORT_DESC||D_DOC_ID
inner join SUBJECT D on D.SJ_ID = B.||V_SHORT_DESC||D_SJ_ID
Inner join PROTOCOL C on C.APP_ID = A.APP_ID
inner join DOCUMENTTYPE F on F. DT_APP_ID = A. APP_ID and F. DT_ID =
B.||V_SHORT_DESC||D_DT_ID
where E.IMG_DOC_ID = 5877630
ORDER BY E.IMG_DOC_ID DESC;
END;

Refactor your code so that the projects are all in the same table and add a project_name column.
CREATE TABLE project_documents (
project_name VARCHAR2(10),
app_id NUMBER,
document CLOB,
pages VARCHAR2(50)
-- ,... etc.
)
If you want to restrict users to only seeing their own projects then you can use a virtual private database.
Then you do not need to use dynamic SQL to build queries with lots of different table names and can just use the one table for all projects and add a filter for the specific project using the added project_name column.
If you cannot do that then you are going to have to either:
use dynamic SQL to build the queries and dynamically set the table and column names each time you run the query; or
create a view of all the projects:
CREATE VIEW all_projects (project_name, app_id, document, pages /*, ... */) AS
SELECT 'A1', a1_app_id, a1_document, a1_pages /*, ... */ FROM a1_table UNION ALL
SELECT 'A2', a2_app_id, a2_document, a2_pages /*, ... */ FROM a2_table UNION ALL
SELECT 'B1', b1_app_id, b1_document, b1_pages /*, ... */ FROM b1_table UNION ALL
SELECT 'B18', b18_app_id, b18_document, b18_pages /*, ... */ FROM b18_table
and then you can query the view using the normalised column names rather than the project-specific names.
(Note: You will have to update the view when new projects are added.)

That looks like a terribly wrong data model. If you want to pass table/column names and use them in your queries, you'll have to use dynamic SQL which is difficult to maintain and debug.
By the way, do you really plan to duplicate, triplicate, ... all your tables to fit all new projects? That's insane!
Should be something like this (table_1 has a foreign key constraint, pointing to project table).
SQL> create table project
2 (id_project number constraint pk_proj primary key,
3 name varchar2(20) not null
4 );
Table created.
SQL> create table table_1
2 (id number constraint pk_t1 primary key,
3 id_project number constraint fk_t1_proj references project (id_project),
4 app varchar2(20),
5 document varchar2(20),
6 pages number
7 );
Table created.
Sample rows:
SQL> insert into project (id_project, name) values (1, 'Project AAA');
1 row created.
SQL> insert into project (id_project, name) values (2, 'Project BBB');
1 row created.
SQL> insert into table_1 (id, id_project, app) values (1, 1, 'App. 1');
1 row created.
SQL> insert into table_1 (id, id_project, app) values (2, 1, 'App. 2');
1 row created.
SQL> insert into table_1 (id, id_project, app) values (3, 2, 'App. 3');
1 row created.
Sample queries:
SQL> select * from project;
ID_PROJECT NAME
---------- --------------------
1 Project AAA
2 Project BBB
SQL> select a.id, p.name project_name, a.app
2 from table_1 a join project p on p.id_project = a.id_project
3 order by p.name, a.id;
ID PROJECT_NAME APP
---------- -------------------- --------------------
1 Project AAA App. 1
2 Project AAA App. 2
3 Project BBB App. 3
SQL>

I have created a view for this view and it worked fine as per my expectation

Related

SQL query for key value table with 1:n relation

I have a table in which I want to store images. Each image has arbitrary properties that I want to store in a key-value table.
The table structure looks like this
id
fk_picture_id
key
value
1
1
camera
iphone
2
1
year
2001
3
1
country
Germany
4
2
camera
iphone
5
2
year
2020
6
2
country
United States
Now I want a query to find all pictures made by an iphone I could to something like this
select
fk_picture_id
from
my_table
where
key = 'camera'
and
value = 'iphone';
This works without any problems. But as soon as I want to add another key to my query I am get stucked. Lets say, I want all pictures made by an iPhone in the year 2020, I can not do something like
select
distinct(fk_picture_id)
from
my_table
where
(
key = 'camera'
and
value = 'iphone'
)
or
(
key = 'year'
and
value = '2020'
)
...because this selects the id 1, 4 and 5.
At the end I might have 20 - 30 different criteria to look for, so I don't think some sub-selects would work at the end.
I'm still in the design phase, which means I can still adjust the data model as well. But I can't think of any way to do this in a reasonable way - except to include the individual properties as columns in my main table.
A pattern you can consider here is to build a table of search parameters, then simply join this to your target table.
You would first create a temporary table with key and value columns then insert into it the search criteria values, any number of values you wish.
Using a CTE in place of a temporary table might look like:
with s as (
select 'camera' key, 'iphone' value
union all
select 'year', '2020'
)
select distinct t.fk_picture_id
from s
join t on t.key=s.key and t.value=s.value
The solution I found - thanks to this article
How to query data based on multiple 'tags' in SQL?
is that I made some changes to the database model
picture
id
name
1
Picture 1
2
Picture 2
And then I created a table for the tags
tag
id
tag
100
Germany
101
IPhone
102
United States
And the cross table
picture_tag
fk_picture_id
fk_tag_id
1
100
1
101
2
101
2
102
For a better understanding of the datasets
Picture
Tagname
Picture 1
Germany & Iphone
Picture 2
United States & IPhone
Now I can use the following statement
SELECT *
FROM picture
INNER JOIN (
SELECT fk_picture_id
FROM picture_tag
WHERE fk_tag_id IN (100, 101)
GROUP BY fk_picture_id
HAVING COUNT(fk_tag_id) = 2
) AS picture_tag
ON picture.id = picture_tag.fk_picture_id;
The only thing I need to do before the query is to collect the IDs of the tags I want to search for and put the number of tags in the having count statement.
If someone needs the example data, here are the sql statements for the tables and data
create table picture (
id integer,
name char(100)
);
create table tag (
id integer,
tag char(100)
);
create table picture_tag (
fk_picture_id integer,
fk_tag_id integer
);
insert into picture values (1, 'Picture 1');
insert into picture values (2, 'Picture 2');
insert into tag values (100, 'Germay');
insert into tag values (101, 'iphone');
insert into tag values (102, 'United States');
insert into picture_tag values (1, 100);
insert into picture_tag values (1, 101);
insert into picture_tag values (2, 101);
insert into picture_tag values (2, 102);

INSERTING VALUES INTO A NESTED TABLE

I am trying to develop a university database with the help of nested tables, I have successfully created all other nested tables required and inserted data as well, but while inserting data into marks table I am facing problem of inconsistent datatype.
codes:
CREATE OR REPLACE TYPE MODULE_MARKS;
CREATE OR REPLACE TYPE MM_NT_TYPE AS TABLE OF REF MODULE_MARKS;
CREATE OR REPLACE TYPE MODULE_MARKS AS OBJECT
(
MODULE REF MODULE_T, MARKS_OBTAINED, TOTAL_MARKS, STATUS
)
CREATE TABLE MARK_TAB
(
student ref student_t,
modules_marks mm_nt_type
)
I am able to insert reference to student correctly but I want to insert data into module_marks.
Tried doing :
INSERT INTO MARK_TAB VALUES((SELECT REF(S) FROM STUDENT_TAB S WHERE
S.S_ID=1),
MM_NT_TYPE( MODULE_MARKS_T((SELECT REF (M) FROM MODULE_TAB M WHERE
M.MODULE_ID =1),
90,100,'PASS')));
this shoes the error ORA-00932. EXPECTED REFERENCE OF MODULE_MARKS_T GOT MODULE_MARKS_T.
It seems a familiar structure to me. Maybe I have created the same structure for one of my projects.
I think you are confused when inserting the record in the column having a type which is table of REF.
I have COURSES_TABLE_TYPE which is table of REF COURSES_T and courses table is table of COURSES_T;
I suggest you do the following:
INSERT INTO DEPARTMENT VALUES (
1,
COURSES_TABLE_TYPE(( -- REFs of single records delimited by comma
SELECT
REF(C)
FROM
COURSE C
WHERE
COURSE_ID = 1
),(
SELECT
REF(C)
FROM
COURSE C
WHERE
COURSE_ID = 2
))
);
MM_NT_TYPE is a collection of REF MODULE_MARKS whereas you are passing MODULE_MARKS objects and not references. Instead, you need to have a table containing containing MODULE_MARKS objects that you can reference:
CREATE TABLE module_marks_tab OF module_marks;
Then you can reference those objects. For example:
INSERT INTO mark_tab VALUES (
( SELECT REF(s) FROM students s WHERE id = 2 ),
MM_NT_TYPE(
( SELECT REF( m ) FROM module_marks_tab m WHERE m.module.id = 1 AND marks_obtained = 3 ),
( SELECT REF( m ) FROM module_marks_tab m WHERE m.module.id = 3 AND marks_obtained = 8 )
)
);
db<>fiddle

Insert data from one table to other using select statement and avoid duplicate data

Database: Oracle
I want to insert data from table 1 to table 2 but the catch is, primary key of table 2 is the combination of first 4 letters and last 4 numbers of the primary key of table 1.
For example:
Table 1 - primary key : abcd12349887/abcd22339887/abcder019987
In this case even if the primary key of table 1 is different, but when I extract the 1st 4 and last 4 chars, the output will be same abcd9887
So, when I use select to insert data, I get error of duplicate PK in table 2.
What I want is if the data of the PK is already present then don't add that record.
Here's my complete stored procedure:
INSERT INTO CPIPRODUCTFAMILIE
(productfamilieid, rapport, mesh, mesh_uitbreiding, productlabelid)
(SELECT DISTINCT (CONCAT(SUBSTR(p.productnummer,1,4),SUBSTR(p.productnummer,8,4)))
productnummer,
ps.rapport, ps.mesh, ps.mesh_uitbreiding, ps.productlabelid
FROM productspecificatie ps, productgroep pg,
product p left join cpiproductfamilie cpf
on (CONCAT(SUBSTR(p.productnummer,1,4),SUBSTR(p.productnummer,8,4))) = cpf.productfamilieid
WHERE p.productnummer = ps.productnummer
AND p.productgroepid = pg.productgroepid
AND cpf.productfamilieid IS NULL
AND pg.productietype = 'P'
**AND p.ROWID IN (SELECT MAX(ROWID) FROM product
GROUP BY (CONCAT(SUBSTR(productnummer,1,4),SUBSTR(productnummer,8,4))))**
AND (CONCAT(SUBSTR(p.productnummer,1,2),SUBSTR(p.productnummer,8,4))) not in
(select productfamilieid from cpiproductfamilie));
The highlighted section seems to be wrong, and because of this the data is not picking up.
Please help
Try using this.
p.productnummer IN (SELECT MAX(productnummer) FROM product
GROUP BY (CONCAT(SUBSTR(productnummer,1,4),SUBSTR(productnummer,8,4))))

How to split the columns?

Name Nameid
P,q,r,s,t One
A,b,c Two
D,e Three
This is my source table, but i want my target table like this
Name Nameid
P One
Q One
R One
S One
T One
A Two
B Two
C Two
D three
In a case like this, I think it would be more elegant to store the data in a different way.
If you are inserting your rows from a program, try splitting your string there, and insert a few more rows instead.
Let me give you a pseudo code example.
number = "One"
many_letters = "P,Q,R,S,T".split(",")
for(letter in many_letters) {
insert_values(letter, number)
}
Here's one way, lifted from here:
SQL> CREATE TABLE t (name VARCHAR2(20), nameid VARCHAR2(10));
Table created.
SQL> INSERT INTO t VALUES ('P,q,r,s,t','One');
1 row created.
SQL> INSERT INTO t VALUES ('A,b,c' ,'Two');
1 row created.
SQL> INSERT INTO t VALUES ('D,e' ,'Three');
1 row created.
SQL> SELECT nameid
2 , REGEXP_SUBSTR (name, '[^,]+', 1, LEVEL) AS token
3 FROM t
4 CONNECT BY PRIOR nameid = nameid
5 AND REGEXP_INSTR (name, '[^,]+', 1, LEVEL) > 0
6 AND PRIOR DBMS_RANDOM.STRING ('p', 10) IS NOT NULL
7 ;
NAMEID TOKEN
---------- --------------------
One P
One q
One r
One s
One t
Three D
Three e
Two A
Two b
Two c
10 rows selected.
SQL>
Sorry I didn't understand the question it was posted. It's been reformatted since.
Oracle does not have a built in Split function, which is what you need here. The following link shows how to create a custom Split function:
http://srinisreeramoju.blogspot.com/2010/03/oracle-custom-split-function.html
Once you've created the types and the function you would simply call it as:
SELECT
Split(Name, ',') AS Name,
NameID
FROM
YourTable

insert one column data from a table into other table, but the other column data will be specified dynamically

i have 2 tables.
TABLE A COLUMNS - aid , aname
TABLE B COLUMNS - bid , bname
from table A, i will pick up data from column 'aid',
AND insert it in 'bid' column of table B , but in bname column of table B, i will insert a new value. how do i do this?
create table A(aid int,aname char)
insert into A values(111, 'e')
create table B(bid int, bname char)
insert into B (bid,bname)
bid will take value from the query : select aid from a
bname will get a new value -m
expected result should be : THE TABLE B WILL HAVE :
bid bname
--- -----
111 m
Try this:
insert into b (bid, bname) select aid, 'm' as bname_fixed_val from a
Two facts enabled the solution above:
The insert .. select clause allows you to insert the values returned with any select.
You can return constant values as fields with select, like for instance:
SELECT 0 as id, 'John' as name
Combining these two points together, I used an insert..select clause to select the field value from the first table (aid), along with a constant value for the second field (m). The AS bname_fixed_val clause is simply a field alias, and can be omitted.
For more information on SQL, here 's a link: http://www8.silversand.net/techdoc/teachsql/index.htm, although googling it wouldn't hurt also.