<rss version="2.0" xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:trackback="http://madskills.com/public/xml/rss/module/trackback/" xmlns:wfw="http://wellformedweb.org/CommentAPI/" xmlns:slash="http://purl.org/rss/1.0/modules/slash/" xmlns:copyright="http://blogs.law.harvard.edu/tech/rss" xmlns:image="http://purl.org/rss/1.0/modules/image/">
    <channel>
        <title>Databases</title>
        <link>http://www.ayende.com/Blog/category/502.aspx</link>
        <description>Databases</description>
        <language>en-US</language>
        <copyright>Ayende Rahien</copyright>
        <managingEditor>Ayende@ayende.com</managingEditor>
        <generator>Subtext Version 1.9.3.51</generator>
        <item>
            <title>SQL CE Transaction Handling</title>
            <link>http://ayende.com/Blog/archive/2008/03/31/SQL-CE-Transaction-Handling.aspx</link>
            <description>&lt;p&gt;&lt;strong&gt;Update:&lt;/strong&gt; Yes, I am crazy! Turn out that I forgot to do "command.Transaction = tx;" and then I went and read some outdated documentation, and got the completely wrong picture, yuck! I still think that requiring "command.Transaction = tx;" is bad API design and error prone (duh!).&lt;/p&gt;  &lt;p&gt;Someone please tell me that I am not crazy. The output out this program is:&lt;/p&gt;  &lt;blockquote&gt;   &lt;p&gt;Wrote item      &lt;br /&gt;Wrote item       &lt;br /&gt;Wrote item       &lt;br /&gt;3       &lt;br /&gt;Wrote item       &lt;br /&gt;Wrote item       &lt;br /&gt;4       &lt;br /&gt;Wrote item       &lt;br /&gt;Wrote item       &lt;br /&gt;5       &lt;br /&gt;Wrote item       &lt;br /&gt;Wrote item       &lt;br /&gt;6       &lt;br /&gt;Wrote item&lt;/p&gt; &lt;/blockquote&gt;  &lt;p&gt;This is wrong on so many levels...&lt;/p&gt;  &lt;pre&gt;&lt;span style="color: #0000ff"&gt;public&lt;/span&gt; &lt;span style="color: #0000ff"&gt;class&lt;/span&gt; Program
{
	&lt;span style="color: #0000ff"&gt;const&lt;/span&gt; &lt;span style="color: #0000ff"&gt;string&lt;/span&gt; connectionString = "&lt;span style="color: #8b0000"&gt;Data Source=test.dsf&lt;/span&gt;";

	&lt;span style="color: #0000ff"&gt;public&lt;/span&gt; &lt;span style="color: #0000ff"&gt;static&lt;/span&gt; &lt;span style="color: #0000ff"&gt;void&lt;/span&gt; Main(&lt;span style="color: #0000ff"&gt;string&lt;/span&gt;[] args)
	{
		File.Delete("&lt;span style="color: #8b0000"&gt;test.dsf&lt;/span&gt;");
		var engine = &lt;span style="color: #0000ff"&gt;new&lt;/span&gt; SqlCeEngine(connectionString);
		engine.CreateDatabase();

		&lt;span style="color: #0000ff"&gt;using&lt;/span&gt; (var connection = &lt;span style="color: #0000ff"&gt;new&lt;/span&gt; SqlCeConnection(connectionString))
		{
			connection.Open();
			SqlCeCommand command = connection.CreateCommand();
			command.CommandText = @"&lt;span style="color: #8b0000"&gt;CREATE TABLE Test(Id INT IDENTITY PRIMARY KEY, Name NVARCHAR(25) NOT NULL)&lt;/span&gt;";
			command.ExecuteNonQuery();
		}

		ThreadPool.QueueUserWorkItem(ReadFromDb);

		&lt;span style="color: #0000ff"&gt;using&lt;/span&gt; (var connection = &lt;span style="color: #0000ff"&gt;new&lt;/span&gt; SqlCeConnection(connectionString))
		{
			connection.Open();
			&lt;span style="color: #0000ff"&gt;using&lt;/span&gt;(IDbTransaction tx = connection.BeginTransaction(IsolationLevel.Serializable))
			{
				&lt;span style="color: #0000ff"&gt;while&lt;/span&gt;(&lt;span style="color: #0000ff"&gt;true&lt;/span&gt;)
				{
					&lt;span style="color: #0000ff"&gt;using&lt;/span&gt; (SqlCeCommand command = connection.CreateCommand())
					{
						command.CommandText = @"&lt;span style="color: #8b0000"&gt;INSERT INTO Test(Name) VALUES('A');&lt;/span&gt;";
						command.ExecuteNonQuery();
					}
					Console.WriteLine("&lt;span style="color: #8b0000"&gt;Wrote item&lt;/span&gt;");
					Thread.Sleep(500);
				}
			}
		}
	}

	&lt;span style="color: #0000ff"&gt;private&lt;/span&gt; &lt;span style="color: #0000ff"&gt;static&lt;/span&gt; &lt;span style="color: #0000ff"&gt;void&lt;/span&gt; ReadFromDb(&lt;span style="color: #0000ff"&gt;object&lt;/span&gt; state)
	{
		Thread.Sleep(1000);
		&lt;span style="color: #0000ff"&gt;using&lt;/span&gt; (var connection = &lt;span style="color: #0000ff"&gt;new&lt;/span&gt; SqlCeConnection(connectionString))
		{
			connection.Open();
			&lt;span style="color: #0000ff"&gt;using&lt;/span&gt; (IDbTransaction tx = connection.BeginTransaction(IsolationLevel.Serializable))
			{
				&lt;span style="color: #0000ff"&gt;while&lt;/span&gt; (&lt;span style="color: #0000ff"&gt;true&lt;/span&gt;)
				{
					&lt;span style="color: #0000ff"&gt;using&lt;/span&gt; (SqlCeCommand command = connection.CreateCommand())
					{
						command.CommandText = @"&lt;span style="color: #8b0000"&gt;SELECT COUNT(*) FROM Test;&lt;/span&gt;";
						Console.WriteLine(command.ExecuteScalar()); 
					}
					Console.WriteLine("&lt;span style="color: #8b0000"&gt;Wrote item&lt;/span&gt;");
					Thread.Sleep(500);
				}
			}
		}
	}
}&lt;/pre&gt;&lt;img src="http://ayende.com/Blog/aggbug/10146.aspx" width="1" height="1" /&gt;</description>
            <dc:creator>Ayende Rahien</dc:creator>
            <guid>http://ayende.com/Blog/archive/2008/03/31/SQL-CE-Transaction-Handling.aspx</guid>
            <pubDate>Mon, 31 Mar 2008 16:50:13 GMT</pubDate>
            <wfw:comment>http://ayende.com/Blog/comments/10146.aspx</wfw:comment>
            <comments>http://ayende.com/Blog/archive/2008/03/31/SQL-CE-Transaction-Handling.aspx#feedback</comments>
            <slash:comments>9</slash:comments>
            <wfw:commentRss>http://ayende.com/Blog/comments/commentRss/10146.aspx</wfw:commentRss>
        </item>
        <item>
            <title>SQLite vs. SQL CE</title>
            <link>http://ayende.com/Blog/archive/2008/01/22/SQLite-vs.-SQL-CE.aspx</link>
            <description>&lt;p&gt;Those two seems to be the most common embedded databases in the .NET world. This is important to me, since I want to do testing against embedded database.&lt;/p&gt; &lt;p&gt;SQL CE can be used with SQL Management Studio, which is nice, but it has three major issues for me so far:&lt;/p&gt; &lt;ul&gt; &lt;li&gt;It doesn't support memory only operations. SQLite does, and it means a difference of 12 seconds vs. 40 seconds in running ~100 tests that hit the DB. This is important, especially kicking up everything seems to take about 10 seconds anyway (using Test Driven.Net)&lt;/li&gt; &lt;li&gt;It doesn't support paging (WTF!)&lt;/li&gt; &lt;li&gt;It doesn't support comparing to a sub query, so this is not legal:&lt;br /&gt;select * from account where 1 = (select allow from permissions)&lt;/li&gt;&lt;/ul&gt; &lt;p&gt;Right now I am experimenting with just how much I can twist around to get everything to break.&lt;/p&gt; &lt;p&gt;I am on the third NH bug this week, and counting :-)&lt;/p&gt;&lt;img src="http://ayende.com/Blog/aggbug/10013.aspx" width="1" height="1" /&gt;</description>
            <dc:creator>Ayende Rahien</dc:creator>
            <guid>http://ayende.com/Blog/archive/2008/01/22/SQLite-vs.-SQL-CE.aspx</guid>
            <pubDate>Tue, 22 Jan 2008 02:18:06 GMT</pubDate>
            <comments>http://ayende.com/Blog/archive/2008/01/22/SQLite-vs.-SQL-CE.aspx#feedback</comments>
            <slash:comments>17</slash:comments>
            <wfw:commentRss>http://ayende.com/Blog/comments/commentRss/10013.aspx</wfw:commentRss>
        </item>
        <item>
            <title>Serialazble isolation level on rows that does not exists</title>
            <link>http://ayende.com/Blog/archive/2008/01/18/Serialazble-isolation-level-on-rows-that-does-not-exists.aspx</link>
            <description>&lt;p&gt;Recently I was asked how to solve this problem. An external service makes a call to the application, to create / update an entity. This call can arrive in one of few endpoints. The catch is that sometimes the external service send the call to create a new entity to all the end points at the same time. This obviously caused issue when trying to insert the same row twice.&lt;/p&gt; &lt;p&gt;I suggested using serializable isolation level to handle this scenario, but I wasn't sure what kind of guarantees is makes for rows that do not exists. I decided to write this simple test case (warning: test code / quick hack, don't write real code like this!).&lt;/p&gt; &lt;blockquote&gt;&lt;pre&gt;&lt;span style="color: #0000ff"&gt;private&lt;/span&gt; &lt;span style="color: #0000ff"&gt;static&lt;/span&gt; &lt;span style="color: #0000ff"&gt;void&lt;/span&gt; InsertToDatabase(&lt;span style="color: #0000ff"&gt;object&lt;/span&gt; state)
{
    &lt;span style="color: #0000ff"&gt;int&lt;/span&gt; &lt;span style="color: #0000ff"&gt;value&lt;/span&gt; = 0;

    &lt;span style="color: #0000ff"&gt;while&lt;/span&gt; (&lt;span style="color: #0000ff"&gt;true&lt;/span&gt;)
    {
        &lt;span style="color: #0000ff"&gt;value&lt;/span&gt; += 1;
        &lt;span style="color: #0000ff"&gt;try&lt;/span&gt;
        {
            &lt;span style="color: #0000ff"&gt;using&lt;/span&gt; (SqlConnection connection =
                    &lt;span style="color: #0000ff"&gt;new&lt;/span&gt; SqlConnection("&lt;span style="color: #8b0000"&gt;data source=localhost;integrated security=sspi;initial catalog=test&lt;/span&gt;"))
            {
                connection.Open();
                &lt;span style="color: #0000ff"&gt;using&lt;/span&gt; (SqlTransaction transaction =
                        connection.BeginTransaction(IsolationLevel.Serializable))
                &lt;span style="color: #0000ff"&gt;using&lt;/span&gt; (SqlCommand command = connection.CreateCommand())
                {
                    command.CommandText = "&lt;span style="color: #8b0000"&gt;SELECT Id FROM Items WHERE Id = @id&lt;/span&gt;";
                    command.Parameters.AddWithValue("&lt;span style="color: #8b0000"&gt;id&lt;/span&gt;", &lt;span style="color: #0000ff"&gt;value&lt;/span&gt;);
                    command.Transaction = transaction;
                    &lt;span style="color: #0000ff"&gt;if&lt;/span&gt; (command.ExecuteScalar() != &lt;span style="color: #0000ff"&gt;null&lt;/span&gt;)
                        &lt;span style="color: #0000ff"&gt;continue&lt;/span&gt;;
                    command.CommandText = "&lt;span style="color: #8b0000"&gt;INSERT INTO Items (Id) VALUES(@id)&lt;/span&gt;";
                    command.ExecuteNonQuery();
                    Console.WriteLine("&lt;span style="color: #8b0000"&gt;{1}: Wrote {0}&lt;/span&gt;", &lt;span style="color: #0000ff"&gt;value&lt;/span&gt;, Thread.CurrentThread.ManagedThreadId);

                    transaction.Commit();
                }
            }
        }
        &lt;span style="color: #0000ff"&gt;catch&lt;/span&gt; (SqlException e)
        {
            &lt;span style="color: #0000ff"&gt;if&lt;/span&gt; (e.Number == 1205) &lt;span style="color: #008000"&gt;//transaction deadlock&lt;/span&gt;
            {
                Console.WriteLine("&lt;span style="color: #8b0000"&gt;{0}: Deadlock recovery&lt;/span&gt;", Thread.CurrentThread.ManagedThreadId);
                &lt;span style="color: #0000ff"&gt;continue&lt;/span&gt;;
            }
            Console.WriteLine(e);
        }
    }
}&lt;/pre&gt;&lt;/blockquote&gt;
&lt;p&gt;As a note, performance for this &lt;em&gt;really&lt;/em&gt; sucks if you have contention, and I got a lot of transaction deadlocks when running it with multiply threads.&lt;/p&gt;
&lt;p&gt;What I have observed was that it indeed inserted only a single id. All other isolation levels (including snapshot) will produce duplicate rows in this scenario.&lt;/p&gt;
&lt;p&gt;Assumption proven, now I only need to find what kind of a lock it takes on a row that doesn't exists.&lt;/p&gt;&lt;img src="http://ayende.com/Blog/aggbug/10008.aspx" width="1" height="1" /&gt;</description>
            <dc:creator>Ayende Rahien</dc:creator>
            <guid>http://ayende.com/Blog/archive/2008/01/18/Serialazble-isolation-level-on-rows-that-does-not-exists.aspx</guid>
            <pubDate>Fri, 18 Jan 2008 01:38:55 GMT</pubDate>
            <comments>http://ayende.com/Blog/archive/2008/01/18/Serialazble-isolation-level-on-rows-that-does-not-exists.aspx#feedback</comments>
            <slash:comments>5</slash:comments>
            <wfw:commentRss>http://ayende.com/Blog/comments/commentRss/10008.aspx</wfw:commentRss>
        </item>
        <item>
            <title>Another paging approach</title>
            <link>http://ayende.com/Blog/archive/2007/12/06/Another-paging-approach.aspx</link>
            <description>&lt;p&gt;I have hard time believing that, but this is something that I learned from how MS CRM works. This is the first time that I ever saw this approach, although it seems so &lt;em&gt;obvious&lt;/em&gt; when you think about it.&lt;/p&gt; &lt;p&gt;&lt;strong&gt;Updated:&lt;/strong&gt; Fixed a bug found by Nathan Baulch, thanks!&lt;/p&gt; &lt;p&gt;The idea here is to solve the problem of SQL 2000 not really having a good way to do paging. The basic pattern is this:&lt;/p&gt; &lt;blockquote&gt;&lt;pre&gt;&lt;a style="color: #0000ff" href="http://search.microsoft.com/default.asp?so=RECCNT&amp;amp;siteid=us%2Fdev&amp;amp;p=1&amp;amp;nq=NEW&amp;amp;qu=select&amp;amp;IntlSearch=&amp;amp;boolean=PHRASE&amp;amp;ig=01&amp;amp;i=09&amp;amp;i=99"&gt;select&lt;/a&gt; &lt;a style="color: #0000ff" href="http://search.microsoft.com/default.asp?so=RECCNT&amp;amp;siteid=us%2Fdev&amp;amp;p=1&amp;amp;nq=NEW&amp;amp;qu=top&amp;amp;IntlSearch=&amp;amp;boolean=PHRASE&amp;amp;ig=01&amp;amp;i=09&amp;amp;i=99"&gt;top&lt;/a&gt; [N] * &lt;a style="color: #0000ff" href="http://search.microsoft.com/default.asp?so=RECCNT&amp;amp;siteid=us%2Fdev&amp;amp;p=1&amp;amp;nq=NEW&amp;amp;qu=from&amp;amp;IntlSearch=&amp;amp;boolean=PHRASE&amp;amp;ig=01&amp;amp;i=09&amp;amp;i=99"&gt;from&lt;/a&gt; [TableName] 
&lt;a style="color: #0000ff" href="http://search.microsoft.com/default.asp?so=RECCNT&amp;amp;siteid=us%2Fdev&amp;amp;p=1&amp;amp;nq=NEW&amp;amp;qu=where&amp;amp;IntlSearch=&amp;amp;boolean=PHRASE&amp;amp;ig=01&amp;amp;i=09&amp;amp;i=99"&gt;where&lt;/a&gt; [orderCriteria] &amp;gt; @orderCriteriaPreviousMaxValue &lt;a style="color: #0000ff" href="http://search.microsoft.com/default.asp?so=RECCNT&amp;amp;siteid=us%2Fdev&amp;amp;p=1&amp;amp;nq=NEW&amp;amp;qu=or&amp;amp;IntlSearch=&amp;amp;boolean=PHRASE&amp;amp;ig=01&amp;amp;i=09&amp;amp;i=99"&gt;or&lt;/a&gt; 
(
	[Id] &amp;gt; @PreviousID &lt;a style="color: #0000ff" href="http://search.microsoft.com/default.asp?so=RECCNT&amp;amp;siteid=us%2Fdev&amp;amp;p=1&amp;amp;nq=NEW&amp;amp;qu=and&amp;amp;IntlSearch=&amp;amp;boolean=PHRASE&amp;amp;ig=01&amp;amp;i=09&amp;amp;i=99"&gt;and&lt;/a&gt; 
	[orderCriteria] = @orderCriteriaPreviousMaxValue
) 
&lt;a style="color: #0000ff" href="http://search.microsoft.com/default.asp?so=RECCNT&amp;amp;siteid=us%2Fdev&amp;amp;p=1&amp;amp;nq=NEW&amp;amp;qu=order&amp;amp;IntlSearch=&amp;amp;boolean=PHRASE&amp;amp;ig=01&amp;amp;i=09&amp;amp;i=99"&gt;order&lt;/a&gt; &lt;a style="color: #0000ff" href="http://search.microsoft.com/default.asp?so=RECCNT&amp;amp;siteid=us%2Fdev&amp;amp;p=1&amp;amp;nq=NEW&amp;amp;qu=by&amp;amp;IntlSearch=&amp;amp;boolean=PHRASE&amp;amp;ig=01&amp;amp;i=09&amp;amp;i=99"&gt;by&lt;/a&gt; [orderCriteria], [Id]
&lt;/pre&gt;&lt;/blockquote&gt;
&lt;p&gt;What do I mean by that?&lt;/p&gt;
&lt;p&gt;Let us say that I want to get the first page of customers, I would issue the following query:&lt;/p&gt;
&lt;blockquote&gt;&lt;pre&gt;&lt;a style="color: #0000ff" href="http://search.microsoft.com/default.asp?so=RECCNT&amp;amp;siteid=us%2Fdev&amp;amp;p=1&amp;amp;nq=NEW&amp;amp;qu=select&amp;amp;IntlSearch=&amp;amp;boolean=PHRASE&amp;amp;ig=01&amp;amp;i=09&amp;amp;i=99"&gt;select&lt;/a&gt; &lt;a style="color: #0000ff" href="http://search.microsoft.com/default.asp?so=RECCNT&amp;amp;siteid=us%2Fdev&amp;amp;p=1&amp;amp;nq=NEW&amp;amp;qu=top&amp;amp;IntlSearch=&amp;amp;boolean=PHRASE&amp;amp;ig=01&amp;amp;i=09&amp;amp;i=99"&gt;top&lt;/a&gt; 25 * &lt;a style="color: #0000ff" href="http://search.microsoft.com/default.asp?so=RECCNT&amp;amp;siteid=us%2Fdev&amp;amp;p=1&amp;amp;nq=NEW&amp;amp;qu=from&amp;amp;IntlSearch=&amp;amp;boolean=PHRASE&amp;amp;ig=01&amp;amp;i=09&amp;amp;i=99"&gt;from&lt;/a&gt; Customers 
&lt;a style="color: #0000ff" href="http://search.microsoft.com/default.asp?so=RECCNT&amp;amp;siteid=us%2Fdev&amp;amp;p=1&amp;amp;nq=NEW&amp;amp;qu=order&amp;amp;IntlSearch=&amp;amp;boolean=PHRASE&amp;amp;ig=01&amp;amp;i=09&amp;amp;i=99"&gt;order&lt;/a&gt; &lt;a style="color: #0000ff" href="http://search.microsoft.com/default.asp?so=RECCNT&amp;amp;siteid=us%2Fdev&amp;amp;p=1&amp;amp;nq=NEW&amp;amp;qu=by&amp;amp;IntlSearch=&amp;amp;boolean=PHRASE&amp;amp;ig=01&amp;amp;i=09&amp;amp;i=99"&gt;by&lt;/a&gt; Customers.Name, Customers.Id&lt;/pre&gt;&lt;/blockquote&gt;
&lt;blockquote&gt;&lt;/blockquote&gt;
&lt;p&gt;Now, I want to get the next page, I can do this using this approach:&lt;/p&gt;
&lt;blockquote&gt;&lt;pre&gt;&lt;a style="color: #0000ff" href="http://search.microsoft.com/default.asp?so=RECCNT&amp;amp;siteid=us%2Fdev&amp;amp;p=1&amp;amp;nq=NEW&amp;amp;qu=select&amp;amp;IntlSearch=&amp;amp;boolean=PHRASE&amp;amp;ig=01&amp;amp;i=09&amp;amp;i=99"&gt;select&lt;/a&gt; &lt;a style="color: #0000ff" href="http://search.microsoft.com/default.asp?so=RECCNT&amp;amp;siteid=us%2Fdev&amp;amp;p=1&amp;amp;nq=NEW&amp;amp;qu=top&amp;amp;IntlSearch=&amp;amp;boolean=PHRASE&amp;amp;ig=01&amp;amp;i=09&amp;amp;i=99"&gt;top&lt;/a&gt; 25 * &lt;a style="color: #0000ff" href="http://search.microsoft.com/default.asp?so=RECCNT&amp;amp;siteid=us%2Fdev&amp;amp;p=1&amp;amp;nq=NEW&amp;amp;qu=from&amp;amp;IntlSearch=&amp;amp;boolean=PHRASE&amp;amp;ig=01&amp;amp;i=09&amp;amp;i=99"&gt;from&lt;/a&gt; Customers
&lt;a style="color: #0000ff" href="http://search.microsoft.com/default.asp?so=RECCNT&amp;amp;siteid=us%2Fdev&amp;amp;p=1&amp;amp;nq=NEW&amp;amp;qu=where&amp;amp;IntlSearch=&amp;amp;boolean=PHRASE&amp;amp;ig=01&amp;amp;i=09&amp;amp;i=99"&gt;where&lt;/a&gt; Customers.Name &amp;gt;= @CustomerNamePreviousMaxValue &lt;a style="color: #0000ff" href="http://search.microsoft.com/default.asp?so=RECCNT&amp;amp;siteid=us%2Fdev&amp;amp;p=1&amp;amp;nq=NEW&amp;amp;qu=or&amp;amp;IntlSearch=&amp;amp;boolean=PHRASE&amp;amp;ig=01&amp;amp;i=09&amp;amp;i=99"&gt;or&lt;/a&gt;
(
	Customers.Id &amp;gt; @PreviousId &lt;a style="color: #0000ff" href="http://search.microsoft.com/default.asp?so=RECCNT&amp;amp;siteid=us%2Fdev&amp;amp;p=1&amp;amp;nq=NEW&amp;amp;qu=and&amp;amp;IntlSearch=&amp;amp;boolean=PHRASE&amp;amp;ig=01&amp;amp;i=09&amp;amp;i=99"&gt;and&lt;/a&gt; 
	Customers.Name &amp;gt; @CustomerNamePreviousMaxValue
)
&lt;a style="color: #0000ff" href="http://search.microsoft.com/default.asp?so=RECCNT&amp;amp;siteid=us%2Fdev&amp;amp;p=1&amp;amp;nq=NEW&amp;amp;qu=order&amp;amp;IntlSearch=&amp;amp;boolean=PHRASE&amp;amp;ig=01&amp;amp;i=09&amp;amp;i=99"&gt;order&lt;/a&gt; &lt;a style="color: #0000ff" href="http://search.microsoft.com/default.asp?so=RECCNT&amp;amp;siteid=us%2Fdev&amp;amp;p=1&amp;amp;nq=NEW&amp;amp;qu=by&amp;amp;IntlSearch=&amp;amp;boolean=PHRASE&amp;amp;ig=01&amp;amp;i=09&amp;amp;i=99"&gt;by&lt;/a&gt; Customers.Name, Customers.Id&lt;/pre&gt;&lt;/blockquote&gt;
&lt;p&gt;All we need to remember is the last row's value from the previous query, and pass it to this one. Using this approach we can efficently page through result sets, without having to deal with the limitations of SQL Server limited top capabilities.&lt;/p&gt;
&lt;p&gt;The usage of the filtering and the ordering ensure that we will always get the next result back. I find it quite elegant. What is surprising, I don't think that I have ever seen it in any other place.&lt;/p&gt;&lt;img src="http://ayende.com/Blog/aggbug/9919.aspx" width="1" height="1" /&gt;</description>
            <dc:creator>Ayende Rahien</dc:creator>
            <guid>http://ayende.com/Blog/archive/2007/12/06/Another-paging-approach.aspx</guid>
            <pubDate>Thu, 06 Dec 2007 11:20:25 GMT</pubDate>
            <comments>http://ayende.com/Blog/archive/2007/12/06/Another-paging-approach.aspx#feedback</comments>
            <slash:comments>21</slash:comments>
            <wfw:commentRss>http://ayende.com/Blog/comments/commentRss/9919.aspx</wfw:commentRss>
        </item>
        <item>
            <title>Perfoming joins without having all the data in memory</title>
            <link>http://ayende.com/Blog/archive/2007/12/06/Perfoming-joins-without-having-all-the-data-in-memory.aspx</link>
            <description>&lt;p&gt;Probably the easier way to perform a join is by a nested loop, given dataset A and dataset B, joining all the rows to dataset C is simple:&lt;/p&gt; &lt;blockquote&gt;&lt;pre&gt;&lt;span style="color: #0000ff"&gt;for&lt;/span&gt; row_a &lt;span style="color: #0000ff"&gt;in&lt;/span&gt; A:
	&lt;span style="color: #0000ff"&gt;for&lt;/span&gt; row_b &lt;span style="color: #0000ff"&gt;in&lt;/span&gt; B:
		&lt;span style="color: #0000ff"&gt;if&lt;/span&gt; condition(row_a, row_b):
			add join(row_a, row_b), C
&lt;/pre&gt;&lt;/blockquote&gt;
&lt;p&gt;Supporting left/right/cross joins is simple matter from here, but this has the issue of having to have both datasets in memory. I run into it while processing big files, I don't want to have to hold them in memory, especially if I need several level of joins in order to correctly process them.&lt;/p&gt;
&lt;p&gt;I thought about bypassing the entire issue by simply writing the data down to a sqlite DB, and doing the join there. While this is possible, I would rather avoid having to do this.&lt;/p&gt;
&lt;p&gt;Any suggestions on where to look to solve this issue?&lt;/p&gt;&lt;img src="http://ayende.com/Blog/aggbug/9918.aspx" width="1" height="1" /&gt;</description>
            <dc:creator>Ayende Rahien</dc:creator>
            <guid>http://ayende.com/Blog/archive/2007/12/06/Perfoming-joins-without-having-all-the-data-in-memory.aspx</guid>
            <pubDate>Thu, 06 Dec 2007 10:22:41 GMT</pubDate>
            <comments>http://ayende.com/Blog/archive/2007/12/06/Perfoming-joins-without-having-all-the-data-in-memory.aspx#feedback</comments>
            <slash:comments>23</slash:comments>
            <wfw:commentRss>http://ayende.com/Blog/comments/commentRss/9918.aspx</wfw:commentRss>
        </item>
        <item>
            <title>The CRM Horror</title>
            <link>http://ayende.com/Blog/archive/2007/10/07/The-CRM-Horror.aspx</link>
            <description>&lt;p&gt;&lt;a href="http://ayende.com/Blog/images/ayende_com/Blog/WindowsLiveWriter/TheCRMHorror_1045E/image.png" atomicselection="true"&gt;&lt;img height="558" alt="image" src="http://ayende.com/Blog/images/ayende_com/Blog/WindowsLiveWriter/TheCRMHorror_1045E/image_thumb.png" width="453" align="right" border="0" /&gt;&lt;/a&gt; It is rare that I get to the foint where I am just flat out speechless from seeing something. Today I went beyond that, I was flat out speechless &lt;em&gt;and&lt;/em&gt; aghast. &lt;/p&gt; &lt;p&gt;The image that you see here is a small part of the FilteredAccount view in Micorosft CRM. Yes, you got that right, a &lt;em&gt;small&lt;/em&gt; part.&lt;/p&gt; &lt;p&gt;I got to this from experimenting with the DB model, trying to figure out how things work. I was strongly adviced not to make any use of any sort of view that started with Filtered. "It would make you cry", they said, "don't say we didn't warn you".&lt;/p&gt; &lt;p&gt;I took a peek inside, to see what all the fuss was about. I disagree with Microsoft of quite a few subjects, but they usually manage to hit the target, even if that target is on a completely wrong continent as far as I am concerned.&lt;/p&gt; &lt;p&gt;Some background, before we continue. Filtered Views in MS CRM are used to show the user just the data that they are supposed to see, according to role based permissions. Using the connected usernmae as the way to filter the view. &lt;/p&gt; &lt;p&gt;As a result of that, all the members of the organization has connect access to the CRM DB, and can just open query analyzer and start executing queries against the DB.&lt;/p&gt; &lt;p&gt;An immediate result of that is that you need to start managing security at the DB level, since those views will not be very good if you can just query the underlying tables.&lt;/p&gt; &lt;p&gt;I gues it would not suprise anyone that I have serious issues with this model, but let us put this aside for a moment. I have issues with this model, but they are manageable ones.&lt;/p&gt; &lt;p&gt;Anyway, using the filtered views is the only supported way of handling accessing the database directly, mostly meant for reports. I think that you can guess how efficent it is going to query a filtered view, right?&lt;/p&gt; &lt;p&gt;I &lt;em&gt;counted&lt;/em&gt;, the FilteredAccount view has:&lt;/p&gt; &lt;ul&gt; &lt;li&gt;&lt;em&gt;5 row level functions&lt;/em&gt;&lt;/li&gt; &lt;li&gt;&lt;strong&gt;40 outer joins&lt;/strong&gt;&lt;/li&gt; &lt;li&gt;2 subqueries&lt;/li&gt;&lt;/ul&gt; &lt;p&gt;At this point, I am considering creating the DETD - Developers for Etichal Treatment of Databases. Surely this is cruel and unusal behavior to put this burden on an unsuspecting database. And I am pretty sure that it is a violation of the Geneva Convention to inflict such a thing on your unsuspecting users.&lt;/p&gt; &lt;p&gt;Left forever wonderring when that query will ever return...&lt;/p&gt;&lt;img src="http://ayende.com/Blog/aggbug/9749.aspx" width="1" height="1" /&gt;</description>
            <dc:creator>Ayende Rahien</dc:creator>
            <guid>http://ayende.com/Blog/archive/2007/10/07/The-CRM-Horror.aspx</guid>
            <pubDate>Sun, 07 Oct 2007 17:33:36 GMT</pubDate>
            <comments>http://ayende.com/Blog/archive/2007/10/07/The-CRM-Horror.aspx#feedback</comments>
            <slash:comments>14</slash:comments>
            <wfw:commentRss>http://ayende.com/Blog/comments/commentRss/9749.aspx</wfw:commentRss>
        </item>
        <item>
            <title>More on Data Layer Componentization</title>
            <link>http://ayende.com/Blog/archive/2007/10/07/More-on-Data-Layer-Componentization.aspx</link>
            <description>&lt;p&gt;A while ago I &lt;a href="http://www.ayende.com/Blog/archive/2007/09/01/Data-Layer-ComponentizationAgain.aspx"&gt;commented&lt;/a&gt; on &lt;a href="http://www.base4.net/blog.aspx"&gt;Alex&lt;/a&gt;'s &lt;a href="http://www.base4.net/blog.aspx?ID=559"&gt;data-layer componentization&lt;/a&gt; idea. &lt;a href="http://www.base4.net/blog.aspx?ID=566"&gt;Alex replied&lt;/a&gt;, and &lt;a href="http://diegov.blogspot.com/2007/09/around-globe-with-composable-data.html"&gt;Diego as well&lt;/a&gt;, but I didn't get around to answering them until now.&lt;/p&gt; &lt;p&gt;To remind you, the basic idea is:&lt;/p&gt; &lt;blockquote&gt; &lt;blockquote&gt; &lt;p&gt;A data-layer component simply allows for information to be shared between different applications.&lt;/p&gt;&lt;/blockquote&gt; &lt;blockquote&gt; &lt;p&gt;By supporting re-use data layer componentization allows you to break the duplication habit. You can still have silos, but the natural tendency to duplicate data disappears. Each new silo will instead contain only new types of data and where old types are required, simply pointers, or cross silo foreign keys.&lt;/p&gt;&lt;/blockquote&gt;&lt;/blockquote&gt; &lt;p&gt;I am going to get to answering their points in a bit, but one thing that I do agree on is the idea of a DataServer, to quote Alex:&lt;/p&gt; &lt;blockquote&gt; &lt;p&gt;I like to think of this as a DataServer as opposed to a Database. The idea being that a DataServer is the place you go to get data in the shape that makes sense for you (i.e. the conceptual model), and is responsible for getting that data from one or more data-sources or databases on your behalf.&lt;/p&gt;&lt;/blockquote&gt; &lt;p&gt;Except that I see this in an entirely different light. I don't like the idea of distributing the data over many sources, I would much rather pull all this information to my own DB, and shift the problem from downed application to stale data. That can be a problem, but that depends on the application at hand. See the end of the post about my ideas of this, for now, I want to get to Alex's points.&lt;/p&gt; &lt;p&gt;I mentioned that taking dependencies on several databases cause a server decrease in the reliability of the system, any of them going down will cause the entire system to go down:&lt;/p&gt; &lt;blockquote&gt; &lt;p&gt;Of course systems with multiple dependencies are less robust, but the alternative opens you up to relying on out of date or incorrect data, which hardly seems any better? &lt;/p&gt;&lt;/blockquote&gt; &lt;p&gt;It really depends on the application at hand, most business application can work with stale data without much problem, and recovering from that isn't really a problem. But it is far less common to find a scenario where possibly stale answer is better than a downed application. &lt;/p&gt; &lt;p&gt;Another point that I raised was that changing a single application can ripple to all the other applications in the organization.&lt;/p&gt; &lt;blockquote&gt; &lt;p&gt;This is essentially the dual schema problem n’est pas? So you handle it the same way you always handle the dual schema problem, with an intermediary conceptual model. Incidentally there is no reason you have to have only one conceptual model for any given data model either. If one application changes and another doesn't create a different conceptual model.&lt;/p&gt;&lt;/blockquote&gt; &lt;p&gt;That is actually the N'th Schema problem. I am nervous about making changes to one application just because another have changed, much less N other applications. Shotgun Surgery in a grand level. Even if we assume that we need to merely update the mapping layer, I have had enough problems with &lt;em&gt;that&lt;/em&gt; to cause me to call it simple or painless. And if we are talking about a change that affect the conceptual model as well, this gets even more &lt;em&gt;interesting&lt;/em&gt;.&lt;/p&gt; &lt;p&gt;Another concern was about business logic that is required to actual do something with the data. Using this data server approach, you need the business logic to make sense of the data, and if this business logic change in the original application, what happens to the rest of the applications?&lt;/p&gt; &lt;blockquote&gt; &lt;p&gt;I’m not claiming that the ability to share data-layers suddenly negates the need for a business layer! In fact by sharing data-layers it finally becomes possible to share business layers. That is what Base4 tried to do, it was after all a client/server ORM.&lt;/p&gt;&lt;/blockquote&gt; &lt;p&gt;I am not seeing how you can share a business layer from multiply application in a single application. You can build a web service that would get the data, already processed, but that is not sharing the business layer from another application, that is accessing that other application for the data. I mentioned security as well, row and column level security that is bounded by business rules, Alex gave much the same answer for this as well, and I have the same answer, I can't see it working after the first change.&lt;/p&gt; &lt;p&gt;Cross databases FK:&lt;/p&gt; &lt;blockquote&gt; &lt;p&gt;Until relational databases matured I am sure the management of standard FK’s was tricky too, we need to recognize that we are the age of distribution, all the data necessary often can’t be in one place, it is an interconnected world and we need to understand this. This is why we need data-servers not just databases.&lt;/p&gt;&lt;/blockquote&gt; &lt;p&gt;Nevertheless, this doesn't solve the problem. And trying to do cross DB FKs is a painful performance issue, with no real good answers. That leads well into the problem of performance when you need to pull the data from disparate sources:&lt;/p&gt; &lt;blockquote&gt; &lt;p&gt;Yes doing joins across databases is likely to be slow, but using this performance issue, to say it shouldn't be done is a little like saying downloading a movie from the internet takes a long time,  so we should never do it. I don't care if it takes a long time, I want to see the damn movie!&lt;/p&gt;&lt;/blockquote&gt; &lt;p&gt;That is fine and good, but saying that you don't care about performance stops when you realize that you &lt;em&gt;have&lt;/em&gt; to send the result back to the client in 500ms or less. There were few to none movie downloads in the 90s, because download speed and cost made it impractical in the extreme. Only when it got to the point where you could download a movie fairly easily is started to be really popular.&lt;/p&gt; &lt;p&gt;There are two kinds of performance problems, the small that you don't care about in advance (calling the DB in a loop is a favorite of mine) and the architectural ones that you really want to at least consider ahead of time. Calling external resources is right there on the top of the list as far as I am concerned. If in order to serve a request I need to make 5 requests &lt;em&gt;by design&lt;/em&gt;, then I am going to think it really hard before I go on. It is simply not the place I want to start with.&lt;/p&gt; &lt;blockquote&gt; &lt;p&gt;Just because something has issues, doesn’t mean it isn’t useful. I am not claiming that data layer componentization solves every problem; rather that it is a very nice trick to have in the arsenal.&lt;/p&gt;&lt;/blockquote&gt; &lt;p&gt;No argument here, I certainly think that Alex has a lot of merit here, I am trying to point out the other side. I don't think that this is something that is quite relevant yet.&lt;/p&gt; &lt;p&gt;Alex then went own to point the flaws in my own preferred approach, bringing all the data into the application's DB using some sort of an ETL process:&lt;/p&gt; &lt;blockquote&gt; &lt;p&gt;For example Ayende's preferred route of using an &lt;a href="http://en.wikipedia.org/wiki/Extract,_transform,_load"&gt;ETL&lt;/a&gt; process to create local copies. &lt;/p&gt;&lt;ol&gt; &lt;li&gt;More often than not developers don't actually know that there is another database that has the information they need or simply don't bother with an ETL process anyway. &lt;/li&gt;&lt;li&gt;Often times every database thinks it is the master, and the natural result is no database can be trusted. &lt;/li&gt;&lt;li&gt;Sometimes It is just not practical to duplicate all data, sometimes there is just too much, or sometimes the cost of transfer is too high, or it takes too long to do it, or you can't tolerate dirty data, or you have too many places to copy that data. &lt;/li&gt;&lt;li&gt;In order to avoid (2), unwieldy and error prone ‘human’ processes need to develop, people need to remember to go into 2, 3, 6, 15 (?) systems to keep everything in sync.&lt;/li&gt;&lt;/ol&gt;&lt;/blockquote&gt; &lt;p&gt;To answer the points in order:&lt;/p&gt; &lt;ol&gt; &lt;li&gt;If they don't know there is another DB, how would they access the data at the first place. I find it hard to believe that a developer will think "Hm, I guess I need the customer data, let us just invent one"&lt;/li&gt; &lt;li&gt;Defining who is the master of the data is often critical, absolutely. Master-Master merging can be a PITA of a high order.&lt;/li&gt; &lt;li&gt;I can usually copy parts of the data, or do diffs, to decrease the cost of moving the data. The point about stale data is a very good one, I'll answer it in the end of this post.&lt;/li&gt; &lt;li&gt;I don't think so, in order to avoid the master/master problem you need to define who the master is, and how you go about updating that piece of the data.&lt;/li&gt;&lt;/ol&gt; &lt;p&gt;&lt;a href="http://ayende.com/Blog/images/ayende_com/Blog/WindowsLiveWriter/MoreonDataLayerComponentization_A14/image_3.png"&gt; &lt;img alt="image" src="http://ayende.com/Blog/images/ayende_com/Blog/WindowsLiveWriter/MoreonDataLayerComponentization_A14/image_thumb_3.png" align="right" border="0" /&gt; Diego's post&lt;/a&gt; is a more thorough analysis of why taking the ETL approach to the extreme is not a good idea. He focus on the cost of making a change to the system after it was deployed, specifically, what happen when you have N ETL processes that needs to be modified as a result of a schema change?&lt;/p&gt; &lt;p&gt;The cheeky answer to that, have the ETL work of the conceptual schema, at which point you have a single place to go and modify, the ETL processes. &lt;/p&gt; &lt;p&gt;But no, I don't think that I would recommend this approach for all the systems in an organization, assuming N systems that needs each other information, you would have NxN processes, and that is problematic. &lt;/p&gt; &lt;p&gt;So, what is my view of the data server? Well, let us start by removing the data, and then the server.&lt;/p&gt; &lt;p&gt;I have said it before, a application database is an implementation detail, you don't want outsiders to start pawing over your data. The way I consider it, define a set of web services for each application, that allows external applications to use the data in the application. If you needs BA reports, ETL the data to a reporting database, you are likely to want to that anyway.&lt;/p&gt; &lt;p&gt;No, I didn't drink the WS koolaid, and I am not really happy that I am suggesting a WS as a "Solution", but that is just my biases against buzzwards. At its heart, this is the same idea, but with WS instead of data, and with the conceptual schema captured as XML messages.&lt;/p&gt; &lt;p&gt;The major difference is that I have more tools at my disposal to work with WS than with databases. Versioning, for one, is something that isn't trivial by any means, but it is something that WS does much better than databases. Most of the other concerns are also mitigated by this approach. Business level decisions, be it logic or security, for instance, are enforced by the service, which is part of the application, so changing that would immediately affect everything else. &lt;/p&gt; &lt;p&gt;But, didn't I just said that I want to &lt;em&gt;minimize &lt;/em&gt;the amount of external dependencies that I have? Doesn't it count as the same thing?&lt;/p&gt; &lt;p&gt;Yes and no. I wouldn't want my application to talk to a dozen services any more than I would want it to talk to a dozen databases. That is where service aggregator come into place. They aggregate related services into a single place. This is the place where I would apply caching, for instance, but not where I would put business logic. This is also a good place to decide what to do about downed service. I can either use a cached value, retry a number of times, try an alternate method, or fail the whole operation.&lt;/p&gt; &lt;p&gt;If this looks like an ESB, I am sorry about it, this is not the intention. I view the service aggregator as a way to aggregate the information from disparate sources into a single place. I can certainly see a way to create some framework for this, and rollout an aggregator per application, to meet that application need. Although I think that several core services or core aggregators should do for most of what the applications need to do.&lt;/p&gt; &lt;p&gt;And yes, that is not my ideal, but I can't really see a way to work with disparate sources without handling this disparity &lt;em&gt;somewhere, &lt;/em&gt;so I don't see a way around that.&lt;/p&gt;&lt;img src="http://ayende.com/Blog/aggbug/9744.aspx" width="1" height="1" /&gt;</description>
            <dc:creator>Ayende Rahien</dc:creator>
            <guid>http://ayende.com/Blog/archive/2007/10/07/More-on-Data-Layer-Componentization.aspx</guid>
            <pubDate>Sat, 06 Oct 2007 23:56:44 GMT</pubDate>
            <comments>http://ayende.com/Blog/archive/2007/10/07/More-on-Data-Layer-Componentization.aspx#feedback</comments>
            <slash:comments>1</slash:comments>
            <wfw:commentRss>http://ayende.com/Blog/comments/commentRss/9744.aspx</wfw:commentRss>
        </item>
        <item>
            <title>Schema to wince by...</title>
            <link>http://ayende.com/Blog/archive/2007/10/03/Schema-to-wince-by.aspx</link>
            <description>&lt;p&gt;I was just sent this table schema (minor modifications to protect the responsible party):&lt;/p&gt; &lt;blockquote&gt;&lt;pre&gt;&lt;a style="color: #0000ff" href="http://search.microsoft.com/default.asp?so=RECCNT&amp;amp;siteid=us%2Fdev&amp;amp;p=1&amp;amp;nq=NEW&amp;amp;qu=CREATE&amp;amp;IntlSearch=&amp;amp;boolean=PHRASE&amp;amp;ig=01&amp;amp;i=09&amp;amp;i=99"&gt;CREATE&lt;/a&gt; &lt;a style="color: #0000ff" href="http://search.microsoft.com/default.asp?so=RECCNT&amp;amp;siteid=us%2Fdev&amp;amp;p=1&amp;amp;nq=NEW&amp;amp;qu=TABLE&amp;amp;IntlSearch=&amp;amp;boolean=PHRASE&amp;amp;ig=01&amp;amp;i=09&amp;amp;i=99"&gt;TABLE&lt;/a&gt; [dbo].[tblEmployees] 
(
           [iEmployeeSirial] [&lt;a style="color: #0000ff" href="http://search.microsoft.com/default.asp?so=RECCNT&amp;amp;siteid=us%2Fdev&amp;amp;p=1&amp;amp;nq=NEW&amp;amp;qu=int&amp;amp;IntlSearch=&amp;amp;boolean=PHRASE&amp;amp;ig=01&amp;amp;i=09&amp;amp;i=99"&gt;int&lt;/a&gt;] &lt;a style="color: #0000ff" href="http://search.microsoft.com/default.asp?so=RECCNT&amp;amp;siteid=us%2Fdev&amp;amp;p=1&amp;amp;nq=NEW&amp;amp;qu=IDENTITY&amp;amp;IntlSearch=&amp;amp;boolean=PHRASE&amp;amp;ig=01&amp;amp;i=09&amp;amp;i=99"&gt;IDENTITY&lt;/a&gt; (1, 1) &lt;a style="color: #0000ff" href="http://search.microsoft.com/default.asp?so=RECCNT&amp;amp;siteid=us%2Fdev&amp;amp;p=1&amp;amp;nq=NEW&amp;amp;qu=NOT&amp;amp;IntlSearch=&amp;amp;boolean=PHRASE&amp;amp;ig=01&amp;amp;i=09&amp;amp;i=99"&gt;NOT&lt;/a&gt; &lt;a style="color: #0000ff" href="http://search.microsoft.com/default.asp?so=RECCNT&amp;amp;siteid=us%2Fdev&amp;amp;p=1&amp;amp;nq=NEW&amp;amp;qu=NULL&amp;amp;IntlSearch=&amp;amp;boolean=PHRASE&amp;amp;ig=01&amp;amp;i=09&amp;amp;i=99"&gt;NULL&lt;/a&gt; ,
           [vcEmployeeID] [&lt;a style="color: #0000ff" href="http://search.microsoft.com/default.asp?so=RECCNT&amp;amp;siteid=us%2Fdev&amp;amp;p=1&amp;amp;nq=NEW&amp;amp;qu=nvarchar&amp;amp;IntlSearch=&amp;amp;boolean=PHRASE&amp;amp;ig=01&amp;amp;i=09&amp;amp;i=99"&gt;nvarchar&lt;/a&gt;] (15) &lt;a style="color: #0000ff" href="http://search.microsoft.com/default.asp?so=RECCNT&amp;amp;siteid=us%2Fdev&amp;amp;p=1&amp;amp;nq=NEW&amp;amp;qu=COLLATE&amp;amp;IntlSearch=&amp;amp;boolean=PHRASE&amp;amp;ig=01&amp;amp;i=09&amp;amp;i=99"&gt;COLLATE&lt;/a&gt; Hebrew_CI_AI &lt;a style="color: #0000ff" href="http://search.microsoft.com/default.asp?so=RECCNT&amp;amp;siteid=us%2Fdev&amp;amp;p=1&amp;amp;nq=NEW&amp;amp;qu=NULL&amp;amp;IntlSearch=&amp;amp;boolean=PHRASE&amp;amp;ig=01&amp;amp;i=09&amp;amp;i=99"&gt;NULL&lt;/a&gt; ,
           [vcEmployeeName] [&lt;a style="color: #0000ff" href="http://search.microsoft.com/default.asp?so=RECCNT&amp;amp;siteid=us%2Fdev&amp;amp;p=1&amp;amp;nq=NEW&amp;amp;qu=nvarchar&amp;amp;IntlSearch=&amp;amp;boolean=PHRASE&amp;amp;ig=01&amp;amp;i=09&amp;amp;i=99"&gt;nvarchar&lt;/a&gt;] (50) &lt;a style="color: #0000ff" href="http://search.microsoft.com/default.asp?so=RECCNT&amp;amp;siteid=us%2Fdev&amp;amp;p=1&amp;amp;nq=NEW&amp;amp;qu=COLLATE&amp;amp;IntlSearch=&amp;amp;boolean=PHRASE&amp;amp;ig=01&amp;amp;i=09&amp;amp;i=99"&gt;COLLATE&lt;/a&gt; Hebrew_CI_AI &lt;a style="color: #0000ff" href="http://search.microsoft.com/default.asp?so=RECCNT&amp;amp;siteid=us%2Fdev&amp;amp;p=1&amp;amp;nq=NEW&amp;amp;qu=NOT&amp;amp;IntlSearch=&amp;amp;boolean=PHRASE&amp;amp;ig=01&amp;amp;i=09&amp;amp;i=99"&gt;NOT&lt;/a&gt; &lt;a style="color: #0000ff" href="http://search.microsoft.com/default.asp?so=RECCNT&amp;amp;siteid=us%2Fdev&amp;amp;p=1&amp;amp;nq=NEW&amp;amp;qu=NULL&amp;amp;IntlSearch=&amp;amp;boolean=PHRASE&amp;amp;ig=01&amp;amp;i=09&amp;amp;i=99"&gt;NULL&lt;/a&gt; ,
           [vcAddress] [&lt;a style="color: #0000ff" href="http://search.microsoft.com/default.asp?so=RECCNT&amp;amp;siteid=us%2Fdev&amp;amp;p=1&amp;amp;nq=NEW&amp;amp;qu=nvarchar&amp;amp;IntlSearch=&amp;amp;boolean=PHRASE&amp;amp;ig=01&amp;amp;i=09&amp;amp;i=99"&gt;nvarchar&lt;/a&gt;] (60) &lt;a style="color: #0000ff" href="http://search.microsoft.com/default.asp?so=RECCNT&amp;amp;siteid=us%2Fdev&amp;amp;p=1&amp;amp;nq=NEW&amp;amp;qu=COLLATE&amp;amp;IntlSearch=&amp;amp;boolean=PHRASE&amp;amp;ig=01&amp;amp;i=09&amp;amp;i=99"&gt;COLLATE&lt;/a&gt; Hebrew_CI_AI &lt;a style="color: #0000ff" href="http://search.microsoft.com/default.asp?so=RECCNT&amp;amp;siteid=us%2Fdev&amp;amp;p=1&amp;amp;nq=NEW&amp;amp;qu=NULL&amp;amp;IntlSearch=&amp;amp;boolean=PHRASE&amp;amp;ig=01&amp;amp;i=09&amp;amp;i=99"&gt;NULL&lt;/a&gt; ,
           [vcCity] [&lt;a style="color: #0000ff" href="http://search.microsoft.com/default.asp?so=RECCNT&amp;amp;siteid=us%2Fdev&amp;amp;p=1&amp;amp;nq=NEW&amp;amp;qu=nvarchar&amp;amp;IntlSearch=&amp;amp;boolean=PHRASE&amp;amp;ig=01&amp;amp;i=09&amp;amp;i=99"&gt;nvarchar&lt;/a&gt;] (30) &lt;a style="color: #0000ff" href="http://search.microsoft.com/default.asp?so=RECCNT&amp;amp;siteid=us%2Fdev&amp;amp;p=1&amp;amp;nq=NEW&amp;amp;qu=COLLATE&amp;amp;IntlSearch=&amp;amp;boolean=PHRASE&amp;amp;ig=01&amp;amp;i=09&amp;amp;i=99"&gt;COLLATE&lt;/a&gt; Hebrew_CI_AI &lt;a style="color: #0000ff" href="http://search.microsoft.com/default.asp?so=RECCNT&amp;amp;siteid=us%2Fdev&amp;amp;p=1&amp;amp;nq=NEW&amp;amp;qu=NULL&amp;amp;IntlSearch=&amp;amp;boolean=PHRASE&amp;amp;ig=01&amp;amp;i=09&amp;amp;i=99"&gt;NULL&lt;/a&gt; ,
	   // many more fields
)&lt;/pre&gt;&lt;/blockquote&gt;
&lt;p&gt;I am not going to enjoy the integration phase...&lt;/p&gt;&lt;img src="http://ayende.com/Blog/aggbug/9725.aspx" width="1" height="1" /&gt;</description>
            <dc:creator>Ayende Rahien</dc:creator>
            <guid>http://ayende.com/Blog/archive/2007/10/03/Schema-to-wince-by.aspx</guid>
            <pubDate>Tue, 02 Oct 2007 21:12:44 GMT</pubDate>
            <comments>http://ayende.com/Blog/archive/2007/10/03/Schema-to-wince-by.aspx#feedback</comments>
            <slash:comments>15</slash:comments>
            <wfw:commentRss>http://ayende.com/Blog/comments/commentRss/9725.aspx</wfw:commentRss>
        </item>
        <item>
            <title>Re: Saving to Blob</title>
            <link>http://ayende.com/Blog/archive/2007/08/18/Re-Saving-to-Blob.aspx</link>
            <description>&lt;p&gt;&lt;a href="http://www.matshelander.com/wordpress/"&gt;Mats&lt;/a&gt; (NPersist) is talking about &lt;a href="http://www.matshelander.com/wordpress/?p=66"&gt;Saving to Blob&lt;/a&gt;, basically suggesting that you can turn your persistence strategy into Table(Id, Blob):&lt;/p&gt; &lt;blockquote&gt; &lt;p&gt;An alternative would be to serialize the non-identity fields into a blob of some kind and save that to just one column in the database. The database table would then have primary key columns matching the identity fields and then just one more blob column for the serialized non-identity fields of the object.&lt;/p&gt;&lt;/blockquote&gt; &lt;p&gt;Since this basically prevent any useful filtering on the data, he suggest that you can extract columns outside the blob, much in the way we currently do with indexes.&lt;/p&gt; &lt;p&gt;As far as I am concerned, this is the worst possible persistence strategy. It means that you have taken some of the great strengths of the database and turned it into a stupid file server, for no real benefit. Mats suggest that this can have positive performance implications, but I doubt that this is the case. Just consider the cost of doing something as simple as providing a sortable, paged, grid using this approach. If the user has chosen to sort on a column that is inside the blob, then you would need to pull &lt;em&gt;all&lt;/em&gt; the table in memory, sort it, and then hand the application just the page it is interested in.&lt;/p&gt; &lt;p&gt;It also completely destroy the possibility of working with the database with database oriented tools. I can go into my DBs and &lt;em&gt;look&lt;/em&gt; at the tabular data, but 0x20495bacfca12312 doesn't have much meaning to me. ETL tools, reporting, etc are going to become useless. Not to mention that now you are in versioning hell. Serialization is notoriously difficult to handle correct across versions, and to have all the data in the DB in a format that is sensitive to serialization is &lt;em&gt;quite &lt;/em&gt;a problem.&lt;/p&gt; &lt;p&gt;Handling associations between objects is another issue, I can use the DB's FK capabilities if I am using the standard mode, but a blob can have no FK enforcement by the DB.  In fact, I can't really think of a good reason to do this, and I can think of plenty of reasons &lt;em&gt;not&lt;/em&gt; to.&lt;/p&gt; &lt;p&gt;As far as I know, there were "Object &amp;lt;-&amp;gt; Blob Mappers" on the Java side at one time, and they are one of the points that always come up when an argument about OR/M come up, because that is a really bad way to handle this requirement.&lt;/p&gt; &lt;blockquote&gt; &lt;p&gt;Note: There is a really good article on this issue with Java Serialized Objects &amp;amp; Reporting that I have read a few years ago in an Oracle site, but I can't find it.&lt;/p&gt;&lt;/blockquote&gt;&lt;img src="http://ayende.com/Blog/aggbug/9621.aspx" width="1" height="1" /&gt;</description>
            <dc:creator>Ayende Rahien</dc:creator>
            <guid>http://ayende.com/Blog/archive/2007/08/18/Re-Saving-to-Blob.aspx</guid>
            <pubDate>Sat, 18 Aug 2007 17:11:46 GMT</pubDate>
            <comments>http://ayende.com/Blog/archive/2007/08/18/Re-Saving-to-Blob.aspx#feedback</comments>
            <slash:comments>19</slash:comments>
            <wfw:commentRss>http://ayende.com/Blog/comments/commentRss/9621.aspx</wfw:commentRss>
        </item>
        <item>
            <title>Re: SSIS - 15 Faults Rebuttal</title>
            <link>http://ayende.com/Blog/archive/2007/07/27/Re-SSIS--15-Faults-Rebuttal.aspx</link>
            <description>&lt;p&gt;Seems like this is the new trend right now :-) &lt;a href="http://www.ssistalk.com/"&gt;Phil Brammer&lt;/a&gt; has &lt;a href="http://www.ssistalk.com/2007/07/27/ssis-15-faults-rebuttal/"&gt;posted a rebuttal&lt;/a&gt; to my &lt;a href="http://ayende.com/Blog/archive/2007/07/15/SSIS-15-Faults.aspx"&gt;original post&lt;/a&gt;. Here are my answers:&lt;/p&gt; &lt;blockquote&gt; &lt;p&gt;&lt;strong&gt;Bad Errors:&lt;/strong&gt;&lt;br /&gt;You have to understand though, that this isn’t .Net.  SSIS has many components/engines at work that obtaining the correct error isn’t always at the heart of the SSIS engine.  It could be a database error.  It could be an ADO error.  Whatever it may be, I agree, some are cryptic, but I’ve generally been able to diagnose my errors.  And if there is an error I don’t know about, I contact the community and finally Microsoft through product support. &lt;/p&gt;&lt;/blockquote&gt; &lt;p&gt;Error handling is a key part of any platform, this should be a first level concern, that it "isn't always at the heart of the SSIS engine" is a serious issue. Errors happens, and I need to know the &lt;em&gt;entire chain&lt;/em&gt; of issues in order to figure how to fix it. To clarify how important that is to me, I am using .Net because it has exceptions, rather than HRESULT. &lt;/p&gt;&lt;blockquote&gt; &lt;p&gt;&lt;strong&gt;Random Errors:&lt;/strong&gt;&lt;br /&gt;The fact these are random should banish this item from your “SSIS 15 Faults” list.&lt;/p&gt;&lt;/blockquote&gt; &lt;p&gt;The product sometimes gives an error, for no deterministic reason, and you consider this a non issue?&lt;/p&gt; &lt;blockquote&gt; &lt;p&gt;&lt;strong&gt;Keeping track of what it shouldn’t:&lt;/strong&gt;&lt;br /&gt;Never had this happen.  NEVER.  NEVER, NEVER, NEVER, NEVER.  Did I make my point? &lt;/p&gt;&lt;/blockquote&gt; &lt;p&gt;Yeah, your point is "It works on my machine". &lt;/p&gt;&lt;blockquote&gt; &lt;p&gt;&lt;strong&gt;Sorry excuse for deployment:&lt;/strong&gt;&lt;br /&gt;I don’t understand any of the points made here.  Deploying to a server has never been a problem.  It is in the manual that metadata cannot change between databases/tables.  So if you’re moving to an environment that has its metadata different than another, you need to reconcile that first. &lt;/p&gt;&lt;/blockquote&gt; &lt;p&gt;I had the package fail with validation errors when moving to production when the DB schema was identical, invalid meta data errors. I don't &lt;em&gt;want&lt;/em&gt; to reconcile anything, the schema is identical for the tables that I am working on. And let me point out that "it is in the manual" carry little weight with me, I am disliking the need, not disputing its documentation.&lt;/p&gt; &lt;blockquote&gt; &lt;p&gt;&lt;strong&gt;UI Formatting instructions: &lt;/strong&gt;&lt;br /&gt;Ah, well, what would you say if the SSIS dev team decided to make SSIS packages binary?  THEN WHAT?  At least you have an XML file that can be parsed.&lt;/p&gt;&lt;/blockquote&gt; &lt;p&gt;Uh, you &lt;em&gt;do&lt;/em&gt; realize that for all intents and purposes, the file &lt;strong&gt;is&lt;/strong&gt; binary, right? The fact that it happens to be XML based doesn't make it human readable, source control friendly or diffable. All of the above are &lt;em&gt;critical&lt;/em&gt; to development.&lt;/p&gt; &lt;blockquote&gt; &lt;p&gt;&lt;strong&gt;Bad interoperability:&lt;/strong&gt;&lt;br /&gt;Is your Oracle example an SSIS problem, or the Oracle driver’s problem?????????&lt;/p&gt;&lt;/blockquote&gt; &lt;p&gt;That is &lt;em&gt;probably&lt;/em&gt; an SSIS error, but see my previous point about bad errors to know what I am not sure.&lt;/p&gt;&lt;img src="http://ayende.com/Blog/aggbug/9565.aspx" width="1" height="1" /&gt;</description>
            <dc:creator>Ayende Rahien</dc:creator>
            <guid>http://ayende.com/Blog/archive/2007/07/27/Re-SSIS--15-Faults-Rebuttal.aspx</guid>
            <pubDate>Fri, 27 Jul 2007 20:13:35 GMT</pubDate>
            <comments>http://ayende.com/Blog/archive/2007/07/27/Re-SSIS--15-Faults-Rebuttal.aspx#feedback</comments>
            <slash:comments>2</slash:comments>
            <wfw:commentRss>http://ayende.com/Blog/comments/commentRss/9565.aspx</wfw:commentRss>
        </item>
    </channel>
</rss>