I have a string format of HISTORY-VDF-DE-EOF-WORK VDF-DE-EOF-WORK FFOR-1!20170904T105949.630 GMT in a column.
Need to extract FFOR-* from it.
where FFOR-* is ascending order and 20170904T105949.630 is date format and timestamp which changes for every entry in column.
Have tried
SELECT REGEXP_SUBSTR('source column', '\d[FFOR-]*') as order FROM table;
but no luck.
How can I extract it.
Thanks in advance
Does it have to be a regular expression solution? Because, an ordinary SUBSTR with INSTR does the job as well:
[EDITED, after seeing what output is required]
SQL> with test as
2 (select 'HISTORY-VDF-DE-EOF-WORK VDF-DE-EOF-WORK FFOR-1!20170904T105949.630 GMT' col
3 from dual
4 )
5 select
6 substr(col,
7 instr(col, 'FFOR-'),
8 instr(col, '!', -1) - instr(col, 'FFOR-')
9 ) result
10 from test;
RESULT
------
FFOR-1
SQL>
[EDIT #2, with REGEXP]
Note that, although it looks "smarter", for large data volume it might be slower than SUBSTR option.
SQL> with test as
2 (select 'HISTORY-VDF-DE-EOF-WORK VDF-DE-EOF-WORK FFOR-1!20170904T105949.630 GMT' col
3 from dual
4 )
5 select regexp_substr(col, 'FFOR-\d+') result
6 from test;
RESULT
------
FFOR-1
SQL>
Related
As the title states, I am trying to get the list of month last days between 2 dates.
Example:
Date 1 -> 2014-08-15
Date 2 -> 2014-10-15
Expected output
2014-08-31
2014-09-30
The query I am using to achieve this is the following:
SELECT DISTINCT LAST_DAY(TO_DATE('2014-08-15') - 1 + ROWNUM)
FROM ALL_OBJECTS
WHERE TO_DATE('2014-08-15') - 1 + ROWNUM <= TO_DATE('2014-10-15');
However I have a feeling there might be a more elegant way to achieve it.
Any input would be appreciated!
To get the same result as yours:
select add_months(LAST_DAY(date'2014-08-15'),level-1)
from dual
connect by add_months(trunc(date'2014-08-15','mm'),level-1)<= date'2014-10-15';
But I think 2014-10-31 doesn't satisfy your condition - "between 2 dates".
So probably it should be something like
select add_months(LAST_DAY(date'2014-08-15'),level-1)
from dual
connect by add_months(LAST_DAY(date'2014-08-15'),level-1)<=date'2014-10-15';
or
select add_months(LAST_DAY(date'2014-08-15'),level-1)
from dual
connect by level<=months_between(date'2014-10-15',date'2014-08-15');
Final query:
select add_months(LAST_DAY(date'2014-11-07'),level-1)
from dual where add_months(LAST_DAY(date'2014-11-07'),level-1)<=date'2014-12-07'
connect by add_months(LAST_DAY(date'2014-11-07'),level-1)<=date'2014-12-07';
I have code with which to create records in the range between the first and last. To do this, I only need to specify the first record and the last. Here is the code:
SELECT SUBSTR (:P5_FIRST_SEALS, 1, 1) ||
LPAD(TO_CHAR ( TO_NUMBER (REGEXP_SUBSTR (:P5_FIRST_SEALS, '\d+$')) + level - 1), 8,'0')
AS x_SEAL_NUMBER
FROM dual
CONNECT BY LEVEL <=
TO_NUMBER (
REGEXP_SUBSTR (:P5_LAST_SEALS, '\d+$'))
- TO_NUMBER (
REGEXP_SUBSTR (:P5_FIRST_SEALS, '\d+$'))
+ 1;
For example, I said the first number A14602157 and the last A14602167. (10 entries) in response to the query would be:
After I find what records I need to make, I write a log in the table, where I specify the first record, the last record, and the date. Here's how I get data from the log table:
SELECT FIRST_SEALS, LAST_SEALS,DATE from SEC_TRANSFER_SEALS
Is it possible to make this query in such a way that not only the first and last record of the range and the number of added records appear?
I would like the answer to the second request to be this :
SELECT FIRST_SEALS, LAST_SEALS,DATE , count(FIRST_SEALS between LAST_SEALS) from SEC_TRANSFER_SEALS
A14602157 , A14602167 , 07-06-21 , 10
You have all info on your Apex page (that is Apex, right?), so just reuse it:
SQL> select :P5_FIRST_SEALS as first_seals,
2 :P5_LAST_SEALS as last_seals,
3 trunc(sysdate) as datum,
4 :P5_LAST_SEALS - :P5_FIRST_SEALS + 1 as total_number
5 from dual;
FIRST_SEALS LAST_SEALS DATUM TOTAL_NUMBER
----------- ---------- ----------- ------------
1 10 07-jul-2021 10
SQL>
Just help me out to display a number whose length is unknown with 2 decimal places.
Example:
If the number is 230000 then i need it to be printed as 230000.00
I tried
Select to_char(amount, 'FM99999999.90') as amt from customer;
Formatting is usually front-end "problem"; every decent tool (be it Oracle Apex, Reports, Forms, Jasper Reports, ...) has an option to format a number as you want it. Why? So that those values remain numbers, to be able to apply e.g. SUM function to them or whatever you may need.
In SQL*Plus (or similar, even GUI tools), formatting is done by the TO_CHAR function and desired format mask. If you want two decimals and (just for example) thousands separator, you might do something like this:
SQL> with customer (amount) as
2 (select 230000 from dual union all
3 select 3.14 from dual union all
4 select 0.0002 from dual union all
5 select 25.123 from dual
6 )
7 select amount,
8 to_char(amount, 'fm999g999g990d00') new_amount,
9 lpad(to_char(amount, 'fm999g999g990d00'), 10, ' ') new_amount2
10 from customer;
AMOUNT NEW_AMOUNT NEW_AMOUNT2
---------- --------------- ----------------------------------------
230000 230.000,00 230.000,00
3,14 3,14 3,14
,0002 0,00 0,00
25,123 25,12 25,12
SQL>
note that new values have ...0d00 format mask which makes sure you'll actually see zeroes around the decimal point.
use G and D for thousand groups and decimals, rather than commas and dots
new_amount2, aditionaly, has lpad applied so that values are right-aligned. I presumed that max length of those values is 10 (you'd know better)
If you do use SQL*Plus (which is quite rare nowadays), you could even use its set numformat command so - no additional modifications are necessary; you just select what you have:
SQL> set numformat 999g990d00
SQL>
SQL> with customer (amount) as
2 (select 230000 from dual union all
3 select 3.14 from dual union all
4 select 0.0002 from dual union all
5 select 25.123 from dual
6 )
7 select amount
8 from customer;
AMOUNT
-----------
230.000,00
3,14
0,00
25,12
SQL>
Use concat as follows:
select concat(amount, '.00') from customer
Really basic question but i have zero experience with SQL. I'm using Tableau to do visualisation with data stored in my company's Oracle server, which contains multiple sheets. The primary table i am working with is named YQ005. One of the fields in the primary table I'm working with contains dates but stored as a String in YYYYMMDD format.
I have to convert this to Date format but doing it through Tableau raises the error "ORA-01843: Not a valid month". How can i do a custom SQL query to select this field, convert it to Date-time format and place this new data in a new column?
Littlefoot has a solid answer but it is definitely not for the inexperienced.
The basic function to convert the string to a date is:
select to_date(yyyymmdd, 'yyyymmdd')
If you are having problems with the month, you can just extract it out to check it:
select (case when substr(yyyymmdd, 5, 2) between '01' and '12'
then to_date(yyyymmdd, 'yyyymmdd')
end)
You can also add a check that the value is all numbers:
select (case when regexp_like(yyyymmdd, '^[0-9]{8}') and
substr(yyyymmdd, 5, 2) between '01' and '12'
then to_date(yyyymmdd, 'yyyymmdd')
end)
Validating dates in Oracle gets much more complicated if you have to validate the whole date -- each month has a different number of days and leap years further complicate matters. But months should always be between 01 and 12.
Error you got means that some values in that table - on 5th and 6th place - don't have a valid month value. For example, it might be 20191823 (there's no 18th month, is there?).
Unfortunately, that's what happens when people store dates as strings. There's no easy way out. If you want to do it with SQL only, you might fail or succeed (if you're VERY lucky). For example, have a look at this example:
SQL> desc yq005
Name Null? Type
----------------------------------------- -------- ----------------
DATUM VARCHAR2(8)
SQL> select * From yq005;
DATUM
--------
20191221
13000815
00010101
19302533 -- 25th month and 33rd day
2013Ab12 -- Ab month
2ooo0513 -- year with letters "o" instead of digits "0"
6 rows selected.
SQL>
A query whose where clause tries to identify invalid values:
SQL> alter session set nls_date_format = 'dd.mm.yyyy.';
Session altered.
SQL> select to_date(datum, 'yyyymmdd') result
2 from yq005
3 where substr(datum, 1, 4) between '0000' and '9999'
4 and substr(datum, 5, 2) between '00' and '12'
5 and substr(datum, 7, 2) between '01' and '31'
6 and regexp_like(datum, '^\d+$');
RESULT
-----------
21.12.2019.
15.08.1300.
01.01.0001.
SQL>
lines #3, 4 and 5 are trying to identify valid year/month/day. The first two are OK, more or less, but - it'll miserably fail on at least half of all months because e.g. 20191131 is "valid", but there are no 31 days in November
line #6 eliminates values that aren't all digits
Just to check that 20191131:
SQL> insert into yq005 values ('20191131');
1 row created.
SQL> select to_date(datum, 'yyyymmdd') result
2 from yq005
3 where substr(datum, 1, 4) between '0000' and '9999'
4 and substr(datum, 5, 2) between '00' and '12'
5 and substr(datum, 7, 2) between '01' and '31'
6 and regexp_like(datum, '^\d+$');
ERROR:
ORA-01839: date not valid for month specified
no rows selected
SQL>
As I said, it won't work; the same goes for other 30-days months, as well as February.
You could try to create a function which converts string to date; if it succeeds, fine. If not, skip that value:
SQL> create or replace function f_valid_date_01 (par_datum in varchar2)
2 return number
3 is
4 -- return 1 if PAR_DATUM is a valid date; return 0 if it is not
5 l_date date;
6 begin
7 -- yyyymmdd is format you expect
8 l_date := to_date(par_datum, 'yyyymmdd');
9 return 1;
10 exception
11 when others then
12 return 0;
13 end;
14 /
Function created.
SQL>
Let's use it:
SQL> select datum original_value,
2 to_char(to_date(datum, 'yyyymmdd'), 'dd.mm.yyyy') modified_value
3 from yq005
4 where f_valid_date_01 (datum) = 1;
ORIGINAL MODIFIED_V
-------- ----------
20191221 21.12.2019
13000815 15.08.1300
00010101 01.01.0001
SQL>
Just the opposite - fetch invalid dates:
SQL> select datum
2 from yq005
3 where f_valid_date_01 (datum) = 0;
DATUM
--------
19302533
2013Ab12
2ooo0513
20191131
SQL>
This is just one option you might use; there certainly are others, just Google for them. The bottom line is: always store dates into a DATE datatype column and let the database take care about (in)valid values.
[EDIT: how to populate a new column with a valid date]
If there's no date datatype column in the table, add it:
SQL> alter table yq005 add new_datum date;
Table altered.
Now run the update; mind the where clause:
SQL> update yq005 set
2 new_datum = to_date(datum, 'yyyymmdd')
3 where f_valid_date_01(datum) = 1;
3 rows updated.
SQL> select * From yq005;
DATUM NEW_DATUM
-------- -----------
20191221 21.12.2019.
13000815 15.08.1300.
00010101 01.01.0001.
19302533
2013Ab12
2ooo0513
20191131
7 rows selected.
SQL>
The best solution would be to have whoever maintains your database alter the table definition to store dates using the DATE datatype instead of some form of string.
But if you can't or don't wish to alter the Oracle schema, then I would try using the DATEPARSE() function in Tableau, as follows (assuming your date field is named XXX_DATE)
In Tableau, rename XXX_DATE to XXX_DATE_ORGINAL
Define a calculated field called XXX_DATE as DATEPARSE("YYYYMMdd", [XXX_DATE_ORIGINAL])
Hide the original field XXX_DATE_ORIGINAL
Now you can use your XXX_DATE field as a date in Tableau
The renaming and hiding of the original field is not strictly necessary. I just find it helps keep the data source understandable. For more info, see the Tableau online help for DateParse
Is there any way I could show the current date on a report without using the SELECT statement?? I dont even want to write it manually I want the system to show the current date itself in the heading of the report.
Using Sql Reporting Services, you should be able to add a new field to the header then right click the field > expression > Category: Built-in Fields Item: Execution Time
There is no built-in show-date-at-top-of-each-page function in sqlplus, you have to work this around. I would use TTITLE and select date from dual prior to running report.
12:59:29 SYSTEM#dwal> col now new_value header_date;
12:59:31 SYSTEM#dwal> select to_char(sysdate, 'ddmmyyyy hh24miss') as now from dual;
NOW
---------------
03042013 125944
Elapsed: 00:00:00.02
12:59:40 SYSTEM#dwal> def header_date
DEFINE HEADER_DATE = "03042013 125944" (CHAR)
13:03:28 SYSTEM#dwal> ttitle left '&header_date'
13:03:32 SYSTEM#dwal> set pagesize 10
13:03:36 SYSTEM#dwal> select rownum from dual connect by rownum <= 20;
03042013 125944
ROWNUM
----------
1
2
3
4
5
6
03042013 125944
ROWNUM
----------
7
8
9
10
11
12
03042013 125944
ROWNUM
----------
13
14
15
16
17
18
03042013 125944
ROWNUM
----------
19
20
20 rows selected.
Elapsed: 00:00:00.03
But if you insist on not having to perform a single additional select - the general idea stays the same, to use a substitution variable. Probably you can pass a current date parameter from your shell while starting sqlplus and refer to it as &1 in your TTITLE specification.