Can I get a query row by index in ColdFusion? - coldfusion

I want to get a specific row in a ColdFusion Query object without looping over it.
I'd like to do something like this:
<cfquery name="QueryName" datasource="ds">
SELECT *
FROM tablename
</cfquery>
<cfset x = QueryName[5]>
But it's giving me an error saying that the query isn't indexable by "5". I know for a fact that there are more than 5 records in this query.

You can't get a row in CF <= 10. You have to get a specific column.
<cfset x = QueryName.columnName[5]>
It's been 8 years since I posted this answer, however. Apparently CF11 finally implemented that feature. See this answer.

This can now be accomplished in coldfusion 11 via QueryGetRow
<cfquery name="myQuery" result="myresult" datasource="artGallery" fetchclientinfo="yes" >
select * from art where ARTID >
<cfqueryparam value="2" cfsqltype="CF_SQL_INTEGER">
</cfquery>
<cfdump var="#myQuery#" >
<cfset data = QueryGetRow(myQuery, 1) >
<cfdump var="#data#" >

I think there is a simpler solution...
I am guessing you know your column names and only want this column or that one. Then you don't need to put the whole row in a struct. You can reference the query by row number (remember its 1 based not 0).
yourQueryName["yourColumnName"][rowNumber]
<cfoutput>
#mycontacts["Name"][13]#
#mycontacts["HomePhone"][13]#
</cfoutput>

You have to convert the query to a struct first:
<cfscript>
function GetQueryRow(query, rowNumber) {
var i = 0;
var rowData = StructNew();
var cols = ListToArray(query.columnList);
for (i = 1; i lte ArrayLen(cols); i = i + 1) {
rowData[cols[i]] = query[cols[i]][rowNumber];
}
return rowData;
}
</cfscript>
<cfoutput query="yourQuery">
<cfset theCurrentRow = GetQueryRow(yourQuery, currentRow)>
<cfdump var="#theCurrentRow#">
</cfoutput>
Hope this points you in the right direction.

I know I come back to this thread any time I Google "cfquery bracket notation". Here's a function I wrote to handle this case using bracket notation. Hopefully this can help someone else too:
<cffunction name="QueryGetRow" access="public" returntype="array" hint="I return the specified row's data as an array in the correct order">
<cfargument name="query" required="true" type="query" hint="I am the query whose row data you want">
<cfargument name="rowNumber" required="true" hint="This is the row number of the row whose data you want">
<cfset returnArray = []>
<cfset valueArray = []>
<cfset cList = ListToArray(query.ColumnList)>
<cfloop from="1" to="#ArrayLen(cList)#" index="i">
<cfset row = query["#cList[i]#"][rowNumber]>
<cfset row = REReplace(row, "(,)", " ")>
<cfset returnArray[i] = row>
<cfset i++>
</cfloop>
<cfreturn returnArray>
</cffunction>
The REReplace is optional, I have it in there to cleanse commas so that it doesn't screw up the arrayToList function later on if you have to use it.

I wanted to extract a single row from a query, and keeping the column names (of course). This is how I solved it:
<cffunction name="getQueryRow" returntype="query" output="no">
<cfargument name="qry" type="query" required="yes">
<cfargument name="row" type="numeric" required="yes">
<cfset arguments.qryRow=QueryNew(arguments.qry.columnlist)>
<cfset QueryAddRow(arguments.qryRow)>
<cfloop list="#arguments.qry.columnlist#" index="arguments.column">
<cfset QuerySetCell(arguments.qryRow,arguments.column,Evaluate("arguments.qry.#arguments.column#[arguments.row]"))>
</cfloop>
<cfreturn arguments.qryRow>
</cffunction>

Methods previously described for obtaining query data by column name and row number (variables.myquery["columnName"][rowNumber]) are correct, but not convenient for getting a full row of query data.
I'm running Railo 4.1. And this is a cool solution. Too bad this can't be done the way we would want outright to get a full row of data, but the following method allows us to get what we want through a few hoops.
When you serializeJSON(variables.myquery) it changes the query to a JSON formatted cfml struct object with two items: "Columns" and "Data". Both of these are arrays of data. The "data" array is a two-dimensional array for rows and then columnar data.
The issue is that now we have an unusable string. Then if we re-serialize it it's NOT a query, but rather usable regular struct in the format described above.
Assume we already have a query variable named 'variables.myquery'. Then look at the following code:
<cfset variables.myqueryobj = deserializeJSON(serializeJSON(variables.myquery)) />
Now you get the two dimensional array by getting this:
<cfset variables.allrowsarray = variables.myqueryobj.data />
And you get one query row array by getting this:
<cfset variables.allrowsarray = variables.myqueryobj.data[1] />
OR the last row this way:
<cfset variables.allrowsarray = variables.myqueryobj.data[variables.myquery.recordCount] />
And you can get individual column values by column order number iteration:
<cfset variables.allrowsarray = variables.myqueryobj.data[1][1] />
Now this might be slow and possibly unwise with large query results, but this is a cool solution nonetheless.

Check out the documentation for queryGetRow. It accepts a query object and an index of the row with the first row being referenced with the index of 1 (NOT 0) The index used this way is required to be a positive integer.
<cfquery name="QueryName" datasource="ds">
SELECT *
FROM tablename
</cfquery>
<!---
This would retrieve the first record of the query
and store the record in a struct format in the variable 'x'.
--->
<cfset x = queryGetRow(QueryName, 1) />
<!---
This is an alternative using the member method form of queryGetRow
--->
<cfset x = QueryName.getRow(1) />

Related

Using Ben Nadel POI to read excel

I'm using Ben Nadel's Reading Excel Files With ColdFusion And POI codes at https://www.bennadel.com/blog/472-reading-excel-files-with-coldfusion-and-poi.htm to read my excel file.
With his example codes I can read the excel file when rows in my excel are made uniform. Ben mentioned about this in his comment. But my excel however does not always has uniform rows. Some has birth date, some does not have gender, etc.
Ben Nadel's codes produce error once it hits the empty cell. It says, the
objCell variable does not exist. The following code crashes:
<cfset objCell = objRow.GetCell(JavaCast( "int", intCell )) />
Does anyone has an example and do not mind sharing the codes that can also read empty cells when looping?
I'm pasting Ben's codes here:
<!---
Create the Excel file system object. This object is
responsible for reading in the given Excel file.
--->
<cfset objExcelFileSystem = CreateObject(
"java",
"org.apache.poi.poifs.filesystem.POIFSFileSystem"
).Init(
CreateObject(
"java",
"java.io.FileInputStream"
).Init(
ExpandPath( "./jenna_jameson.xls" )
)
) />
<!---
Get the workbook from the Excel file system object that
we just created. Remember, the workbook contains the
Excel sheets that have our data.
--->
<cfset objWorkBook = CreateObject(
"java",
"org.apache.poi.hssf.usermodel.HSSFWorkbook"
).Init(
objExcelFileSystem
) />
<!---
For this demo, we are only interested in reading in the
data from the first sheet. Remember, since Java is zero-
based, not one-based like ColdFusion, the first Excel
sheet is at index ZERO (not ONE).
--->
<cfset objSheet = objWorkBook.GetSheetAt(
JavaCast( "int", 0 )
) />
<!---
We are going to build a ColdFusion query that houses the
Excel data, but we don't know anything about the data
just yet. So, just create the place holder for the query
and then we will add to it when we have more information.
--->
<cfset qCell = "" />
<!---
Get the Excel sheet's row iterator. This appears to be some
sort of implementation of the Java class java.util.TreeMap,
but I don't know much about that. What I do know, is that
this will allow us to loop over the rows in the Excel file
until there are no more to loop over. The interface for it
looks like the standard iterator interface.
--->
<cfset objRowIterator = objSheet.rowIterator() />
<!---
User the row iterator to loop over all the physical rows in
the Excel sheet. This condition checks to see if we have a
row to read in. At this point, the iterator is NOT pointing
at a valid Excel data row.
--->
<cfloop condition="objRowIterator.HasNext()">
<!---
We have determined that we have a valid row to read.
Now, move the iterator to point to this valid row.
--->
<cfset objRow = objRowIterator.Next() />
<!---
Get the number of physical cells in this row. While I
think that this can possibly change from row to row,
for the purposes of this demo, I am going to assume
that all rows are uniform and that this row is a model
of how the rest of the data will be displayed.
--->
<cfset intCellCount = objRow.GetPhysicalNumberOfCells() />
<!---
Check to see if the query variable we have it actually
a query. If we have not done anything to it yet, then
it should still just be a string value (Yahoo for
dynamic typing!!!). If that is the case, then let's use
this first data row to set up the query object.
--->
<cfif NOT IsQuery( qCell )>
<!---
Create an empty query. Doing it this way creates a
query with neither column nor row values.
--->
<cfset qCell = QueryNew( "" ) />
<!---
Now that we have an empty query, we are going to
loop over the cells COUNT for this data row and for
each cell, we are going to create a query column
of type VARCHAR. I understand that cells are going
to have different data types, but I am chosing to
store everything as a string to make it easier.
--->
<cfloop index="intCell" from="0" to="#(intCellCount - 1)#"
step="1">
<!---
Add the column. Notice that the name of the
column is the text "column" plus the column
index. I am starting my column indexes at ONE
rather than ZERO to get it back into a more
ColdFusion standard notation.
--->
<cfset QueryAddColumn(qCell,"column#(intCell + 1)#",
"CF_SQL_VARCHAR",ArrayNew( 1 )) />
</cfloop>
</cfif>
<!---
ASSERT: Whether we are on our first Excel data row or
our Nth data row, at this point, we have a ColdFusion
query object that has the proper columns defined.
--->
<!---
Add a row to the query so that we can store this row's
data values.
--->
<cfset QueryAddRow( qCell ) />
<!--- Loop over the cells in this row to find values. --->
<cfloop index="intCell" from="0" to="#(intCellCount - 1)#"
step="1">
<!---
When getting the value of a cell, it is important
to know what type of cell value we are dealing
with. If you try to grab the wrong value type,
an error might be thrown. For that reason, we must
check to see what type of cell we are working with.
These are the cell types and they are constants
of the cell object itself:
0 - CELL_TYPE_NUMERIC
1 - CELL_TYPE_STRING
2 - CELL_TYPE_FORMULA
3 - CELL_TYPE_BLANK
4 - CELL_TYPE_BOOLEAN
5 - CELL_TYPE_ERROR
--->
<!--- Get the cell from the row object. --->
----- **When it hit an empty cell CF throws error** ---
<cfset objCell = objRow.GetCell(JavaCast( "int", intCell)) />
<!--- Get the type of data in this cell. --->
<cfset objCellType = objCell.GetCellType() />
<!---
Get teh value of the cell based on the data type.
The thing to worry about here is cell forumlas and
cell dates. Formulas can be strange and dates are
stored as numeric types. For this demo, I am not
going to worry about that at all. I will just grab
dates as floats and formulas I will try to grab as
numeric values.
--->
<cfif (objCellType EQ objCell.CELL_TYPE_NUMERIC)>
<!---
Get numeric cell data. This could be a
standard number, could also be a date value.
I am going to leave it up to the calling
program to decide.
--->
<cfset objCellValue = objCell.GetNumericCellValue() />
<cfelseif (objCellType EQ objCell.CELL_TYPE_STRING)>
<cfset objCellValue = objCell.GetStringCellValue() />
<cfelseif (objCellType EQ objCell.CELL_TYPE_FORMULA)>
<!---
Since most forumlas deal with numbers, I am
going to try to grab the value as a number. If
that throws an error, I will just grab it as a
string value.
--->
<cftry>
<cfset objCellValue = objCell.GetNumericCellValue() />
<cfcatch>
<!---
The numeric grab failed. Try to get the
value as a string. If this fails, just
force the empty string.
--->
<cftry>
<cfset objCellValue = objCell.GetStringCellValue() />
<cfcatch>
<!--- Force empty string. --->
<cfset objCellValue = "" />
</cfcatch>
</cftry>
</cfcatch>
</cftry>
<cfelseif (objCellType EQ objCell.CELL_TYPE_BLANK)>
<cfset objCellValue = "" />
<cfelseif (objCellType EQ objCell.CELL_TYPE_BOOLEAN)>
<cfset objCellValue = objCell.GetBooleanCellValue() />
<cfelse>
<!--- If all else fails, get empty string. --->
<cfset objCellValue = "" />
</cfif>
<!---
ASSERT: At this point, we either got the cell value
out of the Excel data cell or we have thrown an
error or didn't get a matching type and just
have the empty string by default. No matter what,
the object objCellValue is defined and has some
sort of SIMPLE ColdFusion value in it.
--->
<!---
Now that we have a value, store it as a string in
the ColdFusion query object. Remember again that my
query names are ONE based for ColdFusion standards.
That is why I am adding 1 to the cell index.
--->
<cfset qCell[ "column#(intCell + 1)#" ][ qCell.RecordCount ] =
JavaCast( "string", objCellValue ) />
</cfloop>
</cfloop>
<!---
At this point, the excel data should be in a ColdFusion
query object. However, if the query did not contain any
record, then the row iterator was never launched which
mean we never actually defined a query. As one final check
just make sure we are dealing with a query.
--->
<cfif NOT IsQuery( qCell )>
<!--- Just define an empty query. --->
<cfset qCell = QueryNew( "" ) />
</cfif>
Perform below two steps to read empty cells also in ColdFusion-8:
Add a variable outside loops to represent HSSFCell. Example:
<cfset jCell = createObject("java", "org.apache.poi.hssf.usermodel.HSSFCell")>
Use function objRow.GetLastCellNum() instead of objRow.GetPhysicalNumberOfCells(). Example:
<cfset intCellCount = objRow.GetLastCellNum() />
Add a <cfif> condition to set value of variable objCellType based on existence of variable objCell. Example:
<cfset objCell = objRow.GetCell(JavaCast( "int", intCell)) />
<cfif structKeyExists(variables, "objCell")>
<cfset objCellType = objCell.GetCellType() />
<cfelse>
<cfset objCellType = jCell.CELL_TYPE_BLANK />
</cfif>
Replace variable name objCell in all references to objCell.CELL_TYPE_[type_name] with variable name created in step 1. Example:
<cfif (objCellType EQ jCell.CELL_TYPE_NUMERIC)>

CFSPREADSHEET read in Excel file to query can't access columns in result

I'm trying to automate processing of spreadsheets using ColdFusion 10 and CFSpreadSheet. So far I can read the file in and dump the query object without any issue.
<cfspreadsheet action="read" src="#theFile#" query="qData" headerrow="1" columns="1,8,9,11,33"/>
<cfdump var="#qData#"/>
The issue comes when I try to work with the data. If I do something like:
<cfoutput query="qData" maxrows="#qData.RecordCount#">
#qData.GROUP#<br />
#qData.DOB#<br />
</cfoutput>
I immediately get an error: "Element GROUP is undefined in QDATA."
If I dump the qData.ColumnList I get a column list:
BTBN(002),DOB ,GROUP ,MEMBER/DEPENDENT NAME ,REL
Is it the spaces and ( )s messing it up? If so, how can I get rid of those and update the column names either when reading in the file or immediately thereafter?
I initially thought it may be due to "group" being a reserved SQL keyword. (Don't try using that columnname in a query-of-queries.)
Instead of sanitizing the first row values for known values and resaving the file, you should loop through and use isValid("variablename", ColumnName) to determine if the column name is valid and then use the RenameColumn UDF to rename it. We prefer this method as it's critical that we don't modify the client's original Excel file (especially since Adobe ColdFusion has some bugs when writing files and will likely mess up other worksheets and/or formatting within the file.)
An easy way to remove illegal characters is to use rereplace:
ReReplace(NewColumnName, "[^a-zA-Z0-9!]", "", "ALL")
But you'll also need to ensure that the new column name isn't empty, starts with a letter and isn't already used for another column. If you are expecting the columns to be in a certain order, you could simply safely rename them "col_1" (or use numbering as a default fallback for any non-unique and/or illegal column names.)
Here's the RenameColumn UDF taken from this 2011 blog post:
http://www.neiland.net/blog/article/using-java-to-rename-columns-in-a-coldfusion-query-object/
<cffunction name="renameColumn" access="public" output="false" returntype="query" hint="Uses java to rename a given query object column">
<cfargument name="queryObj" required="true" type="query">
<cfargument name="oldColName" required="true" type="string">
<cfargument name="newColName" required="true" type="string">
<!--- Get an array of the current column names --->
<cfset var colNameArray = queryObj.getColumnNames()>
<cfset var i = 0>
<!--- Loop through the name array and try match the current column name with the target col name--->
<cfif arrayLen(colNameArray)>
<cfloop from="1" to="#arrayLen(colNameArray)#" index="i">
<!--- If we find the target col name change to the new name --->
<cfif compareNoCase(colNameArray[i],arguments.oldColName) EQ 0>
<cfset colNameArray[i] = arguments.newColName>
</cfif>
</cfloop>
</cfif>
<!--- Update the column names with the updated name array --->
<cfset queryObj.setColumnNames(colNameArray)>
<cfreturn queryObj />
</cffunction>
(In case folks do not read the comments ...)
The parenthesis and slashes will be problematic as they do not conform to the standard variable name rules. The simplest option is to use the "columnNames" attribute to specify valid column names instead. (Also, nothing to do with your question, but if you want to exclude the header row, use excludeHeaderRow="true")
<cfspreadsheet action="read" src="c:\path\file.xlsx"
query="qData"
columnNames="BTBN_002,DOB,GROUP_NAME,MEMBER_DEPEND_NAME,REL"
excludeHeaderRow="true"
headerrow="1"
... />
In most cases, you can also access invalid column names using associative array notation. However, using the "columnNames" attribute is simpler/cleaner IMO.
<cfoutput query="qData" maxrows="#qData.RecordCount#">
#qData["BTBN(002)"][currentRow]#<br />
....
</cfoutput>
SOLUTION - there were multiple spaces in the column names and ColdFusion does not tolerate that. This could probably be done better with a regex, and I'll work on that next but here's the quick and dirty solution.
<cfset colNameArray = qData.getColumnNames() />
<cfloop from="1" to="#arrayLen(colNameArray)#" index="i">
<cfset colNameArray[i] = colNameArray[i].replace(' ','') />
<cfset colNameArray[i] = colNameArray[i].replace('(','') />
<cfset colNameArray[i] = colNameArray[i].replace(')','') />
<cfset colNameArray[i] = colNameArray[i].replace('/','') />
</cfloop>
<cfset qData.setColumnNames(colNameArray) />

returning the query row with the way the columns are called in sql query

I have a query where I am calling the columns like the way i want, I cannot use ColumnList of query because it sorts the column alphabatically, another thing i need to get the specific row of the query and its associated columns in a structure:
So here is my function which i am trying to bring the columns in the manner i want:
<cffunction name="rowToStruct" access="public" returntype="struct" output="false">
<cfargument name="queryObj" type="query" required="true" />
<cfargument name="row" type="numeric" required="true" />
<cfset var returnStruct = structNew()>
<cfset var colname = "">
<cfset arguments.queryObj = arrayToList(arguments.queryObj.getMeta().getColumnLabels())>
<cfloop list="#arguments.queryObj#" index="colname">
<cfset "returnStruct.#colname#" = arguments.queryObj[colname][arguments.row]>
</cfloop>
<cfreturn returnStruct/>
</cffunction>
before the above change the function was like this below:
<cffunction name="rowToStruct" access="public" returntype="struct" output="false">
<cfargument name="queryObj" type="query" required="true" />
<cfargument name="row" type="numeric" required="true" />
<cfset var returnStruct = structNew()>
<cfset var colname = "">
<cfloop list="#arguments.queryObj.columnList#" index="colname">
<cfset "returnStruct.#colname#" = arguments.queryObj[colname][arguments.row]>
</cfloop>
<cfreturn returnStruct/>
</cffunction>
Mine above one is giving me an error:
You have attempted to dereference a scalar variable of type class java.lang.String as a structure with members.
The getMetadata() function returns the columns in the order they were defined in the original statement. Docs: GetMetaData.
I can't see why your code would produce that error, although I'd do this:
<cfset returnStruct[colname] = arguments.queryObj[colname][arguments.row]>
Can you update your question with the exact error as it displays on the screen, including the code it focuses on, and the line numbers concerned (switch Robust Exception Handling on, if it's not on already)
The reason for the error is because you are overwriting the query object passed into the function. The new code resets the value of arguments.queryObj to a simple string here:
<cfset arguments.queryObj = arrayToList(arguments.queryObj.getMeta().getColumnLabels())>
Later in the code, you try and use the arguments.queryObj variable as if it were still a query object. Obviously, this causes an error because the variable now represents a string:
<cfset "returnStruct.#colname#" = arguments.queryObj[colname][arguments.row]>
The solution is to use a different variable to store the list of column names instead. Since it will also be function local variable, just be sure to scope it properly with local or var.
That said, as Adam mentioned in his answer, the feature you need is already supported using one of the built-in functions. So there is no really need to use the undocumented methods of the coldfusion.sql.QueryTable query class IMO. GetMetaData(queryObject) returns an array of structures containing two keys: Name (column name), IsCaseSensitive (boolean). To iterate through it, use an "array" loop instead of a "list" loop:
...
<cfset var returnStruct = {}>
<cfset var col = "">
<cfset var colArray = getMetaData(arguments.queryObj)>
<cfloop array="#colArray#" index="col">
<cfset returnStruct[col.Name] = arguments.queryObj[col.Name][arguments.row]>
</cfloop>
...
Update 1:
I cannot use ColumnList of query because it sorts the column
alphabatically
The bigger issue here is that CF structures are not ordered. Even if you add the values to the structure in the proper order, that order will not be maintained. You either need to address that in the calling code (ie use GetMetaData(query)) OR have the cffunction return both the ordered column names AND the row data. The latter seems a bit redundant, but it all depends on how you are using the function in your application.
Update 2:
IF you really do need a sorted structure .. there are also some java classes that do maintain the insertion order, such as a LinkedHashMap. Since it implements java.util.Map it can be used like a CF structure (in most ways).
<cfset var returnStruct = createObject("java", "java.util.LinkedHashMap").init()>
Runnable Example on trycf.com
NB: While having access to java objects is great, a common mistake is to forget that java is strongly typed, unlike CF. There are often subtle nuances that can easily bite you if you are not aware of them. So keep that in mind ... and be sure to read the API.

Merging a array of queries into a single query

I want to merge an array of queries into one query
I have tried this
<cfquery name="MergedData" dbtype="query">
<cfloop from="1" to="#arrayLen(arData)#" index="k">
SELECT *
FROM arData[k]
<cfif k LT ArrayLen(arData)>UNION</cfif>
</cfloop>
ORDER BY EID
</cfquery>
I get an error that looks like
<br><b>Query Of Queries syntax error.</b><br> Encountered "[k].
Try an alternative way of looping over your array. You need to create your own counter though to figure out the logic of if you need to keep appending 'UNION' to your SQL statement.
<cfset i = 1>
<cfquery name="MergedData" dbtype="query">
<cfloop index="k" array="#arData#">
SELECT *
FROM k
<cfif i LT ArrayLen(arData)>UNION</cfif>
<cfset i++>
</cfloop>
ORDER BY EID
</cfquery>
NB: you wouldn't need to calculate a counter yourself if you're using Railo instead of Adobe CF, as you can then do both index and item like so, as Peter mentioned in the comments above:
<cfloop index="i" item="k" array="#arData#">
If you'd like to accomplish this with functional programming, you could use the Underscore.cfc library (requires CF 10+ or Railo 4+):
// instantiate Underscore library
_ = new Underscore();
// convert the array of queries to a single array of structs
mergedArray = _.reduce(arrayOfQueries, function (memo, query) {
// convert current query to an array of structs
// and concatenate it to the rest of the result
return _.concat(memo, _.toArray(query));
}, []);
// convert the array of structs back to a query
mergedQuery = _.toQuery(mergedArray);
This solution utilizes reduce() to combine the array of queries into a single array of structs. The anonymous function passed to reduce() converts each query in the array of queries to an array of structs using toArray(), then concatenates that array with the rest of the array of structs (the memo value).
Once the array of queries has been converted to a single array of structs, it is a simple matter to convert it back to a query using toQuery() (assuming that is necessary).
Note: I wrote the Underscore library
Try like this:
<cfsavecontent variable="testing">
<cfset j = 1>
<cfloop array="#test#" index="i">
SELECT id FROM #i# LIMIT 10
<cfif j LT ArrayLen(test)> UNION </cfif>
<cfset j++>
</cfloop>
</cfsavecontent>
<cfquery name="qTest" datasource="#application.dsn#">
#testing#
</cfquery>
This is what end up working
<cfquery name="local.qryMergedData" dbtype="query">
<cfloop array="#arguments.Data#" index="k">
<cfset local.currentIndex++>
<cfset setvariable("Data_#local.currentIndex#", k)>
SELECT *
FROM Data_#local.currentIndex#
<cfif local.currentIndex LT local.MaxIndex>UNION</cfif>
</cfloop>
ORDER BY EID
</cfquery>
I really don't like that I am setting a variable inside of a <cfquery>, but at least it is only one query

Coldfusion OutOfMemoryError (CF9/Wheels)

I have a function which loops over a query and updates a database row for each item. After about 7000 iterations it's throwing an out of memory error - Java heap space.
Is there anything obviously wrong with this code ?
<cfloop query=loc.fixItems>
<cfset loc.count = loc.count + 1>
<cfset var categoryName = loc.fixItems.categoryName>
<cfinvoke component="Item" method="updateCode"
itemId="#loc.fixItems.itemId#" code="#loc.fixItems.newCode#"/>
<!--- Increment counter for category --->
<cfif structKeyExists(categoryMap, categoryName)>
<cfset var inc = structFind(categoryMap, categoryName) + 1>
<cfset structUpdate(categoryMap, categoryName, inc)>
<cfelse>
<cfset structInsert(categoryMap, categoryName, 1)>
</cfif>
</cfloop>
and in the update component:
<cffunction name="updateCode">
<cfargument name="itemId" type="numeric" required="yes">
<cfargument name="code" type="string" required="yes">
<cfset var loc = {}>
<cfquery name="loc.update">
update items
set code = <cfqueryparam value="#code#">
where id = <cfqueryparam value="#itemId#">
</cfquery>
</cffunction>
Don't use cfinvoke to create your Item component every iteration of your fixItems query. Create it once before that using createObject and simply call the updateCode method each time directly on the object.
The following can be done:
Change your <cfqueryparam> to use the appropriate cf_sql type. Are code and id really strings?
Don't give your <cfquery> a name. You are not keeping the result anyway. var loc doesn't help either
Bump up you memory to the JVM Addtional approach Use Java 7 and G1GC
Every 100 to 1000 iterations do a forced Garbage Collect
Update your data in bulk. XML based table variables can do this.
Make your function silent
Consider ORM on this