query from tables where table name is in the current query - sql

I have this query:
select main_value, subtable, subtable_column from table1
I want to know can I query values from subtable at the same time that this query is running?
EDIT
----
table1 structure
main_value varchar
subtable varchar
subtable_column varchar
branches structure(subtable in the query)
id number
branch_name varchar
when I query from table1 and if got subtable's value as branches, and subtable_column's value as branch_name then query branch_name from branches.(becuase subtable column's value is branches retrieved from the query and column names vise versa).
EDIT for tables and samples
---------------------------
table1
+-----------------+--------------------+
| Field | Type |
+-----------------+--------------------+
| ID | number(20,0) |
| subtable | varchar2(50 BYTE) |
| subtable_column | varchar2(100 BYTE) |
+-----------------+--------------------+
branches
+-------------+-------------------+
| Field | Type |
+-------------+-------------------+
| ID | number(20,0) |
| branch_name | varchar2(50 BYTE) |
+-------------+-------------------+
NB: these are just examples, so please dont suggest other ways rather than select from tables where table names and column names are in the current query at the same time.
i have records in table1 as below:
ID | subtable | subtable_column
-------------------------------
1 | branches | branch_nmae
2 | null | null
and branches table as below:
ID | branch_name |
------------------
1 | new york
2 | colombo
output:
+-------------+-------------------+---------------------------------+
| subtable | subtable_column | selected values from branches |
+-------------+-------------------+---------------------------------+
| branches | branch_name | new york, colombo |
| null | null | |
+-------------+-------------------+---------------------------------+

If you know all possible tables and all possible columns which table1 may contain then you could build query with case when containing all possibilities. Something like this:
select id, subtable, subtable_column,
case
when subtable = 'branches' and subtable_column = 'branch_name' then
(select listagg(branch_name, ', ') within group (order by branch_name)
from branches)
when subtable = 'branches' and subtable_column = 'id' then
(select listagg(id, ', ') within group (order by id)
from branches)
end as subtable_values
from table1;
But more common solution for such problems is to use dynamic SQL. So write a function like here:
create or replace function get_subtable_values(i_table in varchar2, i_column in varchar2)
return varchar2 is
v_sql varchar2(32767);
v_ret varchar2(32767);
begin
if i_table is null then
return null;
end if;
v_sql := 'select listagg('||i_column||', '', '') within group (order by null) from '||i_table;
execute immediate v_sql into v_ret;
return v_ret;
end;
... and use it in query:
select id, subtable, subtable_column,
get_subtable_values(subtable, subtable_column) as subtable_values
from table1;
My test data and output for both queries:
create table table1(id, subtable, subtable_column) as (
select 1, 'branches', 'branch_name' from dual union all
select 2, 'branches', 'id' from dual union all
select 3, null, null from dual);
create table branches(id, branch_name) as (
select 1, 'New York' from dual union all
select 2, 'Colombo' from dual );
Result:
ID SUBTABLE SUBTABLE_COLUMN SUBTABLE_VALUES
------ -------- --------------- --------------------
1 branches branch_name Colombo, New York
2 branches id 1, 2
3 null null null
I used function listagg() which glues all values into one string, comma separated. It's also possible to return table of varchars or user defined types. Of course there may be many issues like strings too long, data types other than varchar, exceptions should be handled, etc. But you see that's possible.

SELECT DISTINCT
table1.id,
table1.subtable,
table1.subtable_column,
t.*
FROM
table1
JOIN branches
LEFT JOIN ( SELECT group_concat( branch_name ) AS NAMES FROM branches ) AS t ON 1 = 1
this sql is ok,but it is less efficient

Related

SELECT subquery with 2 return values

I want to select multiple columns from a subquery. Here my minimal example:
A function that returns two values:
CREATE OR REPLACE FUNCTION dummy_function(my_text text)
RETURNS TABLE (id Integer, remark text) AS $$
BEGIN
RETURN QUERY SELECT 42, upper(my_text);
END;
$$ LANGUAGE plpgsql;
My not working query:
SELECT
id,
city_name,
dummy_function(city_name)
FROM
(SELECT 1 as id, 'Paris' as city_name
UNION ALL
SELECT 2 as id, 'Barcelona' as city_name
) AS dummy_table
My wrong result:
id | city_name | dummy_function
----+-----------+----------------
1 | Paris | (42,PARIS)
2 | Barcelona | (42,BARCELONA)
But I would like to have a result like this:
id | city_name | number | new_text
----+-----------+---------------------
1 | Paris | 42 | PARIS
2 | Barcelona | 42 | BARCELONA
Do you know how to achieve this without running the function twice?
Use the function returning row (or set of rows) in the FROM clause:
SELECT
dummy_table.id,
city_name,
dummy_function.id,
remark
FROM
(SELECT 1 as id, 'Paris' as city_name
UNION ALL
SELECT 2 as id, 'Barcelona' as city_name
) AS dummy_table,
LATERAL dummy_function(city_name)
id | city_name | id | remark
----+-----------+----+-----------
1 | Paris | 42 | PARIS
2 | Barcelona | 42 | BARCELONA
(2 rows)
Per the documentation:
Table functions appearing in FROM can also be preceded by the key word LATERAL, but for functions the key word is optional; the function's arguments can contain references to columns provided by preceding FROM items in any case.
SELECT
dummy_table.id,
city_name,
df.id as number,
df.remark as new_text
FROM
(SELECT 1 as id, 'Paris' as city_name
UNION ALL
SELECT 2 as id, 'Barcelona' as city_name
) AS dummy_table,
dummy_function(city_name) df

replace select table by an update

I have an intermediate table:
text_mining_molecule
|text_mining_id| molecule_id |
| -------------| ---------- |
| ID | ID |
and two other tables:
Table Molécules:
id | main_name | others …
--- | --------- | ------
1 | caféine | others …
Table jsonTextMining:
id | title | molecule_name | others …
---|------- |-------------------------------------|------
1 | title1 | colchicine, cellulose, acid, caféine| others …
text_mining_molecule need to be inserted when select a choice in a list with ID's from 2 others tables json_text_mining and molecule.
Actually there is a dropdown that already insert all rows from json_text_mining to text_mining when choose a score under 4.
INSERT INTO text_mining (id, solrid, originalpaper, annotatedfile, title, keyword, importantsentence, version, klimischscore, moleculename, synonymname, validation)
SELECT id, solrid, originalpaper, annotatedfile, title, keyword, importantsentence, version, klimischscore, molecule_name, synonym_name, validation
FROM json_text_mining WHERE klimischscore < 4
This works but i need text_mining_molecule to be filled also with related ID's so i have also this part of code :
SELECT s.id, m.id
FROM (SELECT id, regexp_split_to_table(molecule_name,', ') AS m_name
FROM json_text_mining) AS s, molecule m
WHERE m.main_name = s.m_name;
How can i update text_mining_molecule table directly with an insert instead a select ?
use CTE. eg if text_mining_molecule.molecule references molecule.id, would be smth like:
with c as (
SELECT s.id sid, m.id mid
FROM (SELECT id, regexp_split_to_table(molecule_name,', ') AS m_name
FROM json_text_mining) AS s, molecule m
WHERE m.main_name = s.m_name
)
update text_mining_molecule t
set sid = c.sid
from c
where t.molecule = c.mid

How to copy rows into a new a one to many relationship

I'm trying to copy a set of data in a one to many relationship to create a new set of the same data in a new, but unrelated one to many relationship. Lets call them groups and items. Groups have a 1-* relation with items - one group has many items.
I've tried to create a CTE to do this, however I can't get the items inserted (in y) as the newly inserted groups don't have any items associated with them yet. I think I need to be able to access old. and new. like you would in a trigger, but I can't work out how to do this.
I think I could solve this by introducing a previous parent id into the templateitem table, or maybe a temp table with the data required to enable me to join on that, but I was wondering if it is possible to solve it this way?
SQL Fiddle Keeps Breaking on me, so I've put the code here as well:
DROP TABLE IF EXISTS meta.templateitem;
DROP TABLE IF EXISTS meta.templategroup;
CREATE TABLE meta.templategroup (
templategroup_id serial PRIMARY KEY,
groupname text,
roworder int
);
CREATE TABLE meta.templateitem (
templateitem_id serial PRIMARY KEY,
itemname text,
templategroup_id INTEGER NOT NULL REFERENCES meta.templategroup(templategroup_id)
);
INSERT INTO meta.templategroup (groupname, roworder) values ('Group1', 1), ('Group2', 2);
INSERT INTO meta.templateitem (itemname, templategroup_id) values ('Item1A',1), ('Item1B',1), ('Item2A',2);
WITH
x AS (
INSERT INTO meta.templategroup (groupname, roworder)
SELECT distinct groupname || '_v1' FROM meta.templategroup where templategroup_id in (1,2)
RETURNING groupname, templategroup_id, roworder
),
y AS (
Insert INTO meta.templateitem (itemname, templategroup_id)
Select itemname, x.templategroup_id
From meta.templateitem i
INNER JOIN x on x.templategroup_id = i.templategroup_id
RETURNING *
)
SELECT * FROM y;
Use an auxiliary column templategroup.old_id:
ALTER TABLE meta.templategroup ADD old_id int;
WITH x AS (
INSERT INTO meta.templategroup (groupname, roworder, old_id)
SELECT DISTINCT groupname || '_v1', roworder, templategroup_id
FROM meta.templategroup
WHERE templategroup_id IN (1,2)
RETURNING templategroup_id, old_id
),
y AS (
INSERT INTO meta.templateitem (itemname, templategroup_id)
SELECT itemname, x.templategroup_id
FROM meta.templateitem i
INNER JOIN x ON x.old_id = i.templategroup_id
RETURNING *
)
SELECT * FROM y;
templateitem_id | itemname | templategroup_id
-----------------+----------+------------------
4 | Item1A | 3
5 | Item1B | 3
6 | Item2A | 4
(3 rows)
It's impossible to do that in a single plain sql query without an additional column. You have to store the old ids somewhere. As an alternative you can use plpgsql and anonymous code block:
Before:
select *
from meta.templategroup
join meta.templateitem using (templategroup_id);
templategroup_id | groupname | roworder | templateitem_id | itemname
------------------+-----------+----------+-----------------+----------
1 | Group1 | 1 | 1 | Item1A
1 | Group1 | 1 | 2 | Item1B
2 | Group2 | 2 | 3 | Item2A
(3 rows)
Insert:
do $$
declare
grp record;
begin
for grp in
select distinct groupname || '_v1' groupname, roworder, templategroup_id
from meta.templategroup
where templategroup_id in (1,2)
loop
with insert_group as (
insert into meta.templategroup (groupname, roworder)
values (grp.groupname, grp.roworder)
returning templategroup_id
)
insert into meta.templateitem (itemname, templategroup_id)
select itemname || '_v1', g.templategroup_id
from meta.templateitem i
join insert_group g on grp.templategroup_id = i.templategroup_id;
end loop;
end $$;
After:
select *
from meta.templategroup
join meta.templateitem using (templategroup_id);
templategroup_id | groupname | roworder | templateitem_id | itemname
------------------+-----------+----------+-----------------+-----------
1 | Group1 | 1 | 1 | Item1A
1 | Group1 | 1 | 2 | Item1B
2 | Group2 | 2 | 3 | Item2A
3 | Group1_v1 | 1 | 4 | Item1A_v1
3 | Group1_v1 | 1 | 5 | Item1B_v1
4 | Group2_v1 | 2 | 6 | Item2A_v1
(6 rows)

In the following PLSQL trigger, , why is everything being casted into AS VARCHAR2 in the pivot code?

I am trying to wrap my head around this trigger. I have trigger code here, that I'm studying:
INSERT INTO ONLINE_PROFILE_HISTORY.ONLINE_ACCOUNT_AVH
SELECT ONLINE_PROFILE_HISTORY.ONLINE_ACCOUNT_AVH_SEQ.NEXTVAL
,tmpVar
,O.ATTR_NM
,O.ATTR_OLD_VLU
,CAST(NULL AS CHAR) AS ATTR_NEW_VLU
FROM (SELECT :OLD.ONL_ACCT_ID
,CAST(:OLD.ONL_ACCT_INTL_ID AS VARCHAR2(1024)) ONL_ACCT_INTL_ID
,CAST(:OLD.ONL_ACCT_EXT_ID AS VARCHAR2(1024)) AS ONL_ACCT_EXT_ID
,CAST(:OLD.LCKED_TS AS VARCHAR2(1024)) AS LCKED_TS
,CAST(:OLD.DISABLED_TS AS VARCHAR2(1024)) AS DISABLED_TS
,CAST(:OLD.LST_SCSFL_LOGIN_TS AS VARCHAR2(1024)) AS LST_SCSFL_LOGIN_TS
/* etc etc*/
,CAST(:OLD.VLDT_SCRT_QUES_FAILURE_CNT AS VARCHAR2(1024)) AS VLDT_SCRT_QUES_FAILURE_CNT
,CAST(:OLD.VLDT_SCRT_QUES_LST_FAILURE_TS AS VARCHAR2(1024)) AS VLDT_SCRT_QUES_LST_FAILURE_TS
,CAST(:OLD.VLDT_SCRT_QUES_SUCCESS_TS AS VARCHAR2(1024)) AS VLDT_SCRT_QUES_SUCCESS_TS
,CAST(:OLD.DLTD_TS AS VARCHAR2(1024)) AS DLTD_TS
,CAST(:OLD.TRMS_CONDS_ACPTED_HOST_ADDR_NM AS VARCHAR2(1024)) AS TRMS_CONDS_ACPTED_HOST_ADDR_NM
FROM dual)
UNPIVOT (ATTR_OLD_VLU FOR
ATTR_NM IN (
ONL_ACCT_INTL_ID
,ONL_ACCT_EXT_ID
,LCKED_TS
,DISABLED_TS
,VLDT_TOKEN_VLU
,VLDT_TOKEN_KEY_NM
,VLDTD_TS
,VLDT_TOKEN_CREATE_TS
,PRIM_BILLING_ACCT_SRC_ID
,PRIM_BILLING_ACCT_SRC_ID_2
,PRIM_BILLING_ACCT_SRC_SYS_CD
,TRMS_CONDS_VER_NUM
,TRMS_CONDS_ACPTED_TS
,PSWD_FMT_NM
,VLDT_SCRT_QUES_FAILURE_CNT
,VLDT_SCRT_QUES_LST_FAILURE_TS
,VLDT_SCRT_QUES_SUCCESS_TS
,DLTD_TS
,TRMS_CONDS_ACPTED_HOST_ADDR_NM
)) O;
Why is it that we are doing the
CAST(:OLD.VLDTD_TS AS VARCHAR2(1024)) AS VLDTD_TS
part there? I generally understand how the pivot is working, but not sure why every column is casted like that.
UNPIVOT transforms the data that is spread across several columns into a single column. If the datatype of those columns in not same, it will result in error.
ORA-01790: expression must have same datatype as corresponding expression
SQL Fiddle
create table mytable(
id_ number,
col1_ varchar2(10),
col2_ number,
col3_ date
);
insert into mytable values(1, 'asd',32,date'2014-03-04');
insert into mytable values(2, 'qwe',16,date'2014-02-11');
select *
from mytable
unpivot(val_ for col_name in (
col1_,col2_,col3_)
);
ORA-01790: expression must have same datatype as corresponding expression : select * from mytable unpivot(val_ for col_name in ( col1_,col2_,col3_) )
That's why you should convert all the columns to same datatype before unpivoting.
SQL Fiddle
select *
from (
select
id_,
col1_,
to_char(col2_) col2_,
to_char(col3_,'dd-mm-yyyy') col3_
from mytable
)
unpivot(
val_ for col_name_ in (
col1_, col2_, col3_
)
)
Results:
| ID_ | COL_NAME_ | VAL_ |
|-----|-----------|------------|
| 1 | COL1_ | asd |
| 1 | COL2_ | 32 |
| 1 | COL3_ | 04-03-2014 |
| 2 | COL1_ | qwe |
| 2 | COL2_ | 16 |
| 2 | COL3_ | 11-02-2014 |

Sql select query based on a column value

I have a Table1 like this:
ApplicableTo IdApplicable
---------------------------
Dept 1
Grade 3
section 1
Designation 2
There other tables like:
tblDept:
ID Name
1 dept1
2 baking
3 other
tblGrade:
ID Name
1 Grd1
2 Manager
3 gr3
tblSection:
id Name
1 Sec1
2 sec2
3 sec3
tblDesignation:
id Name
1 Executive
2 Developer
3 desig3
What I need is a query for table1 in such a way that gives me
ApplicableTo (table1)
Name (from the relevant table based on the value in `ApplicableTo` column)
Is this possible?
Desired Result:
eg: ApplicableTo IdApplicable Name
Dept 1 dept1
grade 3 gr3
Section 1 sec1
Designation 2 Developer.
This is the result I desire.
You could do something like the following so the applicable to becomes part of the JOIN predicate:
SELECT t1.ApplicableTo, t1.IdApplicable, n.Name
FROM Table1 AS t1
INNER JOIN
( SELECT ID, Name, 'Dept' AS ApplicableTo
FROM tblDept
UNION ALL
SELECT ID, Name, 'Grade' AS ApplicableTo
FROM tblGrade
UNION ALL
SELECT ID, Name, 'section' AS ApplicableTo
FROM tblSection
UNION ALL
SELECT ID, Name, 'Designation' AS ApplicableTo
FROM tblDesignation
) AS n
ON n.ID = t1.IdApplicable
AND n.ApplicableTo = t1.ApplicableTo
I would generally advise against this approach, although it may seem like a more consice approach, you would be better having 4 separate nullable columns in your table:
ApplicableTo | IdDept | IdGrade | IdSection | IdDesignation
-------------+--------+---------+-----------+---------------
Dept | 1 | NULL | NULL | NULL
Grade | NULL | 3 | NULL | NULL
section | NULL | NULL | 1 | NULL
Designation | NULL | NULL | NULL | 2
This allows you to use foreign keys to manage your referential integrity properly.
You can use CASE here,
SELECT ApplicableTo,
IdApplicable,
CASE
WHEN ApplicableTo = 'Dept' THEN (SELECT Name FROM tblDept WHERE tblDept.ID = IdApplicable)
WHEN ApplicableTo = 'Grade' THEN (SELECT Name FROM tblGrade WHERE tblGrade.ID = IdApplicable)
WHEN ApplicableTo = 'Section' THEN (SELECT Name FROM tblSection WHERE tblSection.ID = IdApplicable)
WHEN ApplicableTo = 'Designation' THEN (SELECT Name FROM tblDesignation WHERE tblDesignation.ID = IdApplicable)
END AS 'Name'
FROM Table1
The easiest way to achieve that is to add an extra column in table1 to keep the table where id is refferred to. Otherwise you can't know in which table the applicable id is reffered to.
Or you can create the applicable id in a way that you can extract the table afterwords from it for example a1 for id 1 in tblDept. And then use [case] (http://dev.mysql.com/doc/refman/5.0/en/case.html) (for mysql) in order to make the correct Join.