How to split the columns? - sql

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

Related

Query to select rows that don't partially match

I have the following table
STORE_ID|PRICE_1|PRODUCT_ID
--------+-------+----------
1052| 4.99|5157917035
1052| 4.99|5157917035
1052| 4.99|5157917036
1052| 4.99|5157917036
1052| 4.99|5157917037
As you can see these product IDs starts with "5157817". Is there a way to select only part of the value, in this case ignoring the last 3 digits and then filter out rows that are not distinct
Is there a way to select only part of the value
Sure; usually, we use substr function. If column's datatype is one of the CHAR family, just apply it directly. Otherwise, if it is a NUMBER, first convert it to a string (using the to_char function).
For example:
SQL> create table test (col_n number, col_c varchar2(10));
Table created.
SQL> insert into test values (5157917035, '5157917035');
1 row created.
SQL> select substr(to_char(col_n), 1, 7) sub_n,
2 substr(col_c, 1, 7) sub_c
3 from test;
SUB_N SUB_C
---------------------------- ----------------------------
5157917 5157917
SQL>
I didn't quite understand what result you expect out of data set you posted, but - if you ran e.g.
select DISTINCT store_id,
price_1,
substr(product_id, 1, 7)
from your_table
you'd get only one row.

How to pass value as parameter to join tables in Oracle 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

Oracle APEX 5.1 - How to sort selected values in a Shuttle control

I have a shuttle control with a list of countries. So the user can select multiple countries, example: UK, France, Portugal. The order depends on how the user selects or moves the choices (see pic)
.
These are stored as UK:France:Portugal in the underlying table.
The problem is that I need these countries to be stored alphabetically because UK:France:Portugal is not the same as France:Portugal:UK. I know that in an ideal world these are saved in separate rows, but unfortunately this is not an option for me.
Is there a way to sort the selected values within the shuttle (on the right) alphabetically, maybe through a dynamic action when selecting countries?
If not, as an alternative, can we have a Post Calculation Computation to sort and store these values?
Thanks!
I don't know Apex-solution, but I can suggest Oracle-solution.
Here's an example: table test stores information about countries:
SQL> create table test (id number, countries varchar2(30));
Table created.
A database trigger sorts values in the countries column. How? It splits colon-separated values string into rows (that's what the regexp and connect by do), and then merges them back into another colon-separated values string (using listagg), but this time sorted (order by 1):
SQL> create or replace trigger trg_biu_cou
2 before insert or update on test
3 for each row
4 begin
5 select listagg(regexp_substr(:new.countries, '[^:]+', 1, level), ':') within group (order by 1)
6 into :new.countries
7 from dual
8 connect by level <= regexp_count(:new.countries, ':') + 1;
9 end;
10 /
Trigger created.
OK, let's see it work:
SQL> insert into test (id, countries) values (1, 'UK:France:Portugal');
1 row created.
SQL> select * from test;
ID COUNTRIES
---------- ------------------------------
1 France:Portugal:UK
SQL> update test set countries = 'New Zealand:Croatia:Hungary' where id = 1;
1 row updated.
SQL> select * from test;
ID COUNTRIES
---------- ------------------------------
1 Croatia:Hungary:New Zealand
SQL>
Might be OK; give it a try.

Oracle Sequence wastes/reserves values (in INSERT SELECT)

I've been struggling with sequences for a few days. I have this Origin data table called "datos" with the next columns:
CENTRO
CODV
TEXT
INCIDENCY
And a Destiny data table called "anda" with the following:
TIPO = 31 (for all rows)
DESCRI = 'Site' (for all rows)
SECU = sequence number generated with Myseq.NEXTVAL
CENTRO
CODV
TEXT
The last three columns must be filled in with data from "datos" table.
When I execute my query, it all works fine, my table is filled and the sequence generates its values. But, in the INSERT INTO SELECT, I have the following conditions:
Every row in origin "datos" must not already be in the destiny "anda", so it won't be duplicated, and every row in "datos" must have the INCIDENCY flag value to 'N' or NULL.
If each row matches the conditions, it should be filled.
The thing is, that the query works fine and I have been trying with many different values. Here comes the problem:
When a row has its INCIDENCY value set to 'Y' (so it must not be copied into destiny table), it doesn't appear, but the sequence DOES consumes one value, and when I check Myseq.NEXTVAL its value is higher.
How can I prevent the sequence to add any value when it doesn't match the conditions? I've read that Oracle first reserves all the possible values returning from the SELECT query, but I can't find how to prevent it.
Here's the SQL:
INSERT INTO anda (TIPO, DESCRI, SECU, CENTRO, CODV, TEXT)
SELECT( 31 TIPO,
'Site' DESCRI,
Myseq.NEXTVAL,
datos.CENTRO,
datos.CODV,
datos.TEXT
FROM datos
WHERE (CENTRO, CODV) NOT IN
(SELECT CENTRO, CODV
FROM anda)
AND (datos.INCIDENCY = 'N' OR datos.INCIDENCY IS NULL)
)
Thanks in advance!!
Definition of MySeq
CREATE SEQUENCE CREATE SEQUENCE "BBDD"."MySeq" MINVALUE 800000000000
MAXVALUE 899999999999 INCREMENT BY 1 START WITH 800000000000 CACHE 20 ORDER NOCYCLE ;
You might be able to trick Oracle into doing this with a CTE:
INSERT INTO anda (TIPO, DESCRI, SECU, CENTRO, CODV, TEXT)
WITH toinsert as (
SELECT d.*
FROM datos d
WHERE (CENTRO, CODV) NOT IN (SELECT CENTRO, CODV FROM anda) AND
(d.INCIDENCY = 'N' OR d.INCIDENCY IS NULL)
)
SELECT 31 as TIPO, 'Site' as DESCRI, Myseq.NEXTVAL,
d.CENTRO, d.CODV, d.TEXT
FROM toinsert d;
I'm not quite sure if that will work. A more guaranteed approach is to use a before insert trigger (or identity column if you are using 12c+). You would increment the value in the trigger.
However, I do agree with Hugh Jones. You should be confident using the sequence to add a unique value to each row and this value will be increasing. Gaps can appear for other reasons, such as deletes. Also, I know that SQL Server can create gaps when doing parallel inserts; I'm not sure if that also happens with Oracle.
I don't believe you have a real problem(the gaps are not really an issue) but you can put a before insert (at row level) trigger on anda table and set sequ there with your sequence generated value.
But keep in mind that this will keep consecutive only the sequ number in a statement. You'll get gaps anyway for other reasons.
UPDATE: as Alex Poole has commented, the insert itself does not generate gaps.
See a test below:
> drop sequence tst_fgg_seq;
sequence TST_FGG_SEQ dropped.
> drop table tst_fgg;
table TST_FGG dropped.
> drop table tst_insert_fgg;
table TST_INSERT_FGG dropped.
> create sequence tst_fgg_seq start with 1 nocycle;
sequence TST_FGG_SEQ created.
> create table tst_fgg as select level l from dual connect by level < 11;
table TST_FGG created.
> create table tst_insert_fgg as
select tst_fgg_seq.nextval
from tst_fgg
where l between 3 and 5;
table TST_INSERT_FGG created.
> select * from tst_insert_fgg;
NEXTVAL
----------
1
2
3
> insert into tst_insert_fgg
select tst_fgg_seq.nextval
from tst_fgg
where l between 3 and 5;
3 rows inserted.
> select * from tst_insert_fgg;
NEXTVAL
----------
1
2
3
4
5
6
6 rows selected

Read Rows But search first

I want to make an import system that will look into one Datasource and copy new records into another DataSource.
Monthly I want to copy some tables data from one datasource to another datasource
SourceTableName : srcTable
DestinationTableName : destTable
Suppose first month in source table I have:
Id Name 1 john
3 Rahul 5 Andrew
All three rows Will be copy into desTable
Suppose Second Month in Source Table I have
Id Name 1 John
3 Rahul 5 Andrew
6 Vikas 7 Sonam
8 Divya
Firstly Sql Should get the last Row of desTable
and match that row into srcTable
and extract all new records from scrTable and copied into desTable
.....
Please let me know how I can write query for fulfill above purpose. If there is shorter approach, that would be helpful too.
Since you only care about adding new records, and don't need to handle updates or deletes... You can simply add the record from the source table if it doesn't exist in the destination table:
INSERT INTO destTable (ID, Name)
SELECT s.ID, s.Name
FROM
srcTable s
LEFT OUTER JOIN destTable d ON d.ID = s.ID
WHERE
d.ID IS NULL
You can write a stored procedure for do this action and execute that every time you want.
for this action you can from bellow query:
(Part 1 for insert new data, Part 2 for update change data)
Insert Into DestinationTable(ID, Name)
Select ID, Name
From SoiurceTable
Where Not Exists
(Select *
From TDestinationTablest
Where DestinationTable.ID = SoiurceTable.ID)
Go
Update DestinationTable
Set DestinationTable.Name = SoiurceTable.Name
From DestinationTable, SoiurceTable
Where DestinationTable.ID = SoiurceTable.ID
I hope it's helpful.