Creating a survey using Coldfusion: Stuck on the table data insert - sql

I'm trying to create a survey tool in Coldfusion and I'm stuck on one part.
My tables are:
t_forms (id, name, desc)
t_questions (id, question, type, formid, order)
t_cdata (id, email)
t_cqdata (formid, questionid, customerid, answergiven)
The form fields are dynamically built using a url variable and look like this, for example:
<cfquery name="gs">
select * from t_forms where id = #url.sid#
</cfquery>
<cfquery name="gq">
select * from t_questions where fid = #gs.id# ORDER BY order ASC
</cfquery>
<cfform name="survey" method="post" action="">
<cfloop query="gq">
<cfinput type="text" name="q#gq.id#">
</cfloop>
<cfinput type="text" name="email">
<cfinput type="hidden" name="fid" value="#url.fid#">
<cfinput type="submit" name="submit" value="Save">
</cfform>
However, I'm having trouble when I need to put the value of the answer into the t_cqdata table, as the form element input needs to go into the table as well.
If anyone could help or point out where I am going wrong, that would be appreciated .

There is more than one way to have form fields associated with database identifier values. This is the one I find easiest to comprehend.
On the form page.
<cfoutput query="somequery">
<input name="field1#databaseID#">
<input name="field2#databaseID#">
etc
On the processing page.
<cfloop list="#form.fieldnames#" index="ThisElement">
<cfif left (ThisElement, 6) is "field1">
<cfset ThisID = RemoveChars(ThisElement, 1, 6)>
<cfset ThisField1Value = form[ThisElement]>
<cfset ThisField2Value = form['field2' & ThisID]>
Continue to set variables and then do something with them. In fact, in this example, once you've set ThisID, setting more variables is optional. You can simply use the form variables directly using the synax shown.

You will need to use an insert into cfquery.
You would use something like:
INSERT INTO t_cqdata
(q1
,q2
,q3
,q4
,q5
,q6
,q7)
VALUES
(
,
,
,
,
,)

Related

ColdFusion count the amount without using SQL

I am trying to count the amount without using SQL count() code
Is there a way to extract the results including the number amount over ColdFusion?
Select test
From ......
Where .....
<Cfif not test.RecordCount>
No records found
</Cfif>
<cfoutput query="test">#amount#</cfoutput>
It should display like this
TEST CountOutput
TEST A: 22
TEST B: 32
TEST B: 1
TEST C: 23
(From comments ...)
Why can't you use SQL for this? Unless there's a valid reason you can't use COUNT, it's by far the simplest way to aggregate. You don't need to know the specific values in the database table in order to perform a COUNT. Just do:
SELECT column, count(*) AS someAlias
FROM tableName
GROUP BY column
Yes, it is possible to do the same in ColdFusion code, but again unless there's a specific reason to do so, it's more efficient to use SQL.
<cfset data = {} />
<cfset names = {} />
<cfloop query="test">
<cfif not structKeyExists(data, test.name)>
<cfset data[test.name] = 0 />
<!--- if you need to preserve original case of name --->
<cfset names[test.name] = test.name />
</cfif>
<cfset data[test.name] += 1 />
</cfloop>
<cfoutput>
<cfloop collection="#data#" item="key">
#names[key]#: #data[key]#<br />
</cfloop>
</cfloop>
To get the total count without using SQL, Use
QUERYVARIABLE.RECORDCOUNT
Based on the condition used in the SQL query it will give you the count.
FOR Example,
<cfquery name="test">
SELECT * FROM database WHERE name=testA
</cfquery>
<cfdump var="#test.RECORDCOUNT#" />
Use this within a function, call it and store the return value in a stack or an array.
I strongly recommend you use a SQL Count(). I cannot imagine why you would want to do it inside a CFLoop or CFOutput, but it is possible if you use the Group attribute, and count each row that way.
Did you know that after you execute CFQuery against your SQL server to get all those detail records, you can run a "query of queries" summarize the SQL data. This article seems to explain it:
https://www.quackit.com/coldfusion/tutorial/coldfusion_query_of_queries.cfm

CFQuery not saving time portion to Oracle Datetime field

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>

Add and subtract float values from database

So I have this query to get the results from my database tables with the columns in and out.
<cfquery name="getInfo" datasource="testing">
select in, out from test
</cfquery>
Now what I need to do is to take a static number, eg; 100, and ADD the in and SUBTRACT the out from that static number.
So this is what I tried:
<cfquery name="getInfo" datasource="testing">
select in, out from test
</cfquery>
<table>
<cfset balance = 100>
<cfloop query="getInfo">
<cfset balance = balance + in - out> <!--- THIS IS WHAT I TRIED --->
<tr>
<td>#in#</td>
<td>#out#</td>
<td>#balance#</td>
</tr>
</cfloop>
</table>
So as you can see, I did set this code <cfset balance = 100 + in - out>. Basically what I am trying to do is to get the original value of balance which is 100 and add the values of in and subtract the value of out and save it as the new balance.
However, I am getting an error saying The value '' cannot be converted to a number..
I have set the values for in and out to be float in the database.
Where am I going wrong?
You need to update your query to cover NULL conditions
<cfquery name="getInfo" datasource="testing">
select ISNULL([in], 0) AS [in], ISNULL([out], 0) AS [out]
FROM test
</cfquery>
Also I put square brackets around in and out because they look like they might be key words
Also consider doing the math on the DB, you might get better performance

how to prevent coldfusion sql-injection on order by clause

Since cfqueryparam doesn't work in an order by, would using xmlformat stop sql injections?
ORDER BY #xmlformat(myVariable)#
Thanks,
http://www.petefreitag.com/item/677.cfm
A good way to get around this limitation is to use the ListFindNoCase function, to limit the sortable column names, for example:
<cfset sortable_column_list = "age,height,weight,first_name">
<cfquery ...>
SELECT first_name, age, height, weight
FROM people
ORDER BY <cfif ListFindNoCase(sortable_column_list, url.sort_column)>#url.sort_column#<cfelse>first_name</cfif>
</cfquery>
This is from a stored procedure, but translate an #ORDER_BY value to an actual database column and a #SORT_ORDER value to a SQL command.
ORDER BY
CASE WHEN #ORDER_BY = 'LENDER' AND #SORT_ORDER = 'D' THEN l.tms_name END DESC,
CASE WHEN #ORDER_BY = 'LENDER' AND #SORT_ORDER != 'D' THEN l.tms_name END,
CASE WHEN #ORDER_BY = 'LOAN_NUMBER' AND #SORT_ORDER = 'D' THEN p.Loan_Number END DESC,
CASE WHEN #ORDER_BY = 'LOAN_NUMBER' AND #SORT_ORDER != 'D' THEN p.Loan_Number END,
XML Format won't handle all cases.
The column check is good, but I'm guessing that the advantage of letting the user define what the order by is, is because you can make it more complex than just a single column. For instance, you could add several columns and an Ascending, Descending etc...
I'd suggest you make a globally available function that strips out any character that isn't a number, letter or comma. If someone did attempt to do a SQL Injection it would just fail.
<cfif refindnocas('^\w+ ?(desc|asc)?$', myVariable)>
ORDER BY #myVariable#
</cfif>
or
<cfset columnList = 'col1,col2,etc' /> <!--- might want to use in select as well --->
<cfset regexColList = replace(columnList, ',', '|', 'all') />
<cfif not refindnocas('^(#regexColList#) ?(desc|asc)?$', myVariable)>
<cfset myVariable = "DefaultSort" />
</cfif>
ORDER BY #myVariable#
or
ORDER BY #query_sort(myVariable, columnList, defaultSort)#
...
<cffunction name="query_sort">
<cfargument name="sort" />
<cfargument name="columns" />
<cfargument name"default" />
<cfset var regexcolumns = replace(columns, ',', '|', 'all') />
<cfif refindnocas('^(#regexcolumns#) ?(desc|asc)?$', sort)>
<cfreturn sort />
<cfelse>
<cfreturn default />
</cfif>
</cfargument>
etc
Another option is a slight twist on the ListFindNoCase approach. Column information can be stored in a structure. The key would be the publicly visible column name and the value is the real column name. It is a little more complex. But I like the fact that it does not require you to expose your schema. It also supports more compound statements as Dave mentioned.
<cfset sortCols = { defaultCol="DepartmentName"
, date="ReportDate"
, type="DepartmentName"
, num="EmployeeID" } />
....
SELECT Columns
FROM TableName
ORDER BY
<cfif structKeyExists(sortCols, url.sort_column)>
#sortCols[url.sort_column]#
<cfelse>
#sortCols["defaultCol"]#
</cfif>

Using CFQUERY and CFSELECT to pull multiple values based on selection

I have a CFQUERY pulling three columns. The following CFSELECT allows the user to make a selection from various results based on the 'display' parameter, and sets the value to the 'value' parameter.
I would like to pass the third unused value of that record to a variable to be used in a later query. I cannot set the 'value' field to the column I need since that value is needed in a query following this one (queries populate based on previous drop down selections).
Is there a way to do this? To somehow have the CFSELECT grab 2 separate values?
SubRegionName is Displayed.
State is the value to pass.
SubRegionCD for this selection made is needed later.
Code example below:
<cfquery name="qrySubTurf"
DATASOURCESTUFF>
SELECT SubRegionName, SubRegionCd, State
From dbo.tblRegions
where Region='#form.getRegion#' <!---Previous CFSELCT value--->
order by SubRegionName
</cfquery>
<cfselect name="getSubTurf"
style="width:220px"
size=1
multiple="no"
query="qrySubTurf"
value="state" <!---Value passed to the next CFQUERY--->
display="SubRegionName" <!---Value displayed to user--->
queryPosition="below"
onChange="AddForm.submit();">
<option value=""></option>
</cfselect>
Now I need to grab the SubRegionCD associated with the users selection of State and SubRegionName and assign it to a variable that can be used in the final query. I cannot use State alone to determine the SubRegionCD, but I CAN use SubRegionName to make a 1-1 match. Help?
Simplest (in terms of littlest-possible code change) would be to do:
<cfquery name="qrySubTurf"
DATASOURCESTUFF>
SELECT SubRegionName, SubRegionCd + ',' + State AS Key
From dbo.tblRegions
where Region=<cfqueryparam value="#form.getRegion#" cfsqltype="CF_SQL_VARCHAR">
order by SubRegionName
</cfquery>
<cfselect name="getSubTurf"
style="width:220px"
size=1
multiple="no"
query="qrySubTurf"
value="Key"
display="SubRegionName"
queryPosition="below"
onChange="AddForm.submit();">
<option value=""></option>
</cfselect>
And then use ListFirst(FORM.getSubTurf) and ListLast(FORM.getSubTurf). Also, don't forget to use <cfqueryparam>.
To maintain the query result across pages you have to split the value returned to the form's action page ... this is was the answer suggested ... <cfqueryparam value ="#ListFirst(form.***)#>