I am having a problem using a datetime field in an Oracle database with Coldfusion with and don't know how to solve it.
I have a table (Subject) which contains a datetime field ("MODIFIED"). If I look the date I obtain 04-JAN-17 for instance. If I do this query:
select to_char(MODIFIED, 'DD-MM-YYYY HH24:MI:SS') from Subject
It returns:
04-01-2017 09:57:43
I can retrieve this same result in ColdFusion.
I then try to copy that data to another table with the same structure if the "MODIFIED" value has changed. For doing that I retrieved the data in a first query and insert it into the new table. The data is retrieved correctly. Especially the date (I can see it in the ColdFusion script). However, the time of the date is lost. So for instance, I obtain 04-01-2017 00:00:00 instead of 04-01-2017 09:57:43.
Here is the relevant part of my script:
Main page:
<!-------- Get data -------->
<cfquery name="select_Subject_to_insert" datasource="#application.datasource#">
SELECT CODE, MODIFIED, NAME FROM Subject
</query>
<cfloop query="select_Subject_to_insert">
<!-------- Create an object "Subject" -------->
<cfscript>
subject_to_insert = createObject("component", "Subject").init();
subject_to_insert.id = -1;
subject_to_insert.Code = select_Subject_to_insert.CODE;
subject_to_insert.modified = select_Subject_to_insert.MODIFIED;
subject_to_insert.name = select_Subject_to_insert.NAME;
</cfscript>
<!-------- Call the function for saving data -------->
saveSubject(subject_to_insert)
</cfloop>
Main component:
<!--- Function to save (update) Subject. --->
<cffunction name="saveSubject" returntype="string" access="remote">
<cfargument name="subject" required="yes" type="vo.Subject" />
<cfset var timestp = createTimeStamp() />
<!--- insert --->
<cfquery name="insertSubject" datasource="#application.datasource#" result="insertSubjectResult">
insert into Subject (
CODE,
MODIFIED,
NAME
TIMESTAMP
)values (
<cfqueryparam value="#arguments.subject.CODE#" null="no" cfsqltype="cf_sql_varchar"/>,
<cfqueryparam value="#arguments.subject.MODIFIED#" null="no" cfsqltype="cf_sql_date"/>,
<cfqueryparam value="#arguments.subject.NAME#" null="no" cfsqltype="cf_sql_varchar"/>,
<cfqueryparam value="#timestp#" null="no" cfsqltype="cf_sql_numeric"/>
)
</cfquery>
........................................
</cffunction>
Could you please help me to understand why the time is not inserted correctly and solve the problem?
You want to use:
<cfqueryparam value="#arguments.subject.MODIFIED#" null="no" cfsqltype="cf_sql_timestamp"/>
The cf_sql_date type does not have a time component whereas cf_sql_timestamp does. You can check the compatability matrix in the documentation to see that cf_sql_timestamp corresponds to the Oracle DATE data type.
You can also simplify your page to get rid of the function call (assuming it is not doing extra processing):
<cfquery name="insertSubject" datasource="#application.datasource#" result="insertSubjectResult">
insert into Subject (
CODE,
MODIFIED,
NAME
TIMESTAMP
)
SELECT code,
modified,
name,
<cfqueryparam value=createTimeStamp() null="no" cfsqltype="cf_sql_numeric"/>
FROM subject
-- WHERE some_condition
</cfquery>
Related
I have the column deletedTime in my instances table.
Now I want to check in ColdFusion if the value is null.
There is only one record to be printed out with the query.
IsNull(test) is returning the correct value,
but IsNull(searchForDeletedInstances.deletedTime) returns false but the value is in the datatable null..
<cfquery datasource="hostmanager" name="searchForDeletedInstances">
SELECT deletedTime
FROM instances
WHERE instanceId = <cfqueryparam value="#instanceId#"
cfsqltype="CF_SQL_NVARCHAR">
</cfquery>
<cfloop query="searchForDeletedInstances">
<cfset test= #deletedTime#>
</cfloop>
<cfreturn IsNull(test)>
<cfreturn IsNull(searchForDeletedInstances.deletedTime)>
I would do the test in the query.
<cfquery datasource="hostmanager" name="searchForDeletedInstances">
SELECT deletedTime
FROM instances
WHERE instanceId = <cfqueryparam value="#instanceId#" cfsqltype="CF_SQL_NVARCHAR">
AND deletedTime IS NULL
</cfquery>
<cfreturn BooleanFormat(searchForDeletedInstances.recordcount)>
ColdFusion 2018 does allow for NULL values but the server/site has to be configured to use it. This may break a lot of older code.
ColdFusion before 2018 will use an empty string for NULL values.
I would change the query to only look for NULL deletedTime records. Then use the searchForDeletedInstances.recordCount value to determine if a NULL record was found. The code would look something like this:
<cfquery datasource="hostmanager" name="searchForDeletedInstances">
SELECT deletedTime
FROM instances
WHERE instanceId = <cfqueryparam value="#instanceId#" cfsqltype="CF_SQL_NVARCHAR">
AND deletedTime IS NULL
</cfquery>
<cfset nullRecord = searchForDeletedInstances.recordCount ? true : false>
<cfreturn nullRecord>
Null values from a query come back as empty strings. Test for IsDate() instead. You can always use cfdump to show the contents of your query so you can see the data and data types returned.
I'm building a query in my system that should take user input and search the Users table in my database. There are three different options that I need to filter in my query. First would be if user enters just John that is first name, second is if they enter Miller that is last name and the third option is if they combine first and last name separated by comma or space like John Miller or John, Miller. In our old system the query that was filtering these kind of situations where user enters the name looks like this:
<cfif Find(',',arguments.searchFldVal) and ListLen(arguments.searchFldVal) GT 1> <!--- if comma exists --->
<cfset theLName = Trim(ListGetAt(arguments.searchFldVal,1))>
<cfset theFName = Trim(ListGetAt(arguments.searchFldVal,2))>
(
u_lastName LIKE <cfqueryparam value="#theLName#%" cfsqltype="cf_sql_varchar" />
AND
u_firstName LIKE <cfqueryparam value="#theFName#%" cfsqltype="cf_sql_varchar" />
)
<cfelseif Find(' ',arguments.searchFldVal) and not find(',',arguments.searchFldVal)><!--- if space exists --->
<cfset spaceIndex = Find(' ',arguments.searchFldVal)>
<cfset theFName = Trim(Mid(arguments.searchFldVal,1,spaceIndex-1))>
<cfset theLName = Trim(Mid(arguments.searchFldVal,spaceIndex+1,len(arguments.searchFldVal)-spaceIndex))>
(
u_lastName LIKE <cfqueryparam value="#theLName#%" cfsqltype="cf_sql_varchar" />
AND
u_firstName LIKE <cfqueryparam value="#theFName#%" cfsqltype="cf_sql_varchar" />
)
<cfelse><!--- one word --->
(
u_lastName LIKE <cfqueryparam value="#trim(arguments.searchFldVal)#%" cfsqltype="cf_sql_varchar" />
OR
u_firstName LIKE <cfqueryparam value="#trim(arguments.searchFldVal)#%" cfsqltype="cf_sql_varchar" />
)
</cfif>
The solution above works so far but as you can see that involves using a server side language (that is ColdFusion in my case) to check/search input string for special characters. I'm wondering if there is better way to do this with just using SQL? I have read a few articles and some people proposed an option with full text search that I have never used before and I'm not sure if that would be a good fit in this case. The other option is to combine first and last name and then do the search. Something like this:
WHERE u_firstname +' '+ u_lastName LIKE '%#Arguments.searchVal#%'
OR u_firstname +', '+u_lastName LIKE '%#Arguments.searchVal#%'
If anyone has any experience with this type of search algorithm/technique please let me know. I have a few places in my system where I need to implement this and I'm looking for a reliable solution.
Here is my code:
<cfdump var="#addEnt#" >
<!-- ADD -->
<cfquery name="add" datasource="testdatasource" dbtype="OLEDB">
UPDATE tblrequests
SET
lastname='#ucase(form.lastname)#',
firstname='#ucase(form.firstname)#',
middlei='#ucase(form.middlei)#',
title='#form.title#',
eod='#dateformat(form.eod,'m-d-yyyy')#',
dutystation='#form.dutystation#',
requestsnetwork=<cfif parameterexists(form.requestsnetwork)>1<cfelse>0</cfif>,
affiliation='#form.affiliation#',
commentssupvreq='#form.commentssupvreq#',
requestdelete=<cfif form.requestdelete IS NOT ''>'#dateformat(form.requestdelete,'m-d-yyyy')#',<cfelse>Null,</cfif>
commentssupvdelete='#form.commentssupvdelete#',
commentssupvedit='#form.commentssupvedit#',
dateemailrequested=<cfif form.dateemailrequested IS NOT ''>'#dateformat(form.dateemailrequested,'m-d-yyyy')#',<cfelse>Null,</cfif>
commentsit='#form.commentsit#',
bgcomplete=<cfif form.bgcomplete IS NOT ''>'#dateformat(form.bgcomplete,'m-d-yyyy')#',<cfelse>Null,</cfif>
dategroupscreated=<cfif form.dategroupscreated IS NOT ''>'#dateformat(form.dategroupscreated,'m-d-yyyy')#',<cfelse>Null,</cfif>
WHERE recnumber = #addEnt#
</cfquery>
When I submit the form I get an error:
Error Executing Database Query. [Macromedia][SQLServer JDBC
Driver][SQLServer]Incorrect syntax near the keyword 'WHERE'.
My cfdump displays the correct addent number from sql, but using #addEnt# in the sql statement is not working. Other pages in my applications ## for SQL queries and they work fine.
The last line in your set statements has a comma at the end, which is where the SQL will be complaining
(Too long for comments)
As suggested in the comments, there are several improvements you could make to the query: one of the biggest, being the addition of cfqueryparam. It provides several advantages, such as:
CFQueryparam, or bind variables, help improve performance by encouraging databases to reuse query execution plans, rather than generating a new one each time (which is a costly operation).
Bind variables also prevent the execution of client supplied values as SQL commands, which has the happy side effect of preventing common forms of sql injection.
CF's implementation of bind variables also provides an extra layer of validation, by type checking input values, before the query is ever executed. So when invalid parameters are detected, it saves a wasted trip to the database.
A few other tips for improving the query
Although it makes no syntactical difference, consider placing commas at the beginning of each line, rather than at the end. This makes it easier to spot extra or missing commas:
UPDATE SomeTable
SET ColumnA = 'xxxx'
, ColumnB = 'yyyy'
, ColumnC = 'zzzzz'
, ColumnD = 'xxxx'
, ColumnE = 'yyyy'
WHERE ....
It looks like your query is populating several datetime columns. When working with datetime columns, it best to use date objects, not strings. Date strings are ambiguous, and can be interpreted differently than you might expect, depending on the database or settings. To insert a date only, use <cfqueryparam cfsqltype="cf_sql_date" ...>, for both date and time use <cfqueryparam cfsqltype="cf_sql_timestamp" ...>. Obviously, always validate the date strings first.
Consider using cfqueryparam's null attribute. It can be quite handy when inserting null values conditionally. (See example below)
As an aside, ParameterExists was deprecated a while ago and replaced with IsDefined, or preferably StructKeyExists. In this case, another alternative to a CFIF is to declare a default with cfparam, so the form field in question always exists.
Putting it all together, your final query might look something like this. I guessed about the column data types, so adjust as needed.
UPDATE tblrequests
SET lastname = <cfqueryparam value="#ucase(form.lastname)#" cfsqltype="cf_sql_varchar">
, firstname = <cfqueryparam value="#ucase(form.firstname)#" cfsqltype="cf_sql_varchar">
, middlei = <cfqueryparam value="#ucase(form.middlei)#" cfsqltype="cf_sql_varchar">
, title = <cfqueryparam value="#form.title#" cfsqltype="cf_sql_varchar">
, eod = <cfqueryparam value="#form.eod#" cfsqltype="cf_sql_date">
, dutystation = <cfqueryparam value="#form.dutyStation#" cfsqltype="cf_sql_varchar">
, requestsnetwork = <cfqueryparam value="#form.requestsNetwork#" cfsqltype="cf_sql_bit">
, affiliation = <cfqueryparam value="#form.affiliation#" cfsqltype="cf_sql_varchar">
, commentssupvreq = <cfqueryparam value="#form.commentsSupvReq#" cfsqltype="cf_sql_varchar">
, requestdelete = <cfqueryparam value="#form.requestDelete#" cfsqltype="cf_sql_date" null="#not isDate(form.requestDelete)#">
, commentssupvdelete = <cfqueryparam value="#form.commentssupvdelete#" cfsqltype="cf_sql_varchar">
, commentssupvedit = <cfqueryparam value="#form.commentssupvedit#" cfsqltype="cf_sql_varchar">
, dateemailrequested = <cfqueryparam value="#form.dateEmailRequested#" cfsqltype="cf_sql_date" null="#not isDate(form.dateEmailRequested)#">
, commentsit = <cfqueryparam value="#form.commentsit#" cfsqltype="cf_sql_varchar">
, bgcomplete = <cfqueryparam value="#form.bgComplete#" cfsqltype="cf_sql_date" null="#not isDate(form.bgComplete)#">
, dategroupscreated = <cfqueryparam value="#form.dateGroupsCreated#" cfsqltype="cf_sql_date" null="#not isDate(form.dateGroupsCreated)#">
WHERE recnumber = <cfqueryparam value="#addEnt#" cfsqltype="cf_sql_integer">
A user got this error
DIAGNOSTICS: Error Executing Database Query. [Macromedia][SQLServer
JDBC Driver][SQLServer]There are more columns in the INSERT statement
than values specified in the VALUES clause. The number of values in
the VALUES clause must match the number of columns specified in the
INSERT statement. The error occurred on line 252. MESSAGE: Error
Executing Database Query. ROOT CAUSE:
coldfusion.tagext.sql.QueryTag$DatabaseQueryException: Error Executing
Database Query. Server: server2
There should be around 60 values/columns. Possible solution is adding <CFIF isDefined("VARIABLES.xxx")> to each of the values in INSERT and VALUES but this would take a long time. Is there a better way? Here is some of the code
<CFQUERY name="addAgency" datasource="#REQUEST.dsn#">
INSERT INTO agency (agency_name, code, address1, address2, city_id, postal, phone, alternativePhone, fax, provincialRegistration,
<!--- contact, ---> firstname, lastname, username, password, relationship_id, editdate, adddate, email, URL_link,Airport_id,
header, teaser, full_desc, <CFIF isDefined("VARIABLES.specialty")>specialty,</CFIF> hours_1, hours_2, hours_3, hours_4,
hours_5, hours_6, hours_7, merchant_ID,
region_ID, fcApproved, agencyType_ID, agencystatus, crossview, insp_Approved, rbc_Approved,
branch_number, gp_entity_ID,
<CFIF VARIABLES.alias1 gt 100> alias1,</CFIF>
<CFIF VARIABLES.alias2 gt 100> alias2,</CFIF>
<CFIF Trim(VARIABLES.alias3) NEQ ""> alias3,</CFIF>
default_agent_id,LOCAL_PASSWORD,LOCAL_USERNAME,NATIONAL_PASSWORD,NATIONAL_USERNAME,VACATIONCLUB_PASSWORD,VACATIONCLUB_USERNAME,toll_free_number,display_toll_free ,MARKETINGREGIONID, searchPreference, bookingOption,customer_can_choose
<CFIF isDefined("variables.logo_filedata")>,logo_fileName,logo_fileData,logo_mimeType</CFIF>
,gds, pseudo_city, acv_affiliate_code, vip_ext,owner_manager_title_en,owner_manager_title_fr)
VALUES (<CFQUERYPARAM value="#Trim(sanitize(VARIABLES.agency_name))#" cfsqltype = "cf_sql_varchar" maxlength="150">,
<CFQUERYPARAM value="#Trim(sanitize(VARIABLES.code))#" cfsqltype = "cf_sql_varchar" maxlength="50">,
<CFQUERYPARAM value="#Trim(sanitize(VARIABLES.address1))#" cfsqltype = "cf_sql_varchar" maxlength="255">,
<CFQUERYPARAM value="#Trim(sanitize(VARIABLES.address2))#" cfsqltype = "cf_sql_varchar" maxlength="255">,
<CFQUERYPARAM value="#sanitize(VARIABLES.city_id,true,true,false,true,false)#" cfsqltype = "cf_sql_integer">,
<CFQUERYPARAM value="#Trim(sanitize(VARIABLES.postal))#" cfsqltype = "cf_sql_varchar" maxlength="10">,
<CFQUERYPARAM value="#Trim(sanitize(VARIABLES.phone))#" cfsqltype = "cf_sql_varchar" maxlength="20">,
<CFQUERYPARAM value="#Trim(sanitize(VARIABLES.fax))#" cfsqltype = "cf_sql_varchar" maxlength="20">,
<!--- <CFIF IsDefined("VARIABLES.contact")>#Trim(VARIABLES.contact)#, <CFELSE> ' ', </CFIF> --->
<CFQUERYPARAM value="#Trim(sanitize(VARIABLES.firstname))#" cfsqltype = "cf_sql_varchar" maxlength="40">,
<CFQUERYPARAM value="#Trim(sanitize(VARIABLES.lastname))#" cfsqltype = "cf_sql_varchar" maxlength="40">,
'-', '-',
<!--- Dummy un/pw --->
<CFQUERYPARAM value="#sanitize(VARIABLES.relationship_ID,true,true,false,true,false)#" cfsqltype = "cf_sql_integer">,
#CreateODBCDateTime(Now())#,
#CreateODBCDateTime(Now())#,
<CFQUERYPARAM value="#Trim(sanitize(VARIABLES.email))#" cfsqltype = "cf_sql_varchar" maxlength="40">,
Each of your s in the "columns" area MUST be matched with corollary CFIF in the "values" section as long as you are doing it this way. While there are other ways of doing it, they are not necessarily less messy. You could, for example:
set defaults for all columns with some cfparams so you did not have to have any "cfifs"
Write multiple "cleaner" queries
Move the whole thing to a stored procedure and pass what you have into it - the SP would then worry about the logic.
Save a small subset of values then "update" based on your cfif logic (don't like that one!).
As you can see they all have a downside.
Elaboration on 1)
Instead of doing something arduous like:
<CFIF isDefined("VARIABLES.specialty")> Specialty,</cfif>
Before you start your query make sure you have a value for all the variables you need using cfparam as in:
<Cfparam name="Specialty" default=""/>
(of course the default for specialty might be 0 or whatever).
The cfparam tag checks to see IF the variable exist and if it does not exist it creates it with the default you specify. This would mean you can "guarantee" that the variable will indeed exist - meaning you can eliminate the CFIF statement in your query.
One cavaet has to do with NULLs in the database. If you are depending on nulls (meaning if specialty does not exist you wish for the column to remain null), then you will need to add the "null" attribute to your cfqueryparam as in:
<CFQUERYPARAM
value="#Trim(sanitize(VARIABLES.specialty))#"
cfsqltype = "cf_sql_varchar"
null="#mynullfunction(variables.specialty)#"/>,
Note the "mynullfunction" - I ususally create a utility UDF for this that simply returns YES if the value is blank or zero (or whatever I define) and "no" if it's populated.
The best solution is to use the null attribute of cfqueryparam for columns where a default has not be set in the database, this means you can avoid adding conditions to the INSERT INTO and keeps the VALUE to one line per column. When a default does exist for a column then you need to use the long winded condition. This is the simplest, least obtrusive change.
Example where Alias1 has a database default, speciality does not.
` <cfset Variables.IsValidAlias1 = Variables.alias1 gt 100>
<cfquery name="addAgency" datasource="#REQUEST.dsn#">
INSERT INTO agency (agency_name,
specialty
<cfif Variables.IsValidAlias1>
, Alias1
</cfif>)
VALUES (<cfqueryparam value="#Variables.agency_name#" cfsqltype="cf_sql_varchar" maxlength="150">,
<cfqueryparam value="#Variables.specialty#" cfsqltype="cf_sql_varchar" null="#StructKeyExists(Variables,'Specialty') EQ false#" maxlength="199">
<cfif Variables.IsValidAlias1>
,<cfqueryparam value="#Variables.Alias1#" cfsqltype="cf_sql_varchar" maxlength="199">
</cfif>)
</cfquery>`
As an aside you should use StructKeyExists() instead of IsDefined as the former is faster and can be clearer to read. You should put this code inside a function, and that function inside a component so it can be re-used. This is assuming your using CF6 or later.
I would avoid setting empty string defaults for each field or setting values to NULL for non-existent keys. The reason is that you may now (or decide to have in the future) default values in your database columns. As the resulting NULL values would be explicit, they would override your database defaults. If you had passed in an empty string, this might be valid but as a default it likely is not.
My main advice would be to be keep each column on one line so that you easily see that you are being consistent in your conditionals. As Mark said, you must be careful to have the exact same conditionals in the "columns" area as the "values" area of your query.
Somewhat as an aside, I have a free tool called DataMgr that you could use to manage all of this sort of thing for most simple queries.
http://www.bryantwebconsulting.com/docs/datamgr/replace-sql-inserts-and-updates.cfm
http://www.bryantwebconsulting.com/docs/datamgr/getting-started.cfm
The main thing, however, is to be consistent in your conditionals and to format your query in such a way as to make it obvious when you are not.
I want to ask how to update data in all row at the same time. This is my SQL:
<cfloop index="#form.ppp_id#">
<cfquery name="viewPoint" datasource="#application.DataSource#">
update PCRS_PHOTOPOINT set PPP_POINT_FROM = #form.point_from#,
PPP_POINT_UNTIL = #form.point_until#,
PPP_UPDATE_DATE = SYSDATE, PPP_ICONS_NAME = '#form.icons_name#'
where PPP_ID = #form.ppp_id#
</cfquery>
</cfloop>
I got this error:
Attribute validation error for tag 'CFLOOP'
There's no need to use a loop at all. You can use SQL's in operator instead of = to update a list of values all at once, instead of executing a SQL statement in a loop. Also, as someone else already mentioned, you'd better be using cfqueryparam instead of passing user-supplied strings straight to the DB.
<cfquery name="viewPoint" datasource="#application.DataSource#">
update PCRS_PHOTOPOINT set
PPP_POINT_FROM = <cfqueryparam cfsqltype="cf_sql_varchar" value="#form.point_from#">,
PPP_POINT_UNTIL = <cfqueryparam cfsqltype="cf_sql_varchar" value="#form.point_until#">,
PPP_UPDATE_DATE = SYSDATE,
PPP_ICONS_NAME = <cfqueryparam cfsqltype="cf_sql_varchar" value="#form.icons_name#">
where PPP_ID in (<cfqueryparam list="yes" cfsqltype="cf_sql_integer" value="#form.ppp_id#">)
</cfquery>
You should be doing it like this:
<cfloop list="#form.ppp_id#" index="iItems">
<cfquery name="viewPoint" datasource="#application.DataSource#">
update PCRS_PHOTOPOINT set PPP_POINT_FROM = "#form.point_from#",
PPP_POINT_UNTIL = "#form.point_until#",
PPP_UPDATE_DATE = SYSDATE, PPP_ICONS_NAME = "#form.icons_name#"
where PPP_ID = #iItems#
</cfquery>
</cfloop>
Let me know how it goes.