Looping over a Cfquery or a Struct? - coldfusion

I have a query which retrieves Some data.
I want to display that data considering some conditions in different div tags.
Now my question is,
I am doing this by looping the query once and getting the data in three different structs and using these structs while displaying. Is this a good approach or
looping through the query everytime in each div to check the condition is the rirht approach ?
<tr >
<td >
features:
</td>
<td >
<cfloop query="getAttributes">
<cfif getAttributes.type_id EQ 1>
#getAttributes.seat#<br>
</cfif>
</cfloop>
</td>
</tr>
<tr>
<td >
Disclosures:
</td>
<td >
<cfloop query="getAttributes">
<cfif getAttributes.type_id EQ 2>
#getTicketAttributes.seat#<br>
</cfif>
</cfloop>
</td>
</tr>
Or can i use the below approach
seatStruct
disclosureStruct
<cfloop query="getAttributes">
<cfif getAttributes.type_id EQ 1>
Insert seatStruct
<cfelseif getAttributes.type_id EQ 2>
insert disclosureStruct
</cfif>
Now use these structs to display

I think you'll have to edit your question a little bit, put some example.
Less looping is always the best approach :)
Less conversion if not necessary is best approach :)
If your data is in one query, than there's no need to loop more than once, I guess...

The best approach will always depend on your specific problem.
Although fewer loop iterations will always result in faster performance, sometimes it can be acceptable to sacrifice some performance for the sake of improved readability.
Maintenance costs are usually the most expensive part of software, so it is worthwhile to make your code easy to read.
In this specific case:
Unless the getAttributes query result is unusually large (like over
10000 rows) or this page is loaded unusually often (like more than
once/sec), it probably won't make a noticeable difference how many
times you loop over it.
Both options will take exactly the same amount of time, anyways: The first option loops over the query twice. The second option loops through the query once to populate two structs, then your display code loops through each of the generated structs (which combined have the same number of elements as the query has rows), resulting in the same exact number of total iterations (equivalent to getAttributes.recordcount*2).
The code that splits the query results up into different structs is somewhat unusual, thus decreasing readability and increasing maintenence costs. Since it doesn't actually improve performance, it is entirely counter-productive and should not be used.

Related

How to find the nested cfoutput recordcount when using group

Consider the following:
<cfoutput query="resources" group="type">
<h4>#type#</h4>
<cfoutput>
#name#
</cfoutput>
</cfoutput>
resources.recordcount would give me the total number of records, but is there an elegant way of finding out the recordcount of the nested data? e.g
<cfoutput query="resources" group="type">
<h4>#type# (#noofrecords# of #resources.recordcount#)</h4>
<cfoutput>
#name#
</cfoutput>
</cfoutput>
I could probably do something hacky with loops, but wondered if there was a way of doing it using the cfoutput groups specifically.
You could do an output before to get the count. This would be more efficient than doing a query of queries.
<cfoutput query="resources" group="type">
<cfset noofrecords= 0>
<cfoutput>
<cfset noofrecords++>
</cfoutput>
<h4>#type# (#noofrecords# of #resources.recordcount#)</h4>
<cfoutput>
#name#
</cfoutput>
</cfoutput>
I am afraid that you would have to do some counting yourself. There is no RecordCount for the
nested grouped output, because it is really all the same query, CF is just
doing a little formatting for you.
You could get the count in the original query if you wanted an alternative method, but, you will need to test the performance before you go down this route (although using Query of Queries can also have performance issues as there are no indexes).
SELECT f1.id, f1.name, f1.type, f2.typeCount
FROM foo f1
INNER JOIN (
SELECT COUNT(type) as typeCount, type
FROM foo
GROUP BY type
) f2 on f1.type = f2.type
ORDER BY f1.type, f1.name
Then in the CF you can do:
<cfoutput query="resources" group="type">
<h4>#resources.type# (#resources.typeCount# of #resources.recordCount#)</h4>
<cfoutput>
#resources.name#
</cfoutput>
</cfoutput>
As a side note, in CF10 you can also group inside a cfloop with query like so:
<cfloop query="resources" group="type">
<h4>#resources.type# (#resources.typeCount# of #resources.recordCount#)</h4>
<cfloop>
#resources.name#
</cfloop>
</cfloop>
Another solution is:
<!--- I chose the pipe delimiter | names of things often contain commas,
otherwise, use any delimiter you like --->
<cfset TypesList = Valuelist(resources.type,"|")>
<cfoutput query="resources">
<h4>#Type# (#ListValueCount(TypesList,Type,"|")# of #resources.recordcount#)</h4>
<cfoutput>#name#</cfoutput>
</cfoutput>
While this will also work for other applications, it won't work for everything. It expects the Category count ('Type' here) to be unique (no two types with the same name). A better way to handle this is to count based on an ID rather than a name. You might have, for instance, a category setup like this
Fruits
Red Ones
Strawberries
Raspberries
Yellow Ones
Bananas
Vegetables
Green ones
Green peppers
Red Ones
Red Peppers
Tomatoes
Based on Type (as a string), the output would say Red Ones (4), Yellow Ones (1), Green Ones (1), Red Ones (4), but in a category-products relational table structure, coutning based on unique ID of the category would retrieve accurate counts.
To get the count of the nested data and display it where you say you want it, I would do this:
<cfoutput query = "rescources" group = "type">
query of queries to get the count this type
output the type and the count
<cfoutput>
output nested data
</cfoutput>
</cfoutput>

Hiding (not eliminating) duplicates in ColdFusion array

We have a three-panel display in which Panel One shows a list of countries, Panel Two shows a list of organizations, and Panel Three shows a list of aircraft. If you click a country, both the organizations and aircraft are filtered by that country, using IDs for each.
This is all working fine, but the list of aircraft comes from a "linking table" -- each record contains aircraft ID and name, and operator ID and name. Any given type of aircraft can be listed multiple times, depending on how many organizations operate it. For example, "AW159 Wildcat" is listed twice, once for Army Air Corps and once for Royal Navy. The way it works right now, "AW159" is listed in Panel Three twice when the page initially loads.
I would like any given aircraft to be displayed only once on initial page load, regardless of how many times it may be listed in this linking table. But, I want all the "duplicates" available to be shown, if somebody filters on the operating organization.
Is there a way to loop over all the aircraft but somehow hide duplicates of aircraft ID? Or, might there be a larger problem with the way our database is designed? I am open to suggestions. Thank you all very much in advance!
EDIT: sorry for not posting code; here is some. Thanks everybody for all of your help!
1. Query and array population in cfc:
<cfquery name="queryByOperator" datasource="vertipedia">
SELECT view_aircraftbyoperators.aircraftID
, view_aircraftbyoperators.Aircraft
, view_aircraftbyoperators.operatorID
, view_aircraftbyoperators.Operator
, tbl_companies.country
, tbl_companies.companyShortName
FROM view_aircraftbyoperators JOIN tbl_companies
ON tbl_companies.ID = view_aircraftbyoperators.operatorID
ORDER BY Aircraft
</cfquery>
<cfset count = 1>
<cfset aircraftByOperator = arrayNew(2)>
<cfloop query="queryByOperator">
<cfset aircraftByOperator[count][1] = aircraftID>
<cfset aircraftByOperator[count][2] = Aircraft>
<cfset aircraftByOperator[count][3] = operatorID>
<cfset aircraftByOperator[count][4] = Operator>
<cfset aircraftByOperator[count][5] = country>
<cfset count = count+1>
</cfloop>
2. Outputting on page:
<ul>
<cfloop from="1" to="#ArrayLen(aircraftByOperator)#" index="i">
<cfoutput>
<li name="#aircraftByOperator[i][3]#" class="#aircraftByOperator[i][5]#">
<p>
#aircraftByOperator[i][2]#
</p>
</li>
</cfoutput>
</cfloop>
</ul>
[i][1], [i][3] and [i][5] are all IDs. "operatorID" is the one that will change depending on who operates the aircraft. I hope this helps explain what I am trying, and again, many thanks!
Dan Bracuk's comment "Does this mean you are pre-loading all your data into javascript? It might be more efficient to throw some ajax in there and just get what you need when you need it" ended up being the way to go for us. I would like to mark his comment as the answer, but am unable to see how to do that.
For others who may read this post, we
Loaded all of the aircraft initially
When a visitor filters by country and/or organization, a new query is sent via ajax
I was having a hard time formatting the returned data, so I used jQuery's load() to format it and drop it in the proper place in the output panel.
As Dan said, it is very fast and much easier to work with, especially as our database continues to grow.
Thanks Dan!

Using coldfusion.sql.QueryTable.next() in Coldfusion 9

I am trying to write a custom tag that will iterate over a cfquery object in a special way. I found this page: http://www.zrinity.com/developers/mx/undocumentation/query.cfm outlining how to use the underlying java methods to navigate the result set, but it doesn't seem to be working in CF9.
I can call .next(), .previous(), .first(), and .last() just fine, and each method updates query.currentRow, but referencing query.columnName always returns the value from the first row, not currentRow.
Example:
<cfquery name="testQuery" datasource="source">
SELECT FooName FROM NumberedFoos
</cfquery>
<cfloop from="1" to="3" index="i">
<cfoutput>#testQuery.currentRow# => #testQuery.fooName#</cfoutput><br />
<cfset testQuery.next()>
</cfloop>
Produces:
1 => Foo 1
2 => Foo 1
3 => Foo 1
I know i could use something like testQuery.fooName[testQuery.currentRow], but that is pretty undesirable for the people I am making the custom tag for. Was the functionality described in the above link removed from CF9? If so is there an alternative?
EDIT
To expand on the why, the client wants a custom tag that allows them to "assert" certain things about a query. The client has a pretty low level understanding of CF, but are pretty solid writing SQL. Their desired end result is something akin to:
<cfquery name="purchaseTotals">
SELECT PurchaseId, Total FROM Purchases
</cfquery>
<CF_ASSERT query="purchaseTotals">
purchaseTotals.Total gte 0
</CF_ASSERT>
The desired output would be a html table with each row being the row from the query that fails the assertion. So to me, the CF_ASSERT tag need to be able to update the current row.
Edit 2:
The main challenge is to allow html in the body of the tag, while still having query values substituted from the appropriate row:
<CF_ASSERT query="purchaseTotals">
<CF_CONDITION expression="purchaseTotals.Total gte 0">
<!---error message when expression is false--->
<cfoutput>
Purchase #purchaseTotals.purchaseId# has a negative total!
</cfoutput>
</CF_CONDITION>
<CF_CONDITION expression="purchaseTotals.Total eq ''">
#PurchaseTotals.purchaseId# has a null total, this may be caused by:
<ul>
<li>Edge Case 1</li>
<li>Edge Case 2</li>
</ul>
</CF_CONDITION>
<CF_ASSERT>
The output here would be something like:
Purchase 120 has a negative total!
Purchase 157 has a negative total!
Purchase 157 has a null total, this may be caused by:
Edge Case 1Edge Case 2
Was the functionality described in the above link removed from CF9?
The internal stuff has definitely changed since the article was written in 2006. But I suspect the exact functionality you are describing may not have existed in any mx version. A key difference between your code and the linked examples is the usage of <cfoutput query=".."> (not just a plain <cfoutput>). The query attribute obviously provides some extra context when evaluating the variables. Remove it (like in your example) and the results are "the value from the first row, not currentRow.". Even under MX6, which does not bode well for subsequent versions. That exact functionality probably was not removed. It just never worked to begin with.
If so is there an alternative?
Like I said earlier, the cleanest approach would be to use array notion ie #query.column[row]#. Given that you seem to have rejected that option, you are basically left with evaluate(). You would need to loop through the query within the parent tag. Then use evaluate to process the subtag expression and content. It is not particularly elegant or simple IMO. But I think that may be good as it gets without array notation, or a ritual sacrifice of some kind.
ASSERT.cfm
<cfparam name="attributes.query" type="string">
<cfif thisTag.ExecutionMode is 'start'>
<!--- validate attributes.query is a query object --->
<cfif not ( structKeyExists(caller, attributes.query) AND IsQuery(caller[attributes.query]) )>
<cfthrow message="Attributes.query [#attributes.query#] is undefined or not a query object">
</cfif>
</cfif>
<cfif thisTag.ExecutionMode is 'end'>
<cfset variables[attributes.query] = caller[attributes.query]>
<cfloop query="variables.#attributes.query#">
<cfloop array="#thisTag.assocAttribs#" index="subTag">
<cfset variables.matchFound = evaluate(subTag.expression)>
<cfif variables.matchFound>
<cfoutput>[#currentRow#] #evaluate(DE(subTag.Content))#</cfoutput><hr>
</cfif>
</cfloop>
</cfloop>
</cfif>
CONDITION.cfm
Note: Do NOT use <cfoutput> tags within the tag content.
<cfparam name="attributes.expression" type="string">
<cfif thisTag.ExecutionMode is "start">
<cfassociate baseTag="CF_ASSERT">
</cfif>
<cfif thisTag.ExecutionMode is "end">
<cfset attributes.content = thisTag.GeneratedContent>
<cfset thisTag.GeneratedContent = "">
</cfif>
client has a pretty low level understanding of CF, but are pretty
solid writing SQL
Having said all that, are things being implemented this way because it is the best approach or because it is most similar to writing SQL ie comfortable ?
Classic example of the inner platform effect.
I would advise you not to do this as you are trying to create a system which mimics built in functionality of the base or running system which ultimately becomes a poorly implemented version of the system in which it is running on / implemented in.
Sounds confusing, I know - but this is a well known anti-pattern to avoid.
See here for more information : http://en.wikipedia.org/wiki/Inner-platform_effect
P.s. what you are looking for (although I disagree with the implementation) is itteration of a query outside of a cfloop, simply use array syntax :
#queryName.fieldName[rowNumber]#
Using this you can itterate the query however you wish, certainly no need for underlying java. Notice we aren't using queryName.currentRow. For previous() next() functionality, you just change the rowNumber up or down.

Handling CFSELECT

I'm totally unused to Cold Fusion, I'd like to know how to handle a multiple CFSELECT, in particular how to know how many rows I've selected and taking them one by one.
Actually I've managed to see all the rows togheter:
<!--- page_a.cfm --->
<cfform name="fooform" ........>
<cfselect query="myquery" name="fornitori" multiple="yes"></cfselect>
<!--- page_b.cfm --->
<cfoutput>#form.fornitori#</cfoutput>
And, if it's not too much, I'd like to know why it's not correct to write:
<!--- page_b.cfm --->
<cfoutput>#fooform.fornitori#</cfoutput>
Since it is a multiple selection list, the options you select will be submitted to page_b.cfm as a comma delimited list. This means you can use list functions to calculate the total items selected and cfloop to iterate through the selections individually. Note, when using "multiple" select lists, if you select nothing the form field will not exist.
<cfparam name="form.fornitori" default="">
<cfoutput>
Total Items Selected = <cfoutput>#listLen(form.fornitori)#</cfoutput>
Individual Selections:<br>
<cfloop list="#form.fornitori#" index="theSelection">
#theSelection#<br>
</cfloop>
</cfoutput>
why it's not correct to write ..
Because FORM refers to a special system structure, not the name of your html form (ie fooform). FORM contains any form fields submitted via method=POST.
#myquery.recordcount#
btw, use cfdump to display anything. you see some really interesting stuff (I'm not sure whether recordcount is in there though)
getting to the elements:
<cfoutput query="queryname">#title#<br />#content#</cfoutput>
inside the cfoutput you have access to the variables of an element.
coldfusion 9 help

Combining query rows in a loop

I have the following ColdFusion 9 code:
<cfloop from="1" to="#arrayLen(tagArray)#" index="i">
<cfquery name="qryGetSPFAQs" datasource="#application.datasource#">
EXEC searchFAQ '#tagArray[i]#'
</cfquery>
</cfloop>
The EXEC executes a stored procedure on the database server, which returns rows of data, depending on what the parameter is. What I am trying to do is combine the queries into one query object. In other words, if it loops 3 times and each loop returns 4 rows, I want a query object that has all 12 rows in one object. How do I acheive this?
You might want to take a different approach (modify your stored procedure to accept multiple arguments or use a list and fnSplit) and return the dataset all at once. However, to directly answer your question, this is how you could combine the queries as you're asking to:
You can use UNION in a Query of Queries to combine all of the datasets.
<cfloop from="1" to="#arrayLen(tagArray)#" index="i">
<cfquery name="qryGetSPFAQs#i#" datasource="#application.datasource#">
EXEC searchFAQ '#tagArray[i]#'
</cfquery>
</cfloop>
<cfquery name="combined" dbtype="query">
<cfloop from="1" to="#arrayLen(tagArray)#" index="i">
select * from qryGetSPFAQs#i#
<cfif i lt arrayLen(tagArray)>UNION</cfif>
</cfloop>
</cfquery>
A more direct way might be something like this:
<cfset bigQ = queryNew("column")>
<cfloop from="1" to="#arrayLen(tagArray)#" index="i">
<cfquery name="qryGetSPFAQs" datasource="#application.datasource#">
EXEC searchFAQ '#tagArray[i]#'
</cfquery>
<cfset queryAddRow(bigQ)>
<cfset querySetCell(bigQ, "column". qryGetSPFAQs)>
</cfloop>
You will need a querySetCell() assignment for each column. Check out the query functions in the live docs for more information.
Here is an out of the box solution, abandoning the StoredProc for a SQL View (I'll explain).
Disclaimer: without seeing the SP source code, I can't tell if my solution fits. I'm assuming that the SP is fairly basic, and I admit I usually prefer the compiled execution of an SP over a view, but the one-time execution of a SQL View should outperform the looping of the SP x times.
First make a view that looks like the SELECT statement in the SP (minus the parameterization, of course -- you'll cover that in a WHERE clause within the CFQUERY of your new view.
Second, set up your loop to do no more than build a data set we're going to use for the WHERE clause. You'll need to use ArrayToList and a little bit of string manipulation to tidy it up, with the end product being a string stored in a single CF variable looking like this:
('ValueOfArrayElement1','ValueOfArrayElement2','Value_And_So_On')
Building the string is pretty easy, using the delimeter attribute of ArrayToList, and after the loop is complete, append a Left Parenthesis & Single Quote to the Left most position of the string; and append a Single Quote & Right Parenthesis to the Right most position in the string.
Now, write the CFQUERY statement to SELECT the columns you need from your view (instead of executing your SP). And instead of passing a parameter to the SP, you're going to put a WHERE clause in the CFQUERY.
Oh, BTW, I am stating you need a SQL View, but the entire SELECT could be built in CFQUERY. Personally, when I have a multi-table JOIN, I like to define that in a SQL View where it's executed more quickly than a JOIN in CFQUERY. Ultimately a StoredProc is even faster, but our WHERE clause is much friendlier to code and read like this than it would be to send into StoredProc without looping in and out of SQL.
It's a good goal to make only one trip out to the database and back if possible. That's why we looped through the array to write a string equating to all the values in the dataset. This way, we'll only execute one query, one time.
SELECT Col1, Col2, Col_etc
FROM SQL_View_Name
WHERE ColumnName in #BigStringWeMadeFromArrayToList#
when our CFQUERY is rendered, the clause will look just like this in SQL:
WHERE ColumnName in
('ValueOfArrayElement1','ValueOfArrayElement2','Value_And_So_On')
So there you have it. Like I said, this is nice because it makes only one trip to the DB, and since we are building a view, the performance will still be pretty good -- better than running a StoredProc 4+ times. (no offense)
I'll must repeat... without having seen the SP code, I'm not sure if this is do-able. Plus, it's kind of odd to abandon a StoredProc for a SQL View, a "lesser" entity in the RDBMS, but I'm sure we will achieve greater performance and I think it's pretty readable, too.