Thursday, April 28, 2011

Lost usernames

mattmc3 was the handle I chose back in 1997 to use AOL's instant messenger.  Since then, I've tried to sign up as mattmc3 for nearly everything I could, but unfortunately I've not been very successful.  While I happily have the e-mail addresses I want, other services like twitter, slashdot, banks, and various other places mattmc3 was already taken.  It's funny how your online handle can force you to sign up early for things that you have absolutely no interest in just to turf your username.  Interestingly, I think this is part of the reason I don't do twitter.  Not only is the content there mostly useless, the guy who got my mattmc3 username is peddling horoscopes, which I have to wonder if someone looking me up by username wouldn't mistake for me or I for him.  When some rival service (called, say, Sylvester - the hunter of Tweety birds), I'll be ready to sign up... not to use the service, but just to defend my good (user)name.

Wednesday, April 27, 2011

Digital Cable and the DVR

Our TV is an old hand-me-down Zenith from 20-some years ago.  I couldn't care less about digital cable.  But, when the cable company sent us a letter and numerous e-mails telling us that we needed a converter and that our channels were destined for digital cable, I started to worry.  I ordered the (free) converter, and waited.

It didn't take long to figure out that the converter wouldn't work along with the DVR unless I wanted to change the channel on the converter prior to every show being recorded.  Not gonna happen.  I needed an alternative.  Of course, not knowing how the digital cable thing works I had to now figure out what to do.  Are they encrypting the signal to force me to use a cablecard or their DVR?  What is ATSC? QAM? These things make no sense to me.  Thankfully, I discovered that the tv tuner card I bought has a separate port for digital cable.  Cool.  Split the cable, plug the analog one back in and use the other one on the digital line already in the card I bought and haven't touched in 2 years.  Scan for new channels and all is good.  Scary how easy this was, so crossing my fingers and waiting for the other shoe to drop.  I think I just need to re-setup my recordings.  Not bad.

Monday, April 25, 2011

String or binary data would be truncated

This, in my opinion, is the most frustrating error you can get in SQL Server.  It's the error you get when you're trying to do an "INSERT INTO (...) SELECT (...)" to push a bunch of records from a query result into a table.  If one of your (n)varchar fields is too small to hold one of the values, you get this lovely error.  It's nice that SQL Server won't truncate your fields for you, but frustrating that you get this sad little error with no details.  You don't know which field is causing the problem, and if you're inserting into a large number of fields, it's completely frustrating to figure out what caused the issue.  Especially when SQL Server could easily have done that heavy lifting for you and told you in the error details.

I've hit this error enough that I have developed a simple technique for troubleshooting this.  It's not fancy, and you could probably think of something better, but this works and gets me to my resolution in a few seconds so I thought it would be worth sharing.  The trick is to turn your "INSERT INTO (...) SELECT (...)" into a "SELECT (...) INTO _BadDataTable_ (...)" statement.  The SELECT INTO will create a new table for you on the fly, assuming that you use the AS clause in your SELECT to name your fields the same as the destination table you're looking to INSERT INTO.

From there, you can then run this simple SQL script changing the values of @prodTable and @invalidTable into the real names of the destination table and the one you made on the fly containing the bad data.  Kinda hackish, but it works and that's all I'm looking for.  Note that you have to make a real table with the fields named the same for this to work.  Feel free to take and modify to suit your needs, and here's to hoping the next release of SQL Server will fix this completely arcane error:

-- setup
declare
    @prodTable varchar(100) = 'MyDestTable', -- CHANGE THIS!!!
    @invalidTable varchar(100) = '_BadDataTable_', -- AND THIS!!!
    @c cursor,
    @column_name varchar(255),
    @max_len int

if object_id('tempdb..#max_len') is not null drop table #max_len
create table #max_len (max_len int)

-- get schema for prod table
if object_id('tempdb..#prod_table_schema') is not null drop table #prod_table_schema
select
    ordinal_position,
    column_name,
    data_type,
    character_maximum_length
into
    #prod_table_schema
from
    INFORMATION_SCHEMA.COLUMNS a
where
    a.table_name = @prodTable
order by 1

-- get schema for table made from invalid data with the trucate error
if object_id('tempdb..#invalid_table_schema') is not null drop table #invalid_table_schema
select
    ordinal_position,
    column_name,
    data_type,
    cast(null as int) as character_maximum_length,
    case when character_maximum_length is null then 0 else 1 end as is_char_field
into
    #invalid_table_schema
from
    INFORMATION_SCHEMA.COLUMNS a
where
    a.table_name = @invalidTable
order by 1

-- we need to chase after the max(len(COL)) info with a dynamic query
set @c = cursor local fast_forward for
    select a.column_name
    from #invalid_table_schema a
    where a.is_char_field = 1
    order by a.ordinal_position
open @c

fetch next from @c into @column_name
while @@fetch_status = 0 begin
    -- get the maximum data length of the field from the table
    delete #max_len
    insert into #max_len
    exec('select max(len(' + @column_name + ')) as max_len from ' + @invalidTable)
    select @max_len = max_len from #max_len
   
    update #invalid_table_schema
    set character_maximum_length = @max_len
    where column_name = @column_name

    fetch next from @c into @column_name
end
close @c
deallocate @c

-- Tell me which fields have the problem
select
    a.column_name,
    a.character_maximum_length,
    b.character_maximum_length as actual_length_of_data
from
    #prod_table_schema a join
    #invalid_table_schema b on
        a.column_name = b.column_name
where
    a.character_maximum_length < b.character_maximum_length

Sunday, April 24, 2011

VB.NET finally gets the Yield statement

I've found myself multiple times in the last four years having to defend Visual Basic against the disgusted reactions of some of my C# developer friends.  I think most people who have to pinch their noses at the term "VB" aren't really doing it towards the language, but more towards the stereotypical perception of a what they believe to be the quality of VB developers in general.  It's easier to argue technical merits than stereotypical opinion, other than to say that good developers are versatile and objective when evaluating technologies and each other.

I admit that it took me about a week or two when I first started to swallow my pride and accept that we have a pretty extensive codebase written in VB, and there's very little compelling reason to change it.  And now, I actually really enjoy it almost as much as C#... except for lambdas... ug, so verbose.  Anyway, over time C# and VB have converged and the similarities far outweigh the differences.  If you can do it in C#, you can almost certainly do it in VB with a few notable exceptions.  In fact, since getting auto-properties and option infer, lately I've been describing VB thusly:

VB really is no different than C#.  You have all the same libraries.  It's a little bit more verbose in places, but a lot more readable in others.  Once you get used to case-insensitive languages, you'll wonder why there aren't more of them out there.  The only features you might miss are the dynamic and yield keywords, and unsafe if you ever use it (which you shouldn't be).  Actually, dynamic features have been available in VB since the beginning, but they are file scoped instead of variable scoped.  So Yield is really the only thing you'll wish you could do, but can't.

And now, at long last - with the new Microsoft Visual Studio Async CTP (SP1 Refresh), we've finally gotten a Yield keyword in VB.  This changes my whole elevator pitch.

I've installed it, and toyed with it, and it works exactly as you'd expect.  Here's the arbitrary infinite Fibonacci sequence hacked together in 30 seconds in VB:


Module Module1

 Sub Main()
  For Each i In GetFib()
   If i > 500 Then Exit For
   Console.WriteLine(i)
  Next
  Console.WriteLine("Done...")
  Console.ReadKey(True)
 End Sub

 Public Iterator Function GetFib() As IEnumerable(Of Long)
  ' Forget math overflow... this is only a test
  Yield 0
  Yield 1
  Dim previous = 0, current = 1
  While True
   Dim nextVal = previous + current
   previous = current
   current = nextVal
   Yield current
  End While

 End Function

End Module 

It's only 6 years overdue, but still such a welcome addition to the VB family. I can now finally get rid of my bloated iterator classes for my OrderedDictionary and my TreeNode classes. I can start porting projects like Dapper to VB without bloating the code. Thank you Microsoft. You've made my work life so much less painful.

Friday, April 1, 2011

Google Reader Bankruptcy

It's April 1st.  Nothing to see here today that's worth anything... move along.