insert into table by looping over structure - coldfusion

I am trying to insert into the code but it is inserting value in every row, rather than question value in question and answer value in answer:
<cfset StructDelete(structform,'title')>
<cfset StructDelete(structform,'mode')>
<cfset StructDelete(structform,'formsubmission')>
<cfset StructDelete(structform,'file_upload')>
<cfset StructDelete(structform,'czContainer_czMore_txtCount')>
<CFSET StructDelete(structform,'action')>
<CFLOOP collection="#structform#" index="whichPair">
<cfset Questions = "question" & structform[whichPair]>
<cfset answer = "answer" & structform[whichpair]>
<cfquery name="insertData" datasource="aas">
insert into faqsquestions(question,answer,createdon,faqID)
values(<cfqueryparam cfsqltype="cf_sql_varchar" value="#Right(questions, Len(questions)-8)#">,
<cfqueryparam cfsqltype="cf_sql_longvarchar" value="#Right(answer, Len(answer)-8)#">,
<cfqueryparam cfsqltype="cf_sql_date" value="#CreateODBCDate(now())#">,
<cfqueryparam cfsqltype="cf_sql_integer" value="#getLastID#">)
</cfquery>
</CFLOOP>
can anyone tell what i am doing wrong here, i know i am using question as a static value just inside the loop as cfset and doing a right to remove that question variable which makes no sense but i will remove it when i am finished fixing my code
questions and answers are like this:
http://prntscr.com/lntu2l

That's the wrong type of loop for what you're trying to do. The reason is a structure loop iterates once - for each field. When what you want is to loop once - for each pair of fields.
A simple option is add a hidden field to your form, containing the total number of pairs.
<input type="hidden" name="NumberOfQuestions" value="#TheTotalNumberHere#">
Then use the total number with a from and to loop. On each iteration, extract the current value of the question and answer fields, and use them in your query:
<cfloop from="1" to="#FORM.NumberOfQuestions#" index="pairNum">
<cfset question = FORM["question"& pairNum]>
<cfset answer = FORM["answer"& pairNum]>
<cfquery ...>
INSERT INTO faqsQuestions(question,answer,createdon,faqID)
VALUES (
<cfqueryparam cfsqltype="cf_sql_varchar" value="#question#">
, <cfqueryparam cfsqltype="cf_sql_longvarchar" value="#answer#">
, <cfqueryparam cfsqltype="cf_sql_date" value="#now()#">
, <cfqueryparam cfsqltype="cf_sql_integer" value="#getLastID#">
)
</cfquery>
</cfloop>

Related

Check if any field is different from database value

After I submit a form, I need to check if any field in the database table changed. If changed I create a new record, if not, I don't do anything. I can't just update the record.
Is there a way to do it without checking every single field like the code below?
<cfquery name="getData" datasource="myDS">
Select * From table Where ID = #form.ID#
</cfquery>
<Cfset changed = false />
<!-- Check every field -->
<cfif form.data1 neq getData.data1>
<cfset changed = true />
</cfif>
<cfif form.data2 neq getData.data2>
<cfset changed = true />
</cfif>
<cfif form.data3 neq getData.data3>
<cfset changed = true />
</cfif>
...
Thanks
Might depend on what database you are using but you should be able to do a query that will insert if the data does not exist.
As an example I just tested this against Oracle 12c using CF2016 Enterprise and it creates a new record if the data does not exist.
<cfquery name="Testing" datasource="Test">
INSERT INTO TESTTABLE (DATA1, DATA2, DATA3)
SELECT <cfqueryparam value="#Form.Data1#" cfsqltype="CF_SQL_VARCHAR" />, <cfqueryparam value="#Form.Data2#" cfsqltype="CF_SQL_VARCHAR" />, <cfqueryparam value="#Form.Data3#" cfsqltype="CF_SQL_VARCHAR" />
FROM dual
WHERE NOT EXISTS
(SELECT DATA1, DATA2, DATA3 FROM TESTTABLE WHERE DATA1 = <cfqueryparam value="#Form.Data1#" cfsqltype="CF_SQL_VARCHAR" />,
AND DATA2 = <cfqueryparam value="#Form.Data2#" cfsqltype="CF_SQL_VARCHAR" /> AND DATA3 = <cfqueryparam value="#Form.Data3#" cfsqltype="CF_SQL_VARCHAR" />)
</cfquery>
Can you explain this a little further? Why are you allowing a form to be submitted with changed data if you can't update the record itself? If you click Save, then compare the form data with the query data and have the action call a Create function when the data is different.
Let's say you have a thing query and form:
<cfquery name="myThing">
SELECT
thing_id,
thing_name,
thing_foo
FROM
things
where
thingID = <cfqueryparam value="#url.thingID#">
</cfquery>
<form>
<input type="hidden" name="thing_id" value="#myThing.thing_id#">
<input type="text" name="thing_name" value="#myThing.thing_name#">
<input type="text" name="thing_foo" value="#myThing.thing_foo#">
<button type="submit">Submit</button>
</form>
If you need to check the submitted data against what's already in the database, you can just run the query again on the form processing page and compare those values against the submitted form values. This example assumes you named the form fields the same as the database table columns.
<cfset delta = false>
<cfloop item="key" collection="#myThing#">
<cfif structKeyExists(form, key)>
<cfif form[key] NEQ myThing[key]>
<cfset delta = true>
</cfif>
</cfif>
</cfloop>
If any values differ, then create a new record. No idea what you need to do when the submitted values haven't changed.
<cfif delta>
<!--- Create a new record. --->
<cfelse>
<!--- ¯\_(ツ)_/¯ --->
</cfif>
I've also seen it done where the original values are stored in hidden form fields and submitted along with the editable form fields. You could do this, but there's no guarantee that the values in the DB haven't been changed between you rendering the form and then submitting it.
You'll still have some challenge of how to tell if the DB values have changed on the way to the DB, but I'm not sure if you need so granular a check.
We can use the cfquery to check the table having the existing data or not. If not having the same data means we can Insert the form. The following code may related to your scenario.
<cfif structKeyExists(form,"Submit")>
<cfquery name="checkFormExisting" datasource="myDSN">
SELECT *
FROM USERS
WHERE data1 = <cfqueryparam value="#form.data1#" cfsqltype="cf_sql_varchar">
OR data2 = <cfqueryparam value="#form.data2#" cfsqltype="cf_sql_varchar">
OR data3 = <cfqueryparam value="#form.data3#" cfsqltype="cf_sql_varchar">
</cfquery>
<cfif checkFormExisting.recordCount EQ 0>
INSERT INTO
USERS (
data1,data2,data3
)
VALUES (
<cfqueryparam value="#form.data1#" cfsqltype="cf_sql_varchar">,
<cfqueryparam value="#form.data2#" cfsqltype="cf_sql_varchar">,
<cfqueryparam value="#form.data3#" cfsqltype="cf_sql_varchar">
)
</cfif>
</cfif>
Hope, you're asking like the above code for your scenario.

Loop over the cfquery tag values

I have the following query where I loop over a list of values but I am getting an error at the last comma:
<cfquery datasource="#application.dsn#">
INSERT INTO #session.tablename# ( #lFields# )
VALUES (
<cfloop list="#lFields#" index="kk">
<cfqueryparam value="#TRIM(sVideoGame['#kk#'])#" cfsqltype="cf_sql_varchar" null="#NOT LEN(TRIM(sVideoGame['#kk#']))#" />,
</cfloop>
)
</cfquery>
Problem occurs with the last comma. I tried setting up a counter before the cfloop, setting it 0 and incrementing to 1 inside the cfloop. However, I am not sure how to remove the last comma based upon some condition check.
In order to keep track of the position, you will need a from/to loop instead of a list loop. Then add a comma after you pass the first query parameter.
For ColdFusion 2016+ it can be done using the "item" and "index" attributes:
...
<cfloop list="#yourListVariable#" item="keyName" index="position">
<!--- if we've passed the first parameter, add a comma --->
<cfif position gt 1>,</cfif>
<cfqueryparam value="#TRIM(sVideoGame[ keyName ])#"
cfsqltype="cf_sql_varchar"
null="#NOT LEN(sVideoGame[keyName])#" />
</cfloop>
...
CF11 and earlier require a little more work. To simplify the code, I would recommend converting the list to an array:
<cfset keyArray = listToArray(yourListVariable)>
...
<cfloop from="1" to="#arrayLen(keyArray)#" index="position">
<!--- if we've passed the first parameter, add a comma --->
<cfif position gt 1>,</cfif>
<cfqueryparam value="#TRIM(sVideoGame[ keyArray[position] ])#"
cfsqltype="cf_sql_varchar"
null="#NOT LEN(sVideoGame[ keyArray[position] ])#" />
</cfloop>
...
Side note, I noticed the query uses dynamic table and column names. Be sure those values are NOT user supplied, or the query is vulnerable to sql injection.
If your only question is how to deal with the last comma, then you can do it with
<cfset listCount = 1>
<cfloop list="#lFields#" index="kk">
<cfqueryparam value="#TRIM(sVideoGame['#kk#'])#"
cfsqltype="cf_sql_varchar" null="#NOT LEN(TRIM(sVideoGame['#kk#']))#" />
<cfif listLen(lFields) is not listCount>,</cfif>
<cfset listCount = listCount+1>
</cfloop>

CFloop query to store multiple values in database

I have few variables that contain multiple values. Basically I want to store all the values into my database. I am using this code which I got here in Stackoverflow.
<cfquery datasource="databaseName">
INSERT INTO spreadsheet
([Local.Time.Stamp],
[Energy.Delivered..kVAh.],
[Energy.Received..kVAh.],
[Energy.Received..kVARh.],
[Energy.Delivered..kVARh.],
[Real.A..kW.],
[Real.B..kW.])
VALUES
(<cfloop query="excelquery">
'#excelquery.col_1#',
'#excelquery.col_2#',
'#excelquery.col_3#',
'#excelquery.col_4#',
'#excelquery.col_5#',
'#excelquery.col_6#',
'#excelquery.col_7#'
</cfloop>)
</cfquery>
However I always get a syntax error. I believe that my cfloop part is wrong, can someone please tell me the correct way for me to write that cfloop?
The problem is with the generated query not cfloop i.e., for entering multiple values the format should be like this:
INSERT INTO TableName (col,col,...) VALUES (val,val,...),(val,val,...),...
Also, use cfqueryparam to avoid sql injection.
You can try this:
<cfquery datasource="databaseName">
INSERT INTO spreadsheet
([Local.Time.Stamp],
[Energy.Delivered..kVAh.],
[Energy.Received..kVAh.],
[Energy.Received..kVARh.],
[Energy.Delivered..kVARh.],
[Real.A..kW.],
[Real.B..kW.])
VALUES
<cfloop query="excelquery">
<!--- cf_sql_varchar is just an example. --->
(<cfqueryparam cfsqltype="cf_sql_varchar" value="#excelquery.col_1#">,
<cfqueryparam cfsqltype="cf_sql_varchar" value="#excelquery.col_2#">,
<cfqueryparam cfsqltype="cf_sql_varchar" value="#excelquery.col_3#">,
<cfqueryparam cfsqltype="cf_sql_varchar" value="#excelquery.col_4#">,
<cfqueryparam cfsqltype="cf_sql_varchar" value="#excelquery.col_5#">,
<cfqueryparam cfsqltype="cf_sql_varchar" value="#excelquery.col_6#">,
<cfqueryparam cfsqltype="cf_sql_varchar" value="#excelquery.col_7#">)
#excelQuery.currentRow NEQ excelQuery.recordCount ? ',': ''#
</cfloop>
</cfquery>
You might have to add a recordCount check before generating the query to avoid errors for no records.

Why isn't my mysql query seeing this one combination of two variables but sees the rest?

I wrote a script that takes date entries and for some reason whenever I specify just a starting date with a blank end date the query never picks it up. Here's what I wrote.
<cfquery name="stec_mysql_loan_tracking_qry" result="meta_tracking" datasource="STLinux1MySQL">
Select
tslo.created,
tslo.created_by,
tslo.last_modified,
tslo.last_modified_by,
tslo.active,
tslo.is_manager,
tslo.pick_userid,
tslo.customer_code,
tslo.name,
tst.user_ip as ip,
tsl.loan_identifier,
tst.command,
tsl.tax_search_loan_id as id
From
tax_search_loan_officers tslo Left Join
tax_search_loans tsl On tsl.tax_search_loan_officer_id =
tslo.tax_search_loan_officer_id Left Join
tax_search_track tst On tst.pick_userid = tslo.pick_userid
Where
tslo.customer_code In (<cfqueryparam value="#tw_custcodes#" cfsqltype="cf_sql_varchar" list="yes">)
<cfif IsDefined('url.active')>
<cfif url.active neq "">
AND
tslo.active = <cfqueryparam value="#Trim(url.active)#" cfsqltype="cf_sql_varchar" list="yes">
</cfif>
</cfif>
<cfif IsDefined('url.is_managed')>
<cfif url.is_managed neq "">
AND
tslo.is_manager = <cfqueryparam value="#Trim(url.is_managed)#" cfsqltype="cf_sql_varchar" list="yes">
</cfif>
</cfif>
<cfif IsDefined('url.start_end')>
<cfif url.start_date neq "" and url.end_date eq "">
AND
<cfqueryparam value="#Trim(url.start_date)#" cfsqltype="cf_sql_date"> <= DATE_FORMAT(tslo.last_modified, '%Y-%m-%d')
AND
DATE_FORMAT(tslo.last_modified, '%Y-%m-%d') <= DATE_FORMAT(NOW(), '%Y-%m-%d')
</cfif>
</cfif>
<cfif IsDefined('url.start_date')>
<cfif url.end_date neq "" and url.start_date eq "">
AND
'2012-01-01' <= DATE_FORMAT(tslo.last_modified, '%Y-%m-%d')
AND
DATE_FORMAT(tslo.last_modified, '%Y-%m-%d') <= <cfqueryparam value="#Trim(url.end_date)#" cfsqltype="cf_sql_date">
</cfif>
</cfif>
<cfif isDefined('url.start_date')>
<cfif (url.start_date neq "") and (url.end_date neq "")>
AND
<cfqueryparam value="#Trim(url.start_date)#" cfsqltype="cf_sql_date"> <= DATE_FORMAT(tslo.last_modified, '%Y-%m-%d')
AND
DATE_FORMAT(tslo.last_modified, '%Y-%m-%d') <= <cfqueryparam value="#Trim(url.end_date)#" cfsqltype="cf_sql_date">
</cfif>
</cfif>
</cfquery>
And here is what it sees if url.end_date = "" but url.start_date = a value:
Select
tslo.created,
tslo.created_by,
tslo.last_modified,
tslo.last_modified_by,
tslo.active,
tslo.is_manager,
tslo.pick_userid,
tslo.customer_code,
tslo.name,
tst.user_ip as ip,
tsl.loan_identifier,
tst.command,
tsl.tax_search_loan_id as id
From
tax_search_loan_officers tslo Left Join
tax_search_loans tsl On tsl.tax_search_loan_officer_id =
tslo.tax_search_loan_officer_id Left Join
tax_search_track tst On tst.pick_userid = tslo.pick_userid
Where
tslo.customer_code In (?)
However, every other combination is fine. I've tried rewriting the cfif blocks but this structure is the only one that gets 2/3 while the rest fail.
(From the comments ..)
<cfif IsDefined('url.start_end')>
It looks like you have three date variables: url.start_date, url.end_date and url.start_end. What is url.start_end - is it a typo?
As an aside, you might want to set defaults values for the variables so you could eliminate some of the cfif conditions. Then work on simplifying the rest of the logic, because it seems more complex than is necessary ... Dan's response contains some good suggestions. I strongly suspect you could simplify the code and make the query more efficient to boot by getting rid of the DATE_FORMAT(ColumnName, '%Y-%m-%d') statements, because they will prevent the database from properly utilizing indexes on the referenced column.
Update:
After taking a closer look, I think this is what the code is trying to accomplish:
If both dates are present, filter on the given values
If only one of the dates is present, apply a default for the missing date. Then filter on both values.
If neither date is present, skip the filtering.
Something along these lines should mimic the behavior of current code. Note, it uses this type of comparison as a more index-friendly way of handling "time" issues:
WHERE column >= {startDateAtMidnight}
AND column < {dayAfterEndDateAtMidnight}
Example:
<!--- default both to something that is NOT a valid date --->
<cfparam name="url.start_date" default="">
<cfparam name="url.end_date" default="">
<!---
If at least ONE of the dates was supplied, apply
the desired defaults for missing values
--->
<cfif isDate(url.start_date) || isDate(url.end_date)>
<cfset url.start_date = isDate(url.start_date) ? url.start_date : "some default like 2012-01-01 here">
<cfset url.end_date = isDate(url.end_date) ? url.end_date : now()>
</cfif>
<cfquery ....>
SELECT ...
FROM ...
WHERE ...
<!--- apply the filter when both dates are populated. --->
<cfif isDate(url.start_date) and isDate(url.end_date)>
AND tslo.last_modified >= <cfqueryparam value="#url.start_date#" cfsqltype="cf_sql_date">
AND tslo.last_modified < <cfqueryparam value="#dateAdd('d', 1, url.end_date)#" cfsqltype="cf_sql_date">
</cfif>
</cfquery>
It might not be the cause, but you are mixing data types. This:
and <cfqueryparam value="#Trim(url.start_date)#" cfsqltype="cf_sql_date">
<= DATE_FORMAT(tslo.last_modified, '%Y-%m-%d')
will have a date on the left hand side of your comparison operator and a string on the right. Even if it runs without error, you might get unexpected results. As a minimum, remove the date_format function from the right hand side.
Then we have this:
AND DATE_FORMAT(tslo.last_modified, '%Y-%m-%d') <= DATE_FORMAT(NOW(), '%Y-%m-%d')
at least it's comparing a string to a string, but it's inefficient. In the overall scheme of things, maybe you want something like this:
and tslo.last_modified >=
<cfqueryparam value="#Trim(url.start_date)#" cfsqltype="cf_sql_date">
and tslo.last_modified =< now()

What's wrong with my simple insert?

I'm using coldfusion to insert the contents of a struct (key-value pairs) into a database table. This is my code:
<cfloop collection="#results#" item="ID" >
<cfquery name="insertStuff" datasource="myDataSource">
INSERT INTO web..Stuff (ID, Name)
VALUES (#ID#, #results[ID]#)
</cfquery>
</cfloop>
This seems simple enough... but I'm getting the following error:
Incorrect syntax near 'VA'.
Any ideas?
You really ought to think about parameterising your data too.
<cfloop collection="#results#" item="ID" >
<cfquery name="insertStuff" datasource="myDataSource">
INSERT INTO web..Stuff (ID, Name)
VALUES (
<cfqueryparam cfsqltype="cf_sql_varchar" value="#ID#">,
<cfqueryparam cfsqltype="cf_sql_varchar" value="#results[ID]#">)
</cfquery>
</cfloop>
I think I may have solved it... forgot the quotes, and they're both varchar fields :-/
<cfloop collection="#results#" item="ID" >
<cfquery name="insertStuff" datasource="myDataSource">
INSERT INTO web..Stuff (ID, Name)
VALUES ('#ID#', '#results[ID]#')
</cfquery>
</cfloop>