Comparing two character values in a Coldfusion cfquery - coldfusion

I have a database field which contains VARCHAR values like
XY23(CX Web)
AND I am writing a Query of Queries like
SELECT qPdfs.filename, qPdfs.code, qObjects.id
FROM qPdfs, qObjects
WHERE qPdfs.code = qObjects.code
OR like
SELECT qPdfs.filename, qPdfs.code, qObjects.id
FROM qPdfs, qObjects
WHERE <cfqueryparam value="#qObjects.code#" cfsqltype="CF_SQL_VARCHAR"> = <cfqueryparam value="#qPdfs.code#" cfsqltype="CF_SQL_VARCHAR">
But I am getting an error message like
XY23(CX Web) must be interpretable as a valid number in the current locale.
Any help?
Thank you

Are both of the database columns type varchar? If so, the QoQ is probably applying some implicit conversion based on the contents of the columns. If some of the codes are all numeric, the QoQ may be attempting to cast them to numbers internally before performing the comparison. Hence the error. cfqueryparam won't help here, because it can only be used on literals, not query columns. If a straight equality comparison ie ColName = ColName causes that error, try casting instead:
WHERE CAST(qPdfs.code AS VARCHAR) = CAST(qObjects.code AS VARCHAR)
NB: QoQ string comparisons are case sensitive, so you may want to convert the columns to upper/lower case first.

Related

Can you filter rows in a Query of Queries using a property value of a struct column value?

If an object, such as an Array or Struct is used as the column value of a row in a CF query object. Can properties of that object be used in the WHERE clause of a query of queries to limit the result set?
Given:
<cfset local.exampleArray=[
{ id:1,
nestedArray:["Tom","Dick","Harry"],
nestedStruct:{nid:42,name:"unknown"}
},
{ id:2,
nestedArray:["John","Paul","Ringo","George"],
nestedStruct:{nid:12,name:"rockstars"}
},
{ id:3,
nestedArray:["Bonny","Clyde"],
nestedStruct:{nid:43,name:"criminals"}
},
]>
<cfset local.exampleQuery=queryNew("id,nestedArray,nestedStruct","integer,object,object",local.exampleArray)>
The queries of queries:
<cfquery dbtype="query" name="local.exampleQoQ">
SELECT *
FROM [local].exampleQuery
WHERE nestedStruct.nid=12
</cfquery>
<cfquery dbtype="query" name="local.exampleQoQ2">
SELECT *
FROM [local].exampleQuery
WHERE nestedArray.length=3
</cfquery>
Results in the query of queries runtime error:
nestedStruct.nid/nestedArray.length does not match any table in FROM table list
When not using the object type columns in the WHERE clause, the objects are returned correctly when queried and behave as expected:
<cfquery dbtype="query" name="local.exampleQoQ">
SELECT *
FROM [local].exampleQuery
WHERE id=1
</cfquery>
<cfoutput query="local.exampleQoQ">
#local.exampleQoQ.id#:#ArrayLen(local.exampleQoQ.nestedArray)#:#local.exampleQoQ.nestedStruct.nid#
</cfoutput>
Will result in "1:3:42"
Is this just an issue where the QoQ implementation doesn't support accessing the properties of a column value object?
As I mentioned earlier, a database query can have a column with array/structure-ish data, but that's not really what a database is for. As you've seen, it makes querying for the data you want more difficult than it should be, and is really treating a database as little more than a place to store data.
Anyway, you seem to want to filter your query records by a specific value that's contained inside one column's structure data and also filter those results if another columns array data contains a certain number of records.
You don't want Query of Query for this. It's already a highly limited "query" aspect of CF, and should be used only when necessary. If you are using ColdFusion 2016+, you can use a function that was added: queryFilter().
Using your above setup under "Given:", you can use the following:
<cfscript>
/* Instead of QoQ, limit your Query with queryFilter() */
filteredQuery = queryFilter( exampleQuery
,function(o){ return o.nestedStruct.NID == 12 ;
}
) ;
</cfscript>
Which will give you a variable filteredQuery that contains:
Then you can just address filteredQuery.nestedArray to get your array of "John, Paul, George and Ringo".
But you also want to filter for the array in nestedArray to be 3 elements. So you can just add another condition to your callback return:
local.filteredQueryForLength = queryFilter(
local.exampleQuery2,
function(o){ return o.nestedStruct.NID == 12 && arrayLen(o.nestedArray) == 3 ; }
) ;
Which then gives you an empty Query Object, since there are 4 elements to the filteredQuery.nestedArray that you selected.
Finally, queryFilter has a member function that is simply filter(), so you can be even shorter and use this:
local.filteredQueryForLength2 = local.exampleQuery3.filter(
function(o){ return o.nestedStruct.NID == 12 && o.nestedArray.len() == 3 ; }
) ;
Also remember that ColdFusion Query Objects are Pass-By-Reference, so if you do anything (like filter()) that modifies the object, it will change that base object so it will be different if you use it again. Which also means that you don't have to assign it to a variable. You can just call queryFilter and then reference your original query object.
And another note: when using CF Script syntax (which I much prefer), don't forget that=is assignment and==is comparison. I forgot that initially and all of the records were returning withnestedStruct.NIDas12`. :-/
Last note: I created a Fiddle at https://trycf.com/gist/031a090059a46cd471aa44627fc7ee12/acf2016?theme=monokai. I added one extra element to your mocked query, so that you could see what your return object looks like with multiple elements matching the filters.

ColdFusion query of query date comparison

I have a query created from using cfspreadsheet that has a date column named "Actiondate", and looping through the query using isDate() shows that it's a date column, but in a query of that query it is being treated as a string. So when I apply a date filter in the where clause, it doesn't do the comparison correctly:
<cfquery ... >
SELECT *
FROM arguments.q
WHERE 1=1
<cfif isDate(arguments.dateFrom)>
AND actiondate >= <cfqueryparam cfsqltype="cf_sql_timestamp" value="#arguments.dateFrom#">
</cfif>
<cfif isDate(arguments.dateFrom)>
AND actiondate <= <cfqueryparam cfsqltype="cf_sql_timestamp" value="#arguments.dateTo#">
</cfif>
</cfquery>
actiondate comes in the format of mm/dd/yyyy. When I do the where clause using string literals as a test, it works:
...where actiondate = '11/05/2015' --returns all rows with that "date"
<cfset tempdate = createdate(2015,11,5)>...
...where actiondate = <cfqueryparam cfsqltype="cf_sql_date" value="#temp#"> --returns nothing
But again, isDate(actiondate) returns true when looping through the query. I can work around the problem, but is there a way to do what I need to still using a query of query?
(From comments ...)
IsDate verifies a value can be converted into a date. That does not mean the value already is a date/time object. In the case of cfspreadsheet, the returned query values are strings. When you compare them to the cfqueryparam values, which are date/time objects, you are comparing apples and oranges. So the QoQ does an implicit conversion of the date/time values to string and comes up with the wrong answer.
To perform a date comparison on "ActionDate", you must use CAST to convert the strings into a date/time objects first. Assuming all of the values are valid date strings, in the format mm/dd/yyyy, try it with a hard coded value like 11/05/2015 first. Then plug in your variables.
WHERE CAST(actionDate AS DATE) = <cfqueryparam value="11/05/2015"
cfsqltype="cf_sql_date">

PreparedStatement.setNString(int,java.lang.String) error trying to use Query of Queries

I have a simple Query of Queries statement like this:
<cfquery name="rsProductTypeID" dbtype="query">
SELECT
ProductTypeID
FROM
rsProductTypes
WHERE
ProductType = <cfqueryparam value="Computers" cfsqltype="cf_sql_nvarchar"/>;
</cfquery>
All it is doing is asking for the primary key value (ProductTypeID) from the recordset rsProductType depending on which ProductType we are asking for.
For some reason the above query gives me this error:
PreparedStatement.setNString(int,java.lang.String)
But if I change the Query of Queries and remove the <cfqueryparam> part and replace it with normal text, then it works fine. Here is the working code:
<cfquery name="rsProductTypeID" dbtype="query">
SELECT
ProductTypeID
FROM
rsProductTypes
WHERE
ProductType = 'Computers';
</cfquery>
I can't tell what the difference is between the code that is causing it to fail when using <cfqueryparam> and not.
I think I figured it out... when ColdFusion returns a recordset, it doesn't maintain the original datatype from the database. In my database ProductType is defined as nvarchar(50) but ColdFusion returns it as varchar format. So the cfsqltype attribute was causing the error. I changed the cfsqltype attribute to cf_sql_varchar and it works fine. Very annoying but what can you do.
I posted it as a self-answer in case anyone else gets this error. Not sure if that's against the rules or not.
cf_sql_nvarchar is not a valid value for the cfsqltype attribute of cfqueryparam
Docs: https://wikidocs.adobe.com/wiki/display/coldfusionen/cfqueryparam

cfqueryparam with uniqueidentifier type in table

I have a query where I am attempting to insert values into a table and one of these values (ImportID) is of type uniqueidentifier in the database. I have looked in the adobe CF documentation and saw that both cf_sql_char and cf_sql_idstamp should work for the cfquery param for my uniqueidentifier type. I also read this thread which says the same thing: What is the most appropriate Coldfusion cfsqltype to use for MS SQL's uniqueidentifier field type?
I have found a few other places that use the "maxlength" parameter for the cfqueryparam tag and cf_sql_char as the type but have not been able to make my code work. I keep getting an error along the lines of "Conversion failed when converting from a character string to uniqueidentifier." I'm including my code where I generate the UID and attempt the insert below (I replaced some of the variable names and took out all of the other columns for the import):
<cfscript>
ImportID = createUUID();
</cfscript>
<cfquery name="INSERTTableName" datasource="#Application.Datasource#">
INSERT INTO TableNameImport(
ImportID
)
VALUES(
<cfqueryparam value="#variables.ImportID#" cfsqltype="CF_SQL_CHAR">
)
</cfquery>
Any help would be greatly appreciated. Thanks in advance.
*********************************EDIT********************************
Found the answer:
<cfquery name="INSERTTableName" datasource="#Application.Datasource#">
DECLARE #ImportID uniqueidentifier
SET #ImportID = NEWID()
INSERT INTO TableNameImport(
ImportID
)
VALUES(
#ImportID
)
</cfquery>
The problem was that the UUID created from the coldfusion call is a different format from the one created in Microsoft SQL Server.
You are correct. SQL uses a GUID and createUUID manks a UUID. Both are 32hex value but use a differnt text format (location of dashes). And the use a different calculation to generate the random value. You can utilize UUID's if you change the text formating to have the GUID dash locations or send the binary value. For your example above that works you might also want return the GUID if cf needs a reference to that value.

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?