<?xml version='1.0' encoding='UTF-8'?><?xml-stylesheet href="http://www.blogger.com/styles/atom.css" type="text/css"?><feed xmlns='http://www.w3.org/2005/Atom' xmlns:openSearch='http://a9.com/-/spec/opensearchrss/1.0/' xmlns:georss='http://www.georss.org/georss' xmlns:gd='http://schemas.google.com/g/2005' xmlns:thr='http://purl.org/syndication/thread/1.0'><id>tag:blogger.com,1999:blog-26185192</id><updated>2012-01-08T18:06:49.527+08:00</updated><category term='not null'/><category term='linux'/><category term='group_concat'/><category term='postgres'/><category term='postgresql'/><category term='mssql-compatible functions'/><category term='mysql'/><category term='windows benchmark'/><category term='amd 64'/><category term='of each group'/><category term='last row'/><category term='foreign keys'/><category term='customer'/><category term='deleting duplicates'/><category term='intel core 2 duo'/><category term='gpg pg_dump postgres backup'/><category term='functions'/><category term='last balance'/><category term='ms sql'/><category term='account'/><category term='mssql'/><category term='balance'/><title type='text'>MS SQL to PostgreSQL</title><subtitle type='html'></subtitle><link rel='http://schemas.google.com/g/2005#feed' type='application/atom+xml' href='http://mssql-to-postgresql.blogspot.com/feeds/posts/default'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/26185192/posts/default?max-results=100'/><link rel='alternate' type='text/html' href='http://mssql-to-postgresql.blogspot.com/'/><link rel='hub' href='http://pubsubhubbub.appspot.com/'/><author><name>Michael I. Buen</name><uri>http://www.blogger.com/profile/10894538708565915450</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><generator version='7.00' uri='http://www.blogger.com'>Blogger</generator><openSearch:totalResults>31</openSearch:totalResults><openSearch:startIndex>1</openSearch:startIndex><openSearch:itemsPerPage>100</openSearch:itemsPerPage><entry><id>tag:blogger.com,1999:blog-26185192.post-9051667047719360293</id><published>2008-08-26T16:18:00.005+08:00</published><updated>2008-08-27T00:52:11.213+08:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='gpg pg_dump postgres backup'/><title type='text'>gpg primer</title><content type='html'>miblogic@gmail.com procedure:&lt;br /&gt;&lt;br /&gt;gpg --gen-key&lt;br /&gt;gpg --list-keys&lt;br /&gt;&lt;br /&gt;// export public key, --armor makes the key file ascii readable&lt;br /&gt;gpg --armor --export miblogic@gmail.com &gt; miblogic.gmail.public.key&lt;br /&gt;// just the same&lt;br /&gt;gpg -a --export miblogic@gmail.com &gt; miblogic.gmail.public.key&lt;br /&gt;&lt;br /&gt;// export private key&lt;br /&gt;gpg --armor --export-secret-key miblogic@gmail.com &gt;  miblogic.gmail.private.key&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;// import the public key of miblogic@yahoo.com&lt;br /&gt;gpg --import miblogic.yahoo.public.key&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;// gpg a piped output:&lt;br /&gt;pg_dump -i -h 127.0.0.1 -p 5432 -U postgres -F c -v  Cashflow | gpg --recipient miblogic@yahoo.com --encrypt --output Cashflow.backup.gpg&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;// gpg a file:&lt;br /&gt;gpg --recipient miblogic@yahoo.com ---output file.txt.encrypted --encrypt file.txt&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;// miblogic@yahoo.com decrypting a message from miblogic@gmail.com&lt;br /&gt;gpg --recipient miblogic@yahoo.com --output the_file.txt --decrypt file.txt.encrypted&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/26185192-9051667047719360293?l=mssql-to-postgresql.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://mssql-to-postgresql.blogspot.com/feeds/9051667047719360293/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=26185192&amp;postID=9051667047719360293' title='9 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/26185192/posts/default/9051667047719360293'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/26185192/posts/default/9051667047719360293'/><link rel='alternate' type='text/html' href='http://mssql-to-postgresql.blogspot.com/2008/08/gpg-primer.html' title='gpg primer'/><author><name>Michael I. Buen</name><uri>http://www.blogger.com/profile/10894538708565915450</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>9</thr:total></entry><entry><id>tag:blogger.com,1999:blog-26185192.post-5114367017701273329</id><published>2008-07-14T15:04:00.004+08:00</published><updated>2008-07-14T15:57:57.770+08:00</updated><title type='text'>my first time to win computer chess (GNU Chess)</title><content type='html'>killing time from our Linux server this afternoon after i ate lunch  :-D&lt;br /&gt;&lt;br /&gt;&lt;a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="http://bp0.blogger.com/_VDdbclMuay0/SHr66NhsGsI/AAAAAAAAAAY/OkFT04jM5p4/s1600-h/i+win%21.png"&gt;&lt;img style="cursor: pointer;" src="http://bp0.blogger.com/_VDdbclMuay0/SHr66NhsGsI/AAAAAAAAAAY/OkFT04jM5p4/s400/i+win%21.png" alt="" id="BLOGGER_PHOTO_ID_5222762595859831490" border="0" /&gt;&lt;/a&gt;&lt;br /&gt;&lt;br /&gt;:-)  my first time to win again against computer, also my first time to play  GNU Chess (using normal settings)   next time i'll try the difficult level&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/26185192-5114367017701273329?l=mssql-to-postgresql.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://mssql-to-postgresql.blogspot.com/feeds/5114367017701273329/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=26185192&amp;postID=5114367017701273329' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/26185192/posts/default/5114367017701273329'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/26185192/posts/default/5114367017701273329'/><link rel='alternate' type='text/html' href='http://mssql-to-postgresql.blogspot.com/2008/07/my-first-time-to-win-computer-chess-gnu.html' title='my first time to win computer chess (GNU Chess)'/><author><name>Michael I. Buen</name><uri>http://www.blogger.com/profile/10894538708565915450</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><media:thumbnail xmlns:media='http://search.yahoo.com/mrss/' url='http://bp0.blogger.com/_VDdbclMuay0/SHr66NhsGsI/AAAAAAAAAAY/OkFT04jM5p4/s72-c/i+win%21.png' height='72' width='72'/><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-26185192.post-538604866810380659</id><published>2008-07-03T14:10:00.006+08:00</published><updated>2008-07-14T16:01:23.078+08:00</updated><title type='text'>simple rant: a CRUD day</title><content type='html'>i don't get all these trend about using stored procs for CRUD. i'm not a web app programmer, i mostly develop desktop apps. how to code stored procs for header and detail tables (textboxes above and datagridview below) ?&lt;br /&gt;&lt;br /&gt;i would use stored procs for reports. but for desktop app CRUD routines? 怎么办 (how to do?)&lt;br /&gt;&lt;br /&gt;using stored procs for simple database chores such as CRUD have an architecture astronaut ring to me. that's my impression of stored proc'd CRUDs&lt;br /&gt;&lt;br /&gt;if there's a way to pass the delta of the dataset (header and detail tables) directly to just one stored proc (create procedure invoice_save @invoice_rec_id varchar, @invoice_header table, @invoice_detail table), i might use SP for CRUD&lt;br /&gt;&lt;br /&gt;i learned that ms sql 2005 allow passing XML(passing list of rows is now allowed) to stored procs,  so passing of header and detail to SP is now allowed, but then again, another problem persists, for datagrid's changes, does mssql 2005 accepts datatable's delta (datagrid's changes) through XML?&lt;br /&gt;&lt;br /&gt;is enforcing business logic on C# middletier not elegant? Remoting and Webservices can also act as stored proc for rdbms(es) which don't have stored proc. it's also centrally managed, no need to re-distribute the business logic changes to other apps (desktop or otherwise).  and also, these middle-tier stuffs are more amenable to horizontal scaling&lt;br /&gt;&lt;br /&gt;in this age of B2B, do you think enterprise communicate to each other directly via stored procs? web services, ajax, remoting, etc also have these "centrally-managed" aspect in it&lt;br /&gt;&lt;br /&gt;with all these great facilitators (dcom, rds, java rmi, .net web services, .net remoting) of centrally-managed code, would you still use stored procs?&lt;br /&gt;&lt;br /&gt;why app programmers of the world, why do you code your CRUD on stored proc? do middletiers doesn't cut it?&lt;br /&gt;&lt;br /&gt;P.S.&lt;br /&gt;i will not convert my datagridview to to textboxes, users find datagrid editing very intuitive. would you like to earn the ire of my programs' users? :-)&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/26185192-538604866810380659?l=mssql-to-postgresql.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://mssql-to-postgresql.blogspot.com/feeds/538604866810380659/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=26185192&amp;postID=538604866810380659' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/26185192/posts/default/538604866810380659'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/26185192/posts/default/538604866810380659'/><link rel='alternate' type='text/html' href='http://mssql-to-postgresql.blogspot.com/2008/07/simple-rant-crud-day.html' title='simple rant: a CRUD day'/><author><name>Michael I. Buen</name><uri>http://www.blogger.com/profile/10894538708565915450</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-26185192.post-5577873654364505118</id><published>2008-04-24T10:43:00.003+08:00</published><updated>2008-04-26T12:57:17.364+08:00</updated><title type='text'>sql code readability</title><content type='html'>though logically the same, this is easier to read:&lt;br /&gt;&lt;br /&gt;  select coalesce(&lt;br /&gt;              (select  CURRENT_TIMESTAMP &gt; unlocked_expiration&lt;br /&gt;              from invoice&lt;br /&gt;              where invoice_rec_id = 'value here'), true) as has_expired&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;than this:&lt;br /&gt;&lt;br /&gt;  select coalesce(&lt;br /&gt;              (select  unlocked_expiration  &lt; CURRENT_TIMESTAMP&lt;br /&gt;              from invoice&lt;br /&gt;              where invoice_rec_id = 'value here'),  true) as has_expired&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/26185192-5577873654364505118?l=mssql-to-postgresql.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://mssql-to-postgresql.blogspot.com/feeds/5577873654364505118/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=26185192&amp;postID=5577873654364505118' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/26185192/posts/default/5577873654364505118'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/26185192/posts/default/5577873654364505118'/><link rel='alternate' type='text/html' href='http://mssql-to-postgresql.blogspot.com/2008/04/sql-code-readability.html' title='sql code readability'/><author><name>Michael I. Buen</name><uri>http://www.blogger.com/profile/10894538708565915450</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-26185192.post-1423828520380095202</id><published>2008-03-28T23:37:00.008+08:00</published><updated>2008-04-26T12:58:42.557+08:00</updated><title type='text'>NULL should be renamed to UNKNOWN</title><content type='html'>problem:&lt;br /&gt;&lt;p style="margin-bottom: 0in;"&gt;the following has no output when it should has:&lt;br /&gt;&lt;/p&gt;&lt;p style="margin-bottom: 0in;"&gt;select * from src where n not in (select n from dst);&lt;br /&gt;&lt;/p&gt;&lt;br /&gt;&lt;p style="margin-bottom: 0in;"&gt;&lt;br /&gt;&lt;/p&gt;&lt;p style="margin-bottom: 0in;"&gt;reproduce the problem:&lt;br /&gt;&lt;/p&gt;  &lt;p style="margin-bottom: 0in;"&gt;&lt;/p&gt;   &lt;p style="margin-bottom: 0in;"&gt;&lt;br /&gt;create table src (n int);&lt;/p&gt;&lt;br /&gt;create table dst(n int); &lt;p style="margin-bottom: 0in;"&gt;&lt;/p&gt;&lt;p style="margin-bottom: 0in;"&gt;&lt;br /&gt;&lt;/p&gt;    &lt;p style="margin-bottom: 0in;"&gt;insert into src(n) values(1),(2),(3),(4),(5);&lt;br /&gt;insert into dst(n) values(1),(2),(3);&lt;br /&gt;select * from src where n not in (select n from dst); -- lists 4, 5&lt;/p&gt;   &lt;p style="margin-bottom: 0in;"&gt;insert into dst(n) values(null);&lt;br /&gt;select * from src where n not in (select n from dst); -- no output&lt;/p&gt; &lt;p style="margin-bottom: 0in;"&gt;&lt;/p&gt;&lt;p style="margin-bottom: 0in;"&gt;&lt;br /&gt;&lt;/p&gt;&lt;p style="margin-bottom: 0in;"&gt;should list 4 and 5?&lt;/p&gt; &lt;p style="margin-bottom: 0in;"&gt;&lt;br /&gt;&lt;/p&gt; &lt;p style="margin-bottom: 0in;"&gt;  &lt;/p&gt;  &lt;p style="margin-bottom: 0in;"&gt;it isn't, it didn't list anything&lt;/p&gt; &lt;p style="margin-bottom: 0in;"&gt;&lt;br /&gt;&lt;/p&gt; &lt;p style="margin-bottom: 0in;"&gt;hmm.. seems strange&lt;/p&gt; &lt;p style="margin-bottom: 0in;"&gt;&lt;br /&gt;&lt;/p&gt; &lt;p style="margin-bottom: 0in;"&gt;&lt;br /&gt;&lt;/p&gt; &lt;p style="margin-bottom: 0in;"&gt;explanation:&lt;/p&gt; &lt;p style="border-style: none none solid; border-color: -moz-use-text-color -moz-use-text-color rgb(0, 102, 204); border-width: medium medium 1px; padding: 0in 0in 0.02in; margin-bottom: 0in;"&gt;&lt;br /&gt;&lt;br /&gt;SELECT 'SEE' WHERE 7 IN (SELECT  2  UNION SELECT 4)&lt;br /&gt;is a shorthand for:&lt;br /&gt;SELECT 'SEE' WHERE 7 = 2 OR 7 = 4&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;SELECT 'SEE' WHERE 7 NOT IN (SELECT  2  UNION SELECT 4)&lt;br /&gt;is a shorthand for:&lt;br /&gt;SELECT 'SEE' WHERE 7 &lt;&gt; 2 AND 7 &lt;&gt; 4&lt;/p&gt;&lt;br /&gt;&lt;p style="border-style: none none solid; border-color: -moz-use-text-color -moz-use-text-color rgb(0, 102, 204); border-width: medium medium 1px; padding: 0in 0in 0.02in; margin-bottom: 0in;"&gt;&lt;br /&gt;&lt;/p&gt;&lt;br /&gt;now let's inject NULL data :&lt;br /&gt;&lt;br /&gt;SELECT 'SEE' WHERE 7 IN (SELECT  2  UNION SELECT 4 UNION SELECT NULL)&lt;br /&gt;is a shorthand for:&lt;br /&gt;SELECT 'SEE' WHERE 7 = 2 OR 7 = 4 or 7 = NULL&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;SELECT 'SEE' WHERE 7 NOT IN (SELECT  2  UNION SELECT 4 UNION SELECT NULL)&lt;br /&gt;is a shorthand for:&lt;br /&gt;SELECT 'SEE' WHERE 7 &lt;&gt; 2 AND 7 &lt;&gt; 4 AND 7 &lt;&gt; NULL&lt;br /&gt;&lt;p style="border-style: none none solid; border-color: -moz-use-text-color -moz-use-text-color rgb(0, 102, 204); border-width: medium medium 1px; padding: 0in 0in 0.02in; margin-bottom: 0in;"&gt;&lt;br /&gt;&lt;br /&gt;you cannot compare NULL, null means unknown.  by the virtue of it being unknown, the expression 7 &lt;&gt; NULL is neither true nor false. the expression 7 = NULL, is also neither true nor false.   7 &lt;&gt;  NULL: NULL,  7 = NULL: NULL.&lt;br /&gt;&lt;br /&gt;100 &lt;&gt; 500 results true&lt;br /&gt;100 = 500 results false&lt;br /&gt;100 &lt;&gt; NULL results NULL&lt;br /&gt;100 = NULL results NULL&lt;br /&gt;100 IS NULL results false&lt;br /&gt;100 IS NOT NULL results true&lt;br /&gt;NULL = NULL results NULL&lt;br /&gt;NULL &lt;&gt; NULL results NULL&lt;br /&gt;&lt;br /&gt;NULL AND NULL results NULL&lt;br /&gt;NULL AND TRUE results NULL&lt;br /&gt;TRUE AND NULL results NULL&lt;br /&gt;TRUE AND TRUE results TRUE&lt;br /&gt;&lt;br /&gt;NULL OR NULL results NULL&lt;br /&gt;NULL OR TRUE results TRUE&lt;br /&gt;TRUE OR NULL results TRUE&lt;br /&gt;TRUE OR TRUE results TRUE&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;NULL represents unknown, cannot be determined, not yet determined&lt;br /&gt;&lt;br /&gt;rough analogy, think of WHERE A &lt;&gt; B as left hand &lt;&gt; right hand&lt;br /&gt;&lt;br /&gt;WHERE 100  &lt;&gt; NULL&lt;br /&gt;&lt;br /&gt;in plain english: is the left hand holding 100 dollar bill different with the bill in the closed right hand?&lt;br /&gt;&lt;br /&gt;so what's the answer for 100 &lt;&gt; NULL?&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;let's have an example, your friend approach you, his/her left hand is open, you can see 100 bucks, right hand is close, you can't see anything.  then he/she asks you if right hand holds different bill than the left hand, what would you(or any human) would answer? a human would answer, "it's different", "not different", or could answer "i don't know"&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;while on a computer, when you ask it, the computer's answer is always "i don't know."  there is no gratification for a computer to guess correctly.  perhaps, it's human nature to try to predict unknown forces, like trying to gauge if himself is capable of ESP, of capable of correctly predicting things 9 out of 10, or inclined in believing in divination(god will help me correctly guess so i can win this Deal or No Deal).  humans are constantly feeding on gratifications, be it a guessing game, or trying their "pure luck"(believing in divination sometimes plays a large role here).  so much for digressing :-)&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;so there we go, computers can't guess, it always answer "i don't know" when faced with unknown.&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;so the answer for the expression WHERE 100 &lt;&gt; NULL is unknown. neither true nor false.  100 = NULL is also unknown, neither true nor false.  perhaps, RDBMS vendors should rename NULL to UNKNOWN.&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;let's try replacing NULL with UNKNOWN&lt;br /&gt;&lt;br /&gt;100 &lt;&gt; 500 results true&lt;br /&gt;100 = 500 results false&lt;br /&gt;100 &lt;&gt; UNKNOWN results UNKNOWN&lt;br /&gt;100 = UNKNOWN results UNKNOWN&lt;br /&gt;100 IS UNKNOWN results false&lt;br /&gt;100 IS NOT UNKNOWN results true&lt;br /&gt;UNKNOWN = UNKNOWN results UNKNOWN&lt;br /&gt;UNKNOWN &lt;&gt; UNKNOWN results UNKNOWN&lt;br /&gt;&lt;br /&gt;UNKNOWN AND UNKNOWN results UNKNOWN&lt;br /&gt;UNKNOWN AND TRUE results UNKNOWN&lt;br /&gt;TRUE AND UNKNOWN results UNKNOWN&lt;br /&gt;TRUE AND TRUE results TRUE&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;UNKNOWN OR UNKNOWN results UNKNOWN&lt;br /&gt;UNKNOWN OR TRUE results TRUE&lt;br /&gt;TRUE OR UNKNOWN results TRUE&lt;br /&gt;TRUE OR TRUE results TRUE&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;there it is, more intuitive, perhaps when reading NULL, mentally read them as UNKNOWN.&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;these would not return a row:&lt;br /&gt;SELECT * FROM employee WHERE FALSE&lt;br /&gt;SELECT * FROM employee WHERE NULL&lt;br /&gt;&lt;br /&gt;this could:&lt;br /&gt;SELECT * FROM employe WHERE TRUE&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;let's go back on NOT IN construct:&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;SELECT 'SEE' WHERE 7 NOT IN (SELECT  2  UNION SELECT 4 UNION SELECT NULL)&lt;br /&gt;is a shorthand for:&lt;br /&gt;SELECT 'SEE' WHERE 7 &lt;&gt; 2 AND 7 &lt;&gt; 4 AND 7 &lt;&gt; NULL&lt;br /&gt;&lt;br /&gt;resolved to:&lt;br /&gt;SELECT 'SEE' WHERE TRUE AND TRUE AND NULL&lt;br /&gt;&lt;br /&gt;then resolved to:&lt;br /&gt;SELECT 'SEE' WHERE NULL&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;final result: no output&lt;/p&gt;&lt;p style="border-style: none none solid; border-color: -moz-use-text-color -moz-use-text-color rgb(0, 102, 204); border-width: medium medium 1px; padding: 0in 0in 0.02in; margin-bottom: 0in;"&gt;&lt;br /&gt;&lt;/p&gt;&lt;p style="border-style: none none solid; border-color: -moz-use-text-color -moz-use-text-color rgb(0, 102, 204); border-width: medium medium 1px; padding: 0in 0in 0.02in; margin-bottom: 0in;"&gt;perhaps the rationale for using the word NULL is because it sounds so computer-sciencey.   as for not using UNKNOWN as an identifier for er.. unknown, heck, it's hard to read double negatives, NOT UNKNOWN, oh.. humour me  :-)&lt;/p&gt;&lt;p style="border-style: none none solid; border-color: -moz-use-text-color -moz-use-text-color rgb(0, 102, 204); border-width: medium medium 1px; padding: 0in 0in 0.02in; margin-bottom: 0in;"&gt;&lt;br /&gt;but... all  these arguments can be thrown out of the window when mainstream programming languages like c# go the extra mile to make null comparisons not much to fuzz about:&lt;/p&gt;&lt;br /&gt;&lt;p style="border-style: none none solid; border-color: -moz-use-text-color -moz-use-text-color rgb(0, 102, 204); border-width: medium medium 1px; padding: 0in 0in 0.02in; margin-bottom: 0in;"&gt;int? a = 7;&lt;br /&gt;&lt;/p&gt;&lt;p style="border-style: none none solid; border-color: -moz-use-text-color -moz-use-text-color rgb(0, 102, 204); border-width: medium medium 1px; padding: 0in 0in 0.02in; margin-bottom: 0in;"&gt;int? b = null;&lt;/p&gt;&lt;br /&gt;&lt;p style="border-style: none none solid; border-color: -moz-use-text-color -moz-use-text-color rgb(0, 102, 204); border-width: medium medium 1px; padding: 0in 0in 0.02in; margin-bottom: 0in;"&gt;if (a != null) Console.WriteLine("indeed");&lt;/p&gt;&lt;p style="border-style: none none solid; border-color: -moz-use-text-color -moz-use-text-color rgb(0, 102, 204); border-width: medium medium 1px; padding: 0in 0in 0.02in; margin-bottom: 0in;"&gt;if (a != b) Console.WriteLine("still indeed");&lt;/p&gt;&lt;br /&gt;&lt;br /&gt;programmer's life is sometimes difficult, impedance mismatches on our array of tools are in your face.  our  tools sometimes deviates from what we expected and asserted them to be.  nonetheless we're constantly humbly learning to deal and get over with it.&lt;br /&gt;&lt;br /&gt;&lt;p style="border-style: none none solid; border-color: -moz-use-text-color -moz-use-text-color rgb(0, 102, 204); border-width: medium medium 1px; padding: 0in 0in 0.02in; margin-bottom: 0in;"&gt;which led me to the conclusion that i shouldn't have written these IS NULL, IS NOT NULL stuffs in the first place :-)&lt;br /&gt;&lt;/p&gt;&lt;br /&gt;&lt;p style="border-style: none none solid; border-color: -moz-use-text-color -moz-use-text-color rgb(0, 102, 204); border-width: medium medium 1px; padding: 0in 0in 0.02in; margin-bottom: 0in;"&gt;&lt;br /&gt;&lt;/p&gt;&lt;p style="border-style: none none solid; border-color: -moz-use-text-color -moz-use-text-color rgb(0, 102, 204); border-width: medium medium 1px; padding: 0in 0in 0.02in; margin-bottom: 0in;"&gt;// Buen&lt;/p&gt;&lt;br /&gt;&lt;p style="border-style: none none solid; border-color: -moz-use-text-color -moz-use-text-color rgb(0, 102, 204); border-width: medium medium 1px; padding: 0in 0in 0.02in; margin-bottom: 0in;"&gt;&lt;span style=";font-family:arial;font-size:85%;"  &gt;&lt;span style="color: rgb(0, 0, 128);"&gt;&lt;b&gt;Einstein argued that there must be simplified explanations of nature, because God is not capricious or arbitrary. No such faith comforts the software engineer.&lt;br /&gt;- Fred Brooks, Jr.&lt;/b&gt;&lt;/span&gt;&lt;/span&gt;&lt;/p&gt;&lt;p style="border-style: none none solid; border-color: -moz-use-text-color -moz-use-text-color rgb(0, 102, 204); border-width: medium medium 1px; padding: 0in 0in 0.02in; margin-bottom: 0in;"&gt;&lt;br /&gt;&lt;/p&gt;&lt;p style="border-style: none none solid; border-color: -moz-use-text-color -moz-use-text-color rgb(0, 102, 204); border-width: medium medium 1px; padding: 0in 0in 0.02in; margin-bottom: 0in;"&gt;&lt;/p&gt;&lt;br /&gt;P.S.&lt;br /&gt;&lt;p style="border-style: none none solid; border-color: -moz-use-text-color -moz-use-text-color rgb(0, 102, 204); border-width: medium medium 1px; padding: 0in 0in 0.02in; margin-bottom: 0in;"&gt;i'd go on a limb here to generalize VB.NET's inconsistency(for the nth time), it's the MySQL of imperative programming languages.   null handling in vb.net is basically broken.   but perhaps one of VB.NET's few redeeming factor is its block-structured constructs, e.g. If-EndIf, Select-EndSelect, For-Next, Do-Loop, etc, they are very programmer-friendly, i wish C# use those block delineators instead of curly brackets&lt;br /&gt;&lt;/p&gt; &lt;p style="border-style: none none solid; border-color: -moz-use-text-color -moz-use-text-color rgb(0, 102, 204); border-width: medium medium 1px; padding: 0in 0in 0.02in; margin-bottom: 0in;"&gt;&lt;br /&gt;&lt;/p&gt;&lt;br /&gt;string s = null;&lt;br /&gt;if (s == "") MessageBox.Show("Not Good"); else MessageBox.Show("Good");&lt;br /&gt;// outputs Good&lt;br /&gt;&lt;br /&gt;Dim s As String&lt;br /&gt;s = Nothing&lt;br /&gt;If s = "" Then&lt;br /&gt;    MessageBox.Show("Broken")&lt;br /&gt;Else&lt;br /&gt;    MessageBox.Show("Expect Nothing")&lt;br /&gt;End If&lt;br /&gt;' outputs Broken&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/26185192-1423828520380095202?l=mssql-to-postgresql.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://mssql-to-postgresql.blogspot.com/feeds/1423828520380095202/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=26185192&amp;postID=1423828520380095202' title='5 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/26185192/posts/default/1423828520380095202'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/26185192/posts/default/1423828520380095202'/><link rel='alternate' type='text/html' href='http://mssql-to-postgresql.blogspot.com/2008/03/null-should-be-renamed-to-unknown.html' title='NULL should be renamed to UNKNOWN'/><author><name>Michael I. Buen</name><uri>http://www.blogger.com/profile/10894538708565915450</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>5</thr:total></entry><entry><id>tag:blogger.com,1999:blog-26185192.post-2508069171301765758</id><published>2007-12-24T09:31:00.000+08:00</published><updated>2007-12-24T09:48:36.257+08:00</updated><title type='text'>How to detect if field's value changes in UPDATE trigger</title><content type='html'>use this approach if your field is NOT NULLable:&lt;br /&gt;&lt;br /&gt;if NEW.field &lt;&gt; OLD.field then&lt;br /&gt;    raise notice 'something changes';&lt;br /&gt;end if;&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;use this approach if your field is NULLable:  # is the XOR of Postgres&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;false xor false = false&lt;br /&gt;false xor true = true&lt;br /&gt;true xor false = true&lt;br /&gt;true xor true = false&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;if&lt;br /&gt;&lt;br /&gt;( (NEW.field is null)::int # (OLD.field is null)::int ) = 1&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;OR&lt;br /&gt;&lt;br /&gt;NEW.field &lt;&gt; OLD.field&lt;br /&gt;&lt;br /&gt;then&lt;br /&gt;&lt;br /&gt;    raise notice 'something changes';&lt;br /&gt;&lt;br /&gt;end if;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/26185192-2508069171301765758?l=mssql-to-postgresql.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://mssql-to-postgresql.blogspot.com/feeds/2508069171301765758/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=26185192&amp;postID=2508069171301765758' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/26185192/posts/default/2508069171301765758'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/26185192/posts/default/2508069171301765758'/><link rel='alternate' type='text/html' href='http://mssql-to-postgresql.blogspot.com/2007/12/how-to-detect-if-fields-value-changes.html' title='How to detect if field&apos;s value changes in UPDATE trigger'/><author><name>Michael I. Buen</name><uri>http://www.blogger.com/profile/10894538708565915450</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-26185192.post-2447466165739798643</id><published>2007-12-22T19:42:00.000+08:00</published><updated>2007-12-22T19:43:40.491+08:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='mssql-compatible functions'/><category scheme='http://www.blogger.com/atom/ns#' term='functions'/><title type='text'>Calculate Age or Birthday</title><content type='html'>CREATE or replace FUNCTION CalculateAge(m_birthday date, m_datetoday date)&lt;br /&gt;RETURNS INTEGER&lt;br /&gt;AS&lt;br /&gt;$$&lt;br /&gt;BEGIN&lt;br /&gt;&lt;br /&gt;RETURN&lt;br /&gt;&lt;br /&gt;(&lt;br /&gt;select&lt;br /&gt;(&lt;br /&gt;CASE&lt;br /&gt;WHEN dateadd('y', datediff ('y', m_birthday, m_datetoday), m_birthday) &gt; m_datetoday&lt;br /&gt;THEN datediff ('y', m_birthday, m_datetoday) - 1&lt;br /&gt;ELSE datediff ('y', m_birthday, m_datetoday)&lt;br /&gt;END)&lt;br /&gt;&lt;br /&gt;);&lt;br /&gt;&lt;br /&gt;END;&lt;br /&gt;$$ language 'plpgsql';&lt;br /&gt;&lt;br /&gt;-- see my previous post on dateadd and datediff&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/26185192-2447466165739798643?l=mssql-to-postgresql.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://mssql-to-postgresql.blogspot.com/feeds/2447466165739798643/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=26185192&amp;postID=2447466165739798643' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/26185192/posts/default/2447466165739798643'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/26185192/posts/default/2447466165739798643'/><link rel='alternate' type='text/html' href='http://mssql-to-postgresql.blogspot.com/2007/12/calculate-age-or-birthday.html' title='Calculate Age or Birthday'/><author><name>Michael I. Buen</name><uri>http://www.blogger.com/profile/10894538708565915450</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-26185192.post-4679256989764329456</id><published>2007-12-22T19:02:00.000+08:00</published><updated>2007-12-22T19:03:23.664+08:00</updated><title type='text'>gapless sequence without using extra table, however, locks the whole table</title><content type='html'>CREATE OR REPLACE FUNCTION get_tryrecord_lastid()&lt;br /&gt;  RETURNS int AS&lt;br /&gt;$$&lt;br /&gt;begin&lt;br /&gt;lock table tryrecord;&lt;br /&gt;return coalesce( (select tryrecord_id from tryrecord order by tryrecord_id desc limit 1) + 1, 1 );&lt;br /&gt;end;&lt;br /&gt;$$&lt;br /&gt;LANGUAGE 'plpgsql' ;&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;CREATE TABLE tryrecord&lt;br /&gt;(&lt;br /&gt;  tryrecord_id integer NOT NULL DEFAULT get_tryrecord_lastid(),&lt;br /&gt;  name character varying(20) NOT NULL,&lt;br /&gt;  CONSTRAINT pk_tryrecord PRIMARY KEY (tryrecord_id)&lt;br /&gt;);&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;begin;&lt;br /&gt;insert into tryrecord(name) values('see');&lt;br /&gt;insert into tryrecord(name) values('great');&lt;br /&gt;end;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/26185192-4679256989764329456?l=mssql-to-postgresql.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://mssql-to-postgresql.blogspot.com/feeds/4679256989764329456/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=26185192&amp;postID=4679256989764329456' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/26185192/posts/default/4679256989764329456'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/26185192/posts/default/4679256989764329456'/><link rel='alternate' type='text/html' href='http://mssql-to-postgresql.blogspot.com/2007/12/gapless-sequence-without-using-extra.html' title='gapless sequence without using extra table, however, locks the whole table'/><author><name>Michael I. Buen</name><uri>http://www.blogger.com/profile/10894538708565915450</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-26185192.post-6830112864868065234</id><published>2007-12-22T18:55:00.000+08:00</published><updated>2007-12-22T18:56:17.710+08:00</updated><title type='text'>gapless sequence without locking the whole table</title><content type='html'>create table therecord_lastid&lt;br /&gt;(&lt;br /&gt;last_id int default 0,&lt;br /&gt;constraint pk_therecord_lastid primary key (last_id)&lt;br /&gt;);&lt;br /&gt;insert into therecord_lastid values(0);&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;CREATE OR REPLACE FUNCTION get_therecord_lastid()&lt;br /&gt;  RETURNS int AS&lt;br /&gt;$$&lt;br /&gt;declare&lt;br /&gt;x int;&lt;br /&gt;begin&lt;br /&gt;update therecord_lastid set last_id = last_id + 1;&lt;br /&gt;select last_id into x from therecord_lastid;&lt;br /&gt;return x;&lt;br /&gt;end;&lt;br /&gt;$$&lt;br /&gt;LANGUAGE 'plpgsql' ;&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;CREATE TABLE testrecord&lt;br /&gt;(&lt;br /&gt;  testrecord_id integer NOT NULL DEFAULT get_therecord_lastid(),&lt;br /&gt;  name character varying(20) NOT NULL,&lt;br /&gt;  CONSTRAINT pk_testrecord PRIMARY KEY (testrecord_id)&lt;br /&gt;);&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;begin;&lt;br /&gt;insert into testrecord(name) values('see');&lt;br /&gt;insert into testrecord(name) values('great');&lt;br /&gt;end;&lt;br /&gt;&lt;br /&gt;select * from testrecord&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/26185192-6830112864868065234?l=mssql-to-postgresql.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://mssql-to-postgresql.blogspot.com/feeds/6830112864868065234/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=26185192&amp;postID=6830112864868065234' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/26185192/posts/default/6830112864868065234'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/26185192/posts/default/6830112864868065234'/><link rel='alternate' type='text/html' href='http://mssql-to-postgresql.blogspot.com/2007/12/gapless-sequence-without-locking-whole.html' title='gapless sequence without locking the whole table'/><author><name>Michael I. Buen</name><uri>http://www.blogger.com/profile/10894538708565915450</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-26185192.post-21571096191170105</id><published>2007-12-22T11:48:00.000+08:00</published><updated>2007-12-22T12:44:29.062+08:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='foreign keys'/><category scheme='http://www.blogger.com/atom/ns#' term='not null'/><title type='text'>if you have many foreign keys, how to check that only one of them must not be null</title><content type='html'>create table shipment&lt;br /&gt;(&lt;br /&gt;int seq serial bigint not null&lt;br /&gt;&lt;br /&gt;client_code,&lt;br /&gt;supplier_code,&lt;br /&gt;testing_company_code,&lt;br /&gt;branch_code,&lt;br /&gt;&lt;br /&gt;date_to_ship date,&lt;br /&gt;&lt;br /&gt;constraint  fk_shipment__client foreign key(client_code)&lt;br /&gt;references client(client_code),&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;constraint  fk_shipment__supplier foreign key(supplier_code)&lt;br /&gt;references supplier(supplier_code),&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;constraint  fk_shipment__testing_company foreign key(testing_company_code)&lt;br /&gt;references testing_company(testing_company_code),&lt;br /&gt;&lt;br /&gt;constraint  fk_shipment__branch foreign key(branch_code)&lt;br /&gt;references branch(branch_code),&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;constraint ck_shipment__company check&lt;br /&gt;(&lt;br /&gt; (client_code is not null)::int&lt;br /&gt; +&lt;br /&gt; (supplier_code is not null)::int&lt;br /&gt; +&lt;br /&gt; (testing_company_code is not null)::int&lt;br /&gt; +&lt;br /&gt; (branch_code is not null)::int&lt;br /&gt;) = 1&lt;br /&gt;&lt;br /&gt;);&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;Note: if you only need to test at least one of them is not null, just change = 1 to &gt;= 1.&lt;br /&gt;&lt;br /&gt;or better yet, do this:&lt;br /&gt;&lt;br /&gt;constraint ck_shipment__company&lt;br /&gt;&lt;br /&gt;check(coalesce(client_code, supplier_code, testing_company_code, branch_code) is not null)&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/26185192-21571096191170105?l=mssql-to-postgresql.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://mssql-to-postgresql.blogspot.com/feeds/21571096191170105/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=26185192&amp;postID=21571096191170105' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/26185192/posts/default/21571096191170105'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/26185192/posts/default/21571096191170105'/><link rel='alternate' type='text/html' href='http://mssql-to-postgresql.blogspot.com/2007/12/if-you-many-foreign-keys-how-to-check.html' title='if you have many foreign keys, how to check that only one of them must not be null'/><author><name>Michael I. Buen</name><uri>http://www.blogger.com/profile/10894538708565915450</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-26185192.post-8656672541266570908</id><published>2007-12-22T11:34:00.000+08:00</published><updated>2007-12-22T15:36:38.645+08:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='windows benchmark'/><category scheme='http://www.blogger.com/atom/ns#' term='linux'/><category scheme='http://www.blogger.com/atom/ns#' term='intel core 2 duo'/><category scheme='http://www.blogger.com/atom/ns#' term='amd 64'/><title type='text'>Postgres is faster on Windows XP (on AMD only)</title><content type='html'>(an e-mail i wrote to my peers last july 23, guess it's better to post this on blog.  these are all out-of-the-box installation)&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;this is my latest benchmarkings from fresh installation i performed last saturday, i use the same drive on all installation.  Western Digital 160 GB.&lt;br /&gt;&lt;br /&gt;Core 2 Duo E6700 2.67 GHz vs AMD64 3400+ 2.0 GHz&lt;br /&gt;&lt;br /&gt;select * from technicalsheet_color order by techsheetid, recid limit 100:&lt;br /&gt;&lt;br /&gt;c2d 2.67 GHz + ubuntu64 + postgresql = 6 seconds&lt;br /&gt;amd64 2.0 GHz + ubuntu64 + postgresql = 9 seconds&lt;br /&gt;&lt;br /&gt;c2d 2.67 GHz + winxp32  + postgresql = 12 seconds&lt;br /&gt;amd64 2.0 GHz + winxp32  + postgresql = 8 seconds&lt;br /&gt;&lt;a href="javascript:void(0)" tabindex="10" onclick="return false;"&gt;&lt;span&gt;&lt;/span&gt;&lt;/a&gt;&lt;br /&gt;&lt;br /&gt;as you can gloss from the results, the speed differences are not symmetrical, it is hard to draw one-sweeping-generalization™ conclusion ;-)&lt;br /&gt;&lt;br /&gt;PostgreSQL on laptops doesn't bodes well for Windows XP users, because most of laptops are Core 2 Duo than AMD64/Turion.  PostgreSQL runs two times faster on Core 2 Duo if you only use Linux.&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;upsides:&lt;br /&gt;* PostgreSQL on Ubuntu64 is faster on Core 2 Duo&lt;br /&gt;* PostgreSQL on WinXP32 is faster on AMD64&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;downsides:&lt;br /&gt;* PostgreSQL on WinXP32 slow down miserably on Core 2 Duo&lt;br /&gt;* on AMD64, PostgreSQL is only marginally faster on WinXP32 than its Ubuntu64 counterpart&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;it is interesting to note that Dave Cutler (Windows NT,2000,XP,Vista,2003 chief engineer) have work closely/co-developed AMD64 with AMD, this could explain why Windows runs marginally better on AMD64 than Core 2 Duo.&lt;br /&gt;and also, maybe AMD64 or Athlon X2 runs better than Core 2 Duo, i haven't yet pitted AMD's 2.6 GHz against Core 2 Duo 2.67 GHz :-)&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;on Core 2 Duo, WinXP to Ubuntu gains two-fold speed increase, from 12 seconds to 6 seconds.  at first, i thought AMD64 WinXP to Ubuntu would gain the same twofold speed increase (8 to 4 seconds), but as i benchmarked it, it wasn't.&lt;br /&gt;AMD64 seems was designed to run Windows well, Microsoft's Dave Cutler and AMD collaboration at its best :-)&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;just draw your own conclusions :-)&lt;br /&gt;&lt;br /&gt;i'll just choose the best combo of processor + os + database&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;p.s.&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;these are the results without the ordering:&lt;br /&gt;&lt;br /&gt;select * from technicalsheet_color limit 100:&lt;br /&gt;&lt;br /&gt;c2d 2.67 GHz + ubuntu64 + postgresql = 11 seconds&lt;br /&gt;amd64 2.0 GHz + ubuntu64 + postgresql = 16 seconds&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;c2d 2.67 GHz + winxp32  + postgresql = 19 seconds&lt;br /&gt;amd64 2.0 GHz + winxp32  + postgresql = 13 seconds&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/26185192-8656672541266570908?l=mssql-to-postgresql.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://mssql-to-postgresql.blogspot.com/feeds/8656672541266570908/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=26185192&amp;postID=8656672541266570908' title='2 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/26185192/posts/default/8656672541266570908'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/26185192/posts/default/8656672541266570908'/><link rel='alternate' type='text/html' href='http://mssql-to-postgresql.blogspot.com/2007/12/postgres-is-faster-on-windows-xp-on-amd.html' title='Postgres is faster on Windows XP (on AMD only)'/><author><name>Michael I. Buen</name><uri>http://www.blogger.com/profile/10894538708565915450</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>2</thr:total></entry><entry><id>tag:blogger.com,1999:blog-26185192.post-6021570469599722777</id><published>2007-12-20T15:42:00.000+08:00</published><updated>2007-12-22T01:31:48.453+08:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='customer'/><category scheme='http://www.blogger.com/atom/ns#' term='last balance'/><category scheme='http://www.blogger.com/atom/ns#' term='last row'/><category scheme='http://www.blogger.com/atom/ns#' term='balance'/><category scheme='http://www.blogger.com/atom/ns#' term='of each group'/><category scheme='http://www.blogger.com/atom/ns#' term='mssql'/><category scheme='http://www.blogger.com/atom/ns#' term='postgres'/><category scheme='http://www.blogger.com/atom/ns#' term='account'/><title type='text'>get the last balance of each account.  select the last row of each group</title><content type='html'>get the last balance of each account&lt;br /&gt;&lt;br /&gt;MS SQL-way:&lt;br /&gt;&lt;br /&gt;select * from&lt;br /&gt;accountinstallmentfinancingdetailfinal detail&lt;br /&gt;&lt;br /&gt;inner join (select invoiceno, pmtstructureno, max (paymentsortorder) as lastrow&lt;br /&gt;group by invoiceno, pmtstructureno) as  lastrowofaccount&lt;br /&gt;&lt;br /&gt;on lastrowofaccount.invoiceno = detail.invoiceno and lastrowofaccount.pmtstructureno = detail.pmtstructureno and lastrowofaccount.lastrow = detail.paymentsortorder;&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;PostgreSQL way (note: the above would also work on PostgreSQL):&lt;br /&gt;&lt;br /&gt;select distinct on (invoiceno, pmtstructureno) *&lt;br /&gt;from accountinstallmentfinancingdetailfinal&lt;br /&gt;order by invoiceno, pmtstructureno, paymentsortorder desc;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/26185192-6021570469599722777?l=mssql-to-postgresql.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://mssql-to-postgresql.blogspot.com/feeds/6021570469599722777/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=26185192&amp;postID=6021570469599722777' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/26185192/posts/default/6021570469599722777'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/26185192/posts/default/6021570469599722777'/><link rel='alternate' type='text/html' href='http://mssql-to-postgresql.blogspot.com/2007/12/get-last-balance-of-each-account.html' title='get the last balance of each account.  select the last row of each group'/><author><name>Michael I. Buen</name><uri>http://www.blogger.com/profile/10894538708565915450</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-26185192.post-7028401011328472192</id><published>2007-12-19T23:10:00.000+08:00</published><updated>2007-12-27T16:57:51.765+08:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='mysql'/><category scheme='http://www.blogger.com/atom/ns#' term='group_concat'/><category scheme='http://www.blogger.com/atom/ns#' term='postgres'/><category scheme='http://www.blogger.com/atom/ns#' term='postgresql'/><title type='text'>PostgreSQL GROUP_CONCAT</title><content type='html'>CREATE TABLE produk&lt;br /&gt;(&lt;br /&gt;seq_id serial NOT NULL,&lt;br /&gt;name character varying(100) NOT NULL&lt;br /&gt;) ;&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;INSERT INTO produk (seq_id, name) VALUES (1, 'beer');&lt;br /&gt;INSERT INTO produk (seq_id, name) VALUES (2, 'in');&lt;br /&gt;INSERT INTO produk (seq_id, name) VALUES (3, 'beer');&lt;br /&gt;INSERT INTO produk (seq_id, name) VALUES (4, 'can');&lt;br /&gt;INSERT INTO produk (seq_id, name) VALUES (6, 'goods');&lt;br /&gt;INSERT INTO produk (seq_id, name) VALUES (7, 'goods');&lt;br /&gt;INSERT INTO produk (seq_id, name) VALUES (8, 'can');&lt;br /&gt;INSERT INTO produk (seq_id, name) VALUES (5, 'goods');&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;&lt;span style="font-family:courier new;"&gt; name            | id_of_duplicates&lt;/span&gt;&lt;br /&gt;&lt;span style="font-family:courier new;"&gt;----------+------------------&lt;/span&gt;&lt;br /&gt;&lt;span style="font-family:courier new;"&gt; beer            | 1,3&lt;/span&gt;&lt;br /&gt;&lt;span style="font-family:courier new;"&gt; can               | 4,8&lt;/span&gt;&lt;br /&gt;&lt;span style="font-family:courier new;"&gt; goods         | 5,6,7&lt;/span&gt;&lt;br /&gt;&lt;span style="font-family:courier new;"&gt; in                  | 2&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;MySQL:&lt;br /&gt;&lt;br /&gt;select name, group_concat(cast(seq_id as char))  as id_of_duplicates&lt;br /&gt;from produk&lt;br /&gt;group by name&lt;br /&gt;order by name;&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;PostgreSQL:&lt;br /&gt;&lt;br /&gt;create aggregate array_accum (&lt;br /&gt;sfunc = array_append,&lt;br /&gt;basetype = anyelement,&lt;br /&gt;stype = anyarray,&lt;br /&gt;initcond = '{}'&lt;br /&gt;);&lt;br /&gt;&lt;br /&gt;CREATE OR REPLACE FUNCTION _group_concat(text, text)&lt;br /&gt;RETURNS text AS $$&lt;br /&gt;SELECT CASE&lt;br /&gt;WHEN $2 IS NULL THEN $1&lt;br /&gt;WHEN $1 IS NULL THEN $2&lt;br /&gt;ELSE $1 operator(pg_catalog.||) ',' operator(pg_catalog.||) $2&lt;br /&gt;END&lt;br /&gt;$$ IMMUTABLE LANGUAGE SQL;&lt;br /&gt;&lt;br /&gt;CREATE AGGREGATE group_concat (&lt;br /&gt;BASETYPE = text,&lt;br /&gt;SFUNC = _group_concat,&lt;br /&gt;STYPE = text&lt;br /&gt;);&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;first approach:&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;select name, array_accum(seq_id)&lt;br /&gt;from produk&lt;br /&gt;group by name&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;select name, array_to_string(array_accum(seq_id), ',')&lt;br /&gt;from produk&lt;br /&gt;group by name;&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;second approach (mysql-compatible approach):&lt;br /&gt;&lt;br /&gt;select name, group_concat(seq_id) as id_of_duplicates&lt;br /&gt;from produk&lt;br /&gt;group by name&lt;br /&gt;order by name;&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;MySQL GROUP_CONCAT with ordering:&lt;br /&gt;&lt;br /&gt;select name, group_concat(cast(seq_id as char) order by seq_id) as id_of_duplicates&lt;br /&gt;from produk&lt;br /&gt;group by name&lt;br /&gt;order by name;&lt;br /&gt;&lt;br /&gt;PostgreSQL equivalent:&lt;br /&gt;&lt;br /&gt;select name, group_concat(distinct seq_id) as id_of_duplicates&lt;br /&gt;from produk&lt;br /&gt;group by name&lt;br /&gt;order by name;&lt;br /&gt;&lt;br /&gt;using customized sort:&lt;br /&gt;&lt;br /&gt;select name, group_concat(seq_id) as id_of_duplicates&lt;br /&gt;from&lt;br /&gt;(&lt;br /&gt;select name, seq_id&lt;br /&gt;from produk&lt;br /&gt;order by name, seq_id&lt;br /&gt;) as x&lt;br /&gt;group by name&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;PostgreSQL is a lot more nicer, it allows you to define your own aggregate function. GROUP_CONCAT is not built-in, with user-defined aggregate, you can create one.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/26185192-7028401011328472192?l=mssql-to-postgresql.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://mssql-to-postgresql.blogspot.com/feeds/7028401011328472192/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=26185192&amp;postID=7028401011328472192' title='9 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/26185192/posts/default/7028401011328472192'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/26185192/posts/default/7028401011328472192'/><link rel='alternate' type='text/html' href='http://mssql-to-postgresql.blogspot.com/2007/12/cool-groupconcat.html' title='PostgreSQL GROUP_CONCAT'/><author><name>Michael I. Buen</name><uri>http://www.blogger.com/profile/10894538708565915450</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>9</thr:total></entry><entry><id>tag:blogger.com,1999:blog-26185192.post-2253569521621641985</id><published>2007-12-19T19:32:00.000+08:00</published><updated>2007-12-19T21:25:51.709+08:00</updated><title type='text'>return the number of digits after the decimal point</title><content type='html'>select greatest(length(abs(x - trunc(x))) - 2, 0) from (values (12345.12345::numeric), (1234.1234::numeric), (1234.123::numeric), (-1234.123::numeric), (1::numeric), (10::numeric)) as t(x);&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;select length(regexp_replace(x::numeric::text,'^.*\\.',''))&lt;br /&gt;from&lt;br /&gt;(values (12345.12345::numeric), (1234.1234::numeric), (-1234.123::numeric)) as t(x);&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/26185192-2253569521621641985?l=mssql-to-postgresql.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://mssql-to-postgresql.blogspot.com/feeds/2253569521621641985/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=26185192&amp;postID=2253569521621641985' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/26185192/posts/default/2253569521621641985'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/26185192/posts/default/2253569521621641985'/><link rel='alternate' type='text/html' href='http://mssql-to-postgresql.blogspot.com/2007/12/return-number-of-digits-after-decimal.html' title='return the number of digits after the decimal point'/><author><name>Michael I. Buen</name><uri>http://www.blogger.com/profile/10894538708565915450</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-26185192.post-3823134322369535068</id><published>2007-12-19T01:33:00.000+08:00</published><updated>2008-01-29T00:32:01.726+08:00</updated><title type='text'>updates in PostgreSQL, MS SQL, MySQL</title><content type='html'>PostgreSQL: OK, MS SQL: OK (just change the "||" to "+"), MySQL: NOT OK&lt;br /&gt;&lt;br /&gt;/* very orthogonal, since only one table allowed to be updated, no need to "set product.name  = ", "set name = " will do.  in fact if you do: "set product.name = ", postgres will disallow it */&lt;br /&gt;&lt;br /&gt;update product set name = product.name || ':' || prod.name&lt;br /&gt;from prod&lt;br /&gt;where prod.seq_id = product.seq_id&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;**********************************&lt;br /&gt;&lt;br /&gt;PostgreSQL: OK (good for re-using  existing query, must alias the source table if the source table conflicts with to-be-updated-table name), MS SQL: NOT OK, MySQL: NOT OK&lt;br /&gt;&lt;br /&gt;update product set name = product.name || ':' || prod.name&lt;br /&gt;from product x&lt;br /&gt;inner join prod on prod.seq_id = x.seq_id&lt;br /&gt;where x.seq_id = product.seq_id    /* don't forget to filter the to-be-updated-table to queried rows */&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;**********************************&lt;br /&gt;&lt;br /&gt;PostgreSQL: NOT OK, MS SQL: NOT OK, MySQL: OK&lt;br /&gt;&lt;br /&gt;/* note: even only one possible table can be updated, requires the fully-qualified name for the fields to be set.  i.e. set product.name */&lt;br /&gt;&lt;br /&gt;update product,prod set product.name = concat(product.name, ':', prod.name)&lt;br /&gt;where prod.seq_id = product.seq_id;&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;**********************************&lt;br /&gt;&lt;br /&gt;PostgreSQL: NOT OK, MS SQL: OK (good for re-using existing query), MySQL: NOT OK&lt;br /&gt;&lt;br /&gt;update product set name = product.name + ':' + prod.name&lt;br /&gt;from product /* limitation: cannot alias this, must be same name as the table to-be-updated */&lt;br /&gt;inner join prod on prod.seq_id = product.seq_id&lt;br /&gt;&lt;br /&gt;***********************************&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;PostgreSQL: NOT OK, MS SQL: NOT OK, MySQL: OK&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;update product join prod on prod.seq_id = product.seq_id&lt;br /&gt;&lt;br /&gt;set product.name = concat(product.name, ':', prod.name);&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;/* nice update syntax, not ambiguous as MS SQL.  similar to MS Access update join syntax, though not as good/flexible as PostgreSQL, wherein you can just drop-in your previous query to the update statement */&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/26185192-3823134322369535068?l=mssql-to-postgresql.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://mssql-to-postgresql.blogspot.com/feeds/3823134322369535068/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=26185192&amp;postID=3823134322369535068' title='1 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/26185192/posts/default/3823134322369535068'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/26185192/posts/default/3823134322369535068'/><link rel='alternate' type='text/html' href='http://mssql-to-postgresql.blogspot.com/2007/12/updates-in-postgresql-ms-sql-mysql.html' title='updates in PostgreSQL, MS SQL, MySQL'/><author><name>Michael I. Buen</name><uri>http://www.blogger.com/profile/10894538708565915450</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>1</thr:total></entry><entry><id>tag:blogger.com,1999:blog-26185192.post-3831068572498846293</id><published>2007-12-19T00:57:00.001+08:00</published><updated>2009-01-31T15:28:02.434+08:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='mysql'/><category scheme='http://www.blogger.com/atom/ns#' term='mssql'/><category scheme='http://www.blogger.com/atom/ns#' term='deleting duplicates'/><category scheme='http://www.blogger.com/atom/ns#' term='postgres'/><category scheme='http://www.blogger.com/atom/ns#' term='postgresql'/><category scheme='http://www.blogger.com/atom/ns#' term='ms sql'/><title type='text'>delete duplicate rows (and leaving one copy)  in PostgreSQL, MS SQL, MySQL</title><content type='html'>MS SQL: OK, PostgreSQL: OK, MySQL: NOT OK&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;delete from product where&lt;br /&gt;seq_id not in (select min(seq_id) from product group by name)&lt;br /&gt;&lt;br /&gt;to make this work with MySQL:&lt;br /&gt;&lt;br /&gt;delete from product where&lt;br /&gt;seq_id not in (select x from (select min(seq_id) as x from product group by name))&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;MS SQL: OK, PostgreSQL: OK, MySQL: NOT OK&lt;br /&gt;&lt;br /&gt;delete from product where&lt;br /&gt;exists(select * from product x where x.name = product.name and product.seq_id &gt; x.seq_id)&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;MS SQL: OK, PostgreSQL: OK, MySQL: NOT OK&lt;br /&gt;&lt;br /&gt;/* advantage: can re-use existing query's logic for finding the first record of each duplicate */&lt;br /&gt;&lt;br /&gt;delete from product where&lt;br /&gt;exists(&lt;br /&gt;select x.name, min(x.seq_id)&lt;br /&gt;from product x&lt;br /&gt;where x.name = product.name&lt;br /&gt;group by x.name&lt;br /&gt;having product.seq_id &gt; min(x.seq_id)&lt;br /&gt;);&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;MS SQL: OK, PostgreSQL: NOT OK, MySQL: OK&lt;br /&gt;&lt;br /&gt;delete product from product&lt;br /&gt;inner join product b&lt;br /&gt;on b.name = product.name and product.seq_id &gt; b.seq_id;&lt;br /&gt;&lt;br /&gt;MS SQL: OK, PostgreSQL: OK, MySQL: NOT OK&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;delete from product where&lt;br /&gt;seq_id in&lt;br /&gt;(select a.seq_id from product a inner join product b on a.name = b.name and a.seq_id &gt; b.seq_id);&lt;br /&gt;&lt;br /&gt;to make this work with MySQL:&lt;br /&gt;&lt;br /&gt;delete from product where&lt;br /&gt;seq_id in&lt;br /&gt;(select x from (select a.seq_id as x from product a inner join product b on a.name = b.name and a.seq_id &gt; b.seq_id));&lt;br /&gt;&lt;br /&gt;/* MySQL is pretty lame in this last case */&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/26185192-3831068572498846293?l=mssql-to-postgresql.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://mssql-to-postgresql.blogspot.com/feeds/3831068572498846293/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=26185192&amp;postID=3831068572498846293' title='11 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/26185192/posts/default/3831068572498846293'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/26185192/posts/default/3831068572498846293'/><link rel='alternate' type='text/html' href='http://mssql-to-postgresql.blogspot.com/2007/12/deleting-duplicates-in-postgresql-ms.html' title='delete duplicate rows (and leaving one copy)  in PostgreSQL, MS SQL, MySQL'/><author><name>Michael I. Buen</name><uri>http://www.blogger.com/profile/10894538708565915450</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>11</thr:total></entry><entry><id>tag:blogger.com,1999:blog-26185192.post-2670184786085831502</id><published>2007-12-08T14:13:00.000+08:00</published><updated>2007-12-23T00:43:13.864+08:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='mssql-compatible functions'/><title type='text'>PostgreSQL DATEDIFF</title><content type='html'>version 8.0 and higher&lt;br /&gt;&lt;br /&gt;CREATE OR REPLACE FUNCTION datediff(p_Interval "varchar", p_DateFrom date, p_DateTo date)&lt;br /&gt;RETURNS int4 AS&lt;br /&gt;$BODY$&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;BEGIN&lt;br /&gt;  if p_Interval = 'm' then&lt;br /&gt;      return ((date_part('y',p_DateTo) * 12) + date_part('month',p_DateTo))&lt;br /&gt;          - ((date_part('y',p_DateFrom) * 12) + date_part('month',p_DateFrom));&lt;br /&gt;  elseif p_Interval = 'y' then&lt;br /&gt;      return date_part('y',p_DateTo) - date_part('y',p_DateFrom);&lt;br /&gt;  else&lt;br /&gt;      raise exception 'Datediff: Interval not supported';&lt;br /&gt;      return 0;&lt;br /&gt;  end if;&lt;br /&gt;END;&lt;br /&gt;$BODY$&lt;br /&gt;LANGUAGE 'plpgsql' VOLATILE;&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;before version 8.0&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;CREATE OR REPLACE FUNCTION datediff("varchar", date, date)&lt;br /&gt;RETURNS int4 AS&lt;br /&gt;$BODY$&lt;br /&gt;&lt;br /&gt;DECLARE&lt;br /&gt;  p_Interval ALIAS FOR $1;&lt;br /&gt;  p_DateFrom ALIAS FOR $2;&lt;br /&gt;  p_DateTo ALIAS FOR $3;&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;BEGIN&lt;br /&gt;  if p_Interval = 'm' then&lt;br /&gt;      return ((date_part('y',p_DateTo) * 12) + date_part('month',p_DateTo))&lt;br /&gt;          - ((date_part('y',p_DateFrom) * 12) + date_part('month',p_DateFrom));&lt;br /&gt;  elseif p_Interval = 'y' then&lt;br /&gt;      return date_part('y',p_DateTo) - date_part('y',p_DateFrom);&lt;br /&gt;  else&lt;br /&gt;      raise exception 'Datediff: Interval not supported';&lt;br /&gt;      return 0;&lt;br /&gt;  end if;&lt;br /&gt;END;&lt;br /&gt;$BODY$&lt;br /&gt;LANGUAGE 'plpgsql' VOLATILE;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/26185192-2670184786085831502?l=mssql-to-postgresql.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://mssql-to-postgresql.blogspot.com/feeds/2670184786085831502/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=26185192&amp;postID=2670184786085831502' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/26185192/posts/default/2670184786085831502'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/26185192/posts/default/2670184786085831502'/><link rel='alternate' type='text/html' href='http://mssql-to-postgresql.blogspot.com/2007/12/postgresqls-datediff.html' title='PostgreSQL DATEDIFF'/><author><name>Michael I. Buen</name><uri>http://www.blogger.com/profile/10894538708565915450</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-26185192.post-115385077931976707</id><published>2006-07-26T02:04:00.000+08:00</published><updated>2006-07-26T02:06:19.330+08:00</updated><title type='text'>faster join</title><content type='html'>-- slower 91.56%&lt;br /&gt;alter PROCEDURE x_Financing_ExceedsDueDate&lt;br /&gt;@NumberOfDays INT&lt;br /&gt;AS&lt;br /&gt;DECLARE @DayToday DATETIME&lt;br /&gt;SET @DayToday = convert(DATETIME,CONvERT(vARCHAR,GETDATE(),112))&lt;br /&gt;SELECT&lt;br /&gt;X.InvoiceNo, X.PmtStructureNo, Fullname = COALESCE(i.Fullname, m.FUllname),D.PayImmediately,&lt;br /&gt;DaysExceed = DATEDIFF(DAY, BillStatementDate, @DayToday)&lt;br /&gt;FROM  AccountInstallmentFinancingDetailFinal DINNER JOIN    (   SELECT    InvoiceNo, PmtStructureNo, PSO = MAX(PaymentSortOrder)   FROM AccountInstallmentFinancingDetailFinal    GROUP BY InvoiceNo, PmtStructureNo   ) XON X.InvoiceNo = D.InvoiceNoAND X.PmtStructureNo = D.PmtStructureNoAND X.PSO = D.PaymentSortOrder&lt;br /&gt;LEFT JOIN  AccountInstallment ION    I.InvoiceNo = D.InvoiceNo&lt;br /&gt;LEFT JOIN  Financing_AssumptionOfMortgage mON   m.AoMNo = d.InvoiceNo&lt;br /&gt;WHERE   D.BillStatementDate IS NOT NULL   AND @DayToday &gt;= BillStatementDate +  @NumberOfDays    AND PayImmediately &gt; 0&lt;br /&gt;&lt;br /&gt;-- faster 8.44%&lt;br /&gt;&lt;br /&gt;ALTER  PROCEDURE Financing_ExceedsDueDate&lt;br /&gt;@NumberOfDays INT&lt;br /&gt;AS&lt;br /&gt;DECLARE @DayToday DATETIME&lt;br /&gt;SET @DayToday = convert(DATETIME,CONvERT(vARCHAR,GETDATE(),112))&lt;br /&gt;SELECT&lt;br /&gt;X.InvoiceNo, X.PmtStructureNo, A.Fullname,D.PayImmediately,&lt;br /&gt;DaysExceed = DATEDIFF(DAY, BillStatementDate, @DayToday)&lt;br /&gt;FROM  AccountInstallmentFinancingDetailFinal DINNER JOIN    (   SELECT    InvoiceNo, PmtStructureNo, PSO = MAX(PaymentSortOrder)   FROM AccountInstallmentFinancingDetailFinal    GROUP BY InvoiceNo, PmtStructureNo   ) XON X.InvoiceNo = D.InvoiceNoAND X.PmtStructureNo = D.PmtStructureNoAND X.PSO = D.PaymentSortOrder&lt;br /&gt;INNER JOIN  (   SELECT  InvoiceNo, Fullname   FROM AccountInstallment   UNION   SELECT  AoMNo, Fullname   FROM  Financing_AssumptionOfMortgage   ) A&lt;br /&gt;ON   X.InvoiceNo = A.InvoiceNo&lt;br /&gt;WHERE   D.BillStatementDate IS NOT NULL   AND @DayToday &gt;= BillStatementDate +  @NumberOfDays    AND PayImmediately &gt; 0&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/26185192-115385077931976707?l=mssql-to-postgresql.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://mssql-to-postgresql.blogspot.com/feeds/115385077931976707/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=26185192&amp;postID=115385077931976707' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/26185192/posts/default/115385077931976707'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/26185192/posts/default/115385077931976707'/><link rel='alternate' type='text/html' href='http://mssql-to-postgresql.blogspot.com/2006/07/faster-join.html' title='faster join'/><author><name>Michael I. Buen</name><uri>http://www.blogger.com/profile/10894538708565915450</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-26185192.post-114754825054987269</id><published>2006-05-14T03:24:00.000+08:00</published><updated>2006-05-14T03:24:10.596+08:00</updated><title type='text'>math of reproduction</title><content type='html'>&lt;a href="http://slashdot.org/articles/03/05/15/0613212.shtml"&gt;Slashdot | Review: Matrix: Reloaded&lt;/a&gt;: "About 12 people reproducing into 250,000 people so quickly: yes this is possible. if each woman produced as many children as could be done healthily, say 10, you could do it in 4 new generations:&lt;br /&gt;&lt;br /&gt;7 women * 10 children = 70 new ppl.&lt;br /&gt;70 * 0.5 (50% girls) * 10 children = 350 new ppl.&lt;br /&gt;350 *0.5 *10 = +1750 ppl.&lt;br /&gt;8750 *0.5*10 = +43750 ppl.&lt;br /&gt;218750 *0.5*10 = +218750 ppl.&lt;br /&gt;&lt;br /&gt;12 + 70 + 350 + 1750 + 43750 + 218750&lt;br /&gt;= 273,420 ppl.&lt;br /&gt;&lt;br /&gt;Figure the early generation eventually dies off, and some other die off as well, you could get close to 250k easily in 100 years.&lt;br /&gt;&lt;br /&gt;Assuming the first 12 are near child-bearing age when they start, and the bulk of the ppl get the reproducing done around age 25, 25*4 = 100 years."&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/26185192-114754825054987269?l=mssql-to-postgresql.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://mssql-to-postgresql.blogspot.com/feeds/114754825054987269/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=26185192&amp;postID=114754825054987269' title='1 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/26185192/posts/default/114754825054987269'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/26185192/posts/default/114754825054987269'/><link rel='alternate' type='text/html' href='http://mssql-to-postgresql.blogspot.com/2006/05/math-of-reproduction.html' title='math of reproduction'/><author><name>Michael I. Buen</name><uri>http://www.blogger.com/profile/10894538708565915450</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>1</thr:total></entry><entry><id>tag:blogger.com,1999:blog-26185192.post-114714585771301432</id><published>2006-05-09T11:37:00.000+08:00</published><updated>2006-05-09T11:37:37.886+08:00</updated><title type='text'></title><content type='html'>&lt;a href="http://it.slashdot.org/it/06/05/08/0428248.shtml"&gt;Slashdot | Computer Security, The Next 50 Years&lt;/a&gt;: &lt;br /&gt;&lt;br /&gt;&lt;br /&gt;Educating users&lt;br /&gt;(Score:5, Insightful)&lt;br /&gt;by reldruH (956292) on Monday May 08, @02:31AM (#15283657)&lt;br /&gt;(Last Journal: Wednesday May 03, @05:24PM)&lt;br /&gt;What the article is basically saying is that we have to teach people how to use their computers. &gt;85% of all the computer problems I encounter are PEBKAC (Problem Exists Between Keyboard And Chair). It's like the old saying goes, make something idiot proof and the world will make a better idiot. If people just learn how to use their computers (you shouldn't download exe's from people you don't know, a firewall is a good thing to have, ActiveX controls aren't safe and your default response shouldn't be to install them no matter what IE says) a huge number of problems would be eliminated. Like it or not, users are the biggest computer problem today. The problem shouldn't be usability, it should be user-ability.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/26185192-114714585771301432?l=mssql-to-postgresql.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://mssql-to-postgresql.blogspot.com/feeds/114714585771301432/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=26185192&amp;postID=114714585771301432' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/26185192/posts/default/114714585771301432'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/26185192/posts/default/114714585771301432'/><link rel='alternate' type='text/html' href='http://mssql-to-postgresql.blogspot.com/2006/05/slashdot-computer-security-next-50.html' title=''/><author><name>Michael I. Buen</name><uri>http://www.blogger.com/profile/10894538708565915450</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-26185192.post-114701911152486503</id><published>2006-05-08T00:25:00.000+08:00</published><updated>2006-05-08T00:25:12.206+08:00</updated><title type='text'></title><content type='html'>&lt;a href="http://quoteworld.org/quotes/168"&gt;Intolerance of ambiguity is the mark of an authoritarian personality. Theodor Wiesengrund Adorno Quotation&lt;/a&gt;: "'Intolerance of ambiguity is the mark of an authoritarian personality.' -&lt;br /&gt;  --  Theodor Wiesengrund Adorno "&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/26185192-114701911152486503?l=mssql-to-postgresql.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://mssql-to-postgresql.blogspot.com/feeds/114701911152486503/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=26185192&amp;postID=114701911152486503' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/26185192/posts/default/114701911152486503'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/26185192/posts/default/114701911152486503'/><link rel='alternate' type='text/html' href='http://mssql-to-postgresql.blogspot.com/2006/05/intolerance-of-ambiguity-is-mark-of.html' title=''/><author><name>Michael I. Buen</name><uri>http://www.blogger.com/profile/10894538708565915450</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-26185192.post-114549882496562847</id><published>2006-04-20T09:27:00.000+08:00</published><updated>2006-04-20T10:15:23.673+08:00</updated><title type='text'>scalar function</title><content type='html'>&lt;pre&gt;&lt;span style="font-size:130%;"&gt;&lt;br /&gt;MS SQL:&lt;br /&gt;&lt;br /&gt; CREATE FUNCTION FormatterNumber(@i INT) RETURNS VARCHAR(100)&lt;br /&gt; AS&lt;br /&gt; BEGIN&lt;br /&gt;  RETURN CONVERT(VARCHAR, @i) + ' Michael the INT'&lt;br /&gt; END&lt;br /&gt; GO&lt;br /&gt; SELECT DBO.FormatterNumber(1)&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;&lt;/span&gt;&lt;/pre&gt;&lt;span style="font-size:130%;"&gt;&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;PostgreSQL: Note postresql parameter can be also be referenced by their parameter number, e.g. $1, $2..&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;&lt;/span&gt; &lt;pre&gt;&lt;span style="font-size:130%;"&gt;&lt;br /&gt;&lt;br /&gt; -- we don't have to declare varchar(100) in returns clause,&lt;br /&gt; -- it is adjusted dynamically&lt;br /&gt; &lt;br /&gt; -- method 1, this is flexible, can have multiple statements between begin and end&lt;br /&gt; CREATE FUNCTION FormatterNumber(m_I INT) RETURNS VARCHAR&lt;br /&gt; AS&lt;br /&gt; $$&lt;br /&gt; BEGIN&lt;br /&gt;  RETURN m_I::VARCHAR || ' INT';&lt;br /&gt;  -- RETURN $1::VARCHAR || ' INT'; -- this will work too&lt;br /&gt; END;&lt;br /&gt; $$&lt;br /&gt; LANGUAGE 'plpgsql';&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;&lt;br /&gt; -- method 2, just change the language 'plpgsql' to 'sql',&lt;br /&gt; -- this is rigid, only one statement is allowed,&lt;br /&gt; -- though seems this is much faster(haven't yet benchmark against method 1)&lt;br /&gt;&lt;br /&gt; -- somehow the disadvantage of language 'sql' is you can't refer the paramater&lt;br /&gt; -- by its name, only by parameter position&lt;br /&gt;&lt;br /&gt; CREATE FUNCTION FormatterNumber(m_I INT) RETURNS VARCHAR&lt;br /&gt; AS&lt;br /&gt; $$&lt;br /&gt;  SELECT $1::VARCHAR || ' Michael the INT';&lt;br /&gt; $$&lt;br /&gt; LANGUAGE 'sql';&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;&lt;br /&gt; invocation sample:&lt;br /&gt;&lt;br /&gt; SELECT FormatterNumber(1);&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;MS SQL:&lt;br /&gt;&lt;br /&gt; CREATE FUNCTION FormatterDate(@d DATETIME) RETURNS VARCHAR(100)&lt;br /&gt; AS&lt;br /&gt; BEGIN&lt;br /&gt;  RETURN CONVERT(VARCHAR, @d) + ' Michael the date'&lt;br /&gt; END&lt;br /&gt; GO&lt;br /&gt;&lt;br /&gt; SELECT DBO.FormatterDate('November 5 2006')&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;PostgreSQL:&lt;br /&gt;&lt;br /&gt;  &lt;br /&gt;&lt;br /&gt; -- method 1&lt;br /&gt; CREATE OR REPLACE FUNCTION FormatterDate(m_D DATE) RETURNS VARCHAR&lt;br /&gt; AS&lt;br /&gt; $$&lt;br /&gt; BEGIN&lt;br /&gt;  RETURN m_D::VARCHAR || ' Michael the date';&lt;br /&gt; END;&lt;br /&gt; $$&lt;br /&gt; LANGUAGE 'plpgsql';&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;&lt;br /&gt; -- method 2&lt;br /&gt; CREATE OR REPLACE FUNCTION FormatterDate(m_D DATE) RETURNS VARCHAR&lt;br /&gt; AS&lt;br /&gt; $$&lt;br /&gt;  SELECT $1::VARCHAR || ' Michael the date';&lt;br /&gt; $$&lt;br /&gt; LANGUAGE 'plpgsql';&lt;br /&gt;&lt;br /&gt;&lt;br /&gt; SELECT FormatterDate('November 5 2006')&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;&lt;/span&gt;&lt;/pre&gt;&lt;span style="font-size:130%;"&gt;&lt;br /&gt;&lt;br /&gt;PostgreSQL is much flexible in its function, you can overload function name,&lt;br /&gt;you can have two function with same name,&lt;br /&gt;resulting to a more symmetrical usage of function&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;&lt;/span&gt;&lt;pre&gt;&lt;span style="font-size:130%;"&gt;&lt;br /&gt;&lt;br /&gt; -- method 1&lt;br /&gt; CREATE OR REPLACE FUNCTION Formatter(m_I INT) RETURNS VARCHAR&lt;br /&gt; AS&lt;br /&gt; $$&lt;br /&gt; BEGIN&lt;br /&gt;  RETURN m_D::VARCHAR || ' Michael the INT';&lt;br /&gt; END;&lt;br /&gt; $$&lt;br /&gt; LANGUAGE 'plpgsql';&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;&lt;br /&gt; -- method 2&lt;br /&gt; CREATE OR REPLACE FUNCTION Formatter(m_D DATE) RETURNS VARCHAR&lt;br /&gt; AS&lt;br /&gt; $$&lt;br /&gt;  SELECT $1::VARCHAR || ' Michael the date';&lt;br /&gt; $$&lt;br /&gt; LANGUAGE 'plpgsql';&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;&lt;br /&gt; SELECT Formatter(1);&lt;br /&gt; SELECT Formatter('nov 5 2006');&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;&lt;/span&gt;&lt;/pre&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/26185192-114549882496562847?l=mssql-to-postgresql.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://mssql-to-postgresql.blogspot.com/feeds/114549882496562847/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=26185192&amp;postID=114549882496562847' title='1 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/26185192/posts/default/114549882496562847'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/26185192/posts/default/114549882496562847'/><link rel='alternate' type='text/html' href='http://mssql-to-postgresql.blogspot.com/2006/04/scalar-function.html' title='scalar function'/><author><name>Michael I. Buen</name><uri>http://www.blogger.com/profile/10894538708565915450</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>1</thr:total></entry><entry><id>tag:blogger.com,1999:blog-26185192.post-114543044694143804</id><published>2006-04-19T14:47:00.001+08:00</published><updated>2006-04-20T09:23:01.680+08:00</updated><title type='text'>best practice for avoiding dirty read</title><content type='html'>for transactions or triggers that do any data manipulation routine(UPDATE,DELETE,INSERT?), follow guideline below:&lt;br /&gt;&lt;br /&gt;for MS SQL, always put TABLOCKX in SELECT clause:&lt;br /&gt;SET @PreviousQty = (SELECT Qty FROM Inventory WITH(TABLOCKX) WHERE ItemCode = @ItemCode &lt;br /&gt;&lt;br /&gt;FOR PostgreSQL, always put FOR UPDATE in SELECT clause:&lt;br /&gt;m_PreviousQty = (SELECT Qty FROM Inventory WHERE ItemCode = m_ItemCode FOR UPDATE)&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;always put row lock even if the SELECTED will not be the table to be written upon. this will ensure preventing dirty reads from other concurrently executing transactions that modifies table you read.&lt;br /&gt;&lt;br /&gt;Note regarding row-level locking:&lt;br /&gt;WITH(TABLOCKX) and FOR UPDATE can also be used to implement row-level pessimistic locking for LAN-based application transactions, i.e. when you open one record, no other program can read the same record, this is called pessimistic locking.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/26185192-114543044694143804?l=mssql-to-postgresql.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://mssql-to-postgresql.blogspot.com/feeds/114543044694143804/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=26185192&amp;postID=114543044694143804' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/26185192/posts/default/114543044694143804'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/26185192/posts/default/114543044694143804'/><link rel='alternate' type='text/html' href='http://mssql-to-postgresql.blogspot.com/2006/04/best-practice-for-avoiding-dirty-read_19.html' title='best practice for avoiding dirty read'/><author><name>Michael I. Buen</name><uri>http://www.blogger.com/profile/10894538708565915450</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-26185192.post-114542730633618614</id><published>2006-04-19T14:15:00.000+08:00</published><updated>2006-04-19T14:15:06.373+08:00</updated><title type='text'>shared row locks</title><content type='html'>&lt;a href="http://blog.rezra.com/articles/2005/11/07/postgresql-8-1"&gt;PostgreSQL 8.1&lt;/a&gt;:&lt;br /&gt;&lt;br /&gt;Shared row locks are a very important performance improvement for PostgreSQL’s implementation of referential integrity. PostgreSQL uses triggers to implement foreign keys, and thus it acquires locks using SQL statements. Before PostgreSQL 8.1, this meant the least restrictive lock it could acquire was a row exclusive lock (using a SELECT … FOR UPDATE statement). With 8.1, the developers added a method of acquiring a shared row lock via SQL (SELECT … FOR SHARE). This greatly reduces the locking contention in databases with lots of referential integrity.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/26185192-114542730633618614?l=mssql-to-postgresql.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://mssql-to-postgresql.blogspot.com/feeds/114542730633618614/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=26185192&amp;postID=114542730633618614' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/26185192/posts/default/114542730633618614'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/26185192/posts/default/114542730633618614'/><link rel='alternate' type='text/html' href='http://mssql-to-postgresql.blogspot.com/2006/04/shared-row-locks.html' title='shared row locks'/><author><name>Michael I. Buen</name><uri>http://www.blogger.com/profile/10894538708565915450</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-26185192.post-114540876865733480</id><published>2006-04-19T08:29:00.000+08:00</published><updated>2007-12-22T17:34:48.331+08:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='mssql-compatible functions'/><category scheme='http://www.blogger.com/atom/ns#' term='functions'/><title type='text'>DATEADD</title><content type='html'>MSSQL&lt;br /&gt;select dateadd(year,1, '2004-2-29')&lt;br /&gt;&lt;br /&gt;PostgreSQL&lt;br /&gt;select '2004-2-29'::date + cast('1 years' as interval)&lt;br /&gt;&lt;br /&gt;MSSQL:&lt;br /&gt;select dateadd(month,1,'2004-2-29')&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;PostgreSQL:&lt;br /&gt;select '2004-2-29'::date + cast('1 months' as interval)&lt;br /&gt;&lt;br /&gt;MSSQL:&lt;br /&gt;select dateadd(month,1, '1976-11-05 6:29 AM')&lt;br /&gt;&lt;br /&gt;PostgreSQL:&lt;br /&gt;select '2006-01-06 6:29 AM'::timestamptz + cast('1 months' as interval)&lt;br /&gt;&lt;br /&gt;MSSQL:&lt;br /&gt;select dateadd(day,1,'2006-01-06 6:29 AM')&lt;br /&gt;-- or --&lt;br /&gt;select convert(datetime,'2006-01-06 6:29 AM') + 1&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;PosgtgreSQL:&lt;br /&gt;select '2006-01-06 6:29 AM'::timestamp + cast('1 days' as interval);&lt;br /&gt;&lt;br /&gt;------------------------------------------------------------------------&lt;br /&gt;------------------------------------------------------------------------&lt;br /&gt;MS SQL equivalent compatible DATEADD function&lt;br /&gt;&lt;br /&gt;-- Function: dateadd(character varying, integer, date)&lt;br /&gt;&lt;br /&gt;-- DROP FUNCTION dateadd(character varying, integer, date);&lt;br /&gt;&lt;br /&gt;CREATE OR REPLACE FUNCTION dateadd(character varying, integer, date)&lt;br /&gt;  RETURNS date AS&lt;br /&gt;$BODY$&lt;br /&gt;&lt;br /&gt;DECLARE&lt;br /&gt;    p_Interval ALIAS FOR $1;&lt;br /&gt;    p_N ALIAS FOR $2;&lt;br /&gt;    p_Date ALIAS FOR $3;&lt;br /&gt;   &lt;br /&gt;   &lt;br /&gt;&lt;br /&gt;&lt;br /&gt;BEGIN&lt;br /&gt;    if p_Interval = 'm' then&lt;br /&gt;        return p_Date + cast(p_N || ' months' as interval);&lt;br /&gt;    elseif p_Interval = 'y' then&lt;br /&gt;                return p_Date + cast(p_N || ' years' as interval);&lt;br /&gt;    else&lt;br /&gt;        raise exception 'dateadd interval parameter not supported';&lt;br /&gt;        -- raise exception 'hello';&lt;br /&gt;        return null;&lt;br /&gt;    end if;&lt;br /&gt;END;&lt;br /&gt;$BODY$&lt;br /&gt;  LANGUAGE 'plpgsql';&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/26185192-114540876865733480?l=mssql-to-postgresql.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://mssql-to-postgresql.blogspot.com/feeds/114540876865733480/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=26185192&amp;postID=114540876865733480' title='2 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/26185192/posts/default/114540876865733480'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/26185192/posts/default/114540876865733480'/><link rel='alternate' type='text/html' href='http://mssql-to-postgresql.blogspot.com/2006/04/dateadd.html' title='DATEADD'/><author><name>Michael I. Buen</name><uri>http://www.blogger.com/profile/10894538708565915450</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>2</thr:total></entry><entry><id>tag:blogger.com,1999:blog-26185192.post-114538331034726761</id><published>2006-04-19T01:55:00.000+08:00</published><updated>2006-04-19T02:01:50.346+08:00</updated><title type='text'>strip the time portion of the datetime</title><content type='html'>MS SQL:&lt;br /&gt;&lt;br /&gt;    select convert(datetime,convert(varchar,getdate(),101))&lt;br /&gt;&lt;br /&gt;PostgreSQL:&lt;br /&gt;&lt;br /&gt;    select now()::date&lt;br /&gt;&lt;br /&gt;ANSI SQL: PostgreSQL OK, MS SQL NOT&lt;br /&gt;&lt;br /&gt;    select cast(now() as date)&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/26185192-114538331034726761?l=mssql-to-postgresql.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://mssql-to-postgresql.blogspot.com/feeds/114538331034726761/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=26185192&amp;postID=114538331034726761' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/26185192/posts/default/114538331034726761'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/26185192/posts/default/114538331034726761'/><link rel='alternate' type='text/html' href='http://mssql-to-postgresql.blogspot.com/2006/04/strip-time-portion-of-datetime.html' title='strip the time portion of the datetime'/><author><name>Michael I. Buen</name><uri>http://www.blogger.com/profile/10894538708565915450</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-26185192.post-114538256242371390</id><published>2006-04-19T01:41:00.000+08:00</published><updated>2007-12-08T16:33:52.258+08:00</updated><title type='text'>current row version checking. uses for concurrency checking</title><content type='html'>MS SQL:&lt;br /&gt;&lt;br /&gt;-- checks if row has changed since last read&lt;br /&gt;CREATE TABLE Distributor&lt;br /&gt;(&lt;br /&gt;MemberId VARCHAR(20),&lt;br /&gt;TotalSales NUMERIC(10,2),&lt;br /&gt;ConcurrencyCheck TIMESTAMP -- not related to ANSI-SQL 92 timestamp&lt;br /&gt;)&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;INSERT INTO Distributor(MemberId, TotalSales) SELECT 'Michael',1000&lt;br /&gt;SELECT * FROM Distributor&lt;br /&gt;UPDATE Distributor SET TotalSales = TotalSales + 7000&lt;br /&gt;SELECT * FROM Distributor&lt;br /&gt;&lt;br /&gt;Here's the result:&lt;br /&gt;&lt;br /&gt;MemberId             TotalSales   ConcurrencyCheck  &lt;br /&gt;-------------------- ------------ ------------------&lt;br /&gt;Michael              1000.00      0x0000000000007C3B&lt;br /&gt;&lt;br /&gt;MemberId             TotalSales   ConcurrencyCheck  &lt;br /&gt;-------------------- ------------ ------------------&lt;br /&gt;Michael              8000.00      0x0000000000007C3C&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;---------------------------------------------------------&lt;br /&gt;&lt;br /&gt;PostgreSQL:&lt;br /&gt;&lt;br /&gt;Note: there's a hidden field in each postgres table, xmin for example, xmin changes every time changes happen in the row&lt;br /&gt;&lt;br /&gt;-- checks if row has changed since last read&lt;br /&gt;CREATE TABLE Distributor&lt;br /&gt;(&lt;br /&gt;MemberId VARCHAR(20),&lt;br /&gt;TotalSales NUMERIC(10,2)&lt;br /&gt;);&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;INSERT INTO Distributor(MemberId, TotalSales) SELECT 'Michael',1000;&lt;br /&gt;SELECT *, Distributor.xmin FROM Distributor;&lt;br /&gt;UPDATE Distributor SET TotalSales = TotalSales + 7000;&lt;br /&gt;SELECT *, Distributor.xmin FROM Distributor;&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;Here's the result:&lt;br /&gt;&lt;br /&gt; memberid | totalsales | xmin&lt;br /&gt;----------+------------+------&lt;br /&gt; Michael  |    1000.00 | 3624&lt;br /&gt;&lt;br /&gt; memberid | totalsales | xmin&lt;br /&gt;----------+------------+------&lt;br /&gt; Michael  |    8000.00 | 3639&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;to convert xmin to integer(thanks to AndrewSN, freenode #postgresql):  &lt;br /&gt;select int8in(xidout(xmin)) from company&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/26185192-114538256242371390?l=mssql-to-postgresql.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://mssql-to-postgresql.blogspot.com/feeds/114538256242371390/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=26185192&amp;postID=114538256242371390' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/26185192/posts/default/114538256242371390'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/26185192/posts/default/114538256242371390'/><link rel='alternate' type='text/html' href='http://mssql-to-postgresql.blogspot.com/2006/04/current-row-version-checking-uses-for.html' title='current row version checking. uses for concurrency checking'/><author><name>Michael I. Buen</name><uri>http://www.blogger.com/profile/10894538708565915450</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-26185192.post-114537857226530971</id><published>2006-04-19T00:38:00.000+08:00</published><updated>2007-12-08T14:35:10.325+08:00</updated><title type='text'>row-level locking, dirty-read prevention</title><content type='html'>&lt;span style="font-size:130%;"&gt;explicit row-level locking and its uses&lt;br /&gt;&lt;br /&gt;----------&lt;br /&gt;&lt;br /&gt;MS SQL: (have tested WITH(ROWLOCK) doesn't seems working)&lt;br /&gt;&lt;br /&gt;SET @m_Hit = @m_Hit + (SELECT hit FROM topleveldomainhits WITH(TABLOCKX) WHERE tldsuffix = '.com')&lt;br /&gt;&lt;br /&gt;SET @m_Hit = @m_Hit + 1&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;PostgreSQL:&lt;br /&gt;&lt;br /&gt;SELECT hit INTO m_Hit FROM topleveldomainhits WHERE tldsuffix = '.com' FOR UPDATE;&lt;br /&gt;&lt;br /&gt;m_Hit = m_Hit + 1;&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;--------------------------------------------------&lt;br /&gt;&lt;br /&gt;Note:&lt;br /&gt;&lt;br /&gt;Explicit row-Level Locking is not needed if you are not doing complex computation, for  the sake of discussion we just make an example of complex hit computation : )&lt;br /&gt;&lt;br /&gt;here is a non-complex hit computation:&lt;br /&gt;-- statement A:&lt;br /&gt;UPDATE topleveldomainhits SET hit = hit + 1 WHERE tldsuffix = '.com'&lt;br /&gt;&lt;br /&gt;row-level locking is needed if you are doing complex computation,e.g. passing result to variables(likely scenario is doing multi-line computation) and/or the possibility of parallel processing is very high.&lt;br /&gt;&lt;br /&gt;if the statement is is one just one line like statement A, and two users executed it in parallel and even you SET TRANSACTION ISOLATION LEVEL READ UNCOMMITTED, they won't overwrite each other's data. single statements are an atomic operation in itself. so pagehit = 2  :)&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;-------------------------------&lt;br /&gt;-------------------------------&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;another example of where explicit row-level locking is necessary, (complex computations)&lt;br /&gt;&lt;br /&gt;for the purpose of discussion let's say we have only column field which is Bonus and employeeid&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;--- bonus computation prodedure run once every month ---&lt;br /&gt;&lt;br /&gt;&lt;/span&gt;&lt;pre&gt;&lt;span style="font-size:130%;"&gt;&lt;br /&gt;&lt;br /&gt;TotalBonus = $80&lt;br /&gt;YearsWork 5&lt;br /&gt;MonthsCount = 3&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;Final TotalBonus = $1200&lt;br /&gt;&lt;br /&gt;MSSQL:&lt;br /&gt;&lt;br /&gt;computer A:&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;CREATE PROCEDURE ComputeYearBonus&lt;br /&gt;    @EmployeeID VARCHAR(8)&lt;br /&gt;AS&lt;br /&gt;    SET XACT_ABORT ON&lt;br /&gt;&lt;br /&gt;    BEGIN TRAN&lt;br /&gt;    DECLARE @YearsWork INT&lt;br /&gt;&lt;br /&gt;    SELECT @YearsWork = YearCount FROM YearsWork WITH(TABLOCKX) WHERE Employee =&lt;br /&gt;@EmployeeId&lt;br /&gt;&lt;br /&gt;    DECLARE @Bonus MONEY&lt;br /&gt;    -- process 1. read bonus = 80 * 5 = $400&lt;br /&gt;    SET @Bonus = (SELECT TotalBonus FROM Employee WITH(TABLOCKX) WHERE EmployeeId = @EmployeeId) * @YearsWork&lt;br /&gt;&lt;br /&gt;    -- process 4. write bonus = $400&lt;br /&gt;    UPDATE Employee SET TotalBonus = @Bonus WHERE EmployeeId = @EmployeeID&lt;br /&gt;&lt;br /&gt;    COMMIT TRAN&lt;br /&gt;GO&lt;br /&gt;&lt;br /&gt;computer B:&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;CREATE PROCEDURE ComputeMonthBonus&lt;br /&gt;    @EmployeeID VARCHAR(8)&lt;br /&gt;&lt;br /&gt;AS&lt;br /&gt;    SET XACT_ABORT ON&lt;br /&gt;    BEGIN TRAN&lt;br /&gt;&lt;br /&gt;    DECLARE @MonthsWork INT&lt;br /&gt;    SELECT @MonthsWork = MonthCount FROM MonthsWorkThisYear WITH(TABLOCKX) WHERE Employee = @EmployeeId&lt;br /&gt;&lt;br /&gt;    DECLARE @Bonus MONEY&lt;br /&gt;   &lt;br /&gt;    -- process 2. read bonus = 80 * 3 = $240    &lt;br /&gt;    SET @Bonus = (SELECT TotalBonus FROM Employee WITH(TABLOCKX) WHERE EmployeeId = @EmployeeId) * @MonthsWork&lt;br /&gt;&lt;br /&gt;    -- process 4. write bonus = $240&lt;br /&gt;    UPDATE Employee SET TotalBonus = @Bonus WHERE EmployeeId = @EmployeeID&lt;br /&gt;&lt;br /&gt;    COMMIT TRAN&lt;br /&gt;&lt;br /&gt;GO&lt;br /&gt;&lt;br /&gt;&lt;/span&gt;&lt;/pre&gt;&lt;span style="font-size:130%;"&gt;&lt;br /&gt;&lt;br /&gt;without WITH(TABLOCKX), when computer A and computer B execute at the same time, there is a high probability that they will overwrite each other's bonus computation. see the steps above. last written bonus is $240 instead of $1200.&lt;br /&gt;&lt;br /&gt;if we put WITH(TABLOCKX) in our SELECTs, process 2 will wait until computer A commits. after computer A commits, computer B will read the bonus as $400, then $400 multiply by $3 = $1200.&lt;br /&gt;&lt;br /&gt;same thing would effect even if computer B executes first, computer B computes $240. then computer A would read the bonus, computer A can't read the bonus until computer B commits. after B commits, computer A will read the bonus of $240. then computer A will multiply this by 5. $240 * 5 = $1200.&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;you'll notice that in the YearsWork in MonthsWorkThisYear we also put TABLOCKX, same thing, if there are other parallel executing transaction or trigger that updates the two table at the same time with our ComputeBonus. the computebonus will be messed, there will be dirty reads if there is no row-locking, we can't afford to have our bonus miscomputed.&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;------------------------------------------------&lt;br /&gt;&lt;br /&gt;&lt;/span&gt;&lt;pre&gt;&lt;span style="font-size:130%;"&gt;&lt;br /&gt;&lt;br /&gt;PostgreSQL Version:&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;postgresql has implicit BEGIN TRAN AND COMMIT TRAN:&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;CREATE FUNCTION ComputeYearBonus() RETURNS VOID&lt;br /&gt;    m_EmployeeID VARCHAR(8)&lt;br /&gt;AS&lt;br /&gt;$$&lt;br /&gt;DECLARE&lt;br /&gt;    m_YearsWork INT;&lt;br /&gt;    m_Bonus NUMERIC;&lt;br /&gt;BEGIN&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;    SELECT YearCount INTO m_YearsWork  FROM YearsWork WHERE Employee = m_EmployeeId FOR UPDATE;&lt;br /&gt;&lt;br /&gt;    -- process 1. read bonus = 80 * 5 = $400&lt;br /&gt;    m_Bonus = (SELECT TotalBonus FROM Employee WHERE EmployeeId = m_EmployeeId FOR UPDATE) * m_YearsWork;&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;    -- process 4. write bonus = $400&lt;br /&gt;    UPDATE Employee SET TotalBonus = m_Bonus WHERE EmployeeId = m_EmployeeID;&lt;br /&gt;&lt;br /&gt;END;&lt;br /&gt;$$&lt;br /&gt;LANGUAGE 'plpgsql';&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;CREATE FUNCTION ComputeMonthBonus() RETURNS VOID&lt;br /&gt;    m_EmployeeID VARCHAR(8)&lt;br /&gt;&lt;br /&gt;AS&lt;br /&gt;$$&lt;br /&gt;DECRLARE&lt;br /&gt;    m_MonthsWork INT&lt;br /&gt;    m_Bonus NUMERIC&lt;br /&gt;BEGIN&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;    SELECT MonthCount INTO m_MonthsWork FROM MonthsWorkThisYear WHERE Employee = m_EmployeeId FOR UPDATE;&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;    -- process 2. read bonus = 80 * 3 = $240    &lt;br /&gt;    m_Bonus = (SELECT TotalBonus FROM Employee WHERE EmployeeId = m_EmployeeId FOR UPDATE) * m_MonthsWork;&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;    -- process 4. write bonus = $240&lt;br /&gt;    UPDATE Employee SET TotalBonus = m_Bonus WHERE EmployeeId = m_EmployeeID;&lt;br /&gt;&lt;br /&gt;END;&lt;br /&gt;$$&lt;br /&gt;LANGUAGE 'plpgsql';&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;&lt;/span&gt;&lt;/pre&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/26185192-114537857226530971?l=mssql-to-postgresql.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://mssql-to-postgresql.blogspot.com/feeds/114537857226530971/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=26185192&amp;postID=114537857226530971' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/26185192/posts/default/114537857226530971'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/26185192/posts/default/114537857226530971'/><link rel='alternate' type='text/html' href='http://mssql-to-postgresql.blogspot.com/2006/04/row-level-locking-dirty-read.html' title='row-level locking, dirty-read prevention'/><author><name>Michael I. Buen</name><uri>http://www.blogger.com/profile/10894538708565915450</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-26185192.post-114537679846113291</id><published>2006-04-19T00:09:00.000+08:00</published><updated>2006-04-19T01:07:48.086+08:00</updated><title type='text'>table-level locking</title><content type='html'>MS SQL:&lt;br /&gt;SET @m_Hit = (SELECT hit FROM totalhits WITH (TABLOCKX))&lt;br /&gt;SET @m_Hit = @m_Hit + 1&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;PostgreSQL:&lt;br /&gt;&lt;br /&gt;method 1:&lt;br /&gt;SELECT hit INTO m_Hit FROM totalhits FOR UPDATE;&lt;br /&gt;m_Hit = m_Hit + 1;&lt;br /&gt;&lt;br /&gt;method 2:&lt;br /&gt;LOCK TABLE pagehit IN SHARE ROW EXCLUSIVE MODE;&lt;br /&gt;SELECT hit INTO m_Hit FROM totalhits;&lt;br /&gt;m_Hit = m_Hit + 1;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/26185192-114537679846113291?l=mssql-to-postgresql.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://mssql-to-postgresql.blogspot.com/feeds/114537679846113291/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=26185192&amp;postID=114537679846113291' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/26185192/posts/default/114537679846113291'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/26185192/posts/default/114537679846113291'/><link rel='alternate' type='text/html' href='http://mssql-to-postgresql.blogspot.com/2006/04/table-level-locking.html' title='table-level locking'/><author><name>Michael I. Buen</name><uri>http://www.blogger.com/profile/10894538708565915450</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-26185192.post-114516562072504061</id><published>2006-04-16T13:24:00.000+08:00</published><updated>2006-04-16T13:33:40.753+08:00</updated><title type='text'>mssql features not present in postgresql</title><content type='html'>When migrating from PostgreSQL to MS SQL be aware that the following isn't present in  PostgreSQL version 8&lt;br /&gt;&lt;br /&gt;1. Returning multiple recordset from a  function&lt;br /&gt;2. Formula (but can be simulated in trigger)&lt;br /&gt;3. Case Insensitive search (have to use ILIKE or UPPER)&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/26185192-114516562072504061?l=mssql-to-postgresql.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://mssql-to-postgresql.blogspot.com/feeds/114516562072504061/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=26185192&amp;postID=114516562072504061' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/26185192/posts/default/114516562072504061'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/26185192/posts/default/114516562072504061'/><link rel='alternate' type='text/html' href='http://mssql-to-postgresql.blogspot.com/2006/04/mssql-features-not-present-in.html' title='mssql features not present in postgresql'/><author><name>Michael I. Buen</name><uri>http://www.blogger.com/profile/10894538708565915450</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-26185192.post-114512416481582876</id><published>2006-04-16T02:01:00.000+08:00</published><updated>2006-04-16T02:02:44.823+08:00</updated><title type='text'>ms sql stored proc, views, functions, triggers migrations to postgresql</title><content type='html'>&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/26185192-114512416481582876?l=mssql-to-postgresql.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://mssql-to-postgresql.blogspot.com/feeds/114512416481582876/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=26185192&amp;postID=114512416481582876' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/26185192/posts/default/114512416481582876'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/26185192/posts/default/114512416481582876'/><link rel='alternate' type='text/html' href='http://mssql-to-postgresql.blogspot.com/2006/04/ms-sql-stored-proc-views-functions.html' title='ms sql stored proc, views, functions, triggers migrations to postgresql'/><author><name>Michael I. Buen</name><uri>http://www.blogger.com/profile/10894538708565915450</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry></feed>
