SQL Server - Insert Not Working

Just wanted to share a bit of fun I had yesterday. (Actually it wasn't very fun). I had a cfquery that was supposed to copy "Template" records linked to one entity inserting them linked to another entity. So, the file was using an Insert/Select. Never seen one? OK, it looks like this.

<cfquery datasource="#request.DSN#">
      INSERT INTO
         [Template]
         (
            TemplateName,
            OwnerID,
            ...
         )
      SELECT
         TemplateName,
         <cfqueryparam value="#attributes.Target_OwnerID#" cfsqltype="cf_sql_integer">,
         ...
      FROM
         [Template]
      WHERE
         OwnerID = <cfqueryparam value="#attributes.Template_OwnerID#" cfsqltype="cf_sql_integer">
         ...
   </cfquery>

So, we can all agree that insert/select is a great thing. However, this particular one was not working (on the live server). I tried the normal things. I cut the SQL from the .cfm and pasted it into SQL Server Management Studio (SSMS), replaced the params with the values I knew it would be using and ran it (inside of a transaction, of course). Guess what? It worked. So, it works running in SSMS but, not in a cfquery. Hmmm... So, next, I added an "output" clause to the insert/select. That way I could see any records it would be inserting, just to make sure it really wasn't inserting them. Then I added a cfdump that would only run for me. "What's an output clause?" you say. Here I'll show you.

<cfquery name="insTemplates" datasource="#request.DSN#">
      INSERT INTO
         [Template]
         (
            TemplateName,
            OwnerID,
            ...
         )
      OUTPUT
         INSERTED.*
      SELECT
         TemplateName,
         <cfqueryparam value="#attributes.Target_OwnerID#" cfsqltype="cf_sql_integer">,
         ...
      FROM
         [Template]
      WHERE
         OwnerID = <cfqueryparam value="#attributes.Template_OwnerID#" cfsqltype="cf_sql_integer">
         ...
   </cfquery>
   <cfdump var="#insTemplates#" label="insTemplates" />

I ran that and guess how many records were inserted... 0. At this point I'm a bit frustrated. But, it begins to dawn on me that I've seen similar behavior before from SQL Server. There are time when the SQL Server will cache a bad plan for some SQL. In these cases, you can either have the database flush it's entire plan cache or you can alter the SQL enough to force it to make a new plan for your SQL. So, I added "AND 1=1" to my "where" clause.

Guess what? It works now.

This is a fairly edge case. I have not seen it very often. But, if you are positive that a cfquery is not returning what it should, before you jump out of your office window, try adding something innocuous to your "where" clause.

If it does work, feel free to send some happy thoughts my way and curse MS under your breath. :-)

I'm an Adobe UG Co-Manager!

As part of a new Adobe initiatives user groups are supposed to have co-managers. So, I will be the new co-manager for the PDXRIA group. The title feels a little like "Assistant to the Regional Manager" ;-). And in more exiting news, Simeon Bateman will be stepping back up as the main manager.

We will both do our best to provide engaging presentations and valuable opportunities for networking. For more details see Simeon's blog.

P.S. I'm going to try and blog more often.

java.lang.ref.SoftReference in CFML

So, one of the coolest things I learned at the conference was about how Transfer does it's caching. Part of that is using the Java SoftReference class. Soft reference objects are cleared at the discretion of the garbage collector in response to memory demand. Let me show you what I mean.

<cfloop query="OrderedUserQry">
   <cfif NOT structKeyExists(application.UserCache, OrderedUserQry.UserIdentity)>
      <cfset user = dbService.getUserByID(OrderedUserQry.UserID) />
      <cfset application.UserCache[OrderedUserQry.UserIdentity] = user />
   </cfif>
   <cfset application.maxUserInt = OrderedUserQry.UserIdentity />
</cfloop>

This code mimics a caching mechanism that would put a hard reference in the application.UserCache to a "User" object. I just ran this repeatedly this morning until CF would not respond with anything but "java.lang.OutOfMemoryError". Fun yeah?

Change that code to this:

<cfloop query="OrderedUserQry">
   <cfif NOT structKeyExists(application.UserCache, OrderedUserQry.UserIdentity)>
      <cfset user = dbService.getUserByID(OrderedUserQry.UserID) />
      <cfset application.UserCache[OrderedUserQry.UserIdentity] = CreateObject('java','java.lang.ref.SoftReference').Init(user) />
   </cfif>
   <cfset application.maxUserInt = OrderedUserQry.UserIdentity />
</cfloop>

And you can run it all day long. I also created a page to go through the cache and get rid of keys that the soft-referenced object had been GCed. With help from SoftReference, the JVM was able to reclaim memory as needed. So, after I got to about 100,000 users having been put into cache, I ran the culling page and the cache only had arount 18,000 keys left.

<!--- Culling Code --->
<cfloop list="#StructKeyList(application.UserCache)#" index="key">
   <cfset user = application.UserCache[key].get() />
   <cfif NOT structKeyExists(variables,'user')>
      <cfset structDelete(application.UserCache,key) />
   </cfif>
</cfloop>

I definitely will be using this in caching systems in the future. There may be cases where you want a hard-reference cache. But, there are plenty more I think, where it would be fine if some objects in your cache were GCed to give the JVM the memory it needs.

cf.Objective() 2008: From Procedural to OO - Dan Wilson

One of the keywords here is "pragmatism". I know from personal, continuing experience that it can be a daunting proposition to move an application from procedural to OO.

Things you would want to consider when refactoring are in-house skill-sets, and problem spots in your application. If you have a bunch of people that are very procedural programmers, you may be wasting your time with a re-factor. You're team still has to be able to work on the code right? But, if people in your shop have some OO experience or are game to learn, then it's a good idea. Once you get started you want to ask yourself, "Where are our problem spots?". You already need to work on those spots anyway, so it's an efficient choice to start there in your OO refactor.

Always use version control! Even if you work alone! Seriously, however long it takes you to set up your version control repo of choice, it will be time well spent. You will need that version history while re-factoring.

Dan, talked some on patterns. There was a liberal sprinkling of pragmatism when talking about patterns too. Use them where they actually solve a problem and to the extent that you need them. Don't slavishly apply a pattern verbatim! I won't go into detail on the patterns. However, I'd like to mention, the first one (MVC) doesn't even require OO. However, it would be a good first step to get you closer to OO. Once you have separated the code that deals with the data (Model) from the code that displays stuff to the user (View) and the code that wires those together (Controller), then you're ready to see what can be put in Objects.

This was a talk with a lot of practical code examples. So, I think it may lose a bit in the translation.

Update: Dan was kind enough to send me a link to the presentation material. Get it here.

cf.Objective() 2008: Here We Go Again

I'm at cf.Objective() 2008, sitting in the conference room waiting for the keynote to start. Last years cf.Objective() was a great conference! I see no reason this year will be different. :-)

In 2007, I blogged every session I went to in real-time. It was a little stressful (and fun). I think this year I'll just take notes in the sessions and write them up later.

That's it for now. Hopefully you are all here with me and not even reading this post. (Or if you're reading it after the conf, you were here.)

Creating A CFC Generator Object With Illudium

In my last post I showed the raw code that you would need to call the Illudium PU-36 Code Generator outside of it's Flex front-end. That code is just ripe for abstraction. Really all you want is an object with a method signature that takes maybe a com-path, datasource, and table name and does the work of generating the CFCs for you right? OK, now that we're all agreed. The CFC is attached in a zip (just click the "download" link down yonder).

And here is some sample code showing how easy it is to use now that it's packaged up.

<cfset generator = CreateObject('component','_testing.CFC.generator') />
<cfset generator.init('YOURCFADMINPASSWORD','_testing.CFC.generated') />
<cfset generator.GenerateTableCFCs(DataSource='YOURDATASOURCE', Table='SOMETABLE') />

There is one thing that you might want to change depending on your usage. The generator is set up to generate two sets of CFCs based off of two Illudium template sets. A base/volatile set and a custom/non-volatile set. It will always overwrite the base/volatile set. But, it will only write the custom/non-volatile set if it does not exist.

Disclaimer: You will most likely need to edit the CFC a little for pathing issues. And this will not work unless you have patched Illudium per my previous article.(Or until Brian rolls the patch in, which i think he's gonna).

More Entries

BlogCFC was created by Raymond Camden. This blog is running version 5.6.002.