How can I add the current date in ORACLE SQL column? - sql

How can I add the "now" date in a column? I want to achieve the following scenario: if I insert a row today the date column will have the value 25/10/2021 and if tomorrow I'll execute a SELECT statement, I want to see that column updated with the value 26/10/2021 and so on. Is there a way to keep the column updated without me doing anything manually? Thanks.
Later edit: I want to use that column for a PERIOD in a temporal table. Example: PERIOD FOR example_period(some_date, now_date)

In your request comments you have altered your request fundamentally.
One problem: You have an open date range, with a beginning date and an ending date. But when displaying this, you want to substitute the no end" with the current date. This is usually done by storing null (no value) for the date. In a query you can then use COALESCE to replace that with the current date:
SELECT
empno,
salary,
start_date AS first_day,
COALESCE(end_date, TRUNC(SYSDATE)) AS last_day
FROM salaries;
And for convenience you can make this a view:
CREATE VIEW v_salaries AS SELECT <above query>
Another problem is that you want this null to change when a newer salary information gets added.
Manually you would just
INSERT INTO salaries (empno, salary, start_date, end_date)
VALUES(50, 1200, TRUNC(SYDATE), NULL);
UPDATE salaries SET end_date = TRUNC(SYSDATE) - 1
WHERE empno = 50 AND end_date IS NULL;
If you want this update to happen automatically, there are mainly two options:
write a procedure for a new salary that does the insert and update
write a trigger to perform the update

That doesn't make any sense. It is as if you'd just
select empno, ename,
sysdate --> this
from employees
every time.
SELECT won't update any column value, so ... why would you store such a date in the first place, if you want to keep it synchronized with sysdate?
But, if you want Oracle to insert sysdate into that column as its default value, you'd then create column as such, e.g.
SQL> create table test
2 (id number,
3 datum date default sysdate
4 );
Table created.
SQL> alter session set nls_date_format = 'dd.mm.yyyy hh24:mi:ss';
Session altered.
SQL> insert into test (id) values (1);
1 row created.
SQL> select * from test;
ID DATUM
---------- -------------------
1 25.10.2021 14:05:02
SQL>
But, it would remain the same "forever", unless you actually update its value.

Add a trigger that fill a column with sysdate :
create or replace TRIGGER TRG_FILL_DATE
BEFORE INSERT ON MY_TABLE
FOR EACH ROW
WHEN (NEW.MY_DATE_COLUMN IS NULL)
BEGIN
SELECT sysdate INTO :NEW.MY_DATE_COLUMN FROM DUAL;
END;
Or as suggested use 'DEFAULT sysdate' on your column.

You can use a generated column:
CREATE TABLE salaries (
id NUMBER(10,0)
GENERATED ALWAYS AS IDENTITY
CONSTRAINT salaries__id__pk PRIMARY KEY,
emp_id CONSTRAINT salaries__emp_id__fk REFERENCES employees (id)
NOT NULL,
salary NUMBER(12,2)
NOT NULL,
start_date DATE
NOT NULL,
end_date DATE
NULL,
assumed_end_date DATE
GENERATED ALWAYS AS (date_or_now(end_date))
NOT NULL
);
and create the DETERMINISTIC function (in a slight abuse of how it is supposed to be used):
CREATE FUNCTION date_or_now (dt IN DATE) RETURN DATE DETERMINISTIC
IS
BEGIN
RETURN COALESCE(dt, SYSDATE);
END;
/
Then if you:
INSERT INTO salaries (emp_id, salary, start_date)
VALUES (1, 123, DATE '2021-01-01');
Then:
SELECT *
FROM salaries;
The output is:
ID
EMP_ID
SALARY
START_DATE
END_DATE
ASSUMED_END_DATE
1
1
123
2021-01-01 00:00:00
2021-10-25 13:28:08
Then if you do:
UPDATE salaries
SET END_DATE = DATE '2021-10-01'
WHERE id = 1;
INSERT INTO salaries (emp_id, salary, start_date)
VALUES (1, 234, DATE '2021-10-01');
Then:
SELECT *
FROM salaries;
The output is:
ID
EMP_ID
SALARY
START_DATE
END_DATE
ASSUMED_END_DATE
1
1
123
2021-01-01 00:00:00
2021-10-01 00:00:00
2021-10-01 00:00:00
2
1
234
2021-10-01 00:00:00
2021-10-25 13:28:08
db<>fiddle here

Related

How to insert into a table?

Table Name: free_meals_bill
punch_date employee_id employee_name product_name
2021-02-22 12:15:50.086471 123456 john Variety Rice - Curd - Rs.35
2021-02-22 12:19:50.086472 234456 marry Variety Rice - Curd - Rs.35
2021-02-22 12:22:50.086473 355456 peter Variety Rice - Curd - Rs.35
Before inserting into "free_meals_bill" table, I want to check that per employee_id only one punch is allowed.
For example, if john (employee id 123456) is already in the free_meals_bill then again for the same date, john data should not be insert again into the "free_meals_bill" table.
Query:
insert into free_meals_bill (punch_date,employee_id,employee_name,product_name)
Values ('2021-02-22 10:15:50.086471',123456,'john','Variety Rice - Curd - Rs.35')
SELECT
employee_id,
COUNT(*) as count,
date_trunc('day',punch_date) as day
FROM bill_item
WHERE punch_date>= CURRENT_DATE
GROUP BY employee_id, day
HAVING COUNT(*) = 0
You can use a NOT EXISTS condition to check if the to be inserted values already exist:
insert into free_meals_bill (punch_date, employee_id, employee_name, product_name)
select *
from (
values (date '2021-02-22 10:15:50.086471',123456,'john','Variety Rice - Curd - Rs.35')
) as t(punch_date, employee_id, employee_name, product_name
where not exists (SELECT *
FROM free_meals_bill bi
WHERE bi.punch_date::date = t.punch_date::date
AND bi.employee_id = t.employee_id)
But if you only allow one row per (employee_id, punch_date) you should create a unique constraint or index
create unique index only_one_meal_per_day
on free_meals_bills ( (punch_date::date), employee_id);
Then you can do:
insert into free_meals_bill (punch_date, employee_id, employee_name, product_name)
values (date '2021-02-22 10:15:50.086471',123456,'john','Variety Rice - Curd - Rs.35')
on conflict ((punch_date::date), employee_id)
do nothing;
Your select statement has wrong column sequence, it should be the same sequence with your insert statement. date,id,name,product. And, should be the same number of columns too.
demo:db<>fiddle
You cannot use VALUES and SELECT in one INSERT statement. The SELECT statement replaces the VALUES part
You can use EXISTS to check for occurrences.
INSERT INTO free_meals_bill (punch_date,employee_id,employee_name,product_name)
SELECT
*
FROM bill_item
WHERE punch_date >= CURRENT_DATE
AND NOT EXISTS (
SELECT 1 FROM free_meals_bill WHERE employee_id = bill_item.employee_id
);
Note: I used * selector here, because in my example bill_item has the same columns as free_meals_bill. You have to adapt this to your real used case, of course, if it doesn't fit to something like this (depending on how bill_item actually looks like):
SELECT
punch_date,
employee_id,
employee_name,
product_name
...
Edit:
To avoid such duplication by table design you should think about adding a UNIQUE contraint to your table:
ALTER TABLE free_meals_bill ADD CONSTRAINT my_unique_constraint UNIQUE (employee_id);
This prevents INSERT statements from inserting duplicate records automatically, you don't need to do this with SELECT statements

How to format the current date and time in SQL but to only include the time by itself?

I'm not sure how to format the 'default sysdate' to only have the time bit when ran?
So the output would be the current time '15:20:52' without the date.
I am using sql plus.
When creating the table, like this:
create table timeslot
(
tsdate default sysdate not null,
tstime default sysdate not null)
SQLPLUS tag means "Oracle".
As Oracle doesn't provide "time" datatype, you would use a single column:
SQL> create table timeslot
2 (id number primary key,
3 ts date default sysdate not null
4 );
Table created.
SQL> insert into timeslot (id) values (1);
1 row created.
SQL>
What have I inserted?
SQL> select * From timeslot;
ID TS
---------- --------
1 03.06.20
SQL>
Oh boy, that looks ugly ... impossible to know what it really is because it can be
March 6th 2020 or
June 20th 2003 or
June 3rd 1920 or ...
But, that's just because current NLS settings on my database display date datatype values as such. Oracle stores dates in 7 byte binary format (not recognizable by us, humans) so we have to do something to present those values nicely. For example:
SQL> alter session set nls_date_format = 'dd.mm.yyyy';
Session altered.
SQL> select * From timeslot;
ID TS
---------- ----------
1 03.06.2020
OK, here's the date but- where's time?
SQL> alter session set nls_date_format = 'dd.mm.yyyy hh24:mi:ss';
Session altered.
SQL> select * From timeslot;
ID TS
---------- -------------------
1 03.06.2020 19:38:37
SQL>
Of course, you wouldn't alter session each time. You'd use to_char function with appropriate format mask:
SQL> select id,
2 to_char(ts, 'dd.mm.yyyy hh24:mi:ss') ts
3 from timeslot;
ID TS
---------- -------------------
1 03.06.2020 19:38:37
SQL>
Or, as you wanted to split date and time components, you'd
SQL> select id,
2 to_char(ts, 'dd.mm.yyyy') c_date,
3 to_char(ts, 'hh24:mi:ss') c_time
4 from timeslot;
ID C_DATE C_TIME
---------- ---------- --------
1 03.06.2020 19:38:37
SQL>
Alternatively, create a VIEW which does that for you:
SQL> create or replace view v_timeslot as
2 select id,
3 to_char(ts, 'dd.mm.yyyy') c_date,
4 to_char(ts, 'hh24:mi:ss') c_time
5 from timeslot;
View created.
SQL> select * from v_timeslot;
ID C_DATE C_TIME
---------- ---------- --------
1 03.06.2020 19:38:37
SQL>
If you are using MS SQL,try like below-
SELECT GETDATE() AS INPUT,CONVERT(VARCHAR(10), GETDATE(), 108) AS EXPECTED_OUTPUT;
Using SQL Server:
select convert(varchar, getdate(), 8)

Oracle no result from comparing to date

I have run this simple query and return no result
enterselect * from record where recorddate = TO_DATE(2018, 'YYYY');
I have tested
Select to_date(recorddate,'YYYY') from record
It return ora01830:date format picture ends before converting entire input string
Here is my table structure :
create table record(
recordid varchar2(10),
singerid varchar2(10),
producedcountryid varchar2(10),
songid varchar2(10),
recorddate date,
constraint recordid_pk primary key (recordid),
constraint singerid2_fk foreign key (singerid) references singer(singerid),
constraint songid2_fk foreign key (songid) references song(songid)
);
DATEs in Oracle include hours, minutes and seconds.
So unless there are any RECORDDATEs that are at exactly 00:00:00 in the given month, the predicate where recorddate = TO_DATE(2018, 'YYYY') will not find anything to match.
In the second query, to_date(recorddate,'YYYY') is not a valid syntax for using to_date. Please see to_date for more information.
If you are trying to find all the RECORDs with RECORDDATEs in the year 2018, There are many ways to do so. Below are a couple examples.
CREATE TABLE RECORD (LOREM_IPSUM NUMBER, RECORDDATE DATE);
INSERT INTO RECORD VALUES (1,DATE '2017-05-05');
INSERT INTO RECORD VALUES (2,DATE '2018-05-05');
COMMIT;
SELECT * FROM RECORD;
LOREM_IPSUM RECORDDATE
1 05-MAY-17
2 05-MAY-18
Then:
SELECT * FROM RECORD WHERE EXTRACT(YEAR FROM RECORDDATE) = 2018;
Result:
LOREM_IPSUM RECORDDATE
2 05-MAY-18
-- Or:
SELECT * FROM RECORD WHERE TO_CHAR(RECORDDATE,'YYYY') = '2018';
Result:
LOREM_IPSUM RECORDDATE
2 05-MAY-18
If you want records from a specific year + month, you can:
SELECT * FROM RECORD WHERE TRUNC(RECORDDATE,'MM') = DATE '2017-05-01';
Result:
LOREM_IPSUM RECORDDATE
1 05-MAY-17
You will get the result set you want by querying the year-part of the date column recorddate.
select * from record
where extract (year from recorddate) = 2018

Subtract minutes from a date column based on a value in another table

I am trying to subtract minutes from a date column based on the value in another table. But query is getting into error. Can someone advise on this?
TABLE 1 NAME - MYTABLE1
COLUMN NAME "IN_DATE" Data type - DATE
TABLE 2
NAME - CONFIG_TABLE
COLUMN NAME - PARAM_NAME VALUE = SUBTRACT_MINUTE_VALUE
COLUMN NAME PARAM_VALUE VALUE =15
SELECT IN_DATE , IN_DATE - interval (SELECT PARAM_VALUE FROM CONFIG_TABLE WHERE PARAM_NAME='SUBTRACT_MINUTE_VALUE') minute FROM MYTABLE1
Here's how I do it: subtract minutes as number of minutes divided by (24 hours (in a day) x 60 minutes (in an hour))
SQL> alter session set nls_date_format = 'dd.mm.yyyy hh24:mi';
Session altered.
SQL> create table config (subtract_minute_value number);
Table created.
SQL> insert into config values (15);
1 row created.
SQL> create table mytable1 (in_date date);
Table created.
SQL> insert into mytable1 values (sysdate);
1 row created.
SQL>
SQL> select m.in_date, m.in_date - c.subtract_minute_value / (24 * 60) result
2 from mytable1 m, config c;
IN_DATE RESULT
---------------- ----------------
02.02.2018 06:56 02.02.2018 06:41
SQL>
[EDIT based on Aleksej's ANSI JOIN suggestion, along with NUMTODSINTERVAL option]
SQL> select m.in_date,
2 m.in_date - c.subtract_minute_value / (24 * 60) result
3 from mytable1 m join config c on 1 = 1;
IN_DATE RESULT
---------------- ----------------
02.02.2018 07:51 02.02.2018 07:36
SQL>
SQL> select m.in_date,
2 m.in_date - numtodsinterval(c.subtract_minute_value, 'minute') result
3 from mytable1 m join config c on 1 = 1;
IN_DATE RESULT
---------------- ----------------
02.02.2018 07:51 02.02.2018 07:36
SQL>
Another option is to use an interval of one minute and multiply that with the number stored in the config table.
select m.in_date,
m.in_date - (interval '1' minute * c.param_value) as result
from mytable1 m
cross join config_table c on c.param_name = 'SUBTRACT_MINUTE_VALUE'
you can also try this,
ALTER SESSION SET NLS_DATE_FORMAT = 'MM/DD/YYYY HH:MI:SS AM';
SELECT SYSDATE current_time,
SYSDATE - (1/24/60)*:p_min current_time_minus_mins
FROM DUAL;

Oracle: create a new table whenever I update the status of an employee

I have the following table and I want to create a new table whenever I update the status of an employee to "confirm." The new table must contain all columns with data of that particular employee.
SQL> select * from employee;
EMPID EMPNAME SAL DOJ STATUS
----- ------------------------- ---------- --------- --------------------
101 ALEX 10000 12-MAY-16 not_confirmed
102 PETER 20000 12-MAY-16 not_confirmed
Create a new table for employee with the same structure of table employee, e.g. employee_history.
Create a trigger After Update to insert old values in the history table
The code can be:
CREATE OR REPLACE TRIGGER employee_after_update
AFTER UPDATE ON employee
FOR EACH ROW
BEGIN
IF( UPDATING( 'STATUS' ) )
THEN
INSERT INTO employee_history( EMPID, EMPNAME, SAL , DOJ,STATUS)
VALUES( :old.EMPID, :old.EMPNAME , :old.SAL , :old.DOJ , :old.STATUS);
END IF;
END;
You can add extra tracking fields (if needed ) like update_date , user_id (who do changes , ..)