Calculating and store derived attribute in Oracle Sql Developer - sql

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.

Related

Automatically Populate Incremented Day

In my table I have 3 dates that are important:
Scrap_Date
Due_Date
Follow_Up_Date
The Scrap Date is entered into a form. I would like to have the form to where once the "Scrap_Date" is entered, "Due_Date" would automatically populate a date 7 days later, and "Follow_Up_Date" would automatically populate t a date 14 days later.
I still want these dates to display on the form, but for them to automatically populate to these dates without being able to be changed.
What is the best way to go about this?
I've tried setting up a Select List, but struggled with what to code with SQL.
I have also tried this to no success:
select SCRAP_DATE,
DUE_DATE,
FOLLOW_UP_DATE,
from SCRAP_BODY_SYSTEM
where DUE_DATE = SCRAP_DATE + 7
AND FOLLOW_UP_DATE = SCRAP_DATE + 14
I appreciate any help, thank you!
Not sure about APEX GUI issues but two points:
you seem to select rows which already satisfy your "T, T+7, T+14" condition. You might rather want to somehow derive them, for instance compute in select clause?
the syntax for date addition is
select SCRAP_DATE, SCRAP_DATE + interval '7' day, SCRAP_DATE + interval '14' day
from SCRAP_BODY_SYSTEM
You shouldn't be doing it that way. If due_date and follow_up_date are always 7/14 days after scrap_date, then what's their purpose? You can always calculate them when you need them.
If you still want to have them in the table, make them virtual (see lines #5 and 6); doing so, Oracle will do everything for you.
SQL> create table test
2 (id number generated always as identity,
3 scrap_date date,
4 --
5 due_date date as (scrap_date + 7),
6 follow_up_date date as (scrap_date + 14),
7 --
8 constraint pk_test primary key (id)
9 );
Table created.
(just to know what is what; you don't have to do this):
SQL> alter session set nls_date_format = 'dd.mm.yyyy';
Session altered.
In your Apex form, you'd insert only scrap_date:
SQL> insert into test (scrap_date) values (date '2022-06-15');
1 row created.
Table contents is then:
SQL> select * from test;
ID SCRAP_DATE DUE_DATE FOLLOW_UP_
---------- ---------- ---------- ----------
1 15.06.2022 22.06.2022 29.06.2022
SQL>
See? Everything is already set.

Trouble inserting data into tables ORA-01722: invalid number

I'm trying to insert it into a table called bill but I keep getting errors no matter how much I what I do.
Below is the code that I'm using, when I tried replacing the total with NULL (for the sake of testing), it went through and inserted into the table. When I try to use any number no matter how long I keep getting "ORA-01722: invalid number"
insert into Bill ( Bill_code, Total, Start_date, End_date, Customer_ID )
VALUES('1373','5200','02-03-2022','02-05-2021','6724');
The number datatype has a precision of 16 with a scale of 2 and it's nullable.
ORA-01722: invalid number clearly states that this is an Oracle error; what is that postgres tag doing here?
In Oracle - as far as I managed to recreate your test case (as you didn't post table description), it works correctly.
You should really use proper datatypes:
as long as bill_code or customer_id may be strings,
total certainly isn't (you said it is a number), while
dates should be exactly that - DATE datatype
in that case, use date literal (as my example shows) or to_date function with appropriate format model. Don't rely on implicit datatype conversion. What is 02-03-2022? Is it 2nd of March or 3rd of February? Could be both.
So:
SQL> CREATE TABLE bill
2 (
3 bill_code VARCHAR2 (10),
4 total NUMBER (16, 2),
5 start_date DATE,
6 end_date DATE,
7 customer_id VARCHAR2 (10)
8 );
Table created.
SQL> INSERT INTO Bill (Bill_code,
2 Total,
3 Start_date,
4 End_date,
5 Customer_ID)
6 VALUES ('1373',
7 5200,
8 DATE '2022-03-02',
9 DATE '2021-05-02',
10 '6724');
1 row created.
SQL>
If your column type is not varchar or any character based one, do not use quotes for inserting numbers.
insert into Bill ( Bill_code, Total, Start_date, End_date, Customer_ID ) VALUES('1373',5200,'02-03-2022','02-05-2021','6724');

How do i get an age according to the current system time in NLS_DATE_FORMAT? (Oracle SQL)

I have a table called person, how do i get a person age according to the current system time in NLS_DATE_FORMAT?
ALTER SESSION SET NLS_DATE_FORMAT = 'DD.MM.YYYY';
CREATE TABLE person (
person_id NUMBER (9),
birthday DATE CONSTRAINT nn_person_birthday NOT NULL,
...
);
Note: I just want the exact age, not months neither days. Example Age: 43
Note2: I insert birthday like this in my table: TO_DATE('17.05.2003','DD.MM.YYYY')
The date format doesn't matter. I would suggest:
floor(months_between(sysdate, p.birthday) / 12)

Writing a Trigger to find and fill the age of a patient whenever a patient record is inserted into patients table

In this question, I have to add value of age with the help of trigger.
please help me implement this question.
create or replace trigger ia_patient
after insert on patient
for each row
enable
declare
age_value number(10);
begin
DBMS_OUTPUT.PUT_LINE('Please Insert Age');
update patient set age=&age_value where ??;
end;
I am not sure what I should write after "where"
Unless it is a homework question, it really doesn't make much sense as "age" depends on current date.
Anyway, see if this helps.
SQL> create table patient
2 (id number primary key,
3 date_of_birth date,
4 age number
5 );
Table created.
Trigger: calculate age as months between sysdate and date of birth divided by 12 (as there are 12 months in a year); certainly, this is just an approximate value, but - as far as I understood - your task isn't to correctly calculate age, but to learn how to use triggers.
SQL> create or replace trigger ia_patient
2 before insert on patient
3 for each row
4 begin
5 :new.age := round(months_between(sysdate, :new.date_of_birth) / 12);
6 end;
7 /
Trigger created.
Testing:
SQL> insert into patient (id, date_of_birth) values (2, date '2000-10-05');
1 row created.
SQL> select * from patient;
ID DATE_OF_BI AGE
---------- ---------- ----------
2 2000-10-05 20
SQL>
This is written as a frame challenge to the question.
Don't use a trigger; if you do it'll insert the age now and then tomorrow, or in 6 months, or a year the person will have a birthday and your table will be incorrect. This should be something you calculate in a query at the time you want the data and could be put into a view.
CREATE TABLE people (
id INTEGER
GENERATED AS IDENTITY
CONSTRAINT people__id__pk PRIMARY KEY,
name VARCHAR2(50)
NOT NULL,
date_of_birth DATE
NOT NULL
);
Then you can create a view to calculate the ages:
CREATE VIEW people_ages ( id, name, date_of_birth, age ) AS
SELECT id,
name,
date_of_birth,
FLOOR( MONTHS_BETWEEN( SYSDATE, date_of_birth ) / 12 )
FROM people;
Which, if you have the sample data:
INSERT INTO people ( name, date_of_birth )
SELECT 'Alice', DATE '1970-01-01' FROM DUAL UNION ALL
SELECT 'Bob', DATE '2020-10-21' FROM DUAL UNION ALL
SELECT 'Carol', DATE '2000-10-21' FROM DUAL;
Then the output from the view would be:
SELECT * FROM people_ages;
ID | NAME | DATE_OF_BIRTH | AGE
-: | :---- | :------------------ | --:
1 | Alice | 1970-01-01 00:00:00 | 50
2 | Bob | 2020-10-21 00:00:00 | 0
3 | Carol | 2000-10-21 00:00:00 | 20
db<>fiddle here
Your trigger has serious defects. Despite being a totally inappropriate use of a trigger I'll go over them.
Attribute: Age. Lets just say others have sufficiently covered it. But i will return to it.
DBMS_OUTPUT: This is not output when issued. The pl/sql engine does not even output this at all. It builds an internal buffer which can later be read back and processed by the client after the pl/sql has completed. However, there is no requirement that the client do so. Typically used for debugging purposes and not available in production system.
&age_value: This is an attempt at an interactive processing. Pl/sql is not and cannot do interactive processing. This is called a substitution variable and is totally the responsibly of the client. The client sees the &... and prompts for a value. It then physically alters the statement to contain that value. For example if you reply 42 to the prompt what is sent to the compiler is age=42. The compiler actually never sees &age_value.
Update: you CAN NOT do this; it will throw the exception "ORA-04091: table ... is mutating, trigger/function may not see it". Within a trigger you cannot issue DML on the table that caused the trigger to fire. Moreover it is completely unnecessary.
where ??: Since you cannot have the update statement this is moot.
In a trigger you refer to table columns through the pesudo rows :new and/or :old. So assuming you need to stay with a trigger you rewrite as (following assumes a date_of_birth column (dob) name being inserted):
create or replace trigger ia_patient
after insert on patient
for each row
enable
begin
:new.age := trunc(months_between(sysdate, :new.dob)/12);
end;
However the problem being age is now a static value. Which will need regular updates to keep current. A much better approach just let Oracle do the calculation when needed with a view as suggested by #MT0 and abandon the trigger altogether.

How to create alphanumeric sequence using date and sequence number

I want to create an alphanumeric sequence in oracle. Table name is rel_details it consists of four columns.
rel_id
rel_name
rel_modified_date
rel_desc
In rel_id i want to generate ID like REL230420151001
REL is a string ,
23042015 is today's date,
1001 is a starting number.
How to create this type sequence.
If you are on 12c, then here is one way using IDENTITY column and VIRTUAL column.
Identity column was introduced in version 12c, and virtual column was introduced in version 11g.
SQL> CREATE TABLE t
2 (
3 ID NUMBER GENERATED ALWAYS AS IDENTITY
4 START WITH 1000 INCREMENT BY 1,
5 text VARCHAR2(50),
6 dt DATE DEFAULT SYSDATE,
7 my_text varchar2(1000) GENERATED ALWAYS AS (text||to_char(dt, 'DDMMYYYY')||ID) VIRTUAL
8 );
Table created.
SQL>
SQL> INSERT INTO t(text) VALUES ('REL');
1 row created.
SQL>
SQL> SELECT text, my_text FROM t;
TEXT MY_TEXT
----- ------------------------------
REL REL230420151000
SQL>
I created identity column to start with 1000, you could customize the way you want.
There is one small trick about the VIRTUAL column. You will have to explicitly cast it as varchar2 with fixed size, else the implicit conversion will make it up to maximum size. See this for more details Concatenating numbers in virtual column expression throws ORA-12899: value too large for column
if I were you, I wouldn't bother storing such a sequence in a column; I would store the columns containing the relevant information separately and then either have a virtual column that concatenates them together or do the concatenating in a view.
Check this , you may not able to create seq , but you can use select as below.
create sequence mysec
minvalue 0
start with 10001
increment by 1
nocache;
select 'REL'||to_char(sysdate,'DDMMYYYY')||mysec.nextval from dual;