How to insert comma delimited values in one column in oracle? - oracle

I have a table by the name of personal_info and it contains id,name and phone_number as columns. So the following is table structure which I want to store data.
id
name
phone_number
1
ali
03434444, 03454544, 0234334
So how to store data in phone_number column in comma delimited format and how to filter that column in where clause for example
Select * from personal_info where phone_number = 03454544 ;
And which datatype is suitable for phone_number column.

Well, the real good practice would rather be to have another table PHONE with a 1xN association (for example a PHONE_ID primary key, and ID and PHONE columns.)
You may then have the result you want with a view based on your two tables and using the LISTAGG operator : https://fr.wikibooks.org/wiki/Oracle_Database/Utilisation_de_fonctions/fonction_LISTAGG, but this will be much efficient to work with, especially if you want WHERE clauses based on your phone numbers.

Use LIKE with the delimiters:
Select *
from personal_info
where ', ' || phone_number || ', ' LIKE '%, ' || '03454544' || ', %';
However
You should consider changing your data structure to store the phone numbers in a separate table:
CREATE TABLE phone_numbers (
person_id REFERENCES personal_info (id),
phone_number VARCHAR2(12)
);
And then you can get the data using a JOIN
SELECT pi.*,
pn.phone_number
FROM personal_info pi
INNER JOIN phone_numbers pn
ON (pi.id = pn.person_id)
WHERE pn.phone_number = '03434444'
or, if you want all the phone numbers:
SELECT pi.*,
pn.phone_numbers
FROM personal_info pi
INNER JOIN (
SELECT person_id,
LISTAGG(phone_number, ', ') WITHIN GROUP (ORDER BY phone_number)
AS phone_numbers
FROM phone_numbers
GROUP BY person_id
HAVING COUNT(CASE WHEN phone_number = '03434444' THEN 1 END) > 0
) pn
ON (pi.id = pn.person_id)
db<>fiddle here

VARCHAR2 is suitable for phone numbers.
You can get the values this way:
WITH personal_info AS
(
SELECT 1 AS ID, 'Ali' AS NAME, '03434444, 03454544, 0234334' AS phone_number FROM dual
)
SELECT *
FROM (SELECT id, name, TRIM(regexp_substr(phone_number, '[^,]+', 1, LEVEL)) AS phone_number
FROM personal_info
CONNECT BY LEVEL <= LENGTH (phone_number) - LENGTH(REPLACE(phone_number, ',' )) + 1)
WHERE phone_number = '03454544';

Wrong data model, it isn't normalized. You should create a new table:
create table phones
(id_phone number constraint pk_phone primary key,
id_person number constraint fk_pho_per references person (id_person),
phone_number varchar2(30) not null
);
Then you'd store as many numbers as you want, one-by-one (row-by-row, that is).
If you want to do it your way, store it just like that:
insert into person (id, name, phone_number)
values (1, 'ali', '03434444, 03454544, 0234334');
One option of querying such data is using the instr function:
select * from person
where instr(phone_number, '03434444') > 0;
or like:
select * from person
where phone_number like '%'% || '03434444' || '%'
or split it into rows:
select * from person a
where '03434444' in (select regexp_substr(b.phone_number, '[^,]+', 1, level)
from person b
where b.id_person = a.id_person
connect by level <= regexp_count(b.phone_number, ',') + 1
)
I'd do it my way, i.e. with a new table that contains only phone numbers.

Related

Reverse of ascii function in Oracle

When I do following query
select asciistr(first_name) from person where id = 1
the result contains '\200D'.
So what is the reverse function of converting a '\200D' to character, so I can find all names with that specific character.
You want UNISTR (and, maybe, CAST it to a VARCHAR2). If you have the table:
CREATE TABLE person ( first_name, id ) AS
SELECT 'A', 1 FROM DUAL UNION ALL
SELECT CAST( UNISTR( '\200D' ) AS VARCHAR2(20) ), 1 FROM DUAL;
Then the output from your query:
select first_name,
asciistr(first_name)
from person
where id = 1
Is:
FIRST_NAME | ASCIISTR(FIRST_NAME)
:--------- | :-------------------
A | A
? | \200D
db<>fiddle here
\200D is the U+200D ZERO WIDTH JOINER
If you like to find names with this (non printable) character try
SELECT *
FROM person
WHERE first_name LIKE '%'||UNISTR('\200D')||'%'
or
WHERE asciistr(first_name) LIKE '%\200D%'

PL/SQL select the minimum value of a vector

I have a table that looks like this :
CREATE OR REPLACE TYPE tip_orase AS VARRAY(10) of VARCHAR2(50)
/
CREATE table excursie_try (
cod_excursie NUMBER(4),
denumire VARCHAR2(20),
orase tip_orase,
status varchar2(20)
);
And i need to find out 'cod_excursie' of the entry that has in orase the lowest number of entries.
I can do this with a lot of work by counting for each entry the number of cities and selecting a minimum. Then making a query to give 'cod_excursie' of the entry that has the lowest number of entries in orase.
Is there a simpler way ? I tried something like:
select cod_excursie
from excursie_try, (select max(orase.count()) m
from excursie_try) T
where orase.count = T.m
and ROWNUM <= 1;
but it does not work. Any ideas or i have to take the LONG way ?
Try this:
select cod_excursie from (
select et.cod_excursie,
(select count(*) from table(et.orase)) n
from excursie_try et order by 2 desc
) where rownum = 1;
(select count(*) from table(et.orase)) is a single-row subquery, I used TABLE to emulate sql table on varray.
order by 2 desc in the subquery + where rownum = 1 is used for top-N reporting.

Oracle Comma Separated Value (ID) in a Column. How to get Description for each Value in a Comma Separated string.

Sorry for the Confusing title.I myself did not understand it when i read it second time.
So here is the details description.
I have a table say "Awards" which have following Column:
Name,
Amount,
Employee
and Another table "Employee" which have following column:
Emp_Id,
Emp_Name
and in employee column of "Awards" table i have value "01,20" which are actually the Employee ID referenced to "Employee" table.
So is there any way i can get Employee Name in select "Awards" query?
Here is one method:
select a.*, e.EmpName
from Awards a join
Employees e
on ','||a.employee||',' like '%,'||e.emp_id||',%';
This will return the employee names on separate lines. If you want them in a list, then you would need to concatenate them together (and the best function for doing that depends on your version of Oracle).
By the way, this is a very bad data structure, You should have an association table AwardEmployee that has one row for each row and each employee.
Given below is the query to get comma seperated employee ids in form of rows which I put in subquery to get their name. Please edit as per your ewquirements.
Select Ename from employee where employee_id in (
SELECT trim(x.column_value.extract('e/text()')) COLUMNS
from awards t, table (xmlsequence(xmltype('<e><e>' || replace(Employee,':','</e><e>')||
'</e></e>').extract('e/e'))) x )
I have changed the Database (added one more table). and already started changing the CODE, as for the said report i have used following
WITH t AS
(
Select emp_name from employee where emp_id in (
select regexp_substr(Employee ,'[^,]+', 1, level) from awards
connect by regexp_substr((select Employee from awards ), '[^,]+', 1, level) is
not null)
)
SELECT LTRIM(SYS_CONNECT_BY_PATH(emp_name, ','),',') emp_name
FROM ( SELECT emp_name,
ROW_NUMBER() OVER (ORDER BY emp_name) FILA
FROM t )
WHERE CONNECT_BY_ISLEAF = 1
START WITH FILA = 1
CONNECT BY PRIOR FILA = FILA - 1
Which is temporary and i understand very less of it.
Thanks for you help and suggestion.

identify duplicates as well as the matched unique record in oracle

hi i am running the follwoing query to identify the duplicate records.
SELECT *
FROM unique2 P WHERE EXISTS(SELECT 1 FROM unique2 C
WHERE ( (C.surname) = (P.surname))
AND ( (C.postcode) = (P.postcode))
AND ((( (C.forename) IS NULL OR (P.forename) IS NULL)
AND (C.initials) = (P.initials))
OR (C.forename) = (P.forename))
AND ( (C.sex) = (P.sex)
OR (C.title) = (P.title))
AND (( (C.address1))=( (P.address1))
OR ( (C.address1))=( (P.address2))
OR ( (C.address2))=( (P.address1))
OR instr(C.address1_notrim, P.address1_notrim) > 0
OR instr(P.address1_notrim, C.address1_notrim) > 0)
AND C.rowid < P.rowid);
But with this query i can't identify the unique record id which is matched to the duplicate records. Is there a way to identify the
duplicates as well as the unique record id(my table has unique key) to which those duplicates are matched?
select id
from promolog
where surname, postcode, dob in (
select surname, postcode,dob
from (
select surname, postcode, dob, count(1)
from promolog
group by surname,postcode,dob
having count(1) > 1
)
)
You can also do this with analytic functions:
select id, num_of_ids, first_id, surname, postcode, dob
from (
select id,
count(*) over (partition by surname, postcode, dob) as num_of_ids,
first_value(id)
over (partition by surname, postcode, dob order by id) as first_id,
surname,
postcode,
dob
from promolog
)
where num_of_ids > 1;
Based on your update, I think you can just do a self-join, which you can make as complicated as you like:
select dup.*, master.id as duplicate_of
from promolog dup
join promolog master
on master.surname = dup.surname
and master.postcode = dup.postcode
and master.dob = dup.dob
... and <address checks etc. > ...
and master.rowid < dup.rowid;
But maybe I'm still missing something. As the name suggests, exists is for testing the existence of a matching record; if you want to retrieve any of the data from the matched record then you'll need to join to it at some point.

ORACLE: How to select previous different value?

I have table that stores employee job name, it has the following columns:
id; date_from; date_to; emp_id; jobname_id; grade;
Each emp_id can have many consecutive records with the same jobname_id due to many grade changes.
How can I select previous different jobname_id omitting those that are the same like the most current one?
This solution uses the FIRST_VALUE() analytic function to identify each employee's current job. It then filters for all the jobs which dfon't match that one:
select distinct id
, jobname_id
from ( select id
, jobname_id
, first_value(jobname_id) over (partition by id
order by from_date desc) as current_job
from employee
where emp_id = 1234 )
where jobname_id != current_job
order by id, jobname_id
/
Will this work for your issue:
SELECT DISTINCT
e1.emp_id,
e1.jobname_id
FROM employee e1
WHERE NOT EXISTS
(SELECT 1
FROM employee e2
WHERE e1.emp_id = e2.emp_id
AND SYSDATE BETWEEN e2.date_from
AND NVL(e2.date_to, SYSDATE + 1));
(This asumes your table is named "employee" and emp_id is the PK value).
It selects unique emp_id, jobname_id values where the emp_id, jobname_id values are not current.
EDIT: I agree with Chin Boon that fundamentally this is a design issue and perhaps that should be addressed rather than working around the problem.

Resources