cfml and resorting part of a query - coldfusion

Just wondering, given a query and output like so:
<cfoutput query="someItems" group="someColumnName">
... doing some stuff here ..
<cfoutput> doing stuff with some sub items </cfoutput>
</cfoutput>
if there's a way to change the order of elements in the 'inner' cfoutput ?
Can the query be both grouped and sorted by?

You will need to add ORDER BY clauses in your query for this to work, but you can nest cfoutput tags that use the group attribute.
<cfoutput query="someItems" group="someColumnName">
... doing some stuff here ..
<cfoutput group="someOtherColumnName> doing stuff with some sub items </cfoutput>
</cfoutput>
This assumes that in your query you have something that looks like:
ORDER BY someColumnName, someOtherColumnName
Keep in mind that the group attribute of cfquery is not the same as the GROUP BY clause in a SQL statement. You can use the group attribute of cfoutput for ANY column that is in the ORDER BY clause in your query.

One solution is to restructure your code to use the query-of-queries approach. Here is a good example of doing so:
http://www.bennadel.com/blog/2211-ColdFusion-Query-Of-Queries-vs-The-Group-Attribute-In-CFOutput.htm
Basically, you pull out all the data you care about in one master query (probably the query you have already written). You add a second query (against your first query, not against the database) that does the group by and aggregation of data that you need at the top level loop. Inside the loop driven by your second query, you use the row data in the group as a parameter to yet another query (against your first query again, not against the database) to pull out all the data relating to the current row ordered however you desire.
This idea of querying your query seems odd at first, but I have not had performance problems with it and it gives you a lot of flexibility to do what you want in your inner loop. Good luck!

Related

How to read columns with spaces from coldfusion queries?

I am reading data from a spreadsheet. One of the column in the spreadsheet contains spaces.
For Example, Columns names are [first name,last name,roll].
I am getting a qryObj after reading the spreadsheet.
Now when i am trying to read first name from the query
<cfquery dbtype="query" name="getName">
SELECT [first name]
FROM qryObj
</cfquery>
It is throwing db error. I have tried with ['first name'] also but still it is throwing error.
The error is:
Query Of Queries syntax error.
Encountered "[. Incorrect Select List, Incorrect select column
I did crazy stuff like googling to see what people had done in other situations, and tried various SQL approaches to escaping non-standard column names (back ticks, square barackets, double quotes, combos thereof) , and drew a blank. So I agree with #da_didi that QoQ/IMQ does not cater for this. You should raise a ticket in the Adobe bug tracker.
You could do SELECT *, which removes the need to reference the column name. Or you could serialize the query, use a string replace to rename the column, deserialise it again then QoQ on the revised name. I'd only do this with a small amount of data though.
Or you could push back on the owbner of the XLS file and say "no can do unless you revise your column names".
You could also perhaps suppress the column names as they stand from the XLS file using excludeHeaderRow,and then specify your own columns names. How did I find out one could do that? By RTFMing the <cfspreadsheet> docs.
Thats easy:
Query
Select [FIRST NAME]
in output loop of query
["FIRST NAME"]
Try this - set a variable works for me
<cfset first_name = #spreadsheetData['first name'][CurrentRow]#>
You cannot. Best practices: I always replace all spaces with an underline.
Simple. Just alias the select. Select [FIRST NAME] as FIRSTNAME from qryObj

How to access query column with multiple words?

I'm using a cfspreadsheet read to read a sheet into a query object.
<cfspreadsheet action="read" src="TestExcel.xls" sheet="1" query="spreadsheetData" headerrow="1" excludeHeaderRow="true">
The problem is, some of the headers contain more than one word. So I end up with a query a bit like this:
ID Name Start Date End Date
3 Test 1/1/2009 1/1/2013
17 Test 2 11/11/2010 11/11/2012
If I try to access one of the columns that have a space in the column name, I get an error.
<cfoutput query="spreadsheetData">
#start date#
</cfoutput>
I've tried #[start date]# as well, but that didn't work. I cannot control the format of the excel sheet that I receive. Is there any way to access the multiple-worded-header columns?
When using bracket notation the contents must end up as a string, so:
<cfoutput query="spreadsheetData">
#spreadsheetData['start date'][CurrentRow]#
</cfoutput>
If you don't use quotes, you are passing in a variable, which is done like so:
<cfset ColumnName = 'start date' />
<cfoutput query="spreadsheetData">
#spreadsheetData[ColumnName][CurrentRow]#
</cfoutput>
Note that you must use the query name before the brackets - if you simply write [ColumnName] then this is inline array creation notation, not accessing the variable.
Also, if using this outside of a query loop (i.e. not within cfoutput/cfloop with query attribute), you also need to scope the CurrentRow variable, i.e.
spreadsheetData[ColumnName][spreadsheetData.CurrentRow]
(or provide your own explicit number/variable).
As Leigh notes below, for cfspreadsheet-specific behaviour, you can also specify the columnnames attribute, to rename the column to something directly accessible, e.g.
<cfspreadsheet query=".." columnNames="Foo,Bar,StartDate,Etcetera" ..>

coalesce doesn't work when grouping is used

I have an easy question. I'm trying to use coalesce in combination with a group by clause. I want to get 0 values when the variable is null. Here is my sql server code:
SELECT SUM(COALESCE(NETTOTAL,0)) AS NETTOTAL,DATEPART(MM,RECORD_DATE) MONTH
FROM ORDERS WHERE ORDER_EMPLOYEE_ID=#attributes.record_emp_id#
GROUP BY DATEPART(MM,RECORD_DATE) ORDER BY MONTH
.. and my output:
<tr height="20">
<td>Orders</td>
<cfoutput query="get_orders"><td style="text-align:center;">#tlformat(nettotal,2)# - #month#</td></cfoutput>
</tr>
This code is just for the orders. There is also the sales row. Anyway here is the screenshot to make it more clear:
http://i.stack.imgur.com/VIAmr.png
To make it more clear I added the number of the month. As you can see the order is broken since there are no zero values for the other months...
P.S.Thank you all for the help! i really appreciate it!
Your query is not at fault. You are trying to select from ORDERS based on each employee_id. You are then looping over it.
If a given month has no orders, then there will be no row for it within the result set.
Even if there were only orders for the last 4 months, they would get pushed to the first 4 as you are not checking that the month you are currently outputting matches the column header.
For a bit of metacode, I would go down this route
1 - create an array as follows
arrMonths= [
{orders=0,sales=0},
{orders=0,sales=0}....
]
This will give you a stc you can iterate over later.
2 - I would then loop over each query
<cfoutput query="get_orders">
<cfset arrMonths[month].orders = nettotal>
</cfoutput>
3 - I would then iterate over the array
<tr height="20">
<td>Orders</td>
<cfoutput from="1" to="#ArrayLen(arrMonths)#" index="thisMonth">
<td style="text-align:center;">#tlformat(arrMonths[thisMonth].orders,2)# - #thisMonth#</td>
</cfoutput>
</tr>
This way, every month will ALWAYS have a value even if it's 0. You can also ditch the coalesce as the simple fact that rows with no orders have no records means they default to 0 so your query may become
SELECT
SUM(COALESCE(NETTOTAL)) AS NETTOTAL,
DATEPART(MM,RECORD_DATE) MONTH
FROM ORDERS
WHERE ORDER_EMPLOYEE_ID=<cfqueryparam cfsqltype="cf_sql_integer" value="#attributes.record_emp_id#">
GROUP BY DATEPART(MM,RECORD_DATE)
MONTH is now not necessary as it's just inserting into the array which deals with ordering
TRy ISNULL() Instead of COALESCE .
COALESCE is used for multiple argumentsif multiple arguments are not needed you could use ISNULL
Try to invert the order of the functions Sum and Coalesce.
IsNull might be more readable as well:
SELECT IsNull(SUM(NETTOTAL), 0) AS NETTOTAL,
DATEPART(MM, RECORD_DATE) MONTH
FROM ORDERS
WHERE ORDER_EMPLOYEE_ID = #attributes.record_emp_id#
GROUP BY DATEPART(MM, RECORD_DATE)
ORDER BY MONTH
If there are no elements of NETTOTAL, Coalesce from your code would not be called. So a Sum of no rows will be null.

Is there a way to escape and use ColdFusion query reserved words as column names in a query of query?

I'm working with a query that has a column named "Date."
The original query returns okay from the database. You can output the original query, paginate the original query, get a ValueList of the Date column, etc.
Query of Query
<cfquery name= "Query" dbtype= "query">
select
[Query].[Date]
from [Query]
</cfquery>
Response from ColdFusion
Query Of Queries syntax error. Encountered "Date. Incorrect Select
List,
Typically, I use descriptive names so I haven't run across this issue previously.
In this case, I'm working with a stored procedure that someone else wrote. I ended up modifying the stored procedure to use a more descriptive column name.
I have a service I use for transforming, searching and sorting queries with ColdFusion. I'm curious to know the answer to my original question, so that I can modify my service to either throw a better error or handle reserved words.
Is there a way to escape and use ColdFusion query reserved words as column names in a query of query?
The following code works fine for me:
<cfset query = queryNew("date")>
<cfdump var="#query#">
<cfquery name= "Query" dbtype= "query">
select
[Query].[Date]
from [Query]
</cfquery>
<cfdump var="#query#">
In standard mysql you'd "escape" the fields by using the ` character.
So for example:
select `query`.`date` from `query`
Try that and see if it works?

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.