Using array data to drill a query - coldfusion

I am learning about arrays and structs, and putting together a shopping cart as a project. I am currently creating a recently viewed products module for the cart. When a user views a product i collect the product id into an array,
What is the best way to use this info to query my database, my query would be
<cfquery name="getRecent" datasource="#application.dsn#">
SELECT uid_product,txt_product_name
FROM tbl_products
</cfquery>
I thought of outputting the array as a list and using the IN operator, but not sure if that is the right way of doing it, seems a bit cumbersome? Can I use a query of queries using the array data? If so any pointers?

If you just maintain a list of productIDs then you don't need to use arrays or structs, you can just store them as a simple list and query them as follows:
<cfset recentProducts = "4,5" />
<cfquery name="getRecent" datasource="#application.dsn#">
SELECT uid_product,txt_product_name
FROM tbl_products
where productID IN (<cfqueryparam cfsqltype="cf_sql_integer" value="#recentProducts#" list="true" />)
</cfquery>

Firstly, unless you're going to put additional things into the struct, I'd say having a struct just to hold ProductID seems excessive. I'd just have the Array with the productID as each element, no struct.
So your array is structured like:
[
{ ProductID= 4 },
{ ProductID= 5 },
...
]
I'd suggest you just do:
[4,5]
This will make the next step easier. Right now I'd say you have to loop over your array, adding each productID into a list that you can then use in your query. Then modify your original query to use that list (remember and use cfqueryparam).
<cfset lstIDs = "">
<cfloop index="productStruct" array="#productsArray#">
<cfset lstIDs = listAppend(lstIDs, productStruct.ProductID)>
</cfloop>
<cfquery name="getRecent" datasource="#application.dsn#">
SELECT uid_product,txt_product_name
FROM tbl_products
WHERE productID IN <cfqueryparam value="#lstIDs#" cfsqltype="CF_SQL_INTEGER" list="yes">
</cfquery>
Or, if you simplify your array, just do ArrayToList to achieve the same thing.
<cfquery name="getRecent" datasource="#application.dsn#">
SELECT uid_product,txt_product_name
FROM tbl_products
WHERE productID IN <cfqueryparam value="#ArrayToList(productsArray)#" cfsqltype="CF_SQL_INTEGER" list="yes">
</cfquery>

Related

How to loop form values depending on form column name?

I have a form containing 6 check_num fields, 1 account number and an amount. I would like to insert those values into a database table:
<cfquery datasource="test" name="test">
insert into test (ACCOUNT_NUMBER,check_num,amount)
values ('#ACCOUNT_NUMBER#','#check_num#','#amount#')
</cfquery>
However, I don't want to insert all of the values into 1 row. If any of the check_num fields contain a number, I want to insert that value, along with with the acccount_number and amount values.
For example with the data below, I would like to insert 5 rows. The field "check_num6" would be skipped because it is empty.
ACCOUNT_NUMBER 123456789
CHECK_NUM1 56623
CHECK_NUM2 5512
CHECK_NUM3 6562
CHECK_NUM4 653
CHECK_NUM5 6623
CHECK_NUM6
AMOUNT 32.31
FIELDNAMES ACCOUNT_NUMBER,CHECK_NUM1,CHECK_NUM2,CHECK_NUM3,CHECK_NUM4,CHECK_NUM5,CHECK_NUM6,AMOUNT,SUBMIT
SUBMIT Submit
Not perfect but you can start/get some inspiration from here:
<cfloop list="#StructKeyList(form)#" index="i">
<cfif (left('#i#', 9) is "CHECK_NUM") and (trim(form['#i#']) is not "")>
<cfquery datasource="test" name="test">
insert into test (ACCOUNT_NUMBER,check_num,amount)
values ('#ACCOUNT_NUMBER#','#form['#i#']#','#amount#')
</cfquery>
</cfif>
</cfloop>
Also you should validate, see if your DB supports multiple inserts, change your query to use cfqueryparam, research about it. So, a better cfquery code would be:
<cfquery datasource="test" name="test">
insert into test (
ACCOUNT_NUMBER,
check_num,
amount
)
values (
<cfqueryparam cfsqltype="cf_sql_varchar" value="#form.ACCOUNT_NUMBER#">,
<cfqueryparam cfsqltype="cf_sql_varchar" value="#form['#i#']#">,
<cfqueryparam cfsqltype="cf_sql_varchar" value="#form.amount#">
)
</cfquery>

Merging a array of queries into a single query

I want to merge an array of queries into one query
I have tried this
<cfquery name="MergedData" dbtype="query">
<cfloop from="1" to="#arrayLen(arData)#" index="k">
SELECT *
FROM arData[k]
<cfif k LT ArrayLen(arData)>UNION</cfif>
</cfloop>
ORDER BY EID
</cfquery>
I get an error that looks like
<br><b>Query Of Queries syntax error.</b><br> Encountered "[k].
Try an alternative way of looping over your array. You need to create your own counter though to figure out the logic of if you need to keep appending 'UNION' to your SQL statement.
<cfset i = 1>
<cfquery name="MergedData" dbtype="query">
<cfloop index="k" array="#arData#">
SELECT *
FROM k
<cfif i LT ArrayLen(arData)>UNION</cfif>
<cfset i++>
</cfloop>
ORDER BY EID
</cfquery>
NB: you wouldn't need to calculate a counter yourself if you're using Railo instead of Adobe CF, as you can then do both index and item like so, as Peter mentioned in the comments above:
<cfloop index="i" item="k" array="#arData#">
If you'd like to accomplish this with functional programming, you could use the Underscore.cfc library (requires CF 10+ or Railo 4+):
// instantiate Underscore library
_ = new Underscore();
// convert the array of queries to a single array of structs
mergedArray = _.reduce(arrayOfQueries, function (memo, query) {
// convert current query to an array of structs
// and concatenate it to the rest of the result
return _.concat(memo, _.toArray(query));
}, []);
// convert the array of structs back to a query
mergedQuery = _.toQuery(mergedArray);
This solution utilizes reduce() to combine the array of queries into a single array of structs. The anonymous function passed to reduce() converts each query in the array of queries to an array of structs using toArray(), then concatenates that array with the rest of the array of structs (the memo value).
Once the array of queries has been converted to a single array of structs, it is a simple matter to convert it back to a query using toQuery() (assuming that is necessary).
Note: I wrote the Underscore library
Try like this:
<cfsavecontent variable="testing">
<cfset j = 1>
<cfloop array="#test#" index="i">
SELECT id FROM #i# LIMIT 10
<cfif j LT ArrayLen(test)> UNION </cfif>
<cfset j++>
</cfloop>
</cfsavecontent>
<cfquery name="qTest" datasource="#application.dsn#">
#testing#
</cfquery>
This is what end up working
<cfquery name="local.qryMergedData" dbtype="query">
<cfloop array="#arguments.Data#" index="k">
<cfset local.currentIndex++>
<cfset setvariable("Data_#local.currentIndex#", k)>
SELECT *
FROM Data_#local.currentIndex#
<cfif local.currentIndex LT local.MaxIndex>UNION</cfif>
</cfloop>
ORDER BY EID
</cfquery>
I really don't like that I am setting a variable inside of a <cfquery>, but at least it is only one query

Compare mysql query to sql query

I am a lot more at home manipulating arrays and objects in php or javascript, and do next to no work with coldfusion.
I have one query pulling data from mysql. I would like to store results list or array ( or simpler approach)
WHile looping over a second query from msql that generates html output I need to see if one value is contained in prior mysql array or list.
Have tried playing with varierty of cf array/list methods and really not sure what best approach is to resolve following if.
CF version is 9
<cfquery datasource="Mysql" name="premium_submitters">
SELECT submitter_id from table
</cfquery>
<!--- Need loop to store to array or list??--->
<cfquery datasource="Sql" name="main_data">
SELECT * from table
</cfquery>
<cfoutput query="main_data">
<cfif #submiiter_id# <!---is in Mysql array/list return boolean---> >
</cfif>
</cfoutput>
Use ValueList to get the value of one column of a query into a list.
<cfquery datasource="Mysql" name="premium_submitters">
SELECT submitter_id from table
</cfquery>
<cfset submittersList = ValueList(premium_submitters.submitter_id)>
<cfquery datasource="Sql" name="main_data">
SELECT * from table
</cfquery>
<cfoutput query="main_data">
<cfif ListFind(submittersList, main_data.submitter_id) gt 0>
<!--- is in list, do whatever --->
</cfif>
</cfoutput>

how to store query data in variables in ColdFusion for use later?

I am trying to retrieve and store ID's for each item retrieved from my table, so I can use these ID's later. I tried nesting the queries, but this didn't, work. Here is my first query:
<CFQUERY datasource="MyDSN" name="MAIN2"> SELECT * from order_items where orderID= #orderID#</CFQUERY>
Now, if I output this query it displays, 1 and 117 which are the two ID's I need.
My next query is:
<CFQUERY datasource="MyDSN" name="MAIN3">select c.catalogueID,
c.product_name,
c.product_price,
c.description,
p.productID
from products p
join product_catalogue c on c.catalogueid = p.catalogueid
where p.productid = "#productID#"</CFQUERY>
But it is telling me that productID is not defined, it is obviously empty. I am just getting started using ColdFusion, so I am not sure the best way to store the values I need so I use them again. I also need to loop the second query to run for each ID 1 and 117, so twice.
Any suggestions on how to accomplish this would be greatly appreciated.
Thanks
My basic rule is that if I find myself using queries to create other queries or looping over a query to execute other queries; it is time to consider combining the queries.
I'm not sure what field you are using in the MAIN2 query to feed the MAIN3 query. So, I put in "productID" in the query below. You may have to change it to fit your field name.
<CFQUERY datasource="MyDSN" name="MAIN3">select c.catalogueID,
c.product_name,
c.product_price,
c.description,
p.productID
from products p
join product_catalogue c on c.catalogueid = p.catalogueid
where p.productid IN (SELECT DISTINCT productID from order_items where orderID= <cfqueryparam value="#orderID#" cfsqltype="CF_SQL_INTEGER">)
</CFQUERY>
You could also change this query to utilize a "join" to connect [order_items] to the query.
Lastly, you should use the <cfqueryparam> tag for the where clauses; this helps protect your query from sql injection attacks.
Whenever I'm caching data for use later, I tend to ask myself how I'll be using that data, and whether it belongs in another data type rather than query.
For instance, if I'm wanting a bunch of data that I'm likely to access via ID, I can create a structure where the key is the ID, and the data is another structure of a dataset. Then I'll save this structure in application scope and only refresh it when it needs to be. This is zippy fast and so much easier to grab with
rather than querying for it every time. This is especially useful when the query that creates the original data set is kind of a resource hog with lots of joins, sub-queries, magical cross-db stored procedures, but the datasets returns are actually fairly small.
So creating your products structure would look something like this:
<CFQUERY datasource="MyDSN" name="MAIN3">
SELECT
c.catalogueID,
c.product_name,
c.product_price,
c.description,
p.productID
FROM products p
JOIN product_catalogue c
ON c.catalogueid = p.catalogueid
WHERE p.productid = <cfqueryparam value="#ProductID#" cfsqltype="cf_sql_integer">
</CFQUERY>
<cfset products = structNew() />
<cfset item = structNew() />
<cfloop query="MAIN3">
<cfif NOT structKeyExists(products, productID)>
<cfset item = structNew() />
<cfset item.catalogueID = catalogueID />
<cfset item.product_name = product_name />
<cfset item.product_price = product_price />
<cfset item.description = description />
<cfset products[productID] = structCopy(item) />
</cfif>
</cfloop>
<cfset application.products = structCopy(products) />

can't define id ColdFusion

here is my ColdFusion code:
Example1:
<cfquery name="GET_BRAND" datasource="#dsn1#">
SELECT PRODUCT_CATID
FROM PRODUCT_CAT
WHERE PRODUCT_CATID = PRODUCT_CATID
</cfquery>
#get_brand.product_catid#
But it shows all the time number 1, i just can't understand why, and how do i make it work properly, this code should have defined the brand_id, but instead shows 1.
The system is Workcube.
Here is my example for getting from the static product's id, its dynamic price:
Example 2:
<cfset product_id = 630>
<cfquery name="price_standart" datasource="#dsn3#">
SELECT
PRICE_STANDART.PRICE PRICE
FROM
PRICE_STANDART
WHERE
PRICE_STANDART.PRODUCT_ID =
<cfqueryparam value="#product_id#" cfsqltype="cf_sql_integer">
</cfquery>
But this time i need to get from dynamic product's ID its dynamic brand id.
This script works the same way as the Example 1:
<cfquery name="GET_BRAND" datasource="#dsn1#">
SELECT BRAND_ID
FROM PRODUCT_BRANDS
WHERE BRAND_ID = BRAND_ID
</cfquery>
#get_brand.BRAND_ID#
As Andreas shows in his code, your query isn't going to work as written. The statement WHERE PRODUCT_CATID = PRODUCT_CATID doesn't actually pass a value - it would actually just be self-referential within the table values. In this case, it would return everything in the table.
You should instead have:
WHERE PRODUCT_CATID = #PRODUCT_CATID#
Where #PRODUCT_CATID# represents a variable. Better yet, use cfqueryparam as Andreas shows (this prevents SQL injection and improves query performance). However, I am not even sure that is what you intend since if you have the product ID why do you need to get it from the database? Instead, I assume you probably want to get the brands from the product in a particular category. Not knowing your table structure, it's hard to write that query for you but it might look something like:
<cfquery name="GET_BRAND" datasource="#dsn1#">
SELECT PRODUCT.BRAND_ID
FROM PRODUCT
INNER JOIN PRODUCT_CAT
ON PRODUCT.PRODUCT_CATID = PRODUCT_CAT.PRODUCT_CATID
WHERE PRODUCT_CATID = <cfqueryparam cfsqltype="cf_sql_integer" value="#product_catid#">
</cfquery>
Lastly, as both comments indicate, you would need to loop through the results to see all the records returned.
You need to wrap the statement in tags like this.
<cfquery name="GET_BRAND" datasource="#dsn1#">
SELECT PRODUCT_CATID FROM PRODUCT_CAT WHERE PRODUCT_CATID = PRODUCT_CATID
</cfquery>
<cfoutput query =GET_Brand">
#get_brand.product_catid#
</cfoutput>
It's not very clear what your question really is about, but let me guess:
<cfquery name="GET_BRAND" datasource="#dsn1#">
SELECT PRODUCT_CATID
FROM PRODUCT_CAT
WHERE PRODUCT_CATID = <cfqueryparam cfsqltype="cf_sql_integer" value="#product_catid#">
</cfquery>
where #product_catid# refers to a variable you defined earlier in your code or received via form or url scope.
<cfloop query="GET_BRAND">
#get_brand.product_catid#<br />
</cfloop>
will show a list of all the product_catid's returned by the query.
It's not too clear what you are after here, but in the queries there are at least 2 problems. First your WHERE clause
WHERE PRODUCT_CATID = PRODUCT_CATID
is like saying
WHERE 1=1
This will return the full recordset. You can see this by adding
<cfdump var="#GET_BRAND#">
under your code to see the query output. I'm guessing this will show all records in the table.
To match just one record you need your WHERE clause to be like
WHERE PRODUCT_CATID = 3
or have the #...# wrapped around the variable you are trying to match to make it dynamic.
Secondly to the query result may be more than one record, and to see any more than the first record you need to loop over the output. One way is to use
<cfoutput query="GET_BRAND">
#BRAND_ID# <br>
</cfoutput>
My guess of what you are after is
<cfset ID_TO_MATCH=3>
<cfquery name="GET_BRAND" datasource="#dsn1#">
SELECT BRAND_ID
FROM PRODUCT_CAT
WHERE PRODUCT_CATID = #ID_TO_MATCH#
</cfquery>
<cfoutput query="GET_BRAND">
#BRAND_ID# <br>
</cfoutput>