2

I saw this link and this:

If you specify a date value without a time component, then the default time is midnight. If you specify a date value without a date, then the default date is the first day of the current month.

Oracle DATE columns always contain fields for both date and time. If your queries use a date format without a time portion, then you must ensure that the time fields in the DATE column are set to midnight.

The solution is to put a constraint on column with the date data-type and create a trigger (with TRUNC()) when inserting or updating a row in table.

If I use this solution do I have the warranty that Oracle does store less bytes for a date without the time?

With this standard datetime type Oracle create ambiguity. It is so hard to create a date type (only containing a date)? This is my opinion (I came from MSSQL).

Community
  • 1
  • 1
Blocked
  • 340
  • 2
  • 5
  • 20
  • See if [this question](http://stackoverflow.com/questions/10429276/date-type-without-time-in-oracle) helps. [SQL Server 2012](http://msdn.microsoft.com/en-us/library/ms186724.aspx) does indeed have a Date type with no time. – DOK Jun 12 '13 at 18:02
  • I've edited your question, not that much but as I've answered it as well I'd like you to confirm that my edits are exactly what you are asking please? – Ben Jun 12 '13 at 18:12

2 Answers2

7

No, you do not have any warranty whatsoever... whatever happens Oracle is going to store the fact that it's midnight. You cannot store a date without a time.

If you create the following table:

create table a ( dt date);
insert into a values(sysdate);
insert into a values(trunc(sysdate));

and then run this query:

select dt, dump(dt) from a

SQL Fiddle

The values returned are:

+-----------------------------+------------------------------------+
|             DT              |              DUMP(DT)              |
+-----------------------------+------------------------------------+
| June, 12 2013 18:03:15+0000 | Typ=12 Len=7: 120,113,6,12,19,4,16 |
| June, 12 2013 00:00:00+0000 | Typ=12 Len=7: 120,113,6,12,1,1,1   |
+-----------------------------+------------------------------------+

DUMP() returns the datatype, the length in bytes and the internal representation of the data.

In other words, a date with a time, and a date that have been truncated, both have 7 bytes. They're the same length.


As a little aside I would recommend against destroying potentially useful data because you're worried about space.

Ben
  • 51,770
  • 36
  • 127
  • 149
  • So for all my datetime fields i use date.Simply. – Blocked Jun 12 '13 at 18:19
  • For all your date and all your datetime fields you use date, yes @Blocked. It's not going to make any difference to you. – Ben Jun 12 '13 at 18:19
  • If Oracle store the same length for date type(7 bytes) then why create a trigger for truncate our inserted data?This is addtion time for my query.Why not to use TRUNC() only in a select? – Blocked Jun 12 '13 at 18:49
  • If you don't create a trigger then you can't guarantee that the data you insert will be truncated @Blocked. If you want to guarantee it you should create a check constraint on the table to check that (date = trunc(date)). As I say I wouldn't do this though... – Ben Jun 12 '13 at 18:52
1

Storing only dates may save space if you use table compression.

Here's an example showing that storing only dates can reduce the segment size:

create table a (dt date) compress;
create table b (dt date) compress;

--Insert 20 million rows, with time
begin
    for i in 1 .. 20 loop
        insert /*+ append */ into a
        select sysdate + numToDSInterval(level, 'second')
        from dual connect by level <= 1000000;
        commit;
    end loop;
end;
/

--Insert 20 million rows, date only
begin
    for i in 1 .. 20 loop
        insert /*+ append */ into b
        select trunc(sysdate + numToDSInterval(level, 'second'))
        from dual connect by level <= 1000000;
        commit;
    end loop;
end;
/

select segment_name, bytes/1024/1024 MB
from dba_segments
where segment_name in ('A', 'B')
order by segment_name;

SEGMENT_NAME   MB
------------   --
A              256
B              224

Oracle basic table compression only compresses entire values, and if there are fewer distinct values then compression can work better. But never fully believe any compression demo - you need to try it on your own data to be sure. This may be a best-case scenario, it is possible that compression will not help your data at all.

Table compression has many downsides - it requires enterprise edition, DML is slower, you cannot add a column to the table, etc.

Also, as Ben suggested, you should enforce the date-only rule with a check constraint instead of a trigger. It will be simpler, faster, and will not prevent direct-path writes, which are necessary to use basic table compression.

Jon Heller
  • 34,999
  • 6
  • 74
  • 132
  • What would be the size of compressed `b` table created with `create table b (dt int) compress` and filled with integer values `trunc(sysdate + ...)-to_date(1,'J')`? – Egor Skriptunoff Jul 01 '13 at 21:12
  • @EgorSkriptunoff 216 MB. Storing a date diff based on the Julian date does make the values much smaller, just one or two bytes. But that doesn't help the overall table size much since the data is already compressed. Looks like there's a lot of storage overhead here preventing basic table compression from really helping. Perhaps hybrid columnar compression would work much better, but I don't have an Exadata system to test it on. – Jon Heller Jul 01 '13 at 23:02
  • 216 MBytes per 20 mln rows is 11 bytes per row. I think it is too many. I don't understand what kind of junk Oracle stores along with user data. – Egor Skriptunoff Jul 02 '13 at 17:29
  • @EgorSkriptunoff I think Oracle is not optimized for skinny tables. Which makes sense, tables like the ones in my example are uncommon. If I had stored the same date 4 times in each row, the sizes would be 509MB vs. 224MB (same size!). There are several different factors here, such as static [block and row overhead](http://docs.oracle.com/cd/E11882_01/server.112/e25789/logical.htm#i4894), extent size, etc. Which is why I can only demonstrate that compression *can* save space. But there's no good way to estimate how much space it will save in the real world. – Jon Heller Jul 02 '13 at 18:07
  • How can 3-byte-long number require 11 bytes when compressed? – Egor Skriptunoff Jul 02 '13 at 19:19
  • @EgorSkriptunoff Based on the manual chapter on blocks, and from http://nyoug.org/Presentations/2005/20050929tuningoracle.pdf, I think the absolute minimum size of a row is 6 bytes - 1 flag byte, 1 lock byte, 2 for column count, 1 for size, 1 for symbol. And then there's the block header, extra extent space, and probably lots of things I don't know about. With `DBA_SEGMENTS` I'm measuring the actual disk space for this entire table, which is not always clearly related to the logical dump length or the vsize of just the column data. – Jon Heller Jul 02 '13 at 22:41