Oracle, filter by bits flags - oracle

I want to store a structure (Activity) with a Months field, the user can select 1 or more months for each activity, but I have to filter in a query by a specific month.
I have 4 approaches to store de data:
I can create a table Activity_month that each row represents the activity with each month selected.
Store in a Varchar2(12), and each month es a unique character.
Store in a NUMBER(2), and each bit is a month, and the selected month is represented with 1, so the bit array 000100000011 represents that April, November, and December were selected.
Create 12 Number(1) columns for each month.
Which one is better for store and search?

It depends on how you will use it this data... Will you use it to filter data? How often? What filters will be most popular? So you need to answer a few questions:
Will you filter data just by one month? ie show me rows for April
Will you filter it as: give me those rows for April and June, but not January and December?
Will you aggregate rows by month?
Store in a NUMBER(2), and each bit is a month, and the selected month is represented with 1, so the bit array 000100000011 represents that April, November, and December were selected.
Create 12 Number(1) columns for each month.
You can use also virtual columns: for example, you can create 12 Number(1) columns and add virtual column
generated as (jan*power(2,11)+feb*power(2,10)+...+dec*power(2,0)).
But, since Number(1) takes 2 bytes, I'd prefer varchar2(1 byte) with check(... in ('Y','N') (or '0','1'):
SQL> select dump(cast(1 as number(1))) dmp_number_1 from dual;
DMP_NUMBER_1
--------------------
Typ=2 Len=2: 193,2
SQL> select dump(cast('Y' as varchar2(1))) dmp_varchar_1 from dual;
DMP_VARCHAR_1
--------------------
Typ=1 Len=1: 89
In this case you can create virtual column generated as (jan||feb||...||dec).
Or even winter as (jan||feb||dec)
Then you will need to decide if you want to create indexes on them.

Related

Get Data for every weeks average of high and low prices

I have table named as
FP_BASIC_BD
whose structure is like as follows.
Name Null? Type
-------------- -------- ------------
FS_ID NOT NULL VARCHAR2(20)
DATE NOT NULL DATE
CURRENCY CHAR(3)
PRICE FLOAT(126)
PRICE_OPEN FLOAT(126)
PRICE_HIGH FLOAT(126)
PRICE_LOW FLOAT(126)
VOLUME FLOAT(126)
For any value of FS_ID I would like to calculate
weekends date
week start date
average(High of PRICE_HIGH+Low of PRICE_LOW)
for all the weeks.
Weekend is considered as Friday IF Fridays data not available then try to get 1 or any day before but after or equal to Monday of that week.
Week Start is considered as Monday. If Mondays data not available then get 1 or any day ahead but less or equal to to the weekend in above step.
Task of getting weekend and week start dates can be done in different query. But i want to use it as a range in a single query and get the required average.
First, please don't use Oracle keywords (e.g. date) as column names.
Second, your description of how you want to calculate the average is ambiguous, so I included a couple of options.
I think this should work to get your week start / end dates.
select
trunc("DATE", 'IW') as week,
min(trunc("DATE")) as week_start,
max(trunc("DATE")) as week_end,
(max(price_high) + min(price_low)) / 2 as avg_price_weekly,
avg(price_high+price_low) as avg_price_daily
from fp_basic_bd
where to_char("DATE", 'DY') not in ('SAT','SUN')
group by trunc("DATE", 'IW');
From your description, I'm guessing you don't have any Saturday/Sunday dates in this table. If you do, and you specifically want to exclude them from this query, let me know and I'll update my answer.
Edit: updated to exclude weekend days (sat/sun).

SQL Query with date that does not exist

I was recently working on some SQL queries where I had to separate the values into various months (e.g. December, January, Feb...) and make some comparisons. However I was at a loss for wondering about what to use for the ending day of each month. So I was wondering what happens when you define a date that does not technically exist. For example.
WHERE myDate BETWEEN '2016-01-01' AND '2016-02-31' //note Feb 31 does not exist.
My assumption (based on my current query seeming to return the proper results) is that it simply ignores the extra dates that do not exist (e.g. when counting the dates, it simply has no dates for the range outside of the regular dates).
Is this undefined behavior that I may run into trouble with in the future? Or is there a better way to do this to cover all basis?
Why don't you want to use LAST_DAY() function:
SELECT SYSDATE, trunc(LAST_DAY(SYSDATE)) last,
LAST_DAY(SYSDATE) - SYSDATE days_left FROM DUAL;
Output:
SYSDATE LAST DAYS_LEFT
----------------- ----------------- ----------
03.02.16 18:38:26 29.02.16 00:00:00 26
1 row selected.

How to change default date,timestamp dataype for columns in oracle

I have created a table in Oracle in which I have KPI_START_DATE column which is a Date datatype, and KPI_START_TIME which is a TIMESTAMP datatype.
Now I want to modify this date dataype for
KPI_START_DATE to dd/mm/yyyy
and
KPI_START_TIME to HH:MI:SS.
So that user should always enter the date and time in this column in this proper format.
I tried below query but its was giving error:
Alter table KPI_DEFINITION MODIFY(to_char(KPI_START_DATE,'dd/mm/yyyy') )
DATE and TIMESTAMP columns do not have any inherent readable format. The values are stored in Oracle's own internal representation, which has no resemblance to a human-readable date or time. At the point to retrieve or display a value you can convert it to whatever format you want, with to_char().
Both DATE and TIMESTAMP have date and time components (to second precision with DATE, and with fractional seconds with TIMESTAMP; plus time zone information with the extended data types), and you should not try to store them separately as two columns. Have a single column and extract the information you need at any time; to get the information out of a single column but split into two fields you could do:
select to_char(KPI_START, 'dd/mm/yyyy') as KPI_START_DATE,
to_char(KPI_START, 'hh24:mi:ss') as KPI_START_TIME
but you'd generally want both together anyway:
select to_char(KPI_START, 'dd/mm/yyyy hh24:mi:ss')
Also notice the 'hh24' format model to get the 24-hour clock time; otherwise you wouldn't see any difference between 3 a.m. and 3 p.m.
You can store a value in either type of column with the time set to midnight, but it does still have a time component - it is just midnight. You can't store a value in either type of column with just a time component - it has to have a date too. You could make that a nominal date and just ignore it, but I've never seen a valid reason to do that - you're wasting storage in two columns, and making searching for and comparing values much harder. Oracle even provides a default date if you don't specify one (first day of current month). But the value always has both a date and a time part:
create table KPI_DEFINITION (KPI_START date);
insert into KPI_DEFINITION (KPI_START)
values (to_date('27/01/2015', 'DD/MM/YYYY'));
insert into KPI_DEFINITION (KPI_START)
values (to_date('12:41:57', 'HH24:MI:SS'));
select to_char(KPI_START, 'YYYY-MM-DD HH24:MI:SS') from KPI_DEFINITION;
TO_CHAR(KPI_START,'YYYY-MM-DDHH24:MI:SS')
-----------------------------------------
2015-01-27 00:00:00
2015-01-01 12:41:57
Your users should be inserting a single value with both date and time as one:
insert into KPI_DEFINITION (KPI_START)
values (to_date('27/01/2015 12:41:57', 'DD/MM/YYYY HH24:MI:SS'));
select to_char(KPI_START, 'YYYY-MM-DD HH24:MI:SS') from KPI_DEFINITION;
TO_CHAR(KPI_START,'YYYY-MM-DDHH24:MI:SS')
-----------------------------------------
2015-01-27 12:41:57
You can also use date or timestamp literals, and if using to_date() you should always specify the full format - don't rely on NLS settings as they may be different for other users.
You should understand difference between datatype and format. DATE is a datatype. TIMESTAMP is a datatype. None of them have formats, they're just numbers.
When converting character datatype to or from date datatype, format should be applied. It's an attribute of an actual conversion, nothing else.
Look at this:
SQL> create table tmp$date(d date);
Table created
SQL> insert into tmp$date values (DATE '2010-11-01');
1 row inserted
SQL> insert into tmp$date values (DATE '2014-12-28');
1 row inserted
SQL> select d, dump(d) from tmp$date;
D DUMP(D)
----------- ---------------------------------
01.11.2010 Typ=12 Len=7: 120,110,11,1,1,1,1
28.12.2014 Typ=12 Len=7: 120,114,12,28,1,1,1
There is no any 'format' here.
DISPLAYING and STORING are NOT the same when it comes to DATE.
When people say Oracle isn’t storing the date in the format they wanted, what is really happening is Oracle is not presenting the date in the character string format they expected or wanted.
When a data element of type DATE is selected, it must be converted from its internal, binary format, to a string of characters for human consumption. The conversion of data from one type to another is known as known a “conversion”, “type casting” or “coercion”. In Oracle the conversion between dates and character strings is controlled by the NLS_DATE_FORMAT model. The NLS_DATE_FORMAT can be set in any of several different locations, each with its own scope of influence.
I could go on with my leacture over DATE data type, but I am glad that someone has already got a good writeup over this. Please read this https://edstevensdba.wordpress.com/2011/04/07/nls_date_format/

How to create DATE datatype in a create query in oracle database?

Please explain how to write a create query where i can write a spcific date in a specific format (suppose dd/MM/yy) in oracle. Suppose i need my columns in my table ORDERS to be-
order_id, order_date, quantity
From the documentation:
"The database stores dates internally as numbers. Dates are stored in
fixed-length fields of 7 bytes each, corresponding to century, year,
month, day, hour, minute, and second."
And what that looks like:
SQL> select dump(sysdate) from dual
2 /
DUMP(SYSDATE)
--------------------------------------------------------------------------------
Typ=13 Len=8: 222,7,9,7,2,48,32,0
SQL>
Which actually looks like eight bytes but interestingly a date is nine bytes long:
SQL> select lengthb(sysdate) from dual
2 /
LENGTHB(SYSDATE)
----------------
9
SQL>
Anyway, storage is fixed and entirely independent of the displaying of dates.
The default date format is governed by the NLS_DATETIME_FORMAT, which is defaulted by the NLS_TERRITORY setting. This is how Oracle determines Currencym, number, formats, days of the week and so on. Find out more by reading the Globalization Support guide.
If you want a different default format mask for your dates this can be set at the database level. This is a big decision. Fortunately it can also be set at a more granular level:
SQL> select sysdate from dual
2 /
SYSDATE
---------
07-SEP-14
SQL> alter session set nls_date_format='Month DD YYYY HH12:MI AM'
2 /
Session altered.
SQL> select sysdate from dual
2 /
SYSDATE
--------------------------
September 07 2014 03:05 AM
SQL>
As far as input of dates goes, Oracle expects strings containing dates to have the same format as that specified by the NLS_DATE_FORMAT. If this is not the case then we have to apply a conversion using the TO_DATE() function and supplying the mask of the string:
SQL> select to_date('31/05/14','DD/MM/YY') from dual
2 /
TO_DATE('31/05/14','DD/MM/
--------------------------
May 31 2014 12:00 AM
SQL>

maximum storage allocated for date in bytes in oracle

I have searched for this but found nothing even relevant to answer my question. My doubt is like we know that max size for a varchar2 data type is 32767 bytes so on similar lines how much space is allocated to store a value of date data type. Is it treated like the varchar2 data type? such that the size will depend on number of characters? or is it something different.
Thanks
From the documentation:
The database stores dates internally as numbers. Dates are stored in fixed-length fields of 7 bytes each, corresponding to century, year, month, day, hour, minute, and second.
So a date is always 7 bytes. Timestamps are longer (11 bytes) to hold the additional precision, and adding time zone increases that further (to 13 bytes). You can see that if you dump a value:
create table t42 (d date, t timestamp);
insert into t42 (d, t) values (sysdate, systimestamp);
column dumpd format a40
column dumpt format a60
select dump(d) as dumpd, dump(t) as dumpt
from t42;
DUMPD DUMPT
---------------------------------------- ------------------------------------------------------------
Typ=12 Len=7: 120,114,3,21,12,30,11 Typ=180 Len=11: 120,114,3,21,12,30,11,17,32,145,56

Resources