{"id":506,"date":"2011-03-16T08:42:03","date_gmt":"2011-03-16T08:42:03","guid":{"rendered":"http:\/\/bar-solutions.com\/weblog\/?p=506"},"modified":"2011-03-16T08:42:03","modified_gmt":"2011-03-16T07:42:03","slug":"","status":"publish","type":"post","link":"https:\/\/blog.bar-solutions.com\/?p=506","title":{"rendered":"XOR in SQL"},"content":{"rendered":"<p align=\"left\">If you have ever tried to implement a requirement that implies that either one field is filled, or the other one but not both and not both not then you might have thought about using an XOR function. Unfortunately this is not implemented in <a title=\"Oracle\" href=\"http:\/\/www.oracle.com\" rel=\"nofollow\" target=\"_blank\">Oracle<\/a> SQL.<\/p>\n<p><!--more--><\/p>\n<p align=\"left\">First of all, let&#8217;s review some of the truth tables that are implemented in SQL.<br \/>\n  <\/p>\n<table border=\"0\" cellspacing=\"0\" cellpadding=\"2\" width=\"199\">\n<tbody>\n<tr>\n<td valign=\"top\" width=\"41\"><strong>AND<\/strong><\/td>\n<td valign=\"top\" width=\"49\"><em>left<\/em><\/td>\n<td valign=\"top\" width=\"49\"><em>right<\/em><\/td>\n<td valign=\"top\" width=\"58\"><em>result<\/em><\/td>\n<\/tr>\n<tr>\n<td valign=\"top\" width=\"41\">&#160;<\/td>\n<td valign=\"top\" width=\"49\">false<\/td>\n<td valign=\"top\" width=\"49\">false<\/td>\n<td valign=\"top\" width=\"58\">false<\/td>\n<\/tr>\n<tr>\n<td valign=\"top\" width=\"41\">&#160;<\/td>\n<td valign=\"top\" width=\"49\">false<\/td>\n<td valign=\"top\" width=\"49\">true<\/td>\n<td valign=\"top\" width=\"58\">false<\/td>\n<\/tr>\n<tr>\n<td valign=\"top\" width=\"41\">&#160;<\/td>\n<td valign=\"top\" width=\"49\">true<\/td>\n<td valign=\"top\" width=\"49\">false<\/td>\n<td valign=\"top\" width=\"58\">false<\/td>\n<\/tr>\n<tr>\n<td valign=\"top\" width=\"41\">&#160;<\/td>\n<td valign=\"top\" width=\"49\">true<\/td>\n<td valign=\"top\" width=\"49\">true<\/td>\n<td valign=\"top\" width=\"58\">true<\/td>\n<\/tr>\n<\/tbody>\n<\/table>\n<p><\/p>\n<table border=\"0\" cellspacing=\"0\" cellpadding=\"2\" width=\"201\">\n<tbody>\n<tr>\n<td valign=\"top\" width=\"41\"><strong>OR<\/strong><\/td>\n<td valign=\"top\" width=\"48\"><em>left<\/em><\/td>\n<td valign=\"top\" width=\"52\"><em>right<\/em><\/td>\n<td valign=\"top\" width=\"58\"><em>result<\/em><\/td>\n<\/tr>\n<tr>\n<td valign=\"top\" width=\"41\">&#160;<\/td>\n<td valign=\"top\" width=\"48\">false<\/td>\n<td valign=\"top\" width=\"52\">false<\/td>\n<td valign=\"top\" width=\"58\">false<\/td>\n<\/tr>\n<tr>\n<td valign=\"top\" width=\"41\">&#160;<\/td>\n<td valign=\"top\" width=\"48\">false<\/td>\n<td valign=\"top\" width=\"52\">true<\/td>\n<td valign=\"top\" width=\"58\">true<\/td>\n<\/tr>\n<tr>\n<td valign=\"top\" width=\"41\">&#160;<\/td>\n<td valign=\"top\" width=\"48\">true<\/td>\n<td valign=\"top\" width=\"52\">false<\/td>\n<td valign=\"top\" width=\"58\">true<\/td>\n<\/tr>\n<tr>\n<td valign=\"top\" width=\"41\">&#160;<\/td>\n<td valign=\"top\" width=\"48\">true<\/td>\n<td valign=\"top\" width=\"52\">true<\/td>\n<td valign=\"top\" width=\"58\">true<\/td>\n<\/tr>\n<\/tbody>\n<\/table>\n<p><\/p>\n<table border=\"0\" cellspacing=\"0\" cellpadding=\"2\" width=\"150\">\n<tbody>\n<tr>\n<td valign=\"top\" width=\"37\"><strong>NOT<\/strong><\/td>\n<td valign=\"top\" width=\"46\"><em>left<\/em><\/td>\n<td valign=\"top\" width=\"65\"><em>result<\/em><\/td>\n<\/tr>\n<tr>\n<td valign=\"top\" width=\"37\">&#160;<\/td>\n<td valign=\"top\" width=\"46\">false<\/td>\n<td valign=\"top\" width=\"65\">true<\/td>\n<\/tr>\n<tr>\n<td valign=\"top\" width=\"37\">&#160;<\/td>\n<td valign=\"top\" width=\"46\">true<\/td>\n<td valign=\"top\" width=\"65\">false<\/td>\n<\/tr>\n<\/tbody>\n<\/table>\n<p>The way XOR should be implemented is: <\/p>\n<table border=\"0\" cellspacing=\"0\" cellpadding=\"2\" width=\"190\">\n<tbody>\n<tr>\n<td valign=\"top\" width=\"37\"><strong>XOR<\/strong><\/td>\n<td valign=\"top\" width=\"46\"><em>left<\/em><\/td>\n<td valign=\"top\" width=\"53\"><em>right<\/em><\/td>\n<td valign=\"top\" width=\"52\"><em>result<\/em><\/td>\n<\/tr>\n<tr>\n<td valign=\"top\" width=\"37\">&#160;<\/td>\n<td valign=\"top\" width=\"46\">false<\/td>\n<td valign=\"top\" width=\"53\">false<\/td>\n<td valign=\"top\" width=\"52\">false<\/td>\n<\/tr>\n<tr>\n<td valign=\"top\" width=\"37\">&#160;<\/td>\n<td valign=\"top\" width=\"46\">false<\/td>\n<td valign=\"top\" width=\"53\">true<\/td>\n<td valign=\"top\" width=\"52\">true<\/td>\n<\/tr>\n<tr>\n<td valign=\"top\" width=\"37\">&#160;<\/td>\n<td valign=\"top\" width=\"46\">true<\/td>\n<td valign=\"top\" width=\"53\">false<\/td>\n<td valign=\"top\" width=\"52\">true<\/td>\n<\/tr>\n<tr>\n<td valign=\"top\" width=\"37\">&#160;<\/td>\n<td valign=\"top\" width=\"46\">true<\/td>\n<td valign=\"top\" width=\"53\">true<\/td>\n<td valign=\"top\" width=\"52\">false<\/td>\n<\/tr>\n<\/tbody>\n<\/table>\n<p align=\"left\">In PL\/SQL there is a function available that implements this truthtable. It&#8217;s in the standard package and looks like this:<\/p>\n<pre>desc sys.standard.XOR;\r\nParameter Type    Mode Default? \r\n<span style=\"color: #008000\">--------- ------- ---- -------- <\/span>\r\n(RESULT)  BOOLEAN               \r\nLEFT      BOOLEAN IN            \r\nRIGHT     BOOLEAN IN            <\/pre>\n<p align=\"left\">Let&#8217;s create a simple table and fill it up for testing:<br \/>\n  <br \/><font size=\"2\" face=\"Courier New\">create table t<br \/>\n    <br \/>(left&#160; number <\/p>\n<p>,right number); <\/p>\n<p>insert into t (left, right) values (0,0); <\/p>\n<p>insert into t (left, right) values (0,1); <\/p>\n<p>insert into t (left, right) values (1,0); <\/p>\n<p>insert into t (left, right) values (1,1); <\/p>\n<p>commit;<\/font><\/p>\n<p align=\"left\">An idea would be to just call this function in a query like this:<br \/>\n  <br \/><font size=\"2\" face=\"Courier New\">select * from t<br \/>\n    <br \/>where sys.standard.xor(((left &lt;&gt; 0)), ((right &lt;&gt; 0)))<\/font><\/p>\n<p align=\"left\">But, unfortunately the boolean datatype has not been implemented in SQL, so this doesn&#8217;t work.<br \/>\n  <br \/>How to solve this issue then? We can of course write down all the rules in a where clause and make sure it return the correct results: <\/p>\n<p><font size=\"2\" face=\"Courier New\">select * from t<br \/>\n    <br \/>where ( <\/p>\n<p>&#160;&#160;&#160;&#160;&#160;&#160;&#160; (&#160;&#160;&#160; ((left is not null) and (left &lt;&gt; 0)) <\/p>\n<p>&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160; and ((right is null) or (right = 0)) <\/p>\n<p>&#160;&#160;&#160;&#160;&#160;&#160;&#160; ) <\/p>\n<p>&#160;&#160;&#160;&#160;&#160;&#160;&#160; or <\/p>\n<p>&#160;&#160;&#160;&#160;&#160;&#160;&#160; (&#160;&#160;&#160; ((left is null) or (left = 0)) <\/p>\n<p>&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160; and ((right is not null) and (right &lt;&gt; 0)) <\/p>\n<p>&#160;&#160;&#160;&#160;&#160;&#160;&#160; ) <\/p>\n<p>&#160;&#160;&#160;&#160;&#160;&#160; )<\/font><\/p>\n<p align=\"left\">That is one ugly query. How can we rewrite the XOR function using just AND and OR function. The OR function gives almost everything we need, but it gives one result to many. And that is exactly the outcome of applying the AND function to our dataset. So:<br \/>\n  <br \/>XOR = a OR b minus (a AND b) <\/p>\n<p><font size=\"2\" face=\"Courier New\">select * from t<br \/>\n    <br \/>where (&#160;&#160; ((left is not null) and (left &lt;&gt; 0)) <\/p>\n<p>&#160;&#160;&#160;&#160;&#160;&#160;&#160; or ((right is not null) and (right &lt;&gt; 0)) <\/p>\n<p>&#160;&#160;&#160;&#160;&#160;&#160; ) <\/p>\n<p>minus <\/p>\n<p>select * from t <\/p>\n<p>where (&#160;&#160;&#160; ((left is not null) and (left &lt;&gt; 0)) <\/p>\n<p>&#160;&#160;&#160;&#160;&#160;&#160;&#160; and ((right is not null) and (right &lt;&gt; 0)) <\/p>\n<p>&#160;&#160;&#160;&#160;&#160;&#160; )<\/font><\/p>\n<p align=\"left\">or<br \/>\n  <br \/>XOR = a OR b and NOT (a AND b) <\/p>\n<p><font size=\"2\" face=\"Courier New\">select * from t<br \/>\n    <br \/>where (&#160;&#160; ((left is not null) and (left &lt;&gt; 0)) <\/p>\n<p>&#160;&#160;&#160;&#160;&#160;&#160;&#160; or ((right is not null) and (right &lt;&gt; 0)) <\/p>\n<p>&#160;&#160;&#160;&#160;&#160;&#160; ) <\/p>\n<p>&#160;&#160;&#160;&#160;&#160;&#160; and not <\/p>\n<p>&#160;&#160;&#160;&#160;&#160;&#160; (&#160;&#160;&#160; ((left is not null) and (left &lt;&gt; 0)) <\/p>\n<p>&#160;&#160;&#160;&#160;&#160;&#160;&#160; and ((right is not null) and (right &lt;&gt; 0)) <\/p>\n<p>&#160;&#160;&#160;&#160;&#160;&#160; )<\/font><\/p>\n<p align=\"left\">I think these last two queries are more descriptive to what is being done. Which query to choose depends on how much data is in your table. In my example it doesn&#8217;t make any difference in time. I tested with a table with over 29500 rows and it appears the minus operation is a bit faster than the version with the extra predicate.<br \/>\n  <br \/>Rather simple code, but I hope it will help you a bit (it certainly helped me). I wrote this to make sure I don&#8217;t forget it \ud83d\ude09<\/p>\n","protected":false},"excerpt":{"rendered":"<p>If you have ever tried to implement a requirement that implies that either one field is filled, or the other one but not both and not both not then you might have thought about using an XOR function. Unfortunately this is not implemented in Oracle SQL.<\/p>\n","protected":false},"author":1,"featured_media":0,"comment_status":"open","ping_status":"closed","sticky":false,"template":"","format":"standard","meta":{"footnotes":""},"categories":[2,5,9],"tags":[],"class_list":["post-506","post","type-post","status-publish","format-standard","hentry","category-oracle","category-plsql","category-sql"],"_links":{"self":[{"href":"https:\/\/blog.bar-solutions.com\/index.php?rest_route=\/wp\/v2\/posts\/506","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/blog.bar-solutions.com\/index.php?rest_route=\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/blog.bar-solutions.com\/index.php?rest_route=\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/blog.bar-solutions.com\/index.php?rest_route=\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"https:\/\/blog.bar-solutions.com\/index.php?rest_route=%2Fwp%2Fv2%2Fcomments&post=506"}],"version-history":[{"count":0,"href":"https:\/\/blog.bar-solutions.com\/index.php?rest_route=\/wp\/v2\/posts\/506\/revisions"}],"wp:attachment":[{"href":"https:\/\/blog.bar-solutions.com\/index.php?rest_route=%2Fwp%2Fv2%2Fmedia&parent=506"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/blog.bar-solutions.com\/index.php?rest_route=%2Fwp%2Fv2%2Fcategories&post=506"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/blog.bar-solutions.com\/index.php?rest_route=%2Fwp%2Fv2%2Ftags&post=506"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}