Apress.Expert.Oracle.Database.Architecture.9i.and.10g.Programming.Techniques.and.Solutions.Sep.2005
CHAPTER 12 ■ DATATYPES 525 You can see the obvious difference. Using TO_CHAR consumed an order of magnitude more CPU than using TRUNC. That is because TO_CHAR must convert the date to a string, using a much larger code path, taking all of the NLS we have in place to do so. Then we had to compare a string to a string. The TRUNC, on the other hand, just had to set the last 5 bytes to 1. Then it compared 7 binary bytes to 7 binary bytes, and it was done. So, you should never use TO_CHAR on a DATE column simply to truncate it. Additionally, avoid applying a function at all to the DATE column when possible. Taking the preceding example one step further, we can see that the goal was to retrieve all data in the year 2005. Well, what if CREATED had an index on it and a very small fraction of the values in that table were in the year 2005? We would like to be able to use that index by avoiding a function on the database and column using a simple predicate: select count(*) from t where created >= to_date('01-jan-2005','dd-mon-yyyy') and created < to_date('01-jan-2006','dd-mon-yyyy'); We would achieve two things: • An index on CREATED could be considered. • The TRUNC function would not have to be invoked at all, and that overhead would go away entirely. This technique of using a range comparison instead of TRUNC or TO_CHAR applies equally to the TIMESTAMP type discussed shortly. When you can avoid applying a function to a database column in a query, you should. In general, avoiding the function will be more performant and allow the optimizer to choose from a wider variety of access paths. Adding Time to or Subtracting Time from a DATE A question I am frequently asked is, “How do I add time to or subtract time from a DATE type?” For example, how do you add one day to a DATE, or eight hours, or one year, or one month, and so on? There are three techniques you’ll commonly use: • Simply add a NUMBER to the DATE. Adding 1 to a DATE is a method to add 1 day. Adding 1/24 to a DATE therefore adds 1 hour, and so on. • You may use the INTERVAL type, as described shortly, to add units of time. INTERVAL types support two levels of granularity, years and months, or days/hours/minutes/seconds. That is, you may have an interval of so many years and months or an interval of so many days, hours, minutes, and seconds. • Add months using the built-in ADD_MONTHS function. Since adding a month is generally not as simple as adding 28 to 31 days, a special-purpose function was implemented to facilitate this. Table 12-3 demonstrates the techniques you would use to add N units of time to a date (or subtract N units of time from a date, of course).
526 CHAPTER 12 ■ DATATYPES Table 12-3. Adding Time to a Date Unit of Time Operation Description N seconds DATE + n/24/60/60 There are 86,400 seconds in a day. DATE + n/86400 Since adding 1 adds one day, adding DATE + NUMTODSINTERVAL(n,'second') 1/86400 adds one second to a date. I prefer the n/24/60/60 technique over the 1/86400 technique. They are equivalent. An even more readable method is to use the NUMTODSINTERVAL (number to day/second interval) to add N seconds. N minutes DATE + n/24/60 There are 1,440 minutes in a date. DATE + n/1440 Adding 1/1440 therefore adds one DATE + NUMTODSINTERVAL(n,'minute') minute to a DATE. An even more readable method is to use the NUMTODSINTERVAL function. N hours DATE + n/24 There are 24 hours in a day. Adding DATE + NUMTODSINTERVAL(n,'hour') 1/24 therefore adds one hour to a DATE. An even more readable method is to use the NUMTODSINTERVAL function. N days DATE + n Simply add N to the DATE to add or subtract N days. N weeks DATE + 7*n A week is seven days, so just multiply 7 by the number of weeks to add or subtract. N months ADD_MONTHS(DATE,n) You may use the ADD_MONTHS built-in DATE + NUMTOYMINTERVAL(n,'month') function or add an interval of N months to the DATE. Please see the important caveat noted shortly regarding using month intervals with DATEs. N years ADD_MONTHS(DATE,12*n) You may use the ADD_MONTHS built-in DATE + NUMTOYMINTERVAL(n,'year') function with 12*n to add or subtract N years. Similar goals may be achieved with a year interval, but please see the important caveat noted shortly regarding using year intervals with dates. In general, when using the Oracle DATE type I recommend the following: • Use the NUMTODSINTERVAL built-in function to add hours, minutes, and seconds. • Add a simple number to add days. • Use the ADD_MONTHS built-in function to add months and years. I do not recommend using the NUMTOYMINTERVAL function. The reason has to do with how the functions behave at the months’ end. The ADD_MONTHS function treats the end of month days specially. It will in effect “round” the dates for us; for example, if we add one month to a month that has 31 days and the next
- Page 520 and 521: CHAPTER 11 ■ INDEXES 475 If you s
- Page 522 and 523: CHAPTER 11 ■ INDEXES 477 we’ll
- Page 524 and 525: CHAPTER 11 ■ INDEXES 479 Predicat
- Page 526 and 527: CHAPTER 11 ■ INDEXES 481 ops$tkyt
- Page 528 and 529: CHAPTER 11 ■ INDEXES 483 ops$tkyt
- Page 530 and 531: CHAPTER 11 ■ INDEXES 485 This dem
- Page 532 and 533: CHAPTER 11 ■ INDEXES 487 SELECT /
- Page 534 and 535: CHAPTER 12 ■ ■ ■ Datatypes Ch
- Page 536 and 537: CHAPTER 12 ■ DATATYPES 491 • TI
- Page 538 and 539: CHAPTER 12 ■ DATATYPES 493 (in th
- Page 540 and 541: CHAPTER 12 ■ DATATYPES 495 That d
- Page 542 and 543: CHAPTER 12 ■ DATATYPES 497 ops$tk
- Page 544 and 545: CHAPTER 12 ■ DATATYPES 499 Table
- Page 546 and 547: CHAPTER 12 ■ DATATYPES 501 The IN
- Page 548 and 549: CHAPTER 12 ■ DATATYPES 503 ops$tk
- Page 550 and 551: CHAPTER 12 ■ DATATYPES 505 • BI
- Page 552 and 553: CHAPTER 12 ■ DATATYPES 507 NUMBER
- Page 554 and 555: CHAPTER 12 ■ DATATYPES 509 MSG NU
- Page 556 and 557: CHAPTER 12 ■ DATATYPES 511 They a
- Page 558 and 559: CHAPTER 12 ■ DATATYPES 513 ■Not
- Page 560 and 561: CHAPTER 12 ■ DATATYPES 515 Coping
- Page 562 and 563: CHAPTER 12 ■ DATATYPES 517 Note t
- Page 564 and 565: CHAPTER 12 ■ DATATYPES 519 We are
- Page 566 and 567: CHAPTER 12 ■ DATATYPES 521 Format
- Page 568 and 569: CHAPTER 12 ■ DATATYPES 523 ops$tk
- Page 572 and 573: CHAPTER 12 ■ DATATYPES 527 month
- Page 574 and 575: CHAPTER 12 ■ DATATYPES 529 DT2-DT
- Page 576 and 577: CHAPTER 12 ■ DATATYPES 531 DT TS
- Page 578 and 579: CHAPTER 12 ■ DATATYPES 533 ops$tk
- Page 580 and 581: CHAPTER 12 ■ DATATYPES 535 Since
- Page 582 and 583: CHAPTER 12 ■ DATATYPES 537 ops$tk
- Page 584 and 585: CHAPTER 12 ■ DATATYPES 539 ops$tk
- Page 586 and 587: CHAPTER 12 ■ DATATYPES 541 suppor
- Page 588 and 589: CHAPTER 12 ■ DATATYPES 543 Concep
- Page 590 and 591: CHAPTER 12 ■ DATATYPES 545 We can
- Page 592 and 593: CHAPTER 12 ■ DATATYPES 547 buffer
- Page 594 and 595: CHAPTER 12 ■ DATATYPES 549 Note t
- Page 596 and 597: CHAPTER 12 ■ DATATYPES 551 13 dbm
- Page 598 and 599: CHAPTER 12 ■ DATATYPES 553 equall
- Page 600 and 601: CHAPTER 12 ■ DATATYPES 555 ROWID/
- Page 602 and 603: CHAPTER 13 ■ ■ ■ Partitioning
- Page 604 and 605: CHAPTER 13 ■ PARTITIONING 559 6 (
- Page 606 and 607: CHAPTER 13 ■ PARTITIONING 561 els
- Page 608 and 609: CHAPTER 13 ■ PARTITIONING 563 BIG
- Page 610 and 611: CHAPTER 13 ■ PARTITIONING 565 Enh
- Page 612 and 613: CHAPTER 13 ■ PARTITIONING 567 Tab
- Page 614 and 615: CHAPTER 13 ■ PARTITIONING 569 tha
- Page 616 and 617: CHAPTER 13 ■ PARTITIONING 571 PAR
- Page 618 and 619: CHAPTER 13 ■ PARTITIONING 573 35
CHAPTER 12 ■ DATATYPES 525<br />
You can see the obvious difference. Using TO_CHAR consumed an order of magnitude more<br />
CPU than using TRUNC. That is because TO_CHAR must convert the date to a string, using a much<br />
larger code path, taking all of the NLS we have in place to do so. Then we had to compare a<br />
string to a string. The TRUNC, on the other h<strong>and</strong>, just had to set the last 5 bytes to 1. Then it<br />
compared 7 binary bytes to 7 binary bytes, <strong>and</strong> it was done. So, you should never use TO_CHAR<br />
on a DATE column simply to truncate it.<br />
Additionally, avoid applying a function at all to the DATE column when possible. Taking the<br />
preceding example one step further, we can see that the goal was to retrieve all data in the year<br />
2005. Well, what if CREATED had an index on it <strong>and</strong> a very small fraction of the values in that<br />
table were in the year 2005? We would like to be able to use that index by avoiding a function<br />
on the database <strong>and</strong> column using a simple predicate:<br />
select count(*) from t<br />
where created >= to_date('01-jan-2005','dd-mon-yyyy')<br />
<strong>and</strong> created < to_date('01-jan-2006','dd-mon-yyyy');<br />
We would achieve two things:<br />
• An index on CREATED could be considered.<br />
• The TRUNC function would not have to be invoked at all, <strong>and</strong> that overhead would go<br />
away entirely.<br />
This technique of using a range comparison instead of TRUNC or TO_CHAR applies equally to<br />
the TIMESTAMP type discussed shortly. When you can avoid applying a function to a database<br />
column in a query, you should. In general, avoiding the function will be more performant <strong>and</strong><br />
allow the optimizer to choose from a wider variety of access paths.<br />
Adding Time to or Subtracting Time from a DATE<br />
A question I am frequently asked is, “How do I add time to or subtract time from a DATE type?”<br />
For example, how do you add one day to a DATE, or eight hours, or one year, or one month, <strong>and</strong><br />
so on? There are three techniques you’ll commonly use:<br />
• Simply add a NUMBER to the DATE. Adding 1 to a DATE is a method to add 1 day. Adding<br />
1/24 to a DATE therefore adds 1 hour, <strong>and</strong> so on.<br />
• You may use the INTERVAL type, as described shortly, to add units of time. INTERVAL<br />
types support two levels of granularity, years <strong>and</strong> months, or days/hours/minutes/seconds.<br />
That is, you may have an interval of so many years <strong>and</strong> months or an interval of<br />
so many days, hours, minutes, <strong>and</strong> seconds.<br />
• Add months using the built-in ADD_MONTHS function. Since adding a month is generally<br />
not as simple as adding 28 to 31 days, a special-purpose function was implemented to<br />
facilitate this.<br />
Table 12-3 demonstrates the techniques you would use to add N units of time to a date (or<br />
subtract N units of time from a date, of course).