Performing Query-Of-Queries Using ColdFusion 9's Query.cfc Component

Posted July 7, 2011 at 10:16 AM by Ben Nadel

Tags: ColdFusion

A couple of people have asked me about running ColdFusion query-of-queries within the new script-based query object (Query.cfc) in ColdFusion 9. Because the new Query.cfc is an actual ColdFusion component, it introduces some new encapsulation and scoping limitations not previously present in the CFQuery tag. These limitation can be easily overcome; however, if you don't understand how the code is being executed, these limitations can quickly confuse and frustate you.

Moving from CFQuery to Query.cfc involves more than a simple shift in syntax; it also requires a different understanding of execution context. To see what I mean, take a look at some code that would, at first glance, appear to be valid:

  • <!--- Build a query for getting all friends. --->
  • <cfset getFriends = new Query(
  • sql = "SELECT id, name FROM friend",
  • datasource = "testing"
  • ) />
  •  
  • <!--- Get the query results. --->
  • <cfset friends = getFriends.execute().getResult() />
  •  
  • <!--- Dump out the current result set. --->
  • <cfdump
  • var="#friends#"
  • label="ALL Friends"
  • />
  •  
  •  
  • <!--- ----------------------------------------------------- --->
  • <!--- ----------------------------------------------------- --->
  • <br />
  • <!--- ----------------------------------------------------- --->
  • <!--- ----------------------------------------------------- --->
  •  
  •  
  • <!---
  • Now, let's query our previous query, to get all of the friends
  • whose name ends with "a".
  • --->
  • <cfset getAFriends = new Query(
  • sql = "SELECT * FROM friends WHERE name LIKE '%a'",
  • dbtype = "query"
  • ) />
  •  
  • <!--- Get the query results. --->
  • <cfset aFriends = getAFriends.execute().getResult() />
  •  
  • <!--- Dump out the current result set. --->
  • <cfdump
  • var="#aFriends#"
  • label="[%A] Friends"
  • />

In the first query, we are gathering all "friends" from the database. Then, in our second query - our ColdFusion query-of-queries - we are filtering that "friends" record set on names that end in the letter "a". Syntax aside, this is exactly what we would do in previous versions of ColdFusion. Running this in ColdFusion 9, however, gives us the following error:

Error Executing Database Query. Query Of Queries runtime error. Table named friends was not found in memory. The name is misspelled or the table is not defined.

According to the error, the in-memory query engine cannot find the variable, "friends." However, if you look at our code, you can clearly see that we have a "friends" variable that references the original record set. This is true - but, our query-of-queries is not actually executing in the current page context. This is where the real mental shift in script-based components has to happen!

When a Query.cfc is created, SQL executed by that Query instance is executed in the context of the component. That means that the encapsulated CFQuery tag (what actually runs inside of the Query.cfc) executes in the context of the component's own Variables and This scope - the encapsulated CFQuery tag does not execute in the Variables scope of the calling page.


 
 
 

 
 Query.cfc-based queries do not have access to the Variables scope of the calling context - they execute within their own page context. 
 
 
 

This is where the CFQuery tag and the Query.cfc component really diverge - they have completely different execution contexts. In order to run a query-of-queries in the Query.cfc ColdFusion component, you have to pass the necessary in-memory tables into the Query.cfc context before you execute the in-memory SQL.

To do this, you simply have to understand how the Query.cfc (and any of the "script-based" ColdFusion tags) work. Within their init() methods, they copy the named arguments into the local Variables scope:

  • public com.adobe.coldfusion.query function init()
  • {
  • if(!structisempty(arguments))
  • {
  • structappend(variables,arguments,"yes");
  • }
  • return this;
  • }

As you can see, the init() method of the Query.cfc simply appends the arguments scope onto the Variables scope. This means that in order to inject our original friends record set into the query-of-queries execution context, we simply have to pass it in as a named argument to the second Query.cfc instance:

  • <!--- Build a query for getting all friends. --->
  • <cfset getFriends = new Query(
  • sql = "SELECT id, name FROM friend",
  • datasource = "testing"
  • ) />
  •  
  • <!--- Get the query results. --->
  • <cfset friends = getFriends.execute().getResult() />
  •  
  • <!--- Dump out the current result set. --->
  • <cfdump
  • var="#friends#"
  • label="ALL Friends"
  • />
  •  
  •  
  • <!--- ----------------------------------------------------- --->
  • <!--- ----------------------------------------------------- --->
  • <br />
  • <!--- ----------------------------------------------------- --->
  • <!--- ----------------------------------------------------- --->
  •  
  •  
  • <!---
  • Now, let's query our previous query, to get all of the friends
  • whose name ends with "a".
  •  
  • NOTE: This time, we are passing the [friends] query as a named
  • argument into the second query object. This way, it will be
  • injected into the encapsulated VARIABLES scope and will be
  • available in the SQL execution context.
  • --->
  • <cfset getAFriends = new Query(
  • sql = "SELECT * FROM friends WHERE name LIKE '%a'",
  • dbtype = "query",
  • friends = friends
  • ) />
  •  
  • <!--- Get the query results. --->
  • <cfset aFriends = getAFriends.execute().getResult() />
  •  
  • <!--- Dump out the current result set. --->
  • <cfdump
  • var="#aFriends#"
  • label="[%A] Friends"
  • />

As you can see, when we instantiate the second Query.cfc instance, we pass in our original friends variable as a named argument. This time, when we run the code, the friends query is available to the second, encapsulated CFQuery tag. Running the above code gives us the following output:


 
 
 

 
 Query of queries can still be performed using the Query.cfc ColdFusion component. 
 
 
 

This time, the second CFQuery tag - the one executing our ColdFusion query-of-queries - has access to the friends query stored in the Query.cfc's local Variables scope.

As far as I'm concerned, the CFQuery tag is far easier to use, read, and maintain than ColdFusion 9's new script-based Query.cfc ColdFusion component. I don't really see why anyone would want to jump through these kinds of hoops. That said, you can still use all of the features of the CFQuery tag, including ColdFusion query-of-queries, within the Query.cfc component; you just need to understand how everything is being executed.




Reader Comments

Jul 7, 2011 at 10:41 AM // reply »
148 Comments

Okay . . . where do I get the CFQuery.cfc?


Jul 7, 2011 at 10:47 AM // reply »
11,243 Comments

@Lola,

It's buried in your ColdFusion install somewhere. I'm running a multi-instance install and mine was at (broken up on a few lines):

JRUN/servers/cf9-main/cfusion.ear/cfusion.war/
-- WEB-INF/cfusion/
-- CustomTags/com/adobe/coldfusion/Query.cfc

It's basically in the custom-tag path so that it's in one of the places ColdFusion will search for components that aren't in the local folder.


Jul 8, 2011 at 9:49 AM // reply »
3 Comments

Good post Ben. Taking the whole query part out of it (just looking at what happened and why) - this is a perfect example of injecting a dependency.

The way Adobe handled it within this particular CFC I find quite elegant.


Jul 8, 2011 at 11:21 AM // reply »
11,243 Comments

@Michael,

Yeah, I think they dealt with it as best as could be.


Jul 16, 2011 at 8:02 AM // reply »
8 Comments

Ben, thanks for the very clear and concise post.

As for jumping thru hoops, the more I use cfscript, the more I prefer it for relatively complex logic. For me there is simply less visual noise on the page. In this case, I like having the capability to remain in cfscipt rather than mixing tags and script as necessary when I need to process a few queries along the way.


Jul 20, 2011 at 10:37 AM // reply »
11,243 Comments

@Nando,

I can believe that. I use JavaScript all the time, which is, obviously, script based. And, I love it. When it comes to ColdFusion, however, I just can't get into the script-based versions of things. Maybe I just have too much history with the language.

One of these days, I'll try to use CF9 for a site and just see how an all-script diet suits me.


Jul 27, 2011 at 5:02 AM // reply »
6 Comments

Thanks for the variable context explanation Ben, couldn't find any good info on this elsewhere!


Nov 13, 2011 at 2:06 AM // reply »
4 Comments

Ben, have you tried using CFWheels? I'm learning it as my first MVC framework and liking it, although the learning curve for building a site from scratch is steep.

Anyway the reason I mention it is that it's not all-or-nothing with cfscript, I'm using what I've heard is the common approach of using cfscript in models and controllers, and cf tags in views.

I think this approach makes it easier to code complex business logic as @Nando mentioned, but keeps the views designer and WYSIWYG-editor friendly by using tags only.


Jun 7, 2012 at 6:59 PM // reply »
1 Comments

Thank you Ben for once again taking the time to provide valuable information to the CF community in a very clear way.

You have become the CF knowledge base (to say the least).

The truth is that most of the time, when I get an error that I can't figure out I Google the error + "Ben Nadel". I've got a feeling I'm not the only one doing that.

You know, one of these days you should write "The Ben Nadel Bible".


Feb 13, 2013 at 4:00 PM // reply »
17 Comments

@Ben
I've used the cfquery component to great effect when writing my own model component. One of the frustrations in the past has been that you can't use cfqueryparam inside cfscript. The cfquery component allows you to include not just indexed parameters, but even named parameters. This is an incredible tool for building queries on the fly.


Feb 14, 2013 at 9:44 AM // reply »
11,243 Comments

@Chris T,

I haven't tried CFWheels yet; but, I have definitely adopted a partial approach. We use CFScript for all of our controllers and service-oriented components. We still use CF Tags for our "gateways" since they just make writing SQL too easy to abandon. And, of course, our Views are in tags as well.

@Rolando,

Ha ha, thanks my man!!

@Paul,

What I would love is if the addParam() method returns a reference to the query object itself. Then, I could get rid of the intermediary variable that has to store the query before it is executed.

Right now, addParam() returns void; but, if it returned "this", the we could have code that looks like this:

  • var result = new Query(
  • sql = "SELECT * FROM tbl WHERE id = :id ;"
  • )
  • .addParam( "id", 4 )
  • .execute()
  • .getPrefix()
  • ;

... and no intermediary variable.

I guess I could just patch it myself; but, as I was saying above, I mostly use CFQuery tags for my queries anyway.


Feb 19, 2013 at 10:14 AM // reply »
17 Comments

@Ben
Having addParam and other functions that currently return void return references to the objects on which they operate would be nice. I'll admit that I appreciate how jQuery tends to do that (e.g., attr(attribute, value)) where the relevant inherent functions do not (c.f., setAttribute(value)).


Feb 21, 2013 at 8:53 AM // reply »
11,243 Comments

@Paul,

Yeah, I think jQuery kind of ruined for everybody else :)



Post A Comment

Comment Etiquette: Please do not post spam. Please keep the comments on-topic. Please do not post unrelated questions or large chunks of code. And, above all, please be nice to each other - we're trying to have a good conversation here.

Please review the following issues:

Author Name:


Author Email:

Author Website:

Comment:

Supported HTML tags for formatting: <strong>bold</strong>   <em>italic</em>   <code>code</code>







  • Help Wanted - Find Your Next ColdFusion Job
Ben Nadel's Company - Epicenter Consulting Recent Blog Comments
May 23, 2013 at 5:19 AM
Ask Ben: Print Part Of A Web Page With jQuery
How to print also the background color of table cells and table lines ... read »
May 23, 2013 at 3:55 AM
Javascript Array Methods: Unshift(), Shift(), Push(), And Pop()
very interesting and helpful too. ... read »
May 22, 2013 at 5:35 PM
Script Tags, jQuery, And Html(), Text() And Contents()
This is still an issue 2 years later. jQuery is supposed to remediate these cross browser issues, no? I have been unable to find any statement from the jQuery team calling this behavior "by de ... read »
May 22, 2013 at 12:44 PM
Ask Ben: Query Loop Inside CFScript Tags
In cf10, if you call a function that has: local.result = {}; local.result.msg = ""; local.svc = new query(); local.svc.setSQL("SELECT * FROM..."); local.obj = local.svc.exe ... read »
May 22, 2013 at 12:29 PM
Strange Interaction Between DeserializeJson(), ArrayContains(), And Database Values In ColdFusion
@Ben: What version of Java are you using? Also, did you test users.id to see what Java reports as the data type? I wonder if it's not a Java primitive data type, but getting returned as something ... read »
May 22, 2013 at 11:47 AM
Strange Interaction Between DeserializeJson(), ArrayContains(), And Database Values In ColdFusion
@Dana, Awesome - so it looks like this bug was fixed in ColdFusion 10. Thanks so much for double-checking that. ... read »
May 22, 2013 at 11:37 AM
Strange Interaction Between DeserializeJson(), ArrayContains(), And Database Values In ColdFusion
When I c&p and run on cf10, I get: Selected User IDs: 1,4 User 1 selected: YES - YES User 2 selected: NO - NO User 3 selected: NO - NO User 4 selected: YES - YES User 5 selected: NO - ... read »
May 22, 2013 at 11:27 AM
Strange Interaction Between DeserializeJson(), ArrayContains(), And Database Values In ColdFusion
@Tom, Good thought, but no dice. Both of these still exhibit the same behavior: users.id[ users.currentRow ] users[ "id" ][ users.currentRow ] It's just something whacky happening with ... read »
InVision App - Prototyping Made Beautiful With Prototyping Tools