Update every row with auto-generated value - sql

I have a table:
CREATE TABLE "person" (
"ID" NUMBER(38,0),
"NAME" VARCHAR2(50 CHAR)
)
Data:
id name
1 monica
2 null
3 null
4 stephan
5 null
I need update null values with value "person" and auto incremented value (from 1 - person1, person2, person3 etc):
1 monica
2 person1
3 person2
4 stephan
5 person3
What sql (oracle) query should I use?

Analytic function is not needed. The update can be done simply like this:
UPDATE person
SET NAME = 'person' || ROWNUM
WHERE name IS NULL;
Update
Since the person number needs to be sequential with the IDs of the person, this query can be used:
UPDATE person p
SET p.name =
'person'
|| (SELECT person_number
FROM (SELECT p2.id, ROW_NUMBER () OVER (ORDER BY p2.id) AS person_number
FROM person p2
WHERE name IS NULL)
WHERE id = p.id)
WHERE p.name IS NULL;

You actually have 2 issues, although your question only addresses 1 of them. You must not only 'fix the data' but also 'fix the system'. With the second (unaddressed) being the most important. There are 2 methods for this, lets call them
the correct method
-- update existing data
update person1
set name = 'PERSON' || to_char(rownum,'FM999')
where id is null;
-- fix the system
alter table person modify name not null;
the band-aid method
-- prepare for long term bandaid
create sequence unknown_person_seq;
-- update existing data
update person
set name = 'PERSON' || to_char(unknown_person_seq.nextval, 'FM99')
where name is null;
select * from person2;
-- adjust the system
alter table person modify name default 'Person' || to_char(unknown_person_seq.nextval, 'FM99');
The difference being the correct method keeps further data corruption out of the system. See demo here. It is far better to raise an exception for invalid data than it is to allow or generate bogus data. Consider your Person table holds customer information and you refer to them as PERSON4. How long will they remain your customer?

It works:
SET
name = (
SELECT n
FROM(
SELECT
id, 'person' || ROW_NUMBER() OVER (ORDER BY id) AS n
FROM
person
WHERE
name IS NULL
)u
WHERE
u.id = person.id)
WHERE
NAME IS NULL

Related

Own id for every unique name in the table?

Is it possible to make a table that has like auto-incrementing id's for every unique name that I make in the table?
For example:
ID NAME_ID NAME
----------------------
1 1 John
2 1 John
3 1 John
4 2 Mary
5 2 Mary
6 3 Sarah
7 4 Lucas
and so on.
Use the window function rank() to get a unique id per name. Or dense_rank() to get the same without gaps:
SELECT id, dense_rank() OVER (ORDER BY name) AS name_id, name
FROM tbl;
I would advise not to write that redundant information to your table. You can generate that number on the fly. Or you shouldn't store name redundantly in that table, name would typically live in another table, with name_id as PRIMARY KEY.
Then you have a "names" table and run "SELECT or INSERT" there to get a unique name_id for every new entry in the main table. See:
Is SELECT or INSERT in a function prone to race conditions?
First add the column to the table.
ALTER TABLE yourtable
ADD [UID] INT NULL;
``
ALTER TABLE yourtable
ADD constraint fk_yourtable_uid_id foreign key ([UID]) references yourtable([Serial]);
Then you can update the UID with the minimum Serial ID per Name.
UPDATE t
SET [UID] = q.[UID]
FROM yourtable t
JOIN
(
SELECT Name, MIN([Serial]) AS [UID]
FROM yourtable
GROUP BY Name
) q ON q.Name = t.Name
WHERE (t.[UID] IS NULL OR t.[UID] != q.[UID]) -- Repeatability

How to get id of parent tables which haven't any foreign key with another table

I have a structure of a table have many fields
table_id
name_table
name_filed
PK
FK
1
person
id
pk
1
person
name
1
person
age
2
dog
id
pk
2
dog
name
2
dog
owner
fk
3
phone
id
pk
3
phone
name
3
phone
owner
fk
How to get id(s) of parent tables which haven't any foreign key with another table
in this case expected result is 1
I've tried
select distinct table_id from tables_structure where fk!=''
Also I've tried with group by
select table_id from tables_structure where fk!=''
group by table_id
having fk!=''
The first issue is that you are comparing to an empty string, generally we expect the empty value to be represented by a null value, so try comparing using IS NULL
select distinct table_id from tables_structure where fk IS NULL
But that isn't likely to help you here, your data represents an UNPIVOT structure, your second attempt would work if you used a COUNT in your HAVING clause, here we don't even have to compare nulls because COUNT will exclude nulls for us!
select table_id
from tables_structure
group by table_id
having COUNT(fk) = 0
If the values really are empty strings, and not nulls, then we can still use count with nulls by treating '' as a null value using NULLIF:
select table_id
from tables_structure
group by table_id
having COUNT(NULLIF(fk,'')) = 0
We can't just filter by fk <> '' as that will modify the dataset and return ALL records.
You can use a SUM over a CASE statement that computes a 1 or 0 for each record, but now things are getting complicated:
select table_id
from tables_structure
group by table_id
having SUM(CASE fk WHEN '' THEN 0 ELSE 1)) = 0

How to manipulate VARRAYS in sql (oracle)?

Supposing i am using a table person, and persons might have multiple last names, so that attribute should be a varray of 3 elements for example (it's not about where to store last names), here is a simple sql for creating the type last name, the table person and adding an example row in oracle's sql developper (11G XE):
create type lastn as varray(3) of varchar2(10);
CREATE TABLE person
(
ID NUMBER NOT NULL
, last_name lastn
, CONSTRAINT EXEMPLE_PK PRIMARY KEY
(
ID
)
ENABLE
);
insert into person values(1,lastn('dani','bilel'));
I know how to update all last names at once, but i need to preserve existing last names and add other last names, or remove a single last name without affecting the others. In a nutshell, i want my code to be like (i am not familiar with PL/SQL):
insert into table
(select last_name from example where id=1)
values lastn('new');
This is the case where i want to get persons that have a first last name of 'bilel' and second last_name as 'dani'
select * from person where id in (select id from pernom p,table(p.last_name)
where column_value(1)='bilel' and column_value(2)='dani');
I know that it doesn't work like that, but i want to know CRUD(create update delete) statements in that case. and select statement with varray in where statement.
Thanks for your response.
From the docs:
Oracle does not support piecewise updates on VARRAY columns. However, VARRAY columns can be inserted into or updated as an atomic unit.
As shown in the examples there, you can manipulate the collection through PL/SQL instead; incuding adding an element to the array:
declare
l_last_name lastn;
begin
select last_name into l_last_name
from person where id = 1;
l_last_name.extend();
l_last_name(l_last_name.count) := 'third';
update person
set last_name = l_last_name
where id = 1;
end;
/
PL/SQL procedure successfully completed.
select last_name from person where id = 1;
LAST_NAME
--------------------------------------------------
LASTN('dani', 'bilel', 'third')
You can also do this via cast(multiset(...) as ...):
-- rollback; to reverse PL/SQL block actions above
update person p
set last_name = cast(multiset(
select column_value
from table (last_name)
union all
select 'third' from dual
) as lastn)
where id = 1;
1 row updated.
select last_name from person where id = 1;
LAST_NAME
--------------------------------------------------
LASTN('dani', 'bilel', 'third')
That explodes the existing last_name value into multiple rows, union's in a new value, and then converts the combined result back into your varray type.
And you can delete or update elements in a similar way:
update person p
set last_name = cast(multiset(
select column_value
from table (last_name)
where column_value != 'bilel'
) as lastn)
where id = 1;
1 row updated.
select last_name from person where id = 1;
LAST_NAME
--------------------------------------------------
LASTN('dani', 'third')
update person p
set last_name = cast(multiset(
select case column_value when 'third' then 'second' else column_value end
from table (last_name)
) as lastn)
where id = 1;
1 row updated.
select last_name from person where id = 1;
LAST_NAME
--------------------------------------------------
LASTN('dani', 'second')
For the select statement, i've figured out the solution, which goes like this :
select * from person p where id in (select id from table(p.last_name) where
column_value='bilel' intersect select id from table(p.last_name) where
column_value='dani');
or
select * from agent ag where id in (select id from table(ag.prenom)
t1,table(ag.prenom) t2,table(ag.prenom) t3 where t1.column_value='bilel' and
t2.column_value='dani' and t3.column_value='third');

How to delete values from first table by using name of the second sql

I have a table groups
group_id | name_group
1 ISI
2 IZI
And a table students
id | first_name | last_name | group_id
6 Bob Surname1 1
17 John Surname2 2
How can I delete all information from student table by using groups.name?
i.e. I need query which select all students with the same group_id which is equivalent to name.
group_id 1 = 'ISI'
group_id 2 = 'IZI'
And a query must delete exactly by name.
You can use this query
Delete from Students where group_id=(Select group_id from groups where name_group='ISI');
This all the records with the group_id of 1 (via group_name='ISi').
There are different ways. A simple one, could be selecting the Id of the group and deleting from there. Example:
DECLARE
#name as nvarchar(20) = 'myName'
-- we display the data just for check
SELECT s.*, g.group_id
FROM students s ON g.group_id = s.group_id
WHERE g.name_group = #name
--we look the group id and delete the matches with students
DELETE
FROM students
WHERE group_id in (SELECT group_id FROM groups WHERE name_group = #name)
PD: This basic approach could work on both: MySQL and MSSQL.

SQL Where clause

My application initially had a query similar to this one:-
SELECT column_name from PERSON
WHERE name in (list);
where list is comma separated list.
But, now the requirement has changed and i have to query the Persons table with name and age given.
I have the nameAgeList.
Initially, i thought a query similar to this would work (Create nameList and ageList from nameAgeList)
SELECT column_name from Person
WHERE name in (nameList)
AND age in (ageList)
But after carefully thinking, this seems to be a wrong query.
Please let me know how should I proceed ahead with this query.
Under Oracle, you can do this:
SELECT * FROM Person
WHERE
(name, age) IN (
('name1', age1),
('name2', age2)
-- Etc...
)
You can have up to 1000 tuples in this list.
One option is to create a temporary table (or if SQL Server, a table variable), place your names and ages in this table, and then simply join to it:
SELECT column_name from Person p
INNER JOIN myTempTable t ON t.Name = p.Name AND t.age = p.age
It's not pretty, and this one only works when you can generate your statement in code:
SELECT column from Person
WHERE 1=1
AND ( ( name = name1 and age = age1 )
OR ( name = name2 and age = age2 )
OR ( name = name3 and age = age3 )
OR ( name = name4 and age = age4 )
OR ( name = name5 and age = age5 )
... et cetera
)
Now, if you could put those lists into tables you could do alot better than this. Is there any way you can get those lists into the database? I assume that you really need some Person table that holds name and age for each individual.