The data length in our production DB, DATE data type, is 7, the sysdate function return 8 characters (dd/mm/yy) .
Is there any way to eliminate '/' and only populate 'ddmmyy'.
Tried the below but no luck.
INSERT INTO TESTER(tablename,columnname,defaultdate,prime_number) VALUES ('tabL7845','field894',REPLACE(SYSDATE,'/',''),105);
INSERT INTO TESTER(tablename,columnname,defaultdate,prime_number) VALUES ('ta68888','fiG987',TO_CHAR(sysdate,'MMDDYY'),180);
INSERT INTO TESTER(tablename,columnname,defaultdate,prime_number) VALUES ('tab345','field464',TRIM(BOTH '/' FROM SYSDATE),65);
Row is getting inserted but in table i could find the same sysdate format,
for eg ) 07/08/20
How can i populate as 070820
Trigger code :
SET SERVEROUTPUT ON;
CREATE OR REPLACE TRIGGER def_trig
BEFORE INSERT OR UPDATE OF datedef ON Test
REFERENCING OLD AS O NEW AS N
FOR EACH ROW
DECLARE
V_DATA_LENGTH NUMBER;
V_DATA_TYPE VARCHAR2(15);
BEGIN
SELECT DATA_LENGTH,DATA_TYPE
INTO V_DATA_LENGTH,V_DATA_TYPE
FROM all_tab_columns
WHERE table_name = :n.tablename
AND column_name =:n.columnname
IF INSERTING THEN
IF v_data_type = 'DATE' THEN
IF length(:n.datedef) > V_DATA_LENGTH THEN
RAISE_APPLICATION_ERROR(-20001,'DATE FIELD LENGTH IS MORE THAN THE CORESPONDING COLUMN DATA LENGTH');
END IF;
ELSE
DBMS_OUTPUT.PUT_LINE('INSERT IS SUCCESSFUL');
END IF;
END if;
END;
/
Dates in Oracle do not have any internal sort of text formatting; they are stored as binary. If you want to view a SYSDATE value in a certain text format, then use TO_CHAR with the appropriate format mask:
INSERT INTO TESTER (tablename, columnname, defaultdate, prime_number)
VALUES ('ta68888', 'fiG987', sysdate, 180); -- just insert plain SYSDATE here
SELECT
tablename,
columnname,
TO_CHAR(defaultdate, 'DDMMYY'), -- then view SYSDATE however you want
prime_number
FROM TESTER;
Here is the output from the above query:
Demo
Internally dates are stored as a structure of 7 1byte integers, that's where the length 7 comes from. From that structure any valid format can be displayed without any data change. To see this select the same column with multiple formats. For example:
alter session st nls_date_format = 'hh24:mi:ss dd-Mon-yyyy';
select defaultdate
from teaster
where defaultdate is not null
and row_num<2;
alter session set nls_date_format = 'Dy dd-Mon-yyyy # hh24:mi:ss' ;
select defaultdate
from teaster
where defaultdate is not null
and row_num<2;
To get a glimpse at the internal format run:
select defaultdate, dump(defaultdate) from teaster;
This will show you the default date (interpreted as directed by nls_date_format) and a glimpse of the internal structure.
Related
Error while trying to insert a query which transforms multiple merged date(eg.20230208065521019355) into proper timestamp format to a new column.
INSERT INTO NEWTABLE2(RC_DATETIME)
SELECT TO_CHAR(TO_TIMESTAMP(RC_TIMESTAMP, 'YYYY-MM-DD HH:MI:SS:FF'), 'YYYY-MM-DD HH:MI:SS.FF')
FROM NEWTABLE;
Upon just executing the SELECT statement I get the query but while including the INSERT I get the error of 'not valid month'.
Data within the RC_TIMESTAMP(VARCHAR) are the merged data which are as follows:
20230208065521019355, 20230208065523019356, 20230208065532019357, etc.
RC_DATETIME has VARCHAR(35) datatype.
I have tried reordering the format of TO_CHAR, 'YYYY-MM-DD HH:MI:SS.FF' to 'Mon-DD-YYYY HH:MI:SS.FF' to name a few.
From what you posted:
Source table:
SQL> CREATE TABLE newtable (rc_timestamp)
2 AS (SELECT '20230208065521019355' FROM DUAL);
Table created.
Target table:
SQL> CREATE TABLE newtable2
2 (rc_datetime VARCHAR2 (35));
Table created.
Insert:
SQL> INSERT INTO newtable2 (rc_datetime)
2 SELECT TO_CHAR (TO_TIMESTAMP (rc_timestamp, 'yyyymmddhh24missff6'),
3 'yyyy-mm-dd hh24:mi:ss:ff')
4 FROM newtable;
1 row created.
However, you'd rather store timestamps into a timestamp column, not as a string. What benefit do you expect? It causes problems in later data processing.
SQL> DROP TABLE newtable2;
Table dropped.
SQL> CREATE TABLE newtable2
2 (rc_datetime TIMESTAMP);
Table created.
SQL> INSERT INTO newtable2
2 SELECT TO_TIMESTAMP (rc_timestamp, 'yyyymmddhh24missff6') FROM newtable;
1 row created.
SQL>
You commented that you still have the "not a valid month" error.
It means that data - at position where TO_TIMESTAMP expects a valid month value (01, 02, ..., 12) - contains something else. What? No idea, you have all the data. Try to find it by selecting a substring (month starts at position 5 and takes 2 places):
SQL> SELECT rc_timestamp, SUBSTR (rc_timestamp, 5, 2) month FROM newtable;
RC_TIMESTAMP MO
-------------------- --
20230208065521019355 02
SQL>
Invalid data is most probably here:
SELECT rc_timestamp
FROM newtable
WHERE SUBSTR (rc_timestamp, 5, 2) NOT BETWEEN '01' AND '12';
Once you find invalid values, you'll decide what to do with it. Maybe you'll ignore those values (so you'd include appropriate where clause into the insert statement), or fix it (somehow; can't tell how as it depends on what you'll find), or ...
If you want to identify invalid values during insert, a simple option is a loop with an inner begin-exception-end block which lets you capture those values and still proceed with other row(s). Something like this:
create table invalid_values as
select id, value from source_table where 1 = 2;
begin
for cur_r in (select * from source_table) loop
begin
insert into newtable2 ...
exception
when others then
insert into invalid_values (id, value) values (cur_r.id, cur_r.value);
end;
end loop;
end;
Once you're done, select * from invalid_value so that you could deal with what's left.
That should be OK as you have 10.000 rows so loop won't take infinite time to complete. True, it will be slower than set-oriented operation, but ... you have to fetch invalid rows, somehow.
What i want to do, is to change my date column type from varchar to timestamp w/o timezone, and migrate all my data. This is what im doing:
ALTER TABLE mytable ALTER COLUMN "datecol"
TYPE timestamp without time zone USING(to_timestamp("datecol", 'YYYY-MM-DD')::timestamp without time zone);
This works fine if data in datecol is in date format. But if i have some not valid data, like random strings (e.g "abc") how can i validate and check if date format is good? I want to set default value for those invalid fields.
EDIT: Thanks Ludvig, i solved my problem:
create or replace function is_date(s varchar) returns boolean as $$
begin
perform s::date;
return true;
exception when others then
return false;
end;
$$ language plpgsql;
ALTER TABLE mytable ALTER COLUMN "datecol"
TYPE timestamp without time zone USING(
CASE WHEN is_date("datecol") = true
THEN to_timestamp("datecol", 'YYYY-MM-DD')::timestamp without time zone
ELSE '1970-01-01 00:00:00.000'
END
);
You can't alter type for column with try/catch logic, so you either have to regexp all possible formats or you can:
add column "ts" of timestamp data type
add column "exc" boolean
do $$
declare _r record;
begin
for _r in (select * from mytable) loop
update mytable set "ts" = to_timestamp("datecol", 'YYYY-MM-DD')
where ...PK..=_r.PK;
when others
then update mytable set "exc" = true;
end loop; end; $$;
drop datecol, rename "ts" to "datecol"
deal with values where "exc"
The big minus would be changed order of columns in mytable. So poor apology that you can after create table tt as select NEEDED order from mytable; drop table mytable;alter table tt rename to mytable, but then you will have to rebuild all references and dependants as well of course. Or even more exotic way - you can start adding column in needed order, dropping renaming until you get the old set...
Instead of testing for a valid date I would create a function that tries casting using different formats and returns the default date in case none of them work:
create or replace function convert_date(s varchar)
returns timestamp
as
$$
begin
-- test standard ISO format
begin
return to_timestamp(s, 'yyyy-mm-dd');
exception when others then
-- ignore
end;
begin
return to_timestamp(s, 'yyyy.mm.dd');
exception when others then
-- ignore
end;
begin
return to_timestamp(s, 'yyyy/mm/dd');
exception when others then
-- ignore
end;
begin
return to_timestamp(s, 'yyyy-mm');
exception when others then
-- ignore
end;
begin
return to_timestamp(s, 'yyyy');
exception when others then
-- ignore
end;
return timestamp '1970-01-01 00:00:00';
end
$$ language plpgsql;
Then use:
ALTER TABLE mytable
ALTER COLUMN "datecol" TYPE timestamp without time zone
USING (convert_date(datecol));
This won't be very efficient, but for a one-time job it should work
I've created an empty table--in my website that holds a bunch of tables-- that has the following columns/data types:
NAME -- VARCHAR2
MRN -- NUMBER
DATE_S -- DATE
E -- DATE
DELI -- DATE
WB -- VARCHAR2
ST_ID -- VARCHAR2
COMMENTS --VARCHAR2
EI -- NUMBER
Below is one of almost 800 rows of code I am using to populate the table.
INSERT INTO SANDBOX.W_C VALUES ('S,E',11300033,'2012-02-18 00:00:00','2012-03-01 00:00:00','2013-02-18 00:00:00','N','006i',NULL,NULL);
When I run that piece of code I get the following message: literal does not match format string. What am I doing wrong?
You need to_Date
INSERT INTO SANDBOX.W_C VALUES ('S,E',11300033,
TO_DATE('2012-02-18', 'yyyy-mm-dd'),
TO_DATE('2012-03-01', 'yyyy-mm-dd'),
TO_DATE('2013-02-18', 'yyyy-mm-dd'),'N','006i',NULL,NULL);
When you provide a date as a string, the database uses it's default settings to try to convert the string. The best way to handle this is the use of to_date, as in scaisEdge's answer.
However, you can also change the default date mask using alter session before you run the insert statements:
ALTER SESSION SET NLS_DATE_FORMAT='yyyy-mm-dd hh24:mi:ss';
I need to fill a table column (in Oracle database) with string values that have variable part, e. g. AB0001, AB0002,...,AB0112...,AB9999, where AB is constant string part, 0001 -9999 is variable number part. i've tried the following solution in SQL for a table with 2 columns:
create table tbl1
(seq1 number(8),
string1 varchar(32));
declare
tst number(8) :=0;
begin
for cntr in 1..100
loop
tst := cntr;
insert into TBL1 values (someseq.nextval, concat('AB',tst));
end loop;
end;
But in this case I get STRING1 filled with values AB1,AB2,...,AB10,.. which is not exactly what I need.
How should I modify my script to insert values like AB0001,...,AB0010?
Either pad the number with zeros, or format it with leading zeros:
insert into TBL1
values (someseq.nextval, concat('AB', to_char(tst, 'FM0000'));
The 'FM' format modifier prevents a space being added (to allow for a minus sign).
For your specific example you don't need a PL/SQL block; you could use a hierarchical query to generate the data for the rows:
insert into tbl1(seq1, string1)
select someseq.nextval, concat('AB', to_char(level, 'FM0000'))
from dual
connect by level <= 100;
use the lpad function
select lpad(1, 4, '0') from dual
--> '0001'
try this one
INSER INTO table_name(code)
VALUES(CONCAT('AB', LPAD('99', 4, '0'));
or You can Update on the basis of PK after insertion
UPDATE table_name SET code = CONCAT('AB', LPAD(PK_Column_Name, 4, '0') ;
or You Can Use Triggers
CREATE TRIGGER trgI_Terms_UpdateTermOrder
ON DB.table_name
AFTER INSERT
AS
BEGIN
UPDATE t
SET code = CONCAT('AB', LPAD(Id, 4, '0')
FROM DB.table_name t INNER JOIN inserted i ON t.Id = I.Id
END;
GO
I have a query where i have to use the result of it multiple times. So instead of running the query multiple times i want to save the query value into a variable once and use it multiple times to accelerate the query speed.
for example:
Declare VAr = select M_DATE from TBT
If you want to do this in an interactive client, the answer depends on the client. For SQLPlus you could do this:
VARIABLE my_date VARCHAR2(10);
BEGIN
SELECT to_char(sysdate, 'YYYY-MM-DD' ) INTO :my_date FROM dual;
END;
/
PRINT my_date;
SELECT * FROM my_table WHERE date_column = TO_DATE( :my_date, 'YYYY-MM-DD' );
Note that SQLPlus does not support a DATE type for its variables, so you need to convert to a string when storing then back to a date when using the value. I recommend always using an explicit conversion and format string, simply to avoid unexpected results if the default format is not what you are expecting.
Within a PL/SQL block or procedure, it would be a little simpler:
DECLARE
l_mydate DATE;
BEGIN
SELECT sysdate INTO l_mydate FROM dual;
SELECT whatever INTO l_whatever FROM my_table
WHERE date_column = l_mydate;
<etc...>
END;
/
If you want to store the result of the query then you need to use a select ... into;
var v_storedate VARCHAR2(19);
exec select m_date into :v_storedate from tbt;
print v_storedate;
For an anonymous SQL block
begin
select m_date
into :v_storedate
from tbt;
end;
/