I have a list like this thing1,thing2,thing3. And I want to insert them into a look-up table with the same foreign key. So ideally it would look like this:
Update:
Ultimately the real problem here was that the feature of inserting multiple sets of values with a single VALUES
clause is only supported in SQL Server 2008+ and the OP is using 2000. So they went with the select / union all approach instead.
(Expanded from the comments)
Sure you can loop inside a cfquery
. All cfml code is processed on the CF server first. Then the resulting SQL string is sent to the database for execution. As long as your CF code results in a valid SQL statement, you can do just about anything you want :) Whether you should is a different question, but this kind of looping is perfectly fine.
Getting back to your question, just switch to a from/to
loop instead and use list functions like getToken(list, index)
to get the individual elements (see Matt's example) or use an array instead. Obviously you should also validate the list is not empty first. My personal preference is arrays. Not tested, but something like this:
<cfset thingArray = listToArray(things, ",")>
<cfquery datasource="#ds#" result="insert_things">
INSERT INTO lkp_things (foreign_key, thing) VALUES
<cfloop from="1" to="#arrayLen(thingArray)#" index="x">
<cfif x gt 1>,</cfif>
(
<!--- Note: Replace cfsqltype="..." with correct type --->
<cfqueryparam value="#id#" cfsqltype="...">
, <cfqueryparam value="#thingArray[x]#" cfsqltype="...">
)
</cfloop>
</cfquery>
Having said that, what is the source of your #thing#
list? If those values exist in a database table, you could insert them directly with a SELECT
statement, instead of a loop:
INSERT INTO lkp_things (foreign_key, thing)
SELECT <cfqueryparam value="#id#" cfsqltype="...">, thing
FROM ThingTable
WHERE thing IN
(
<cfqueryparam value="#thingList#" list="true" cfsqltype="...">
)
I would add a counter and increment it inside the loop. Also you need to use listLen()
rather than len
on your list to get the number of items.
<cfquery datasource="#ds#" result="insert_things">
INSERT INTO lkp_things (foreign_key, thing) VALUES
<cfset count = 1>
<cfloop list="#things#" index="thing">
(
<cfqueryparam cf_sql_type="cf_sql_integer" value="#id#">,
<cfqueryparam cf_sql_type="cf_sql_varchar" value="#thing#">
)<cfif count NEQ listLen(things)>,</cfif>
<cfset count++>
</cfloop>
</cfquery>
You should use cfqueryparam
on all your values. I've guessed on what type the column is.
I was getting a database error with
INSERT INTO lkp_things (foreign_key,thing)
VALUES (1,'thing1'),(1,'thing2')
Leigh helped me realize that I was actually using SQL Server '00, and that version does not allow this method of insertion. So, I had to use this:
INSERT INTO lkp_things (foreign_key,thing)
SELECT 1,'thing1'
UNION ALL
SELECT 1,'thing2'
In CF that looks like this:
<cfset thingArray = listToArray(form.things,",")>
<cfquery datasource="aliaba_jdbc" name="insert_courses">
INSERT INTO lkp_Things (id,thing)
<cfloop from="1" to="#arrayLen(thingArray)#" index="x">
SELECT <cfqueryparam cfsqltype="CF_SQL_INTEGER" value="#form.id#">,<cfqueryparam cfsqltype="CF_SQL_VARCHAR" value="#thingArray[x]#">
<cfif x lt arrayLen(thingArray)>UNION ALL</cfif>
</cfloop>
</cfquery>
I found this solution here: How do I insert multiple rows WITHOUT repeating the "INSERT INTO dbo.Blah" part of the statement?
This answer is similar to Matt's but does not use conditional logic.
<cfquery>
insert into lkp_things (foreign_key, thing)
select 0, ''
where 1=2
<cfloop list="#things#" index="thing">
union
select <cfqueryparam cf_sql_type="cf_sql_integer" value="#id#">
, <cfqueryparam cf_sql_type="cf_sql_varchar" value="#thing#">
</cfloop>
Both our answers assume that the id variable does not change and was set somewhere else.