Build struct and then populate with the values from query? - sql

I have task to update/insert some fields in two different tables. Before I run my update I have to grab values from my query and put them in structure where one of my values should be the key. My query looks like this:
<cfquery name="getRecords" datasource="test">
Select
s.ID
,f.USER_ID
,s.USER_NUMBER
,s.STATUS
,f.DINING
From USERS s
Left Outer Join FIELDS f ON s.ID = f.USER_ID
</cfquery>
I need USER_NUMBER to use as a key in my structure and store all other values from the query above. I will use this structure to compare values from my other list and then build final list that I will use for update/insert. I tried something like the code below, but it did not work:
Here is a stand alone example using a manual query:
<cfset getRecords = queryNew("")>
<cfset queryAddColumn(getRecords, "ID", [1,2,3])>
<cfset queryAddColumn(getRecords, "USER_ID", ["userA","userB","userC"])>
<cfset queryAddColumn(getRecords, "STATUS", ["Active","Active","Active"])>
<cfset queryAddColumn(getRecords, "DINING", ["X","Y","Z"])>
<cfset myStruct = StructNew()>
<cfloop query="getRecords">
<cfset myStruct = [key:#USER_NUMBER#{
id:#ID#
,userid:##USER_ID
,status:#STATUS#
,dining:#DINING#
}]>
</cfloop>
If anyone can help with this code please let me know. I usually use arrays but this time I have to use struct because of some other reasons. Thank you.

I believe you just need to move your key up a level so that the loop doesn't overwrite the values. so try something like:
<cfset myStruct = StructNew()>
<cfloop query="getRecords">
<cfset myStruct[getRecords.USER_ID] = {
id:getRecords.ID,
userid:getRecords.USER_ID,
status:getRecords.STATUS,
dining:getRecords.DINING
}>
</cfloop>
Then to access the variables you can use something like:
<cfoutput>#htmlEditFormat(myStruct[1].dining)#</cfoutput>

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

Loop through variable

I need to loop through variable and write returned values into DB.
Here is my code so far:
<cffunction name="csv">
<cffile action="read" file="test.csv" variable="csvfile" charset="utf-8">
<cfset csv_query = app.csvToQuery.CSVToQuery(CSV = csvFile.Trim()) />
<cfloop query="csv_query">
<cfset getInfo = app.directory.searchAll(
findNo = "#replace(csv_query.column_1, "test", "", 'all')#",
findBy = "Null"
)>
<!--- <cfdump var="#getInfo#"> --->
<cfoutput><cfdump var="#getInfo.NAME#"></cfoutput>
<cftry>
<cfquery datasource="#app.dsn#">
INSERT INTO WRITEINFO (
Name,
) VALUES (
'#val(getInfo.NAME)#',
)
</cfquery>
<cfcatch type="database">
</cfcatch>
</cftry>
</cfloop>
</cffunction>
It writes into DB, but it only writes the first value it gets. I am already looping through query, and when I dumped the values(<cfdump var="#getInfo.NAME#">), I can see all the values I needed, but they're just not inserted into DB
It looks as if this fails silently. You catch database errors but don't do anything in case of an error. One insert statement works. On other inserts it might fail (e.g. unique index violation, "Name" inserted to long for the column in the table, etc.). Maybe get rid of the try/catch block (at least temporarily) and see what happens.

How to compare values from two separate quires but not use inner loop?

I'm working on my project where I have to compare values from DB to the values from .csv file. I have used 'cfhttp' to convert my .csv to query and then I loop over that query and another query that I used to get values from DB. Inside of those two loop I used if statements to compare my values and check if they match. Then I stored them in the lists and use those list in cfquery tags for my update. I have 14k records in DB and about the same number of records in my .csv file. My current code takes less than 2 minutes to output records from the list to the screen for testing purpose. I still did not test how long update will take. Before I run my update I would like to see if any of you would recommend any other approach for my project? Could I reduce my execution time to less than I currently have? Here is my code that I have:
<cfhttp name="records" columns="A,B,C,D,E,F,G,H,I,J,K,L,M,N,O,P,Q,R,S,T,U,V,W,X" method="get" url="http://path to csv/temp.csv"/>
<cfquery name="getRecords" datasource="test">
Select s.ID, f.URID, s.UR_NUMBER, CODE, f.FIELD
From USERS s
Left Outer Join MAPS f
ON s.ID = f.URID
</cfquery>
<cfset NumA = "">
<cfset NumB = "">
<cfset NumC = "">
<cfset NumD = "">
<cfset NumE = "">
<cfset updNumD = "">
<cfset updNumE = "">
<cfloop query="records">
<cfloop query="getRecords">
<cfif records.A EQ getRecords.UR_NUMBER>
<cfif records.W NEQ getRecords.CODE>
<cfif records.W EQ 'A'>
<cfset NumA = ListAppend(NumA,"#records.A#")>
<cfelseif records.W EQ 'B'>
<cfset NumB = ListAppend(NumB,"#records.A#")>
<cfelse>
<cfset NumC = ListAppend(NumC,"#records.A#")>
</cfif>
</cfif>
<cfif getRecords.URID EQ ''>
<cfif records.W EQ 'D'>
<cfset NumD = ListAppend(NumD, "#getRecords.ID#")>
<cfelseif records.W EQ 'E'>
<cfset NumE = ListAppend(NumE, "#getRecords.ID#")>
</cfif>
<cfelse>
<cfif records.W EQ 'E'>
<cfset updNumD = ListAppend(updNumD, "#getRecords.URID#")>
<cfelseif records.W EQ 'D'>
<cfset updNumE = ListAppend(updNumE, "#getRecords.URID#")>
</cfif>
</cfif>
</cfif>
</cfloop>
</cfloop>
Here I dump my lists to the screen:
<cfdump var="#NumA#">
<cfdump var="#NumB#">
<cfdump var="#NumC#">
<br>
<cfdump var="#NumE#">
<cfdump var="#NumD#">
<br>
<cfdump var="#updNumE#">
<cfdump var="#updNumD#">
Also I was wondering if it's possible to get out with cfhttp tag just columns that I need from my .csv? Now I'm garbing all columns from my .csv even if I use only two of them column A and W. Also is it possible to join query from 'cfhttp' to my query from DB? If anyone can give me some advise on this project please let me know.
So I think you are only interested in data where records.A is equal to getRecords.UR_NUMBER. You could use a Query of Queries to get the intersection of the two datasets. Something like:
<cfquery name="intersection" dbtype="query">
select *
from records, getRecords
where records.A = getRecords.UR_NUMBER
</cfquery>
That should give you combined dataset which you can then loop through and build up your lists. As Matt Busche mentioned in the comments, list operations are slow, so arrayAppend will be faster.
If Query or Query is too slow, then you may want to look at converting one of the recordsets to a struct (of key value pairs) as look ups are faster. For example:
<cfscript>
// convert to structs as key lookups are fast
dbData = {};
for (row in getRecords) {
dbData[row.UR_NUMBER] = row;
}
csvData = {};
for (row in records) {
if (structKeyExists(dbData, row.A)) {
// we have a match, so build the lists here...
writeDump(dbData[row.A]);
}
}
</cfscript>

Count variables in a loop

I want to count the variables and show it somewhere in my code. I have a loop:
<cfloop query="get_serial">
<cfif PROCESS_ID eq attributes.action_id> #SERIAL_NO# </cfif>
</cfloop>
and its query:
<cfquery name="get_serial" datasource="#dsn3#">
SELECT *
FROM SERVICE_GUARANTY_NEW
WHERE STOCK_ID = #attributes.action_row_id#
ORDER BY SERIAL_NO
</cfquery>
Everything works fine, but I want to count how many variables are displayed exactly. I actually want to do it in this way because I lack of database variables. Actually it is possible to get the amount of these variables from database, I just don't know which variables to use. That is why I want to count it manually.
ColdFusion exposes a variable that tells you how many rows are returned in your query, without running a loop to count them:
<cfquery name="get_serial" datasource="#dsn3#">
SELECT *
FROM SERVICE_GUARANTY_NEW
WHERE STOCK_ID = #attributes.action_row_id#
ORDER BY SERIAL_NO
</cfquery>
<cfoutput>There are #get_serial.recordCount# rows.</cfoutput>
<cfoutput query="get_serial">
<p>#get_serial.serial_no#</p>
</cfoutput>
If I understand you correctly you can do it with a counter in your loop.
<cfset counter = 0 />
<cfloop query="get_serial">
<cfif PROCESS_ID eq attributes.action_id>
#SERIAL_NO#
<cfset counter ++ />
</cfif>
</cfloop>
<cfoutput>Output #counter# times!</cfoutput>
EDIT: to answer your follow up question:
<cfset counter = 0 />
<cfsavecontent variables="myContent">
<cfloop query="get_serial">
<cfif PROCESS_ID eq attributes.action_id>
#SERIAL_NO#
<cfset counter ++ />
</cfif>
</cfloop>
</cfsavecontent>
<cfoutput>
<p>Output #counter# times!</p>
<p>#myContent#</p>
</cfoutput>
Hope that helps!
Here's another couple answers, just for fun :), although having the counter is probably the best way since you're already incurring the performance hit of using the loop.
<cfquery name="getCount" dbtype="query">
SELECT PROCESS_ID FROM get_serial WHERE PROCESS_ID = #attributes.action_id#
</cfquery>
<cfset total = getCount.RecordCount />
And using lists, always fun:
<cfset total = ListValueCount(ValueList(get_serial.PROCESS_ID), attributes.action_id) />
Point being that there are a multiple ways to solve that kind of problem, so have fun :)

SQL Array being returned as number, not String in ColdFusion

In Coldfusion, I'm using a cfc that binds one select box to another (basically, choose a State from one box, the second box is populated with County names.) The value for the County box is a 5-digit number WHICH IS FORMATTED AS TEXT (ie. the value comes from a text field.)
The problem is that I'm finding that if the value of the selected county id starts with a '0', it's been cut off.
So I get stuff like:
ID County
11223 A
2300 B (should be 02300)
Can someone help make sure that leading 0s are not cut off?
Here's the select boxes on the page:
<!--- State Name options --->
<b>State:</b><br />
<cfselect bind="cfc:states.getStates()" bindonload="true" name="search_state" id="search_state" value="StateUSAbb" display="StateName">
</cfselect><br />
<!--- County Name options --->
<b>County:</b><br />
<cfselect bind="cfc:states.getCounties({search_state})" name="search_county" id="search_county" value="FIPS_County" display="CountyName">
</cfselect>
I hate pasting the whole .cfc but pay attention to the latter part, particularly the cfloop which uses a cfset to populate array RESULT:
<cfcomponent output="false">
<!--- Get array of media types --->
<cffunction name="getStates" access="remote" returnType="array">
<!--- Define variables --->
<cfset var data="">
<cfset var result=ArrayNew(2)>
<cfset var i=0>
<!--- Get data --->
<cfquery name="data" datasource="bridges">
SELECT DISTINCT tblLoc.StateUSAbb, lkuState.StateName
FROM lkuState INNER JOIN tblLoc ON lkuState.FIPS_State = tblLoc.FIPS_State
WHERE (lkuState.StateName <> 'New Brunswick')
UNION
SELECT '' AS StateUSAbb, ' ALL' AS StateName
FROM lkuState
ORDER BY StateName
</cfquery>
<!--- Convert results to array --->
<cfloop index="i" from="1" to="#data.RecordCount#">
<cfset result[i][1]=data.StateUSAbb[i]>
<cfset result[i][2]=data.StateName[i]>
</cfloop>
<!--- And return it --->
<cfreturn result>
</cffunction>
<!--- Get counties by state --->
<cffunction name="getCounties" access="remote" returnType="array">
<cfargument name="stateabb" type="string" required="true">
<!--- Define variables --->
<cfset var data="">
<cfset var result=ArrayNew(2)>
<cfset var i=0>
<!--- Get data --->
<cfquery name="data" datasource="bridges">
SELECT '' AS FIPS_COUNTY, ' ALL' as CountyName
FROM lkuCnty
UNION
SELECT FIPS_County, CountyName
FROM lkuCnty
WHERE StateAbb = '#ARGUMENTS.stateabb#'
ORDER BY CountyName
</cfquery>
<!--- Convert results to array --->
<cfloop index="i" from="1" to="#data.RecordCount#">
<cfset result[i][1]=data.FIPS_County[i]>
<cfset result[i][2]=data.CountyName[i]>
</cfloop>
<!--- And return it --->
<cfreturn result>
</cffunction>
</cfcomponent>
If the data is a fixed length you can use NumberFormat to force leading zero's. In general, CF is typeless so there must be some underlying conversion happening that is causing the data to get corrupt. You might try forcing the value toString(), or to debug add something like a single quote as the first character in the column value (eg. SELECT '''' + FIPS_County, '''' + CountyName FROM lkuCnty) to see if they keep all their characters.
[Update]
Based on your comments about how SQL is not returning 5 char, use this updated query to go from INT to VARCHAR with leading zeros.
SELECT DISTINCT
RIGHT('00000' + CONVERT(VARCHAR(5),StateUSAbb),5),
lkuState.StateName
FROM lkuState INNER JOIN tblLoc ON lkuState.FIPS_State = tblLoc.FIPS_State
WHERE (lkuState.StateName <> 'New Brunswick')
UNION
SELECT '' AS StateUSAbb,
' ALL' AS StateName
FROM lkuState
ORDER BY StateName
append a space to the end of the number, then CF will treat it as a string and no leading 0 will be chopped.
simple workaround: <cfset result[i][1]=data.StateUSAbb[i] & " ">
btw, you know that query object is supported for populating cfselect right? So you don't even need the loop. You can do the same workaround but in SQL inside your cfquery
UPDATE: anyway, the idea is that if u want to preserve the leading 0 and keep using CF's built in serializeJSON() or calling the cfc remote method in JSON style (which will internally invoke serializeJSON(), you can append a space so CF will treat it as a string and leading 0 will be preserved. If your script somehow must need "012345" with no trailing space, then look for another JSON seralizer from riaforge or cflib.
You're sure the data returned from your query is a text string which contains the leading zero, rather than just the integer value? Regardless, I think Zachary's suggestion of NumberFormat(x, "00000") is the way to go.