Create a new Column in database from data in existing column - sql

I have a table
TableName: MACAddresses
Columns:
- Computer
- MACAddress
I would like to create a sql script that creates a new column and correctly formats the mac address with the colon (ie with Substring) - To create a new column called CorrectMAC
How would I do this with Oracle?

Here is some test data:
SQL> select * from MACAddresses
2 /
COMPUTER MACADDRESS
---------- ------------
100 123456789abc
200 acef35dd6ecc
SQL>
Adding the new column is quite straightforward:
SQL> alter table MACAddresses
2 add corrected_MACAddress varchar2(17)
3 /
Table altered.
SQL>
Note that you cannot make it NOT NULL at this point, because you already have some records in the table. So if you want to apply such a constraint, you need to populate it first.
This is the simplest way of updating the new column.
SQL> update MACAddresses
2 set corrected_MACAddress = substr(macaddress, 1,2)||':'||
3 substr(macaddress, 3,2)||':'||
4 substr(macaddress, 5,2)||':'||
5 substr(macaddress, 7,2)||':'||
6 substr(macaddress, 9,2)||':'||
7 substr(macaddress, 11,2)
8 /
2 rows updated.
SQL> select * from MACAddresses
2 /
COMPUTER MACADDRESS CORRECTED_MACADDR
---------- ------------ -----------------
100 123456789abc 12:34:56:78:9a:bc
200 acef35dd6ecc ac:ef:35:dd:6e:cc
SQL>
Now, if you had a more complicated pattern, or if you wanted to perform this operation on a regukar basis I suggest you expend the effort to turn it into a function, and perhaps remove that repetition at the same time.
Finally, if you wanted to enforce a mandatory constraint you can:
SQL> alter table MACAddresses
2 modify corrected_MACAddress not null
3 /
Table altered.
SQL>

Related

Calculating and store derived attribute in Oracle Sql Developer

I am just trying to calculate and store age attribute derivedly from dob(date of birth). How can ı do that in Oracle SQL Developer?
CREATE TABLE Patient (
dob DATE,
age NUMBER(3));
I mean when de dob inserted like that 01/01/2000 the age will automatically calculated to 21.
No, you're planning to do it in a wrong way. Why would you so badly want to do that?
It's the same as people - who fill their profile - say "I've got 5 years of experience with Oracle". That was probably true at the moment of writing that text, but - is it still valid now? Two years later it should be "I've got 7 years of experience".
So, what kind of information would be storing fixed age into that column? That person was 21 years old at the moment of insert, but they won't stay forever young. Believe me, I know.
You were suggested to create a virtual column. Well, you can't. Why? Because - in order to calculate someone's age - you have to refer to today's date. Function that returns it is SYSDATE. Bad luck - it is not deterministic, which means that it doesn't return the same value each time you call it with the same parameters. It doesn't accept any, that's true, but it doesn't matter. If you try it, it'll fail:
SQL> create table patient
2 (id number constraint pk_pat primary key,
3 date_of_birth date not null,
4 age number generated always as
5 (round(months_between(sysdate, date_of_birth)/12, 0))
6 virtual
7 );
(round(months_between(sysdate, date_of_birth)/12, 0))
*
ERROR at line 5:
ORA-54002: only pure functions can be specified in a virtual column expression
SQL>
So, what can you do? You can create a view.
SQL> create table patient
2 (id number constraint pk_pat primary key,
3 date_of_birth date not null
4 );
Table created.
SQL> create or replace view v_patient as
2 select id,
3 date_of_birth,
4 round(months_between(sysdate, date_of_birth)/12, 0) as age
5 from patient;
View created.
SQL>
Let's test it.
SQL> insert into patient (id, date_of_birth) values (1, date '2000-01-01');
1 row created.
SQL> select * from v_patient;
ID DATE_OF_BI AGE
---------- ---------- ----------
1 01.01.2000 21
SQL>
Looks OK to me.

Oracle sql data represent problem (showing 1.12455E+10 format)

I'm facing some problems while inserting and query data from the oracle SQL table. my table name is Table_Name
and it has two column names (id number, password varchar2). I make the inserting operation finely but when I query data then it as "1.14662E+10" this format.
so whats the problem is?
In SQL*Plus, You can set the format of the column to display the content of the column. For your case, You need something like this:
column your_column_name format 9999999999
The number of 9s in the above command should be the number of digits in your column's max value.
See below example(ROWNM column):
SQL> select * from T;
ROWNM NAME TOTAL COLUMN1
---------- ---------- ---------- ----------
1.2346E+17 Tejash ########## test
SQL> column rownm format 999999999999999999
SQL>
SQL> select * from T;
ROWNM NAME TOTAL COLUMN1
------------------- ---------- ---------- ----------
123456789123456789 Tejash ########## test
SQL>
The data is correctly stored and it's only a question of the way the client program (here SQL*Plus) is displaying the data.
As already answered you can use SQL*Plus formatting commands or use TO_CHAR function:
SQL> select x from t;
X
----------
1
1234567890
1.2346E+19
SQL> select to_char(x) from t;
TO_CHAR(X)
------------------------------------------------------------------------------------------------------------------------
1
1234567890
12345678901234567890
SQL>
One more trick for SQL*Plus (and SQLcl and SQL Developer) if you have several large number columns and don't want to set them all with column. There is a default display width for numbers, and beyond that size the formatting changes to scientific notation; but you can change that:
SQL> show numwidth
numwidth 10
SQL> select 12345, 123456789123456789 from dual;
12345 123456789123456789
---------- ------------------
12345 1.2346E+17
SQL> set numwidth 20
SQL> select 12345, 123456789123456789 from dual;
12345 123456789123456789
-------------------- --------------------
12345 123456789123456789
SQL>
It affects all number columns, even for smaller numbers, as you can see - so it's a broader approach and less targeted than individual column settings, but that can be useful sometimes.
You could also use set numformat to do more involved formatting, like adding group separators. Read more about set options in the documentation.

table mutating in oracle

I have a table, participated, which has a trigger that returns the total damage amount for a driver id when a new record is inserted into the table.
create or replace trigger display
after insert on participated
for each row
declare
t_id integer;
total integer;
begin
select driver_id, sum(damage_amount)
into t_id, total
from participated
where :new.driver_id = driver_id
group by driver_id;
dbms_output.put_line(' total amount ' || total' driver id' || t_id);
end;
/
The trigger is created, but it returns this error:
ORA-04091: table SQL_HSATRWHKNJHKDFMGWCUISUEEE.PARTICIPATED is mutating,
trigger/function may not see it ORA-06512: at
"SQL_HSATRWHKNJHKDFMGWCUISUEEE.DISPLAY", line 5
ORA-06512: at "SYS.DBMS_SQL", line 1721
Please help with this trigger.
As commented above, this feels like a code-smell. A row level trigger cannot change the table being changed, since that would fire another trigger, which would end up in an endless loop of calling triggers.
Changing this to a statement level trigger is not doing the same thing.
Preferred solution:
1) put this to the application logic, and calculate after row has been inserted - this is trivial as #kfinity mentioned.
2) earmark newly inserted rows and use a statement level trigger. For example, have an extra column, say is_new default 1 - therefore all new inserted rows will have this flag. Then use a statement level trigger suggested by #hbourchi to calculate and update all drivers that is_new is 1, and then set this flag back to zero
3) the logic in 2) can be implemented using pl/sql and in-memory pl/sql tables. The pl/sql table collects affected driver ids using a row level trigger, and then updates the totals of the selected drivers. Tom Kyte has lots of examples on this, this is not a rocket science, however if you lack of PL/SQL knowledge, then this is probably not your way. (For the note: PL/SQL is super important when using Oracle - without that Oracle is just an expensive Excel sheet like any other db. Worth of using it.)
4) probably, you shall revise your data model - and the problem solves itself. The participated table shows multiple rows per driver id. You want to calculate one total row per driver id - why would you put that summary to the same table? Simply add a new table, participated_total which has driver_id and damaged_amount fields. Then feel free to insert or update that from your trigger as you planned originally!
5) in fact, you can calculate these totals on the fly (depending on the number of rows and your performance expectations), by simply crafting the right SQL when querying - this way no need to store the pre-calculated totals.
6) but if you wish Oracle to store these totals for you, you can do 5) and use materialized views. These are in-fact tables, which are updated and maintained automatically by Oracle, so your actual query at 5) does not need to calculate anything on the fly but can get the automatically pre-calculated data from the materialized view.
How about just no triggers at all ?
SQL> create table participated (
2 incident_id int primary key,
3 driver_id int not null,
4 damage_amount int not null
5 );
Table created.
SQL>
SQL> insert into participated
2 select rownum, mod(rownum,10), dbms_random.value(1000,2000)
3 from dual
4 connect by level <= 200;
200 rows created.
SQL> create materialized view log on participated with rowid, (driver_id,damage_amount), sequence including new values;
Materialized view log created.
SQL> create materialized view driver_tot
2 refresh fast on commit
3 with primary key
4 as
5 select driver_id,
6 sum(damage_amount) tot,
7 count(*) cnt
8 from participated
9 group by driver_id;
Materialized view created.
SQL> select driver_id, tot
2 from driver_tot
3 order by 1;
DRIVER_ID TOT
---------- ----------
0 32808
1 29847
2 28585
3 29714
4 32148
5 30491 <====
6 29258
7 32103
8 30131
9 26834
10 rows selected.
SQL>
SQL> insert into participated values (9999,5,1234);
1 row created.
SQL> commit;
Commit complete.
SQL>
SQL> select driver_id, tot
2 from driver_tot
3 order by 1;
DRIVER_ID TOT
---------- ----------
0 32808
1 29847
2 28585
3 29714
4 32148
5 31725 <====
6 29258
7 32103
8 30131
9 26834
10 rows selected.
SQL>
SQL>
You didn't post your trigger definition but normally you can not query a table, inside trigger when updating records in the same table.
Try using after update trigger. it might work in your case. Something like this:
CREATE OR REPLACE TRIGGER my_trigger AFTER UPDATE ON my_table FOR EACH ROW
DECLARE
...
BEGIN
...
END;
another option would be to make your trigger AUTONOMOUS_TRANSACTION:
CREATE OR REPLACE TRIGGER my_trigger BEFORE INSERT ON my_table FOR EACH ROW
DECLARE
PRAGMA AUTONOMOUS_TRANSACTION;
BEGIN
...
END;
However read this before choosing for this option:
https://docs.oracle.com/cd/B14117_01/appdev.101/b10807/13_elems002.htm

Delete duplicate row in Teradata?

This is a tricky one:
How can I delete 1 instance of a duplicate row from a table with only 1 column.
Eg.. Input Table: temp
myColumn
1
2
2
3
4
I want to delete one instance of 2 without using a temporary table.
Output Table:
myColumn
1
2
3
4
NOTE: You cannot use temporary table and SQL should be written in Teradata syntax only.

SQL Database Restrict View of Data

Okay so I am new to SQL and was just wondering if there where a way to limit who would have access to what type of data in terms of when retriving information from the tables. Like for example I had a table with information about a client which would have his name, phone, address, SSN, salary, and etc. I want to figure out if there is a way to limit what is being shown based on who is viewing that data. As in a Person A can see everything while Person B can see everything except for SSN and Salary
EDIT:
Could the use of a trigger to limit the view of a certain group work?
You can use views. After that it depends on usage, how the persons access to data (some access rights or user groups etc.)
To make columns not appear for certain users, you'd either need distinct views for each user group (ie view A that didnt have SSN, salary for users in the restricted group etc). or use fine grained access control
FGAC: http://docs.oracle.com/cd/B19306_01/network.102/b14266/apdvcntx.htm
fine grained access control applies straight to the table, so you wouldn't need views to implement that.
e.g. a very simple test to show you. lets say anyone with the Oracle role "SSN_AUTH" can view SSN/Salary for all rows. those without it,cannot.
SQL> create table person(id number, name varchar2(200), ssn varchar2(20), salary number(*,2));
Table created.
now we create a function (by all means, put this in a package for real code). the function will apply a silent predicate exists (select null from session_roles where role = 'SSN_AUTH') for every query fired on the person table. i.e. that predicate will mean that if you don't have an enabled role called SSN_AUTH, you won't see the data.
SQL> create or replace function person_rls (p_owner in varchar2, p_name in varchar2)
2 return varchar2 as
3 v_sql varchar2(2000);
4 begin
5 v_sql := 'exists (select null from session_roles where role = ''SSN_AUTH'')';
6 return v_sql;
7 end person_rls;
8 /
Function created.
now, i dont want to supress rows (though we could do). we just want to supress column data. so we add this function as a policy to the table and tell it the columns to secure:
SQL> BEGIN
2 DBMS_RLS.ADD_POLICY(object_schema=>user, object_name=>'PERSON',
3 policy_name=>'SEC_PERSON', function_schema=>user,
4 policy_function=>'PERSON_RLS',--our function
5 sec_relevant_cols=>'ssn,salary', -- secure these cols.
6 sec_relevant_cols_opt=>dbms_rls.ALL_ROWS);
7 END;
8 /
PL/SQL procedure successfully completed.
SQL> insert into person values (1, 'DazzaL', 'asdklakjd', 10000.12);
1 row created.
SQL> commit;
Commit complete.
now if we don't have the role set:
SQL> set role none;
Role set.
SQL> select * from person;
ID NAME SSN SALARY
---------- -------------------- -------------------- ----------
1 DazzaL
the salary + SSN is blank..but if we enable the role.
SQL> set role all;
Role set.
SQL> select * From session_roles;
ROLE
------------------------------
CONNECT
RESOURCE
SELECT_CATALOG_ROLE
HS_ADMIN_SELECT_ROLE
PLUSTRACE
SSN_AUTH <--- we have it now.
SQL> select * from person;
ID NAME SSN SALARY
---------- -------------------- -------------------- ----------
1 DazzaL asdklakjd 10000.12
the data magically appears.