Related
I have a two lists: One that is dynamic, based off of a recordcount of students and the other is the students..i.e., 001,002,003, etc. and 123456 (student data). I need some help being able to randomly assign students one of those numbers. For example, if I have 5 students (123456,234561, etc.), I need to be able to randomly assign 001,002, etc. to those students. So far, hitting a wall. The following is what I have so far:
<cfquery name="testjd" datasource="student">
SELECT SoonerID FROM dbo.CurrentStudentData
WHERE status = 'Student' OR status = 'JD'
</cfquery>
<cfset testList = valueList(testjd.soonerid)>
<cfset totalRecordsJd = #testjd.recordcount#>
<cfloop from="001" to="#totalRecordsJd#" index="i">
<cfset examNo = i>
<cfif len(i) is 1>
<cfset examNo = "00" & i>
<cfelseif len(i) is 2>
<cfset examNo = "0" & i>
</cfif>
<cfif not listFind(usedJDExams, examNo)>
#examNo#<!---is NOT being used!---><br/>
</cfif>
</cfloop>
CF9 makes it a little less fun than later versions. I believe this should work (except for my query mockup).
https://trycf.com/gist/3667b4a650efe702981cb934cd325b08/acf?theme=monokai
First, I create the fake query.
<!---
Simple faked up query data. This is just demo data. I think this
queryNew() syntax was first available in CF10.
--->
<cfscript>
testjd = queryNew("SoonerID", "varchar", [
["123456"],
["564798"],
["147258"],
["369741"]
]);
</cfscript>
Now that I've got a list of students who need tests, I create an array of numbers for those tests.
<!--- Create array of Available Exam Numbers --->
<cfset examNos = ArrayNew(1)>
<cfloop from=1 to=100 index="i">
<cfset examNos[i] = right('00' & i,3)>
</cfloop>
We now combine the two sets of data to get the Exam Numbers assigned to the Student.
<!--- Loop through the query and build random assignments --->
<cfloop query="#testjd#">
<!---Get random exam no.--->
<cfset examNo = examNos[randrange(1,arrayLen(examNos))]>
<!---Build struct of exam assignments--->
<cfset testAssignments[SoonerID] = examNo>
<!---Delete that num from exam array. No reuse.--->
<cfset blah = arrayDelete(examNos,examNo)>
</cfloop>
And this gives us
<cfoutput>
<cfloop collection="#testAssignments#" item="i">
For User #i#, the test is #testAssignments[i]#.<br>
</cfloop>
</cfoutput>
The unused tests are: <cfoutput>#ArrayToList(examNos)#</cfoutput>.
--------------------------------------------------------------------
For User 369741, the test is 054.
For User 147258, the test is 080.
For User 564798, the test is 066.
For User 123456, the test is 005.
The unused tests are:
001,002,003,004,006,007,008,009,010
,011,012,013,014,015,016,017,018,019,020
,021,022,023,024,025,026,027,028,029,030
,031,032,033,034,035,036,037,038,039,040
,041,042,043,044,045,046,047,048,049,050
,051,052,053,055,056,057,058,059,060
,061,062,063,064,065,067,068,069,070
,071,072,073,074,075,076,077,078,079
,081,082,083,084,085,086,087,088,089,090
,091,092,093,094,095,096,097,098,099,100.
A couple of code review notes for the OP code:
1) It's easier to work with arrays or structures than it is to work with a list.
2) cfloop from="001" to="#totalRecordsJd#": from "001" is a string that you are comparing to an integer. ColdFusion will convert "001" to a number in the background, so that it can actually start the loop. Watch out for expected data types, and make sure you use arguments as they were intended to be used.
3) cfif len(i) is 1...: First, it's less processing to build this string in one pass and them trim it - right('00' & i,3). Second (and this is a personal nitpick), is and eq do essentially the same thing, but I've always found it good practice to apply is to string-ish things and eq to number-ish things.
=====================================================================
For CF10+, I would use something like
https://trycf.com/gist/82694ff715fecd328c129b255c809183/acf2016?theme=monokai
<cfscript>
// Create array of random string assignments for ExamNo
examNos = [] ;
for(i=1; i lte 100;i++) {
arrayAppend(examNos,right('00'& i,3)) ;
}
///// Now do the work. ////
//Create a struct to hold final results
testAssignments = {} ;
// Loop through the query and build random assignments
for(row in testjd) {
// Get random exam no.
examNo = examNos[randrange(1,arrayLen(examNos))] ;
// Build struct of exam assignments
structInsert(testAssignments, row.SoonerID, examNo) ;
// Delete that num from exam array. No reuse.
arrayDelete(examNos,examNo) ;
}
</cfscript>
If it is a small query, why not just sort the records (psuedo) randomly using NEWID()? Since the records will already be randomized, you cna use query.currentRow to build the "examNo".
<cfquery name="testjd" datasource="student">
SELECT SoonerID
FROM CurrentStudentData
WHERE status IN ('Student', 'JD')
ORDER BY NewID()
</cfquery>
<cfoutput query="yourQuery">
#yourQuery.SoonerID# #NumberFormat(yourQuery.currentRow, "000000")#<br>
</cfoutput>
This is a formatted comment. After you run this query:
<cfquery name="testjd" datasource="student">
SELECT SoonerID FROM dbo.CurrentStudentData
WHERE status = 'Student' OR status = 'JD'
</cfquery>
Do this:
<cfset QueryAddColumn(testJd, "newcolumn", arrayNew(1))>
Then loop through the queries and assign values to the new column with QuerySetCell.
I have a query as below which
<cfquery name="qryGetXXX" datasource="#sDataSource#">
SELECT COUNT(*) AS TotalCount,Quality.Qualitydesc
FROM QualityCheck INNER JOIN Quality
ON QualityCheck.QualityID = Quality.qualityID
WHERE DATEDIFF(d,DueDate,GETDATE()) >= 90
GROUP BY quality.qualityDesc
</cfquery>
will result in
1) *total count* 21 *QualityDesc* IO
2) *total count* 1 *QualityDesc* Max
3) *total count* 1 *QualityDesc* Min
4) *total count* 1 *QualityDesc* Other
5) *total count* 3 *QualityDesc* Reg
In order to get the first row I am using,
<cfif #qryGetXXX.RecordCount# gt 0 >
<cfloop query="qryGetXXX" startrow="1" endrow="1">
<cfset XXXTimeTotal =#qryGetXXX.TotalCount# >
</cfloop>
<cfelse>
<cfset XXTotal = 0 >
</cfif>
which is checking for the first the recordcount of the whole query but is there any way I can check whether the first row (i.e. startrow 1 and endrow 1) is has a value and then if the startrow 2 and endrow 2 has a value and so on? Can I put the results in an array and will that be easier?
Frank,
You are correct, i am new to coldfusion, hence all the confusion.The query provides me with the results i.e i have the result set as explained above, however i cannot figure out a way to check every row individually. For example i have to check if there are results for the first row and if it does i have to pass that value to my output, and if doesnot i have to put in no value was entered or '0'. I need to do this for the five rows and there will always only be 5 rows of results, which is why i was using start and endrow, however start and endrow donot allow me the flexibility to check for empty row values. In essence i would like to check the row as something like below.
<!--- check the first row of resuslt i.e. Max values ---!>
<cfif startrow1.endrow1 gt 0 >
<cfset nIOCount =#qryGetXXX.TotalCount# >
<cfelse>
<cfset nIOCount = '0'>
</cfif>
<!--- then check the second row resuslts Max ---!>
<cfif startrow2.endrow2 gt 0 >
<cfset nMaxCount =#qryGetXXX.TotalCount# >
<cfelse>
<cfset nMaxCount = '0'>
</cfif>
<!---Output the values---!>
Totals
<tr>
<th>IO</th>
<td>#nIoCount#</td>
</tr>
<tr>
<th>Max </th>
<td>#nMaxCount#</td>
</tr>
<tr>
I know i cannot reference startrow and endrow as i want, so i am looking for a way to reference each row individually in the result set some other way. Any suggestions?
thanks
That query/code is rough (I rendered it to something I could work with).
Your query: (if yours works just leave it as is (I'm making two assumptions below)).
<cfquery name="test" datasource="#sdatasource#">
select count(a.*) as totalcount, a.qualitydesc
from quality a, qualitycheck b
where a.qualityid = b.qualityid
and datediff(d,a.duedate,getdate()) >= 90
group by a.qualitydesc
</cfquery>
Then do your check if and sets like this: (instead of an array I did a struct (making more assumptions as well)).
<cfset totals = structnew()>
<cfif test.recordcount>
<cfoutput query="test">
<cfif test.totalcount neq "">
<cfset StructInsert( totals, test.QualityDesc, test.totalcount )>
<cfelse>
<cfset xxtotal = 0>
</cfif>
</cfoutput>
</cfif>
In fact, you can skip that nested if statement and for the xxtotal and do something else out of the loop leaving you with tighter code that looks like this:
<cfset totals = structnew()>
<cfif test.recordcount>
<cfoutput query="test">
<cfset StructInsert( totals, test.QualityDesc, test.totalcount )>
</cfoutput>
</cfif>
So if these are your targets:
1) totalcount 21 QualityDesc IO
2) totalcount 1 QualityDesc Max
3) totalcount 1 QualityDesc Min
4) totalcount 1 QualityDesc Other
5) totalcount 3 QualityDesc Reg
Then your looped values will look like this and any missing values or whatever will be bypassed (again you will need to do some checking down stream)...
totals.IO = 21
totals.Max = 1
totals.Min = 1
totals.Other = 1
totals.Reg = 3
Let me know if this helps and makes sense.
(Summary from comments...)
If you mean some of the descriptions, like "IO", are not included in query results at all, then that is what I suspected earlier. Since you are using an INNER join, the query will only return counts for descriptions that exist in both tables. If you want to include all descriptions, even if there is no match in "QualityCheck", you need to use an OUTER join instead. For example:
SELECT Quality.Qualitydesc
, COUNT(QualityCheck.QualityID) AS TotalCount
FROM Quality LEFT JOIN QualityCheck
ON QualityCheck.QualityID = Quality.qualityID
AND DATEDIFF(d,DueDate,GETDATE()) >= 90
GROUP BY quality.qualityDesc
Though as I mentioned on another thread, how you construct the filter can negatively impact the query's performance. See What makes a SQL statement sargable? for details and alternatives.
Side note, while you provided a lot of detail here, you omitted the most important part: .. the actual goal ;-) You will get faster and more accurate responses if you clearly summarize what you are trying to do first, then include the code.
The Golden Rule: Imagine You're Trying To Answer The Question
The XY Problem
How to Ask
I am creating a INSERT Starement for my table. Till now all going good and i have been able to create the Insert Statement. Only Issue Left is: It shows a trailing comma after the end of every single record. Can you guys have a look around what mess I am doing here
<cfset listcount = getQueryColumns(insertData)>
<cfset counter = 1>
<cfloop query="insertData">
<cfoutput>
INSERT INTO `mytable` (#listcount#)
VALUES(
<cfloop index="col" list="#listcount#">'#insertData[col][currentRow]#'
<cfif counter LT insertData.recordcount>,</cfif>
</cfloop>);<br><br>
</cfoutput>
<cfset counter++>
</cfloop>
Your error is due to the fact that you are incrementing your counter in the outer loop instead of the inner loop.
I think I've got it. I believe this is what you need:
<cfset listcount = getQueryColumns(insertData)>
<cfloop query="insertData">
<cfset counter = 1>
<cfoutput>
INSERT INTO `mytable` (#listcount#)
VALUES(
<cfloop index="col" list="#listcount#">'#insertData[col][currentRow]#'
<cfif counter LT listcount>,</cfif>
<cfset counter++>
</cfloop>);<br><br>
</cfoutput>
</cfloop>
What I changed is:
As Dan Bracuk pointed out, I moved <cfset counter++> inside the inner loop. I also moved <cfset counter = 1> inside the outer loop, as it will need to be reinitialized through successive INSERT statements.
I changed <cfif counter LT insertData.recordcount> to <cfif counter LT listcount>, as you don't want to iterate over the recordcount (this is why your commas stopped appearing after Priority, which was the 8th field). Instead, you want to iterate over the number of columns.
EDIT: See my more recent answer. I'm leaving this one in place because the comments were useful in the diagnosis.
I think Dan Bracuk is correct about your counter increment. But you might be able to simplify your code and avoid the <cfif > statement entirely if you you use the list attribute in <cfqueryparam >. For example:
<cfqueryparam value="#NAME_OF_LIST#" list="yes" >
By default this will put a comma between your list values before sending them to the database.
Check out the other attributes it takes at http://www.cfquickdocs.com/cf8/#cfqueryparam.
It's a bit of tricky question, however, my page for most rated bands displays the band logos in order of how high they have been rated. My only problem is i want to count through the records by using a cfloop from 1 to 10 and because it is split in to two columns, have one counting 1 through to 9 and the other 2 through to 10, each of them with steps of two.
Can anybody help me with this? If i've confused just mention it and ill try to clarify exactly what i mean.
<DIV class="community_middle">
<cfoutput query="top10MostRated">
<cfloop from="2" to="10" index="i" step="2">
<DIV class="communityContent">
#chr(i)#
<IMG src="logo/#top10MostRated.Logo#" alt="#top10MostRated.Name#" width="100%" height="100%"></IMG>
</DIV>
<BR/>
</cfloop>
</cfoutput>
</DIV>
If you're looking to do odd/even lists separately, then you can use the currentrow property of the query combined with the modulo operator (%) to work out if the row is odd or even:
<cfloop query="topBands>
<cfif topBands.currentRow % 2 = 1>
<!--- do your odd number output here --->
</cfif>
</cfloop>
<cfloop query="topBands>
<cfif topBands.currentRow % 2 = 0>
<!--- do your even number output here --->
</cfif>
</cfloop>
I think these answers address your side-by-side part of your question but does not explain the "same image" issue. Their code is written correctly but does not explain the reason.
Your code:
<IMG src="logo/#top10MostRated.Logo#"
alt="#top10MostRated.Name#"
width="100%" height="100%"></IMG>
... would be fine if you were only inside a <cfloop query = "top10MostRated"> or <cfoutput query = "top10MostRated"> block. The reason is because inside these types of blocks CF is smart enough to know you want the data for the current row. It would be the same as:
<IMG src="logo/#top10MostRated.Logo[top10MostRated.currentRow]#"
alt="#top10MostRated.Name[top10MostRated.currentRow]#"
width="100%" height="100%" />
Because you're nesting the to/from cfloop inside a <cfoutput query = ""> block, you are getting unexpected results. Your existing code is always asking for the record provided by your outer loop. Hence you see the same image 5 times. (using any of the fine examples provided will help you get out of this) but, you can remove the query from your cfoutput and simply ask CF to show you the value for the correct row in your loop using your index (you set your index to "i") so the below would show you the image that corresponds to your loop.
<IMG src="logo/#top10MostRated.Logo[i]#"
alt="#top10MostRated.Name[i]#"
width="100%" height="100%" />
It sounds like what you'd like to get is a collection of even-numbered records and a collection of odd-numbered records. In Coldfusion 10 or Railo 4, you can use groupBy() from Underscore.cfc to split up your query result into manageable sub-sets, like so:
_ = new Underscore();// instantiate the library
groupedBands = _.groupBy(topBands, function (val, index) {
return index % 2 ? "odd" : "even";
});
This returns a struct with two elements odd and even, each containing an array of records which are odd or even. Example result:
{
odd: [{name: "Band one"}, {name: "Band three"}],
even: [{name: "Band two"}, {name: "Band four"}]
}
Splitting your results into logical sub-sets makes the code more readable:
<cfoutput>
<cfloop from="1" to="5" index="i">
<div class="left">#groupedBands.odd[i].name#</div>
<div class="right">#groupedBands.even[i].name#</div>
</cfloop>
</cfoutput>
You'll also be able to use those sub-sets in other places on your page if you need to.
Note: I wrote Underscore.cfc
Ben Nadel has a post exactly for this. Link here
A breakdown for this is
<cfloop query="top10MostRated">
<cfif top10MostRated.CurrentRow MOD 2>
<!--- Add to the "odd list" --->
<cfelse>
<!--- Add the record to the "even list" --->
</cfif>
</cfloop>
Then you'll have 2 lists oddList and evenList. Then it's just a matter of displaying them.
I'd do it a different way. The objective is to have records 1 and 2 side by side and I don't see that in #barnyr's answer.
<cfoutput>
<cfloop from="2" to="topbands.recordcount + 1" index = "i" step="2">
#topbands.fieldname[i-1]#
<cfif i lte topbands.recordcount>
#topbands.fieldname[i]# <br />
</cfif>
</cfloop>
</cfoutput>
I have a simple cfquery which outputs 3 columns with their respective data. The columns are name, address and age.
I want to transpose this set of data so that the names become the columns and the address and age are displayed under each column.
I know that we can use QueryAddColumn or something like this for this issue. Can someone help me out with this problem?
EDIT:
Based on the comment below this is the intended output:
Oct 2011 Nov 2011 Dec 2011 Jan 2012 Feb 2012
NumberofPeople NumberofPeople NumberofPeople NumberofPeople NumberofPeople
EmploymentRate EmploymentRate EmploymentRate EmploymentRate EmploymentRate
I have included a sample data row at the top where you would put your cfquery statement.
<cfset firstQuery = queryNew("date,NumberofPeople,EmploymentRate")>
<cfset aRow = queryAddRow(firstQuery)>
<cfset querySetCell(firstQuery,"date","OCT_2011",aRow)>
<cfset querySetCell(firstQuery,"NumberofPeople","28",aRow)>
<cfset querySetCell(firstQuery,"EmploymentRate","50%",aRow)>
<cfset aRow = queryAddRow(firstQuery)>
<cfset querySetCell(firstQuery,"date","NOV_2011",aRow)>
<cfset querySetCell(firstQuery,"NumberofPeople","28",aRow)>
<cfset querySetCell(firstQuery,"EmploymentRate","56%",aRow)>
<cfset aRow = queryAddRow(firstQuery)>
<cfset querySetCell(firstQuery,"date","DEC_2011",aRow)>
<cfset querySetCell(firstQuery,"NumberofPeople","29",aRow)>
<cfset querySetCell(firstQuery,"EmploymentRate","55%",aRow)>
<cfset aRow = queryAddRow(firstQuery)>
<cfset querySetCell(firstQuery,"date","JAN_2012",aRow)>
<cfset querySetCell(firstQuery,"NumberofPeople","30",aRow)>
<cfset querySetCell(firstQuery,"EmploymentRate","52%",aRow)>
<!--- Will Create new query with names as column headers--->
<cfset newQuery = queryNew(valueList(firstQuery.date,','))>
<!--- Will Create new query with names as column headers--->
<cfset people = queryAddRow(newQuery)>
<cfset rate = queryAddRow(newQuery)>
<cfloop query='firstQuery'>
<!---Syntax for this function is: QuerySetCell(query, column_name, value [, row_number ]) --->
<cfset querySetCell(newQuery,firstQuery.date,firstQuery.NumberofPeople,people)>
<cfset querySetCell(newQuery,firstQuery.date,firstQuery.EmploymentRate,rate)>
</cfloop>
<cfdump var="#newQuery#">
<cfdump var="#ArrayToList(newQuery.getColumnNames())#">
This is How I would Do it, But I can't think of why I would do it. I'd be interested to hear your use case. Anyway, I hope this helps.
(P.S This is tested in CF9, so you should be able to copy and paste it to test for yourself.)
EDIT -(Again):
Forgot to mention, this can only work if the names your retrieveing from the DB are valid column names, so no spaces (In this example spaces in dates have been replaced by underscores)!
>>> New code snippet for the updated data structure, the function valueList(firstQuery.date,',') doesn't re-order your columns. The columns are re-ordered on output when dumping. I have used the function ArrayToList(newQuery.getColumnNames()) to show that internally CF maintains the column order and you need only ask it nicely. You should be able to use all this information to nicely output your data how you need it.
Maybe I'm missing something but it seems like a simple SQL query with the ORDER BY clause would work. Something like this:
<cfquery name="myquery" datasource="yourdatasourcename">
select name, address, age
from tablename
order by name
</cfquery>
Then in your ColdFusion output page, you can use the tag with the group attribute. Something like this:
<cfoutput query="myquery">
<p>name = #name#
<cfoutput group="name">
age = #age#
address = #address#<br />
</cfoutput>
</p>
</cfoutput>
Obviously, you can format the output however you wish.
EDIT --
If you are wanting to display like:
Mary Joe Sam Suzie
28 36 25 42
123 Maple 16 Oak 3723 Street 832 Busy St.
Perhaps something like (I have not tested this, just brainstorming):
<cfoutput query="myquery" group="name">
<div style="float:left;">name = #name#
<cfoutput>
<p>
age = #age#<br />
address = #address#
</p>
</cfoutput>
</div>
</cfoutput>
I think you are describing a pivot query in SQL.