<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0"
	xmlns:content="http://purl.org/rss/1.0/modules/content/"
	xmlns:wfw="http://wellformedweb.org/CommentAPI/"
	xmlns:dc="http://purl.org/dc/elements/1.1/"
	xmlns:atom="http://www.w3.org/2005/Atom"
	xmlns:sy="http://purl.org/rss/1.0/modules/syndication/"
	xmlns:slash="http://purl.org/rss/1.0/modules/slash/"
	xmlns:series="http://unfoldingneurons.com/"
	>

<channel>
	<title>DbTracer &#187; Uncategorized</title>
	<atom:link href="http://blog.dbtracer.org/category/uncategorized/feed/" rel="self" type="application/rss+xml" />
	<link>http://blog.dbtracer.org</link>
	<description>Database Comparison and Deployment</description>
	<lastBuildDate>Wed, 28 Dec 2011 14:56:38 +0000</lastBuildDate>
	<language>en</language>
	<sy:updatePeriod>hourly</sy:updatePeriod>
	<sy:updateFrequency>1</sy:updateFrequency>
	<generator>http://wordpress.org/?v=3.3.1</generator>
		<item>
		<title>Audit Trail – Tracing Data Changes in Database</title>
		<link>http://blog.dbtracer.org/2010/08/30/audit-trail-%e2%80%93-tracing-data-changes-in-database/</link>
		<comments>http://blog.dbtracer.org/2010/08/30/audit-trail-%e2%80%93-tracing-data-changes-in-database/#comments</comments>
		<pubDate>Mon, 30 Aug 2010 20:41:00 +0000</pubDate>
		<dc:creator>Petr Kozelek</dc:creator>
				<category><![CDATA[Uncategorized]]></category>

		<guid isPermaLink="false">http://blog.dbtracer.org/?p=127</guid>
		<description><![CDATA[The common requirement in many enterprise applications is logging of data changes in a database – what data has changed, who changed them and when (audit logging). Bloggers published many articles about this old problem but there is only few general approaches how to do that in relational databases. Promiscuous Women Cause Problems for Database [...]
No related posts.]]></description>
			<content:encoded><![CDATA[<p>The common requirement in many enterprise applications is logging of  data changes  in a database – what data has changed, who changed them  and when (<a href="http://en.wikipedia.org/wiki/Audit_trail">audit logging</a>).   Bloggers published many articles about this old problem but there is  only few general approaches how to do that in relational databases.<span id="more-127"></span></p>
<h2>Promiscuous Women Cause Problems for Database Administrators</h2>
<p>This is a fiction story that never happened:</p>
<p>Juliet Capulet meets Paul Smith; they fell in love and marry on 25th  August 2009. When Juliet is back at work she asks the db admin to change  her name in a company database. The admin is not happy to have  additional work with this stuff but he does it for her.</p>
<p>Juliet’s and Paul’s marriage is not going well and Juliet divorces  Paul after a short period. Soon Juliet feels alone, she meets Romeo  Montague and, of course, falls in desperate love with him. Logically,  another wedding is arranged and Juliet asks db admin to change her name  again on 10th January 2010.</p>
<p>The tragedy of all Juliet’s love relationships finishes when she  finds her husband Romeo as a roulette player spending all their money.  She divorces again and changes her name to Juliet Singleton on 21st July  2010 with the vision not to marry anybody again. Needless to say, the  db admin is close to committing suicide because his database does not  allow changing the name more than twice.</p>
<p>What would you suggest the database admin to do better in his next life?</p>
<h2>Problem description</h2>
<p>Let us summarize what the db admin was asked to do with Juliet’s personal data:</p>
<ul>
<li>Juliet Capulet gets the job on 2005-03-01</li>
<li>Name Juliet Capulet changes to Juliet Smith on 2009-08-25</li>
<li>Name Juliet Smith changes to Juliet Montague on 2010-01-10</li>
<li>Name Juliet Montague changes to Julie Singleton on 2010-07-21</li>
</ul>
<h2>Solution No.1: Roll off Out-dated data</h2>
<p>The simplest solution for keeping historical data in the database is  to create a duplicated record before update. After that we can update  the requested data. In case of tracing Juliet’s name  we get these data  in the database table:</p>
<table>
<tbody>
<tr>
<td>id</td>
<td>name</td>
<td>created_date</td>
<td>created _by</td>
</tr>
<tr>
<td>124</td>
<td>Juliet Capulet</td>
<td>2005-03-01</td>
<td>admin</td>
</tr>
<tr>
<td>124</td>
<td>Juliet Smith</td>
<td>2009-08-25</td>
<td>admin</td>
</tr>
<tr>
<td>124</td>
<td>Juliet Montague</td>
<td>2010-01-10</td>
<td>admin</td>
</tr>
<tr>
<td>124</td>
<td>Juliet Singleton</td>
<td>2010-07-21</td>
<td>admin</td>
</tr>
</tbody>
</table>
<p>In real database schema, id is primary key and has to be unique. So  we need extra column containing information about row id from which data  were rolled off. Additionally, it would be helpful to have a column  representing status of the record. Because situation gets more complex  here I show how data evolve in database:</p>
<p>Juliet Capulet gets the job on 2005-03-01.</p>
<table>
<tbody>
<tr>
<td>id</td>
<td>original_id</td>
<td>status</td>
<td>name</td>
<td>created_date</td>
<td>created _by</td>
</tr>
<tr>
<td>124</td>
<td>124</td>
<td>active</td>
<td>Juliet Capulet</td>
<td>2005-03-01</td>
<td>admin</td>
</tr>
</tbody>
</table>
<p>Name Juliet Capulet changes to Juliet Smith on 2009-08-25.</p>
<table>
<tbody>
<tr>
<td>id</td>
<td>original_id</td>
<td>status</td>
<td>name</td>
<td>created_date</td>
<td>created _by</td>
</tr>
<tr>
<td>124</td>
<td>124</td>
<td>active</td>
<td>Juliet Smith</td>
<td>2009-08-25</td>
<td>admin</td>
</tr>
<tr>
<td>254</td>
<td>124</td>
<td>history</td>
<td>Juliet Capulet</td>
<td>2005-03-01</td>
<td>admin</td>
</tr>
</tbody>
</table>
<p>Name Juliet Smith changes to Juliet Montague on 2010-01-10.</p>
<table>
<tbody>
<tr>
<td>id</td>
<td>original_id</td>
<td>status</td>
<td>name</td>
<td>created_date</td>
<td>created _by</td>
</tr>
<tr>
<td>124</td>
<td>124</td>
<td>active</td>
<td>Juliet Montague</td>
<td>2010-01-10</td>
<td>admin</td>
</tr>
<tr>
<td>254</td>
<td>124</td>
<td>history</td>
<td>Juliet Capulet</td>
<td>2005-03-01</td>
<td>admin</td>
</tr>
<tr>
<td>347</td>
<td>124</td>
<td>history</td>
<td>Juliet Smith</td>
<td>2009-08-25</td>
<td>admin</td>
</tr>
</tbody>
</table>
<p>Name Juliet Montague changes to Julie Singleton on 2010-07-21.</p>
<table>
<tbody>
<tr>
<td>id</td>
<td>original_id</td>
<td>status</td>
<td>name</td>
<td>created_date</td>
<td>created _by</td>
</tr>
<tr>
<td>124</td>
<td>124</td>
<td>active</td>
<td>Juliet Singleton</td>
<td>2010-07-21</td>
<td>admin</td>
</tr>
<tr>
<td>254</td>
<td>124</td>
<td>history</td>
<td>Juliet Capulet</td>
<td>2005-03-01</td>
<td>admin</td>
</tr>
<tr>
<td>347</td>
<td>124</td>
<td>history</td>
<td>Juliet Smith</td>
<td>2009-08-25</td>
<td>admin</td>
</tr>
<tr>
<td>489</td>
<td>124</td>
<td>history</td>
<td>Juliet Montague</td>
<td>2010-01-10</td>
<td>admin</td>
</tr>
</tbody>
</table>
<p>You can see that each change to the record with id=124 invokes a)  creating duplicated row with status=history and b) updating the column  name to a requested value with created_date set to current date. When  the active record is going to be “deleted” then only the status flag  changes the value from active to history.</p>
<h3>Advantages</h3>
<p>It is simple to implement audit logging based on rolling out old  data. You do not need additional tables. Maintenance is simple, too. If  you decide to remove old data then it is the matter of one SQL.</p>
<h3>Disadvantages</h3>
<p>But what if you need to implement audit logging in more tables? You  need to add columns original_id, status, created_date and created_by.  And you have to implement previously introduced roll-off logic, either  to database directly (usually with table triggers) or in the  application.</p>
<p>The problem can be that when data changes then the whole record is  copied, i.e. also data which do not change. It causes data duplication  and, potentially, database disk space can increase markedly if changes  occur frequently. For example, if table persons has a column photo with  binary data containing the photography then each and every time Juliet’s  name changes the whole record (including the photo) is copied to  rolled-off record.</p>
<p>Another disadvantage is that the complexity of each table supporting  audit logging increases. You must have in mind all the time that  retrieving the records is not simple. You always have to use the SELECT  clause with condition</p>


<div class="wp-geshi-highlight-wrap5"><div class="wp-geshi-highlight-wrap4"><div class="wp-geshi-highlight-wrap3"><div class="wp-geshi-highlight-wrap2"><div class="wp-geshi-highlight-wrap"><div class="wp-geshi-highlight"><div class="sql"><pre class="de1"><span class="kw1">SELECT</span> <span class="sy0">*</span> <span class="kw1">FROM</span> persons <span class="kw1">WHERE</span> <span class="kw1">STATUS</span><span class="sy0">=</span><span class="st0">'active'</span></pre></div></div></div></div></div></div></div>


<p>When you need to roll back historical data for a particular record  you have to copy all columns from the historical record to the active  record. It would be easier just to switch status value from “history” to  “active” but it would break reference integrity if the record was  referenced in other tables.</p>
<h2>Solution No.2: Dedicated Data-Tracing Table</h2>
<p>Another approach is based on separate audit log table that is  dedicated to logging data changes coming from all tables having audit  log feature:</p>
<table>
<tbody>
<tr>
<td>id</td>
<td>table</td>
<td>column</td>
<td>row</td>
<td>changed_date</td>
<td>changed_by</td>
<td>old_value</td>
<td>new_value</td>
</tr>
<tr>
<td>1241</td>
<td>persons</td>
<td>name</td>
<td>124</td>
<td>2005-03-01</td>
<td>admin</td>
<td>Juliet Capulet</td>
<td>Juliet Capulet</td>
</tr>
<tr>
<td>1654</td>
<td>persons</td>
<td>name</td>
<td>124</td>
<td>2009-08-25</td>
<td>admin</td>
<td>Juliet Capulet</td>
<td>Juliet Smith</td>
</tr>
<tr>
<td>2547</td>
<td>persons</td>
<td>name</td>
<td>124</td>
<td>2010-01-10</td>
<td>admin</td>
<td>Juliet Smith</td>
<td>Juliet Montague</td>
</tr>
<tr>
<td>3645</td>
<td>persons</td>
<td>name</td>
<td>124</td>
<td>2010-07-21</td>
<td>admin</td>
<td>Juliet Montague</td>
<td>Juliet Singleton</td>
</tr>
</tbody>
</table>
<h3>Advantages</h3>
<p>This solutions separates the concerns much better. There is only one  common data store for all historical data. Further, you have to  implement audit log feature only once for all tables which should  support it.</p>
<p>Compared to the previous solution, when the record changes only the  changed data are logged. Thus it is expected to be a little bit faster  but I did not do any tests so I cannot prove it exactly. The real  advantage is that you need lower disk space to store data changes. You  can improve the table structure when you normalize the log table and you  put just integer references to “table” and “column”.</p>
<p>You can easily retrieve user activity just by the query</p>


<div class="wp-geshi-highlight-wrap5"><div class="wp-geshi-highlight-wrap4"><div class="wp-geshi-highlight-wrap3"><div class="wp-geshi-highlight-wrap2"><div class="wp-geshi-highlight-wrap"><div class="wp-geshi-highlight"><div class="sql"><pre class="de1"><span class="kw1">SELECT</span> <span class="sy0">*</span> <span class="kw1">FROM</span> audit_log <span class="kw1">WHERE</span> changed_by<span class="sy0">=</span><span class="st0">'admin'</span></pre></div></div></div></div></div></div></div>


<h3>Disadvantages</h3>
<p>All data changes are logged in a common table so old and new values  stored there must be of some generic type. For example, in MS SQL it can  be “sql_variant”. Or you can convert the values to “varchar” before  storing them in the log table.</p>
<p>Rollback is rather difficult task to be implemented. If you want to roll data back to 3rd March 21010 you have to use query</p>


<div class="wp-geshi-highlight-wrap5"><div class="wp-geshi-highlight-wrap4"><div class="wp-geshi-highlight-wrap3"><div class="wp-geshi-highlight-wrap2"><div class="wp-geshi-highlight-wrap"><div class="wp-geshi-highlight"><div class="sql"><pre class="de1"><span class="kw1">SELECT</span> <span class="br0">&#91;</span><span class="kw1">TABLE</span><span class="br0">&#93;</span><span class="sy0">,</span> <span class="br0">&#91;</span><span class="kw1">COLUMN</span><span class="br0">&#93;</span><span class="sy0">,</span> old_value<span class="sy0">,</span> new_value <span class="kw1">FROM</span> audit_log <span class="kw1">WHERE</span> <span class="br0">&#91;</span><span class="kw1">TABLE</span><span class="br0">&#93;</span><span class="sy0">=</span><span class="st0">'persons'</span> <span class="kw1">AND</span> <span class="kw1">ROW</span><span class="sy0">=</span><span class="nu0">124</span> <span class="kw1">AND</span> changed_date <span class="sy0">&gt;</span> <span class="st0">'2010-03-01'</span> <span class="kw1">ORDER</span> <span class="kw1">BY</span> changed_date</pre></div></div></div></div></div></div></div>


<p>and then you iterate through the result set and build the dynamic  query because table and column info is in the result. It is more  complicated compared to the previous solution. However, the feature has to be implemented only once and then it is reused across the whole application.</p>
<p>No related posts.</p>]]></content:encoded>
			<wfw:commentRss>http://blog.dbtracer.org/2010/08/30/audit-trail-%e2%80%93-tracing-data-changes-in-database/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>Speed Up Typing Text with WatiN</title>
		<link>http://blog.dbtracer.org/2010/08/05/speed-up-typing-text-with-watin/</link>
		<comments>http://blog.dbtracer.org/2010/08/05/speed-up-typing-text-with-watin/#comments</comments>
		<pubDate>Thu, 05 Aug 2010 20:49:28 +0000</pubDate>
		<dc:creator>Petr Kozelek</dc:creator>
				<category><![CDATA[Uncategorized]]></category>

		<guid isPermaLink="false">http://blog.dbtracer.org/?p=134</guid>
		<description><![CDATA[I use WatiN for UA testing and I was facing the problem that WatiN is slow while typing text into text field with TextField.TypeText(string) method. TypeText(string) is slow because during run it fires sequences of key events: KeyDown(char), KeyPress(char), KeyUp(char). The sequence is invoked as many times as the length of the input string is. As the [...]
No related posts.]]></description>
			<content:encoded><![CDATA[<p>I use <a href="http://watin.org/">WatiN</a> for UA  testing and I was facing the problem that WatiN is slow while typing  text into text field with TextField.TypeText(string) method.</p>
<p>TypeText(string) is slow because during run it fires sequences of key  events: KeyDown(char), KeyPress(char), KeyUp(char). The sequence is  invoked as many times as the length of the input string is. As the  result, the input text is typed to the text field one by one char. It is  slow and when you have hundreds of tests then you wait tens of minutes  for their execution! I created simple extension method to speed up  typing of text markedly – for a string 10 chars long the performance of  typing is around 30 times higher.</p>


<div class="wp-geshi-highlight-wrap5"><div class="wp-geshi-highlight-wrap4"><div class="wp-geshi-highlight-wrap3"><div class="wp-geshi-highlight-wrap2"><div class="wp-geshi-highlight-wrap"><div class="wp-geshi-highlight"><div class="csharp"><pre class="de1"><span class="kw1">public</span> <span class="kw1">static</span> <span class="kw4">class</span> WatiNExtensions
<span class="br0">&#123;</span>
    <span class="kw1">public</span> <span class="kw1">static</span> <span class="kw4">void</span> TypeTextQuickly<span class="br0">&#40;</span><span class="kw1">this</span> TextField textField, <span class="kw4">string</span> text<span class="br0">&#41;</span>
    <span class="br0">&#123;</span>
        textField<span class="sy0">.</span><span class="me1">SetAttributeValue</span><span class="br0">&#40;</span><span class="st0">&quot;value&quot;</span>, text<span class="br0">&#41;</span><span class="sy0">;</span>
    <span class="br0">&#125;</span>
<span class="br0">&#125;</span></pre></div></div></div></div></div></div></div>


<p>In my solution I do not use simulated key events. I use direct  setting of value attribute in text field. The limitation of this  solution is that it does not raise focus and blur events. Additionally,  no key events are fired.</p>
<p>Usage is simple:</p>


<div class="wp-geshi-highlight-wrap5"><div class="wp-geshi-highlight-wrap4"><div class="wp-geshi-highlight-wrap3"><div class="wp-geshi-highlight-wrap2"><div class="wp-geshi-highlight-wrap"><div class="wp-geshi-highlight"><div class="csharp"><pre class="de1">var browser <span class="sy0">=</span> <span class="kw3">new</span> IE<span class="br0">&#40;</span><span class="st0">&quot;http://google.com&quot;</span><span class="br0">&#41;</span><span class="sy0">;</span>
browser<span class="sy0">.</span><span class="me1">TextField</span><span class="br0">&#40;</span><span class="st0">&quot;q&quot;</span><span class="br0">&#41;</span><span class="sy0">.</span><span class="me1">TypeTextQuickly</span><span class="br0">&#40;</span><span class="st0">&quot;WatiN&quot;</span><span class="br0">&#41;</span><span class="sy0">;</span> <span class="co1">// fast - raises neither key events nor focus and blur on text field</span>
<span class="co1">// browser.TextField(&quot;q&quot;).TypeText(&quot;WatiN&quot;); // slow - raises key events</span></pre></div></div></div></div></div></div></div>


<p>No related posts.</p>]]></content:encoded>
			<wfw:commentRss>http://blog.dbtracer.org/2010/08/05/speed-up-typing-text-with-watin/feed/</wfw:commentRss>
		<slash:comments>2</slash:comments>
		</item>
		<item>
		<title>Porting a Subversion to a Git Repository with an Error &#8220;Permission denied: Can&#8217;t open &#8216;/tmp/report.tmp&#8217;&#8221;</title>
		<link>http://blog.dbtracer.org/2010/01/20/porting-subversion-git-with-error/</link>
		<comments>http://blog.dbtracer.org/2010/01/20/porting-subversion-git-with-error/#comments</comments>
		<pubDate>Wed, 20 Jan 2010 17:02:27 +0000</pubDate>
		<dc:creator>Petr Kozelek</dc:creator>
				<category><![CDATA[Uncategorized]]></category>
		<category><![CDATA[Git]]></category>
		<category><![CDATA[solutions]]></category>
		<category><![CDATA[Subversion]]></category>

		<guid isPermaLink="false">http://petrkozelek.e-blog.cz/?p=102</guid>
		<description><![CDATA[I was dealing with a problem of converting existing Subversion repository to a new git repository on my local Windows XP station.I carefully followed steps described here: C:\Program Files\Git\bin&#62;git svn init -s file:///D:/path/to/svn/repository Initialized empty Git repository in C:/Program Files/Git/bin/.git/ But Ooops! Error occurred when I tried to fetch all the history to Git repository: [...]
No related posts.]]></description>
			<content:encoded><![CDATA[<p>I was dealing with a problem of converting existing Subversion repository to a new git repository on my local Windows XP station.<span id="more-102"></span>I carefully followed steps described <a href="http://blog.romansklenar.cz/porting-a-subversion-repository-into-git/">here</a>:</p>
<pre>C:\Program Files\Git\bin&gt;git svn init -s file:///D:/path/to/svn/repository
Initialized empty Git repository in C:/Program Files/Git/bin/.git/</pre>
<p>But Ooops! Error occurred when I tried to fetch all the history to Git repository:</p>
<pre>C:\Program Files\Git\bin&gt;git svn fetch
 A       notes.txt
 A       pages.txt
 A       settings.config
r7 = f3dba0984f11e2f358923c23d89440789eb4591b (refs/remotes/references)
<strong>Permission denied: Can't open '/tmp/report.tmp': Permission denied at C:\Program Files\Git/libexec/git-core/git-svn line 2723</strong></pre>
<p>It was an misguiding error since I had neither a &#8220;tmp&#8221; directory in the original <kbd>svn</kbd> repository nor &#8220;report.tmp&#8221; directory<strong><strong>.</strong></strong> For my surprise the problem was caused by different formats used by in original Subversion repository and by Subversion module included in my Git instance that probably uses older Subversion format. I use Git 1.6.5.1.1367.gcd48 that probably has Subversion module supporting format 4 but my Subversion repository was in format 5.</p>
<h2>Solution</h2>
<p>Luckily the solution for such WTF <a href="http://code.google.com/p/msysgit/issues/detail?id=298#c5">exists</a>. If I access Subversion repository via SVN server instead of directly via file path problem will not occur. Thus, at first it is necessary to run SVN server:</p>
<pre>svnserve.exe -d -r D:/parent/path/to/repository/</pre>
<p>And then you can access Subversion repository via its server:</p>
<pre>git.exe svn init -s "<strong>svn://localhost</strong>/relative/path/to/subversion/repository"</pre>
<p>No related posts.</p>]]></content:encoded>
			<wfw:commentRss>http://blog.dbtracer.org/2010/01/20/porting-subversion-git-with-error/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
	</channel>
</rss>

