Use count() function in ColdFusion query of query - coldfusion

I wanted to use count() function in ColdFusion Query object.
Here is my code and test:
<cfset x = querynew("id,name")>
<cfquery name="y" dbtype="query">
select count(*) as total from x
</cfquery>
<cfoutput>Test1: #y.total#</cfoutput>
<cfset temp = QueryAddRow(x)>
<cfset Temp = QuerySetCell(x, "id", 1)>
<cfset Temp = QuerySetCell(x, "Name", "Vikas")>
<cfquery name="y" dbtype="query">
select count(*) as total from x
</cfquery>
<cfoutput>Test2: #y.total#</cfoutput>
Should I use convert function? Like if total is [enpty string] then result should be 0.
Or is there any other best/proper way to do that?

It does seem like this is a bug, however there is an easy way around it. Simply wrap the y.total in val(), so it would read:
<cfoutput>Test1: #val(y.total)#</cfoutput>
<cfoutput>Test2: #val(y.total)#</cfoutput>
val() will return 0 if an empty string is passed to it.

I think you've found a bug in CF here (perhaps log it # http://cfbugs.adobe.com/cfbugreport/flexbugui/cfbugtracker/main.html).
The first query should return 0, not [nothing]!
In your simple example, I think just dispense with the QoQ entirely, and just use x.recordCount.
But obviously this is not much chop if you have a WHERE filter in your QoQ, in which case You're gonna need to do something like you suggest. I can't see a better approach here.
If you raise that bug with Adobe, let us know the bug ref so we can vote for it ;-)
--
Adam

If you just want the number of rows in de query-object use: #x.recordcount#

Related

ColdFusion 11 DateFormat

I am moving one of our applications from ColdFusion 9.01 to ColdFusion 11 and encountered a situation where I cannot get the date formatted the way I want it using "DateFormat". I read through the docs since things have changed in CF versions, but I honestly can't figure out why this isn't working. It worked beautifully in CF 9. I know it's probably something very easy, but I am just not seeing it.
The query (Oracle DB) provides me a list of the last 30 days and the loop is simply to reformat the date output from "2014-07-01 00:00:00.0" to a more friendly looking display of 01-Jul-2014 except that I cannot get it to format as "dd-mmm-yyyy" it just spits back the original output from the query. I hard coded the date where normally there would be a cfquerparam. Any ideas?
<cfquery name="qryDateArray" datasource="#request.db#">
select trunc(to_date('07/01/2014', 'mm/dd/yyyy') + 1 - rownum) as ref_date
from dual connect by rownum <= 30
</cfquery>
<cfloop from="1" to="#qryDateArray.recordcount#" index="j">
<cfset qryDateArray.ref_date[j] = DateFormat(qryDateArray.ref_date[j], "dd-mmm-yyyy")>
</cfloop>
<cfoutput>
<cfdump var="#qryDateArray#">
</cfoutput>
I could not test this on CF11 since I do not have it handy. I did verify that your code though returns results as you explained when I ran it on my CF10 environment here. So what you can do is add a column to the query object and define it as a varchar and add your formatted data to that. This in turn dumped out the formatted dates.
<cfquery name="qryDateArray" datasource="#request.db#">
select trunc(to_date('07/01/2014', 'mm/dd/yyyy') + 1 - rownum) as ref_date
from dual connect by rownum <= 30
</cfquery>
<cfset aryData = [] />
<cfloop from="1" to="#qryDateArray.recordcount#" index="j">
<cfset ArrayAppend(aryData, DateFormat(qryDateArray.ref_date[j], "dd-mmm-yyyy")) />
</cfloop>
<cfset QueryAddColumn(qryDateArray, "STRDATE", "VarChar", aryData) />
<cfoutput>
<cfdump var="#qryDateArray#">
</cfoutput>
If dependent on the query column names then could use something like Ben's method explained here to do some renaming of the columns: http://www.bennadel.com/blog/357-ask-ben-changing-coldfusion-query-column-names.htm
It'd be great if you'd given us a portable test case rather than one that relies on your database, but I suspect it is because ColdFusion has become more rigid with its type management of query columns.
So CF considers your ref_date column to be of type date, so when you try to put the formatted string back into the query column, CF tries (and succeeds) to convert the string back into a date.
Aside:
I have to wonder why you don't format the data string in the DB from the outset, and just return it the way you need it, rather than returning something else, then looping over the thing to adjust it..?

ArrayMin on query column with a null value

CF8
I was using this line to get the MIN value of a query column. I just noticed a null value in a recordset causes an error. Is there a simply way to tell ArrayMin to skip nulls w/o having to loop the column and load an array with all non-null values?
<cfset temp_data_min = #round(ArrayMin(query_x["some_field"]))#>
thanks!
Building off of what Al said with using the query-of-queries, just adding the Min() call into the query.
<cfquery name="query_x_fixed" dbtype="query">
SELECT Min(some_field) as some_field
FROM query_x
WHERE some_field IS NOT NULL
</cfquery>
<cfset temp_data_min = round(query_x_fixed.some_field)>
Tested to work in CF9
You could loop through your array and create a new array that doesn't contain any null values. Then apply the ArrayMin function to the new array.
<cfset newArray = []>
<cfloop index="x" array="#query_x["some_field"]#">
<cfif x NEQ 'null'>
<cfset arrayAppend(newArray, x)>
</cfif>
</cfloop>
<cfset temp_data_min = round(ArrayMin(newArray))>
NOT TESTED
Simplest way is probably to do a query-of-queries with just that column and remove the nulls.
<cfquery name="query_x_fixed" dbtype="query">
SELECT some_field
FROM query_x
WHERE some_field IS NOT NULL
</cfquery>
<cfset temp_data_min= round(ArrayMin(query_x_fixed["some_field"]))>
(not tested)
You can keep the solution at one line by converting the column to a list then into an array. ListToArray defaults to ignoring empty list items, which is what the null values will be.
<cfset temp_data_min = Round(ArrayMin(ListToArray(ValueList(query_x.some_field, Chr(7)), Chr(7))))>
This should be faster than any of the other proposed solutions and is less code.
I've specified the delimiter for locales that use the comma as the decimal separator in numbers. If you're in the US or another locale that uses the "." then you can remove the delimiter arguments.
Additional functions used:
ListToArray
ValueList
Chr

how to store query data in variables in ColdFusion for use later?

I am trying to retrieve and store ID's for each item retrieved from my table, so I can use these ID's later. I tried nesting the queries, but this didn't, work. Here is my first query:
<CFQUERY datasource="MyDSN" name="MAIN2"> SELECT * from order_items where orderID= #orderID#</CFQUERY>
Now, if I output this query it displays, 1 and 117 which are the two ID's I need.
My next query is:
<CFQUERY datasource="MyDSN" name="MAIN3">select c.catalogueID,
c.product_name,
c.product_price,
c.description,
p.productID
from products p
join product_catalogue c on c.catalogueid = p.catalogueid
where p.productid = "#productID#"</CFQUERY>
But it is telling me that productID is not defined, it is obviously empty. I am just getting started using ColdFusion, so I am not sure the best way to store the values I need so I use them again. I also need to loop the second query to run for each ID 1 and 117, so twice.
Any suggestions on how to accomplish this would be greatly appreciated.
Thanks
My basic rule is that if I find myself using queries to create other queries or looping over a query to execute other queries; it is time to consider combining the queries.
I'm not sure what field you are using in the MAIN2 query to feed the MAIN3 query. So, I put in "productID" in the query below. You may have to change it to fit your field name.
<CFQUERY datasource="MyDSN" name="MAIN3">select c.catalogueID,
c.product_name,
c.product_price,
c.description,
p.productID
from products p
join product_catalogue c on c.catalogueid = p.catalogueid
where p.productid IN (SELECT DISTINCT productID from order_items where orderID= <cfqueryparam value="#orderID#" cfsqltype="CF_SQL_INTEGER">)
</CFQUERY>
You could also change this query to utilize a "join" to connect [order_items] to the query.
Lastly, you should use the <cfqueryparam> tag for the where clauses; this helps protect your query from sql injection attacks.
Whenever I'm caching data for use later, I tend to ask myself how I'll be using that data, and whether it belongs in another data type rather than query.
For instance, if I'm wanting a bunch of data that I'm likely to access via ID, I can create a structure where the key is the ID, and the data is another structure of a dataset. Then I'll save this structure in application scope and only refresh it when it needs to be. This is zippy fast and so much easier to grab with
rather than querying for it every time. This is especially useful when the query that creates the original data set is kind of a resource hog with lots of joins, sub-queries, magical cross-db stored procedures, but the datasets returns are actually fairly small.
So creating your products structure would look something like this:
<CFQUERY datasource="MyDSN" name="MAIN3">
SELECT
c.catalogueID,
c.product_name,
c.product_price,
c.description,
p.productID
FROM products p
JOIN product_catalogue c
ON c.catalogueid = p.catalogueid
WHERE p.productid = <cfqueryparam value="#ProductID#" cfsqltype="cf_sql_integer">
</CFQUERY>
<cfset products = structNew() />
<cfset item = structNew() />
<cfloop query="MAIN3">
<cfif NOT structKeyExists(products, productID)>
<cfset item = structNew() />
<cfset item.catalogueID = catalogueID />
<cfset item.product_name = product_name />
<cfset item.product_price = product_price />
<cfset item.description = description />
<cfset products[productID] = structCopy(item) />
</cfif>
</cfloop>
<cfset application.products = structCopy(products) />

can't define id ColdFusion

here is my ColdFusion code:
Example1:
<cfquery name="GET_BRAND" datasource="#dsn1#">
SELECT PRODUCT_CATID
FROM PRODUCT_CAT
WHERE PRODUCT_CATID = PRODUCT_CATID
</cfquery>
#get_brand.product_catid#
But it shows all the time number 1, i just can't understand why, and how do i make it work properly, this code should have defined the brand_id, but instead shows 1.
The system is Workcube.
Here is my example for getting from the static product's id, its dynamic price:
Example 2:
<cfset product_id = 630>
<cfquery name="price_standart" datasource="#dsn3#">
SELECT
PRICE_STANDART.PRICE PRICE
FROM
PRICE_STANDART
WHERE
PRICE_STANDART.PRODUCT_ID =
<cfqueryparam value="#product_id#" cfsqltype="cf_sql_integer">
</cfquery>
But this time i need to get from dynamic product's ID its dynamic brand id.
This script works the same way as the Example 1:
<cfquery name="GET_BRAND" datasource="#dsn1#">
SELECT BRAND_ID
FROM PRODUCT_BRANDS
WHERE BRAND_ID = BRAND_ID
</cfquery>
#get_brand.BRAND_ID#
As Andreas shows in his code, your query isn't going to work as written. The statement WHERE PRODUCT_CATID = PRODUCT_CATID doesn't actually pass a value - it would actually just be self-referential within the table values. In this case, it would return everything in the table.
You should instead have:
WHERE PRODUCT_CATID = #PRODUCT_CATID#
Where #PRODUCT_CATID# represents a variable. Better yet, use cfqueryparam as Andreas shows (this prevents SQL injection and improves query performance). However, I am not even sure that is what you intend since if you have the product ID why do you need to get it from the database? Instead, I assume you probably want to get the brands from the product in a particular category. Not knowing your table structure, it's hard to write that query for you but it might look something like:
<cfquery name="GET_BRAND" datasource="#dsn1#">
SELECT PRODUCT.BRAND_ID
FROM PRODUCT
INNER JOIN PRODUCT_CAT
ON PRODUCT.PRODUCT_CATID = PRODUCT_CAT.PRODUCT_CATID
WHERE PRODUCT_CATID = <cfqueryparam cfsqltype="cf_sql_integer" value="#product_catid#">
</cfquery>
Lastly, as both comments indicate, you would need to loop through the results to see all the records returned.
You need to wrap the statement in tags like this.
<cfquery name="GET_BRAND" datasource="#dsn1#">
SELECT PRODUCT_CATID FROM PRODUCT_CAT WHERE PRODUCT_CATID = PRODUCT_CATID
</cfquery>
<cfoutput query =GET_Brand">
#get_brand.product_catid#
</cfoutput>
It's not very clear what your question really is about, but let me guess:
<cfquery name="GET_BRAND" datasource="#dsn1#">
SELECT PRODUCT_CATID
FROM PRODUCT_CAT
WHERE PRODUCT_CATID = <cfqueryparam cfsqltype="cf_sql_integer" value="#product_catid#">
</cfquery>
where #product_catid# refers to a variable you defined earlier in your code or received via form or url scope.
<cfloop query="GET_BRAND">
#get_brand.product_catid#<br />
</cfloop>
will show a list of all the product_catid's returned by the query.
It's not too clear what you are after here, but in the queries there are at least 2 problems. First your WHERE clause
WHERE PRODUCT_CATID = PRODUCT_CATID
is like saying
WHERE 1=1
This will return the full recordset. You can see this by adding
<cfdump var="#GET_BRAND#">
under your code to see the query output. I'm guessing this will show all records in the table.
To match just one record you need your WHERE clause to be like
WHERE PRODUCT_CATID = 3
or have the #...# wrapped around the variable you are trying to match to make it dynamic.
Secondly to the query result may be more than one record, and to see any more than the first record you need to loop over the output. One way is to use
<cfoutput query="GET_BRAND">
#BRAND_ID# <br>
</cfoutput>
My guess of what you are after is
<cfset ID_TO_MATCH=3>
<cfquery name="GET_BRAND" datasource="#dsn1#">
SELECT BRAND_ID
FROM PRODUCT_CAT
WHERE PRODUCT_CATID = #ID_TO_MATCH#
</cfquery>
<cfoutput query="GET_BRAND">
#BRAND_ID# <br>
</cfoutput>

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.