{"id":12307,"date":"2019-03-12T23:41:02","date_gmt":"2019-03-12T22:41:02","guid":{"rendered":"https:\/\/www.dbi-services.com\/blog\/sql-tuning-mix-null-not-null-values\/"},"modified":"2019-03-12T23:41:02","modified_gmt":"2019-03-12T22:41:02","slug":"sql-tuning-mix-null-not-null-values","status":"publish","type":"post","link":"https:\/\/www.dbi-services.com\/blog\/sql-tuning-mix-null-not-null-values\/","title":{"rendered":"SQL Tuning &#8211; Mix NULL \/ NOT NULL Values"},"content":{"rendered":"<p>One of the difficulty when writing a SQL query (static SQL) is to have in the same Where Clause different conditions handling Null Values and Not Null Values for a predica.<\/p>\n<p>Let&#8217;s me explain you by an example :<\/p>\n<p>Users can entered different values for a user field from an OBI report:<br \/>\n   &#8211; If no value entered then all rows must be returned.<br \/>\n   &#8211; If 1 value entered then only row(s) related to the filter must be returned.<br \/>\n   &#8211; If List Of Values entered then only row(s) related to the filter must be returned.<\/p>\n<p>The SQL we want to write must take into account all the conditions possible (the 3 listed above).<\/p>\n<p>Here is the first version of the SQL query written by the customer :<\/p>\n<pre class=\"brush: bash; gutter: true; first-line: 1\">\nselect * \nfrom my_table a\nWHERE a.pt_name LIKE decode(:PT_PARAM, NULL, '%', '')\nOR a.pt_name IN (:PT_PARAM);\n<\/pre>\n<p>:PT_PARAM is the user variable.<\/p>\n<p>The problem with this query is that the both conditions :<br \/>\n   &#8211; a.pt_name LIKE decode(:PT_PARAM, NULL, &#8216;%&#8217;, &#8221;)<br \/>\n   &#8211; a.pt_name IN (:PT_PARAM)<br \/>\nare always TRUE, so unnecessary work will be done by oracle optimizer.<\/p>\n<p>We can prove that by checking the execution plan :<\/p>\n<p>If :PT_PARAM is equal to &#8216;Value1&#8217; :<\/p>\n<pre class=\"brush: bash; gutter: true; first-line: 1\">\nEXPLAIN PLAN FOR\nselect * \nfrom my_table a  \nWHERE a.pt_name LIKE decode('Value1', NULL, '%', '')\nOR a.pt_name IN ('Value1');\n\nSELECT * FROM TABLE(DBMS_XPLAN.DISPLAY);\n\nPlan hash value: 1606647163\n \n--------------------------------------------------------------------------------------------------------\n| Id  | Operation                           | Name             | Rows  | Bytes | Cost (%CPU)| Time     |\n--------------------------------------------------------------------------------------------------------\n|   0 | SELECT STATEMENT                    |                  |     5 |  1140 |     3   (0)| 00:00:01 |\n|   1 |  TABLE ACCESS BY INDEX ROWID BATCHED| MY_TABLE         |     5 |  1140 |     3   (0)| 00:00:01 |\n|   2 |   BITMAP CONVERSION TO ROWIDS       |                  |       |       |            |          |\n|   3 |    BITMAP OR                        |                  |       |       |            |          |\n|*  4 |     BITMAP INDEX SINGLE VALUE       | BIX_DMED_TERM_01 |       |       |            |          |\n|   5 |     BITMAP MERGE                    |                  |       |       |            |          |\n|*  6 |      BITMAP INDEX RANGE SCAN        | BIX_DMED_TERM_01 |       |       |            |          |\n--------------------------------------------------------------------------------------------------------\n \nPredicate Information (identified by operation id):\n---------------------------------------------------\n \n   4 - access(\"A\".\"PT_NAME\"='Value1')\n   6 - access(\"A\".\"PT_NAME\" LIKE NULL)\n       filter(\"A\".\"PT_NAME\" LIKE NULL AND \"A\".\"PT_NAME\" LIKE NULL)\n<\/pre>\n<p>Oracle Optimizer does 2 access :<br \/>\n &#8211; 1 access for NULL value<br \/>\n &#8211; 1 access for &#8216;Value1&#8217; value<\/p>\n<p>The first access is not necessary since the user has selected a Not Null Value (&#8216;Value1&#8217;). Indeed if the user select one Not Null value (&#8216;Value1&#8217;), we don&#8217;t want oracle execute condition for NULL value.<\/p>\n<p>To avoid this couple of access, it&#8217;s necessary to re-write the SQL statement like that :<\/p>\n<pre class=\"brush: bash; gutter: true; first-line: 1\">\nselect * \nfrom my_table a\nwhere (:PT_PARAM is null AND a.pt_name like '%')\nOR (:PT_PARAM IS NOT NULL AND a.pt_name in (:PT_PARAM));\n<\/pre>\n<p>We just add a SQL clause indicating that if the first condition is TRUE, the second condition is FALSE and vice versa:<br \/>\nif (:PT_PARAM is null AND a.pt_name like &#8216;%&#8217;) is TRUE then (:PT_PARAM IS NOT NULL AND a.pt_name in (:PT_PARAM)) is FALSE<br \/>\nif (:PT_PARAM IS NOT NULL AND a.pt_name in (:PT_PARAM)) is TRUE then (:PT_PARAM is null AND a.pt_name like &#8216;%&#8217;) is FALSE<\/p>\n<p>Checking the execution plan related to the new SQL statement :<\/p>\n<pre class=\"brush: bash; gutter: true; first-line: 1\">\nEXPLAIN PLAN FOR\nselect * \nfrom my_table a\nwhere ('Value1' is null AND a.pt_name like '%')\nOR ( 'Value1' IS NOT NULL AND a.pt_name in ('Value1'));\n\nPlan hash value: 2444798625\n \n--------------------------------------------------------------------------------------------------------\n| Id  | Operation                           | Name             | Rows  | Bytes | Cost (%CPU)| Time     |\n--------------------------------------------------------------------------------------------------------\n|   0 | SELECT STATEMENT                    |                  |     5 |  1140 |     2   (0)| 00:00:01 |\n|   1 |  TABLE ACCESS BY INDEX ROWID BATCHED| MY_TABLE         |     5 |  1140 |     2   (0)| 00:00:01 |\n|   2 |   BITMAP CONVERSION TO ROWIDS       |                  |       |       |            |          |\n|*  3 |    BITMAP INDEX SINGLE VALUE        | BIX_DMED_TERM_01 |       |       |            |          |\n--------------------------------------------------------------------------------------------------------\n \nPredicate Information (identified by operation id):\n---------------------------------------------------\n \n   3 - access(\"A\".\"PT_NAME\"='Value1')\n<\/pre>\n<p>Now only one access is done, the one related to the value &#8216;Value1&#8217; selected by the user.<\/p>\n<h3>Conclusion:<\/h3>\n<p>Tuning a SQL query can be made within different way : modify the physical design for a table (indexes, partitioning), influence the optimizer (Hints) to force an execution plan, modify oracle optimizer database parameters.<\/p>\n<p>But very often, SQL tuning can be made &#8220;simply&#8221; by re-written the SQL query. Most of the time, performance problem is due to bad written SQL statement.<\/p>\n<p>The first advice before to write a SQL query is:<br \/>\n  &#8211; always understand the business needs in order to avoid bad interpretations.<br \/>\n  &#8211; avoid unnecessary step for oracle optimizer by checking oracle execution plan in details to control the path oracle choose to access the data.<br \/>\n  &#8211; avoid writing complex SQL &#8211; SQL is a very simple language, don&#8217;t forget it.<\/p>\n","protected":false},"excerpt":{"rendered":"<p>One of the difficulty when writing a SQL query (static SQL) is to have in the same Where Clause different conditions handling Null Values and Not Null Values for a predica. Let&#8217;s me explain you by an example : Users can entered different values for a user field from an OBI report: &#8211; If no [&hellip;]<\/p>\n","protected":false},"author":27,"featured_media":0,"comment_status":"open","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"_acf_changed":false,"footnotes":""},"categories":[368,59],"tags":[98],"type_dbi":[],"class_list":["post-12307","post","type-post","status-publish","format-standard","hentry","category-development-performance","category-oracle","tag-sql"],"acf":[],"yoast_head":"<!-- This site is optimized with the Yoast SEO Premium plugin v27.2 (Yoast SEO v27.2) - https:\/\/yoast.com\/product\/yoast-seo-premium-wordpress\/ -->\n<title>SQL Tuning - Mix NULL \/ NOT NULL Values - dbi Blog<\/title>\n<meta name=\"robots\" content=\"index, follow, max-snippet:-1, max-image-preview:large, max-video-preview:-1\" \/>\n<link rel=\"canonical\" href=\"https:\/\/www.dbi-services.com\/blog\/sql-tuning-mix-null-not-null-values\/\" \/>\n<meta property=\"og:locale\" content=\"en_US\" \/>\n<meta property=\"og:type\" content=\"article\" \/>\n<meta property=\"og:title\" content=\"SQL Tuning - Mix NULL \/ NOT NULL Values\" \/>\n<meta property=\"og:description\" content=\"One of the difficulty when writing a SQL query (static SQL) is to have in the same Where Clause different conditions handling Null Values and Not Null Values for a predica. Let&#8217;s me explain you by an example : Users can entered different values for a user field from an OBI report: &#8211; If no [&hellip;]\" \/>\n<meta property=\"og:url\" content=\"https:\/\/www.dbi-services.com\/blog\/sql-tuning-mix-null-not-null-values\/\" \/>\n<meta property=\"og:site_name\" content=\"dbi Blog\" \/>\n<meta property=\"article:published_time\" content=\"2019-03-12T22:41:02+00:00\" \/>\n<meta name=\"author\" content=\"Oracle Team\" \/>\n<meta name=\"twitter:card\" content=\"summary_large_image\" \/>\n<meta name=\"twitter:label1\" content=\"Written by\" \/>\n\t<meta name=\"twitter:data1\" content=\"Oracle Team\" \/>\n\t<meta name=\"twitter:label2\" content=\"Est. reading time\" \/>\n\t<meta name=\"twitter:data2\" content=\"4 minutes\" \/>\n<script type=\"application\/ld+json\" class=\"yoast-schema-graph\">{\"@context\":\"https:\/\/schema.org\",\"@graph\":[{\"@type\":\"Article\",\"@id\":\"https:\/\/www.dbi-services.com\/blog\/sql-tuning-mix-null-not-null-values\/#article\",\"isPartOf\":{\"@id\":\"https:\/\/www.dbi-services.com\/blog\/sql-tuning-mix-null-not-null-values\/\"},\"author\":{\"name\":\"Oracle Team\",\"@id\":\"https:\/\/www.dbi-services.com\/blog\/#\/schema\/person\/66ab87129f2d357f09971bc7936a77ee\"},\"headline\":\"SQL Tuning &#8211; Mix NULL \/ NOT NULL Values\",\"datePublished\":\"2019-03-12T22:41:02+00:00\",\"mainEntityOfPage\":{\"@id\":\"https:\/\/www.dbi-services.com\/blog\/sql-tuning-mix-null-not-null-values\/\"},\"wordCount\":485,\"commentCount\":0,\"keywords\":[\"SQL\"],\"articleSection\":[\"Development &amp; Performance\",\"Oracle\"],\"inLanguage\":\"en-US\",\"potentialAction\":[{\"@type\":\"CommentAction\",\"name\":\"Comment\",\"target\":[\"https:\/\/www.dbi-services.com\/blog\/sql-tuning-mix-null-not-null-values\/#respond\"]}]},{\"@type\":\"WebPage\",\"@id\":\"https:\/\/www.dbi-services.com\/blog\/sql-tuning-mix-null-not-null-values\/\",\"url\":\"https:\/\/www.dbi-services.com\/blog\/sql-tuning-mix-null-not-null-values\/\",\"name\":\"SQL Tuning - Mix NULL \/ NOT NULL Values - dbi Blog\",\"isPartOf\":{\"@id\":\"https:\/\/www.dbi-services.com\/blog\/#website\"},\"datePublished\":\"2019-03-12T22:41:02+00:00\",\"author\":{\"@id\":\"https:\/\/www.dbi-services.com\/blog\/#\/schema\/person\/66ab87129f2d357f09971bc7936a77ee\"},\"breadcrumb\":{\"@id\":\"https:\/\/www.dbi-services.com\/blog\/sql-tuning-mix-null-not-null-values\/#breadcrumb\"},\"inLanguage\":\"en-US\",\"potentialAction\":[{\"@type\":\"ReadAction\",\"target\":[\"https:\/\/www.dbi-services.com\/blog\/sql-tuning-mix-null-not-null-values\/\"]}]},{\"@type\":\"BreadcrumbList\",\"@id\":\"https:\/\/www.dbi-services.com\/blog\/sql-tuning-mix-null-not-null-values\/#breadcrumb\",\"itemListElement\":[{\"@type\":\"ListItem\",\"position\":1,\"name\":\"Accueil\",\"item\":\"https:\/\/www.dbi-services.com\/blog\/\"},{\"@type\":\"ListItem\",\"position\":2,\"name\":\"SQL Tuning &#8211; Mix NULL \/ NOT NULL Values\"}]},{\"@type\":\"WebSite\",\"@id\":\"https:\/\/www.dbi-services.com\/blog\/#website\",\"url\":\"https:\/\/www.dbi-services.com\/blog\/\",\"name\":\"dbi Blog\",\"description\":\"\",\"potentialAction\":[{\"@type\":\"SearchAction\",\"target\":{\"@type\":\"EntryPoint\",\"urlTemplate\":\"https:\/\/www.dbi-services.com\/blog\/?s={search_term_string}\"},\"query-input\":{\"@type\":\"PropertyValueSpecification\",\"valueRequired\":true,\"valueName\":\"search_term_string\"}}],\"inLanguage\":\"en-US\"},{\"@type\":\"Person\",\"@id\":\"https:\/\/www.dbi-services.com\/blog\/#\/schema\/person\/66ab87129f2d357f09971bc7936a77ee\",\"name\":\"Oracle Team\",\"image\":{\"@type\":\"ImageObject\",\"inLanguage\":\"en-US\",\"@id\":\"https:\/\/secure.gravatar.com\/avatar\/f711f7cd2c9b09bf2627133755b569fb5be0694810cfd33033bdd095fedba86d?s=96&d=mm&r=g\",\"url\":\"https:\/\/secure.gravatar.com\/avatar\/f711f7cd2c9b09bf2627133755b569fb5be0694810cfd33033bdd095fedba86d?s=96&d=mm&r=g\",\"contentUrl\":\"https:\/\/secure.gravatar.com\/avatar\/f711f7cd2c9b09bf2627133755b569fb5be0694810cfd33033bdd095fedba86d?s=96&d=mm&r=g\",\"caption\":\"Oracle Team\"},\"url\":\"https:\/\/www.dbi-services.com\/blog\/author\/oracle-team\/\"}]}<\/script>\n<!-- \/ Yoast SEO Premium plugin. -->","yoast_head_json":{"title":"SQL Tuning - Mix NULL \/ NOT NULL Values - dbi Blog","robots":{"index":"index","follow":"follow","max-snippet":"max-snippet:-1","max-image-preview":"max-image-preview:large","max-video-preview":"max-video-preview:-1"},"canonical":"https:\/\/www.dbi-services.com\/blog\/sql-tuning-mix-null-not-null-values\/","og_locale":"en_US","og_type":"article","og_title":"SQL Tuning - Mix NULL \/ NOT NULL Values","og_description":"One of the difficulty when writing a SQL query (static SQL) is to have in the same Where Clause different conditions handling Null Values and Not Null Values for a predica. Let&#8217;s me explain you by an example : Users can entered different values for a user field from an OBI report: &#8211; If no [&hellip;]","og_url":"https:\/\/www.dbi-services.com\/blog\/sql-tuning-mix-null-not-null-values\/","og_site_name":"dbi Blog","article_published_time":"2019-03-12T22:41:02+00:00","author":"Oracle Team","twitter_card":"summary_large_image","twitter_misc":{"Written by":"Oracle Team","Est. reading time":"4 minutes"},"schema":{"@context":"https:\/\/schema.org","@graph":[{"@type":"Article","@id":"https:\/\/www.dbi-services.com\/blog\/sql-tuning-mix-null-not-null-values\/#article","isPartOf":{"@id":"https:\/\/www.dbi-services.com\/blog\/sql-tuning-mix-null-not-null-values\/"},"author":{"name":"Oracle Team","@id":"https:\/\/www.dbi-services.com\/blog\/#\/schema\/person\/66ab87129f2d357f09971bc7936a77ee"},"headline":"SQL Tuning &#8211; Mix NULL \/ NOT NULL Values","datePublished":"2019-03-12T22:41:02+00:00","mainEntityOfPage":{"@id":"https:\/\/www.dbi-services.com\/blog\/sql-tuning-mix-null-not-null-values\/"},"wordCount":485,"commentCount":0,"keywords":["SQL"],"articleSection":["Development &amp; Performance","Oracle"],"inLanguage":"en-US","potentialAction":[{"@type":"CommentAction","name":"Comment","target":["https:\/\/www.dbi-services.com\/blog\/sql-tuning-mix-null-not-null-values\/#respond"]}]},{"@type":"WebPage","@id":"https:\/\/www.dbi-services.com\/blog\/sql-tuning-mix-null-not-null-values\/","url":"https:\/\/www.dbi-services.com\/blog\/sql-tuning-mix-null-not-null-values\/","name":"SQL Tuning - Mix NULL \/ NOT NULL Values - dbi Blog","isPartOf":{"@id":"https:\/\/www.dbi-services.com\/blog\/#website"},"datePublished":"2019-03-12T22:41:02+00:00","author":{"@id":"https:\/\/www.dbi-services.com\/blog\/#\/schema\/person\/66ab87129f2d357f09971bc7936a77ee"},"breadcrumb":{"@id":"https:\/\/www.dbi-services.com\/blog\/sql-tuning-mix-null-not-null-values\/#breadcrumb"},"inLanguage":"en-US","potentialAction":[{"@type":"ReadAction","target":["https:\/\/www.dbi-services.com\/blog\/sql-tuning-mix-null-not-null-values\/"]}]},{"@type":"BreadcrumbList","@id":"https:\/\/www.dbi-services.com\/blog\/sql-tuning-mix-null-not-null-values\/#breadcrumb","itemListElement":[{"@type":"ListItem","position":1,"name":"Accueil","item":"https:\/\/www.dbi-services.com\/blog\/"},{"@type":"ListItem","position":2,"name":"SQL Tuning &#8211; Mix NULL \/ NOT NULL Values"}]},{"@type":"WebSite","@id":"https:\/\/www.dbi-services.com\/blog\/#website","url":"https:\/\/www.dbi-services.com\/blog\/","name":"dbi Blog","description":"","potentialAction":[{"@type":"SearchAction","target":{"@type":"EntryPoint","urlTemplate":"https:\/\/www.dbi-services.com\/blog\/?s={search_term_string}"},"query-input":{"@type":"PropertyValueSpecification","valueRequired":true,"valueName":"search_term_string"}}],"inLanguage":"en-US"},{"@type":"Person","@id":"https:\/\/www.dbi-services.com\/blog\/#\/schema\/person\/66ab87129f2d357f09971bc7936a77ee","name":"Oracle Team","image":{"@type":"ImageObject","inLanguage":"en-US","@id":"https:\/\/secure.gravatar.com\/avatar\/f711f7cd2c9b09bf2627133755b569fb5be0694810cfd33033bdd095fedba86d?s=96&d=mm&r=g","url":"https:\/\/secure.gravatar.com\/avatar\/f711f7cd2c9b09bf2627133755b569fb5be0694810cfd33033bdd095fedba86d?s=96&d=mm&r=g","contentUrl":"https:\/\/secure.gravatar.com\/avatar\/f711f7cd2c9b09bf2627133755b569fb5be0694810cfd33033bdd095fedba86d?s=96&d=mm&r=g","caption":"Oracle Team"},"url":"https:\/\/www.dbi-services.com\/blog\/author\/oracle-team\/"}]}},"_links":{"self":[{"href":"https:\/\/www.dbi-services.com\/blog\/wp-json\/wp\/v2\/posts\/12307","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/www.dbi-services.com\/blog\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/www.dbi-services.com\/blog\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/www.dbi-services.com\/blog\/wp-json\/wp\/v2\/users\/27"}],"replies":[{"embeddable":true,"href":"https:\/\/www.dbi-services.com\/blog\/wp-json\/wp\/v2\/comments?post=12307"}],"version-history":[{"count":0,"href":"https:\/\/www.dbi-services.com\/blog\/wp-json\/wp\/v2\/posts\/12307\/revisions"}],"wp:attachment":[{"href":"https:\/\/www.dbi-services.com\/blog\/wp-json\/wp\/v2\/media?parent=12307"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/www.dbi-services.com\/blog\/wp-json\/wp\/v2\/categories?post=12307"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/www.dbi-services.com\/blog\/wp-json\/wp\/v2\/tags?post=12307"},{"taxonomy":"type","embeddable":true,"href":"https:\/\/www.dbi-services.com\/blog\/wp-json\/wp\/v2\/type_dbi?post=12307"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}