{"id":278,"date":"2009-06-26T16:06:31","date_gmt":"2009-06-26T21:06:31","guid":{"rendered":"http:\/\/appcrawler.com\/wordpress\/?p=278"},"modified":"2011-07-06T10:17:08","modified_gmt":"2011-07-06T15:17:08","slug":"range-scans-when-using-varchar2-instaed-of-number","status":"publish","type":"post","link":"http:\/\/appcrawler.com\/wordpress\/2009\/06\/26\/range-scans-when-using-varchar2-instaed-of-number\/","title":{"rendered":"Range scans when using VARCHAR2 instead of NUMBER"},"content":{"rendered":"<p>I was a little surprised today when another DBA did not know the effect of using a VARCHAR2 datatype for what is functionally a number.\u00a0 I wanted to ensure I had an article to which I could point when this question comes up in the future, so here it is. <\/p>\n<p>A VARCHAR2 that functions as a number will not be very usable for what would normally result in a range scan execution plan.  If you think about it, this makes sense.  You can intuitively look at all values (regardless of &#8220;datatype&#8221;) between 10 and 900 and say there are 890 values between the two.  If however, you are a computer, you will sort it as though 10 is a alphabetic character, and 900 is an alphabetic character, since that is what the data is.  In other words, 1,000,000,000,000 is less than 2, since the leading 1 is less than 2.  <\/p>\n<p>Below shows a test case and the optimizer execution plan for each.<\/p>\n<pre lang=\"sql\" line=\"1\">\r\nSQL> create table vchar(c varchar2(20));\r\n\r\nTable created.\r\n\r\nSQL> create table num(c number);\r\n\r\nTable created.\r\n\r\nSQL> begin\r\n  2    for i in 1..100000 loop\r\n  3      insert into vchar values(i);\r\n  4      insert into num values(i);\r\n  5    end loop;\r\n  6  end;\r\n  7  \/\r\n\r\nPL\/SQL procedure successfully completed.\r\n\r\nSQL> create index vchar_i on vchar(c);\r\n\r\nIndex created.\r\n\r\nSQL> create index num_i on num(c);\r\n\r\nIndex created.\r\n\r\nSQL> exec dbms_stats.gather_table_stats(USER,'VCHAR');\r\n\r\nPL\/SQL procedure successfully completed.\r\n\r\nSQL> exec dbms_stats.gather_table_stats(USER,'NUM');\r\n\r\nPL\/SQL procedure successfully completed.\r\n\r\nSQL> explain plan for select * from vchar where c between '10' and '900';\r\n\r\nExplained.\r\n\r\nSQL> select * from table(dbms_xplan.display());\r\n\r\nPLAN_TABLE_OUTPUT\r\n-----------------------------------------------------------------------------------------------------------------\r\nPlan hash value: 600218931\r\n--------------------------------------------------------------------------------\r\n| Id  | Operation            | Name    | Rows  | Bytes | Cost (%CPU)| Time     |\r\n--------------------------------------------------------------------------------\r\n|   0 | SELECT STATEMENT     |         | 88900 |   434K|    45   (5)| 00:00:01 |\r\n|*  1 |  INDEX FAST FULL SCAN| VCHAR_I | 88900 |   434K|    45   (5)| 00:00:01 |\r\n--------------------------------------------------------------------------------\r\n\r\nPredicate Information (identified by operation id):\r\n---------------------------------------------------\r\n\r\n   1 - filter(\"C\"<='900' AND \"C\">='10')\r\n\r\n13 rows selected.\r\n\r\nSQL> explain plan for select * from num where c between 10 and 900;\r\n\r\nExplained.\r\n\r\nSQL> select * from table(dbms_xplan.display());\r\n\r\nPLAN_TABLE_OUTPUT\r\n-------------------------------------------------------------------------------------------------------------\r\nPlan hash value: 3771115364\r\n--------------------------------------------------------------------------\r\n| Id  | Operation        | Name  | Rows  | Bytes | Cost (%CPU)| Time     |\r\n--------------------------------------------------------------------------\r\n|   0 | SELECT STATEMENT |       |   892 |  3568 |     3   (0)| 00:00:01 |\r\n|*  1 |  INDEX RANGE SCAN| NUM_I |   892 |  3568 |     3   (0)| 00:00:01 |\r\n--------------------------------------------------------------------------\r\n\r\nPredicate Information (identified by operation id):\r\n----------------------------------------------------\r\n   1 - access(\"C\">=10 AND \"C\"<=900)\r\n\r\n13 rows selected.\r\n\r\nSQL>\r\n<\/pre>\n","protected":false},"excerpt":{"rendered":"<p>I was a little surprised today when another DBA did not know the effect of using a VARCHAR2 datatype for what is functionally a number.\u00a0 I wanted to ensure I had an article to which I could point when this&hellip;<\/p>\n<p class=\"more-link-p\"><a class=\"more-link\" href=\"http:\/\/appcrawler.com\/wordpress\/2009\/06\/26\/range-scans-when-using-varchar2-instaed-of-number\/\">Read more &rarr;<\/a><\/p>\n","protected":false},"author":2,"featured_media":0,"comment_status":"open","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"_mi_skip_tracking":false,"footnotes":""},"categories":[19,22],"tags":[],"_links":{"self":[{"href":"http:\/\/appcrawler.com\/wordpress\/wp-json\/wp\/v2\/posts\/278"}],"collection":[{"href":"http:\/\/appcrawler.com\/wordpress\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"http:\/\/appcrawler.com\/wordpress\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"http:\/\/appcrawler.com\/wordpress\/wp-json\/wp\/v2\/users\/2"}],"replies":[{"embeddable":true,"href":"http:\/\/appcrawler.com\/wordpress\/wp-json\/wp\/v2\/comments?post=278"}],"version-history":[{"count":13,"href":"http:\/\/appcrawler.com\/wordpress\/wp-json\/wp\/v2\/posts\/278\/revisions"}],"predecessor-version":[{"id":287,"href":"http:\/\/appcrawler.com\/wordpress\/wp-json\/wp\/v2\/posts\/278\/revisions\/287"}],"wp:attachment":[{"href":"http:\/\/appcrawler.com\/wordpress\/wp-json\/wp\/v2\/media?parent=278"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"http:\/\/appcrawler.com\/wordpress\/wp-json\/wp\/v2\/categories?post=278"},{"taxonomy":"post_tag","embeddable":true,"href":"http:\/\/appcrawler.com\/wordpress\/wp-json\/wp\/v2\/tags?post=278"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}