Replace in ColdFusion spoiling SQL query - coldfusion

I've got the below MySQL query, it's causing an error, the error is below too.
SELECT DISTINCT s.id as id, s.auctioneer as auctioneer, s.advertType as advertType, s.saleType as saleType, an.name as auctioneerName, st.entryCopy as saleTypeName, at.entryCopy as advertTypeName, s.heading AS heading, sl.city AS city, sd.id AS sdId, sd.startDate AS startDate
FROM sales s LEFT JOIN saleloc sl ON sl.saleId = s.id LEFT JOIN saledates sd ON sd.saleLoc = sl.id,
auctioneers an,
lookupcopy st,
lookupcopy at
#replace(findWhere,"''","'","all")# AND
s.id = sd.saleId AND sl.saleId = s.id
AND an.id = s.auctioneer
AND st.id = s.saleType
AND at.id = s.advertType
GROUP BY id
ORDER BY startDate, auctioneerName, city
Error from database
SELECT DISTINCT s.id as id, s.auctioneer as auctioneer, s.advertType as advertType, s.saleType as saleType, an.name as auctioneerName, st.entryCopy as saleTypeName, at.entryCopy as advertTypeName, s.heading AS heading, sl.city AS city, sd.id AS sdId, sd.startDate AS startDate
FROM sales s
LEFT JOIN saleloc sl ON sl.saleId = s.id
LEFT JOIN saledates sd ON sd.saleLoc = sl.id, auctioneers an, lookupcopy st, lookupcopy at
'WHERE s.advertType > 0
AND s.saleType > 0
AND sl.region = "2" '
AND s.id = sd.saleId
AND sl.saleId = s.id
AND an.id = s.auctioneer
AND st.id = s.saleType
AND at.id = s.advertType
GROUP BY id
ORDER BY startDate, auctioneerName, city
I didn't write this code and I'm not sure why the #Replace()# is being used, can anyone see how to fix the syntax error it's causing?

Before the query code, do a replace as follows:
<cfset findWhere = Replace(findWhere, "''", "'", "ALL")#
<cfif Left(findWhere, 1) EQ "'">
<cfset findWhere = Right(findWhere, Len(findWhere) - 1)>
</cfif>
<cfif Right(findWhere, 1) EQ "'">
<cfset findWhere = Left(findWhere, Len(findWhere) - 1)>
</cfif>
<cfquery name="qry" datasource="mysql">
SELECT DISTINCT s.id as id, s.auctioneer as auctioneer, s.advertType as advertType, s.saleType as saleType, an.name as auctioneerName, st.entryCopy as saleTypeName, at.entryCopy as advertTypeName, s.heading AS heading, sl.city AS city, sd.id AS sdId, sd.startDate AS startDate
FROM sales s
LEFT JOIN saleloc sl ON sl.saleId = s.id
LEFT JOIN saledates sd ON sd.saleLoc = sl.id,
auctioneers an,
lookupcopy st,
lookupcopy at
#findWhere# AND
s.id = sd.saleId AND sl.saleId = s.id
AND an.id = s.auctioneer
AND st.id = s.saleType
AND at.id = s.advertType
GROUP BY id
ORDER BY startDate, auctioneerName, city
</cfquery>

The value stored in findWhere includes single-quotes at the beginning and end of the string.

On another note: Unless you created findWhere without any direct user input value, then you need to secure it.
Better to do:
...
WHERE 1= 1
<cfif listFind( 'foo' , findWhere )>
foo= 2
<cfelseif listFind( 'bar' , findWhere )>
bar= 209
</cfif>
...

Just to clarify, I don't believe you can do a distinct and a group by statement in the same query.
They both do the same kind of thing, but for different reasons.

Related

Coldfusion Dynamic Variable (?) parsed to SQL Query

This is the first time I am attempting to parse a variable to a CF query, but I have run into a few little issues.
In summary, I am creating a pivot table of sales by operators by week. Manually, no hassle, but I only want a subset of weeks, not all. Again, no real problem if I want hardcoded weeks, but the problem comes in when I try and parse a week number to the SQL query to create the subset of dynamic weeks.
The CFDUMP shows me that the query is executing based on what I am sending to it, but when it comes to outputting the value of the field (the week), it takes the variable name value, and not the field value if that makes sense?
I know that I shouldn't be having field names as values, but I still tryingh to test right now. With the manual query, I prefix the week number with a 'W' e.g. W9, but when I try and do that I get
MANUAL QUERY
SELECT UserName,
ISNULL([W6], 0) AS [W6],
ISNULL([W7], 0) AS [W7],
ISNULL([W8], 0) AS [W8],
ISNULL([W9], 0) AS [W9],
ISNULL([W10], 0) AS [W10]
FROM ( SELECT CASE
WHEN SUBSTRING(Username, 1, 3) = 'd.S' THEN 'DS'
WHEN SUBSTRING(Username, 1, 3) = 'p.R' THEN 'PR'
WHEN SUBSTRING(Username, 1, 3) = 'j.G' THEN 'JG'
WHEN SUBSTRING(Username, 1, 3) = 'b.c' THEN 'BC'
ELSE 'Other' END AS Username,
CONCAT('W', DATEPART(isowk, ERCFullAuditDate)) as XWeek,
COUNT(1) [SalesCount]
FROM [ERC-Transactions].[dbo].[ERC-Audit]
WHERE ( ERCModule = 'Carriage Return on Account'
AND ERCFullAuditDate >= DATEADD(week, -4, GETDATE())
OR ( ERCFullAuditDate <= DATEADD(week, -52, convert(datetime, GETDATE()))
and ERCFullAuditDate >= DATEADD(week, -56, convert(datetime, GETDATE()))))
GROUP BY DATEPART(isowk, ERCFullAuditDate),
UserName) ST
PIVOT ( SUM(SalesCount)
for XWeek in ([W6], [W7], [W8], [W9], [W10])) as StorePivot
The above yields this result.
THE COLDFUSION DYNAMIC QUERY
Now when I try and do the same, but by parsing variables to the query, the CFDUMP yields the correct values, but as I said, when I try and output this, I get the field name and not the value.
To be honest, I have two issues here in that I need to address. The variable field name, but also when I come to adding the concatenation of 'W' to the week number, I am seeing an 'Error converting data type nvarchar to int.' I think I may need a cfqueryparam, but I am not sure.
<cfset WEEK_2 = DATETImeFormat(DateAdd("ww",-2,now()),"w")>
<cfoutput>Week: #WEEK_2#</cfoutput> (This is the value of the WEEK_2 variable)<br>
<cfset XX = "">
<cfset XX = XX & "SELECT UserName, ">
<cfset XX = XX & "ISNULL([#WEEK_2#], 0) AS [#WEEK_2#] ">
<cfset XX = XX & "FROM ( SELECT CASE ">
<cfset XX = XX & "WHEN SUBSTRING(Username,1,3) = 'd.S' THEN 'DS' ">
<cfset XX = XX & "WHEN SUBSTRING(Username,1,3) = 'p.R' THEN 'PR' ">
<cfset XX = XX & "WHEN SUBSTRING(Username,1,3) = 'j.G' THEN 'JG' ">
<cfset XX = XX & "WHEN SUBSTRING(Username,1,3) = 'b.c' THEN 'BC' ">
<cfset XX = XX & "ELSE 'Other' END AS Username, ">
<cfset XX = XX & "DATEPART(isowk, ERCFullAuditDate) as XWeek, ">
<cfset XX = XX & "COUNT(1) [SalesCount] ">
<cfset XX = XX & "FROM [EBS-ERC-Transactions].[dbo].[ERC-Audit] ">
<cfset XX = XX & "WHERE ( ERCModule = 'Carriage Return on Account' ">
<cfset XX = XX & "AND ERCFullAuditDate >= DATEADD(week, -4, GETDATE()) ">
<cfset XX = XX & "OR ( ERCFullAuditDate <= DATEADD(week, -52, convert(datetime, GETDATE())) ">
<cfset XX = XX & "and ERCFullAuditDate >= DATEADD(week, -56, convert(datetime, GETDATE())))) ">
<cfset XX = XX & "GROUP BY DATEPART(isowk, ERCFullAuditDate), ">
<cfset XX = XX & "UserName) ST ">
<cfset XX = XX & "PIVOT ( SUM(SalesCount) ">
<cfset XX = XX & "for XWeek in ([#WEEK_2#])) as StorePivot ">
<cfquery name = "QueryTest" dataSource = "EBSERC">
#PreserveSingleQuotes(XX)#
</cfquery>
<br>
<cfoutput Query="QueryTest">
#UserName#, #WEEK_2#<br>
</cfoutput>
<br>
<cfdump var="#QueryTest#" />
And this is the result ...
Ultimately, as mentioned before, I want to concatenate 'W' to the week number field.
Any guidance or a steer in the right direction will be VERY appreciated and thanks for your time.
Thank you very much for reading.
This is too long for comments.
Since the column labels are dynamic and closely bound to the database, I'd probably do this all on the database side. Preferably inside a stored procedure.
If you're using SQL Server 2017+, a CTE could be used to generate the weeks within the desired date range. Then STRING_AGG() used to convert the results into comma separated lists.
; WITH dates AS (
-- Generate range between -56 and -52 weeks ago
SELECT DateAdd(wk, -56, GETDATE()) AS WeekDate
UNION ALL
SELECT DateAdd(wk, 1, WeekDate)
FROM dates
WHERE DateAdd(wk, 1, WeekDate) <= DateAdd(wk, -52, GETDATE())
)
, dateLabels AS (
-- extract week number and construct label "W1","W2",etc..
SELECT DATEPART(isowk, WeekDate) AS WeekNum
, QUOTENAME( CONCAT('W', DATEPART(isowk, WeekDate) )) AS WeekLabel
FROM dates
)
-- convert to comma separated lists
SELECT STRING_AGG( WeekLabel, ',')
WITHIN GROUP (ORDER BY WeekNum) AS PivotColumns
, STRING_AGG( CONCAT('ISNULL(', WeekLabel, ',0) AS ', WeekLabel ), ',')
WITHIN GROUP (ORDER BY WeekNum) AS SelectColumns
FROM dateLabels
;
The results would look like this (minus any line wrapping)
PivotColumns
SelectColumns
[W6],[W7],[W8],[W9],[W10]
ISNULL([W6],0) AS [W6],ISNULL([W7],0) AS [W7],ISNULL([W8],0) AS [W8],ISNULL([W9],0) AS [W9],ISNULL([W10],0) AS [W10]
Then simply plug the two lists into the SQL query. In CF:
<cfscript>
// ...
sqlString = "
SELECT UserName
, #SelectColumns#
FROM (
...
) ST
PIVOT
(
SUM(SalesCount)
FOR XWeek IN ( #PivotColumns# )
) AS StorePivot
";
qPivot = queryExecute( sqlString );
// ...
</cfscript>
Results:
SELECT UserName
, ISNULL([W6],0) AS [W6]
, ISNULL([W7],0) AS [W7]
, ISNULL([W8],0) AS [W8]
, ISNULL([W9],0) AS [W9]
, ISNULL([W10],0) AS [W10]
FROM (
...
) ST
PIVOT (
SUM(SalesCount)
FOR XWeek IN (
[W6],[W7],[W8],[W9],[W10]
)
) AS StorePivot
WHERE oh where are the parentheses?
A few observations about the original query:
The current WHERE clause is incorrect. When mixing AND/OR operators, parentheses must be used to ensure the order of operations. Conceptually, the current query is doing this:
WHERE Condition1 AND Condition2 OR Condition3
It should be using parentheses here:
WHERE Condition1 AND ( Condition2 OR Condition3 )
GETDATE() already returns a datetime value, so there's no need to convert it to a datetime again here:
DATEADD(week, -52, convert(datetime, GETDATE()))
Lastly, there shouldn't be a need for all the concatenation. Strings can usually span multiple lines, and there's always cfsavecontent if needed. Removing all the & will greatly improve readability.
If you're trying to copy the SQL exactly I think you missed the 'W' in the cfset at the top:
<cfset WEEK_2 = 'W' & DATETImeFormat(DateAdd("ww",-2,now()),"w")>
Outputs:
Week: W9 (This is the value of the WEEK_2 variable)
You have this command:
<cfset XX = XX & "ISNULL([#WEEK_2#], 0) AS [#WEEK_2#] ">
Since the week_2 variable has a value of 9, when that command gets executed, it becomes this:
<cfset XX = XX & "ISNULL([9], 0) AS [9] ">
and your query becomes
select username
, 9 as 9
from etc
You are going to have to put some more thought into generating your week numbers.

Writing a spreadsheet using CFspreadsheet to avoid error this file might be corrupted

I am trying to figure out how to add my html table to a CFspreadsheet to show in excel. All the examples online that I have found are not as crazy as mine (only a simple one basic query). Any help with this would be greatly appreciated. This is what I have been able to figure out so far for my spreadsheet:
<cfset objSpreadsheet = SpreadsheetNew()>
<cfset filename = expandPath("./myexcel.xls")>
<!--- Create and format the header row. --->
<cfset SpreadsheetAddRow( objSpreadsheet, "Associate Name,Location,Checklists Generated by Associate,Checklists Generated by Selected Location(s),Associate Percentage of Location Total" )>
<cfset SpreadsheetFormatRow( objSpreadsheet, {bold=true, alignment="center"}, 1 )>
<cfheader name="Content-Disposition" value="attachment; filename=#filename#">
<cfcontent type="application/vnd.ms-excel" variable="#SpreadsheetReadBinary( objSpreadsheet )#">
My table trying to convert:
<table class="table table-hover">
<thead>
<th><strong>Associate Name</strong></th>
<th><strong>Location</strong></th>
<th><strong>Checklists Generated by Associate</strong></th>
<th><strong>Checklists Generated by Selected Location(s)</strong></th>
<th><strong>Associate Percentage of Location Total</strong></th>
</thead>
<tbody>
<cfoutput query="GetEmployeeInfo">
<tr>
<td><cfif rnA EQ 1><strong>#assoc_name#</strong></cfif></td>
<td><cfif rnL EQ 1>#trans_location#</cfif></td>
<td>#checklistsByAssocLoc#</td>
<td>#assocChecklistsByLoc#</td>
<td>#DecimalFormat(totalChecklistsByAssocLocPct)# %</td>
<!---<td> rnA: #rnA# | rnL: #rnL# | rnTotAssoc: #rnTotAssoc# </td> --->
</tr>
<cfif rnTotAssoc EQ 1>
<tr>
<td>Associate Total</td>
<td></td>
<td>#totalChecklistsByAssoc#</td>
<td>#totalAssocChecklistsByAllFilteredLoc#</td>
<td>#DecimalFormat(totalChecklistsByLocPct)# %</td>
</tr>
</cfif>
</cfoutput>
</tbody>
</table>
My crazy queries!:
<cfquery datasource="#application.dsn#" name="GetEmployeeInfo">
SELECT s4.associate /* Associate's ID */
, s4.assoc_name /* Associate's Name */
, s4.trans_location /* Associate's Location */
, s4.checklistsByAssocLoc /* Gives you a count of Checklists by Associate for a specific Location. */
, s4.assocChecklistsByLoc /* Gives you a count of Total Checklists by All Associates in a Location. */
, s4.totalChecklistsByAssoc /** Gives you a count of Total Checklists by Specific Associate in All Locations. */
, s4.totalAssocChecklistsByAllFilteredLoc /* Gives you a count of Total Checklists by Specific Associates in All Locations. */
, CASE WHEN ( coalesce(s4.assocChecklistsByLoc,0) > 0 ) THEN (CAST(s4.checklistsByAssocLoc AS decimal(8,2))/s4.assocChecklistsByLoc) * 100 ELSE 0 END AS totalChecklistsByAssocLocPct /* This gives you a percent of associate location checklists over count of checklists by Associate in a Location. */
, CASE WHEN ( coalesce(s4.totalAssocChecklistsByAllFilteredLoc,0) > 0 ) THEN (CAST(s4.totalChecklistsByAssoc AS decimal(8,2))/s4.totalAssocChecklistsByAllFilteredLoc) * 100 ELSE 0 END AS totalChecklistsByLocPct /* This gives you a percent of Total Associate Checklists in All Locations over count of Checklists by All Associate in All Locations. */
, s4.rnA /* Placeholder for a record to display the Associate Name. */
, s4.rnL /* Placeholder for a record to display the Location. */
, s4.rnTotAssoc /* Placeholder for the last Associate Location row. The next row should be an Associate Total. */
FROM (
SELECT s3.*
, SUM(s3.assocChecklistsByLoc) OVER (PARTITION BY s3.associate) AS totalAssocChecklistsByAllFilteredLoc /* Gives you a count of Total Checklists by Specific Associates in All Locations. */
FROM (
SELECT s2.*
FROM (
SELECT a.assoc_name
, s1.associate
, s1.trans_location
, s1.checklistsByAssocLoc
, s1.assocChecklistsByLoc
, s1.totalChecklistsByAssoc
, ROW_NUMBER() OVER (PARTITION BY s1.associate ORDER BY s1.associate, s1.trans_location) AS rnA /* Placeholder for a record to display the Associate Name */
, ROW_NUMBER() OVER (PARTITION BY s1.associate, s1.trans_location ORDER BY s1.associate, s1.trans_location) AS rnL /* Placeholder for a record to display the Location */
, ROW_NUMBER() OVER (PARTITION BY s1.associate ORDER BY s1.trans_location DESC) AS rnTotAssoc /* Placeholder for the last Associate Location row. The next row should be an Associate Total. */
FROM (
SELECT c.associate
, c.trans_location
, COUNT(*) OVER (PARTITION BY c.associate, c.trans_location) AS checklistsByAssocLoc /* Gives you a count of Checklists by Associate for a specific Location. */
, COUNT(*) OVER (PARTITION BY c.associate) AS totalChecklistsByAssoc /* Gives you a count of Total Checklists by Associate in All Locations. */
, COUNT(*) OVER (PARTITION BY c.trans_location) AS assocChecklistsByLoc /* Gives you a count of Total Checklists by All Associates in a Location. */
FROM cl_checklists c
LEFT OUTER JOIN tco_associates a ON c.associate = a.assoc_id
AND a.assoc_id IN ( <cfqueryparam value="#FORM.EmployeeName#" cfsqltype="cf_sql_varchar" list="true" /> ) /* SELECTED ASSOCIATE IDs */
WHERE c.[DATE] >= <cfqueryparam value="#date1#" cfsqltype="cf_sql_timestamp" /> /* SELECTED DATES */
AND c.[DATE] <= <cfqueryparam value="#date2#" cfsqltype="cf_sql_timestamp" />
AND c.trans_location IN ( <cfqueryparam value="#locList#" cfsqltype="cf_sql_varchar" list="true" /> ) /* SELECTED LOCATIONS */
) s1
INNER JOIN tco_associates a ON s1.associate = a.assoc_id
AND a.assoc_id IN ( <cfqueryparam value="#FORM.EmployeeName#" cfsqltype="cf_sql_varchar" list="true" /> ) /* SELECTED ASSOCIATE IDs */
) s2
WHERE s2.rnA = 1 OR s2.rnL = 1 /* There will be a final Location (rnL=1 and rnTotAssoc=1). This is the final row. */
) s3
) s4
ORDER BY s4.assoc_name, s4.trans_location
</cfquery>
This is the path I was thinking but I truly dont understand calling the rows and columns. Do I even have the right idea or am I way off?
<cfoutput query="GetEmployeeInfo">
<cfif rnA EQ 1><cfset SpreadsheetSetCellValue( objSpreadsheet, #assoc_name#, 2, 1) ></cfif>
<cfif rnL EQ 1><cfset SpreadsheetSetCellValue( objSpreadsheet, #trans_location#, 2, 1) ></cfif>
<cfset SpreadsheetSetCellValue( objSpreadsheet, #checklistsByAssocLoc#, 2, 1) >
<cfset SpreadsheetSetCellValue( objSpreadsheet, #assocChecklistsByLoc#, 2, 1) >
<cfset SpreadsheetSetCellValue( objSpreadsheet, #DecimalFormat(totalChecklistsByAssocLocPct)# %, 2, 1) >
<cfif rnTotAssoc EQ 1>
<cfset SpreadsheetSetCellValue( objSpreadsheet, 'Associate Total', 2, 1) >
<cfset SpreadsheetSetCellValue( objSpreadsheet, '', 2, 1) >
<cfset SpreadsheetSetCellValue( objSpreadsheet, #totalChecklistsByAssoc#, 2, 1) >
<cfset SpreadsheetSetCellValue( objSpreadsheet, #totalAssocChecklistsByAllFilteredLoc#, 2, 1) >
<cfset SpreadsheetSetCellValue( objSpreadsheet, #DecimalFormat(totalChecklistsByLocPct)# %, 2, 1) >
</cfif>
</cfoutput>
Also tried:
<cfoutput query="GetEmployeeInfo">
<cfset SpreadsheetAddRow( objSpreadsheet, "<cfif rnA EQ 1>#assoc_name#</cfif>,<cfif rnL EQ 1>#trans_location#</cfif>,#checklistsByAssocLoc#,#assocChecklistsByLoc#,#DecimalFormat(totalChecklistsByAssocLocPct)# %" )>
<cfif rnTotAssoc EQ 1>
<cfset SpreadsheetAddRow( objSpreadsheet, "Associate Total,'',#totalChecklistsByAssoc#,#totalAssocChecklistsByAllFilteredLoc#,#DecimalFormat(totalChecklistsByLocPct)# %" )>
</cfif>
</cfoutput>
For your final snippet ColdFusion tags will not be evaluated in a string literal. For if else statements, you could use the ternary operator to toggle some of the output. Also, if any of your data contains comma/s it'll split the data between cells. To combat this, try wrapping each cell value in quotes. This may keep your text in one cell. Others still have had issues with this fix.
<cfset rowNumber = 0 />
<cfoutput query="GetEmployeeInfo">
<cfset rowNumber++ />
<cfset rowList = "'#(rnA eq 1)?assoc_name:''#', '#(rnl eq 1)?trans_location:''#', '#checklistsByAssocLoc#,#assocChecklistsByLoc#', '#DecimalFormat(totalChecklistsByAssocLocPct)# %'">
<cfset SpreadsheetAddRow( objSpreadsheet, rowList)>
<cfset spreadsheetFormatCell( objSpreadsheet, {bold: true}, rowNumber, 1 )>
<cfif rnTotAssoc EQ 1>
<cfset rowNumber++ />
<cfset rowList = "'Associate Total','','#totalChecklistsByAssoc#','#totalAssocChecklistsByAllFilteredLoc#','#DecimalFormat(totalChecklistsByLocPct)# %'" >
<cfset SpreadsheetAddRow( objSpreadsheet, rowList )>
</cfif>
</cfoutput>
Personally, due to slow rendering time of using many (few hundred+) spreadsheetAddRows to create a spread sheet, and working with lists of string data can be a pain, I almost always place my spreadsheet data into a query object. Once the data is in a query object, it takes one call to spreadsheetAddRows to get the data into the spreadsheet.
qReportData = queryNew("Name, Age",
"varchar, integer",
[{name: "Tom", age: 25},{name: "Dick", age: 40},{name: "Harry", age: 55}]
);
sheet = SpreadsheetNew(false);
//Add and format headers
bold = {bold: true};
spreadsheetAddRow(sheet, "Name, Age");
spreadsheetFormatRow(sheet, bold, 1);
spreadsheetAddRows(sheet, qReportData);
Given that some of your report data can be on multiple rows, under specific situations, you won’t be able to just export your report query, so we'll have to build a new one with code. We'll iterate over the report and generate rows in our spreadsheet query. In my example, I'll add an extra row any time the person is over the age of 40.
qSheetOutput = queryNew("Name, Age");
for(row in qReportData){
queryAddRow(qSheetOutput, {
name: row.name,
age: row.age
});
if(row.age > 40){
queryAddRow(qSheetOutput, {
name: row.name & " is over 40"
});
}
}
// now writing the generated query to the spreadsheet
spreadsheetAddRows(sheet, qSheetOutput);
The last step will be to iterate and format the cells of the spreadsheet. As I iterate over the output I have to offset the row I'm working with by the count of headers in the sheet, witch in this example is 1. Also for this example, the extra row for a person over the age of 40 will not be bolded, and will span 2 cells.
for(row in qSheetOutput){
if(!len(row.age)){
spreadsheetFormatCell(sheet, {dataformat="#", alignment="center"}, qSheetOutput.currentRow + 1, 1);
spreadsheetFormatCell(sheet, qSheetOutput.currentRow + 1, qSheetOutput.currentRow + 1, 1, 2);
}
else{
spreadsheetFormatCell( sheet, bold, qSheetOutput.currentRow + 1, 1 );
}
}
If looking at the output is too difficult to determine the correct format/s needed for the row, you could iterate over an array/s of row numbers that require a specific format/s. Once again notice I'm using the header count as an offset again.
dataRows = [];
messageRows = [];
for(row in qReportData){
queryAddRow(qSheetOutput, {
name: row.name,
age: row.age
});
arrayAppend(dataRows, qSheetOutput.recordCount + 1);
if(row.age > 40){
queryAddRow(qSheetOutput, {
name: row.name & " is over 40"
});
arrayAppend(messageRows, qSheetOutput.recordCount + 1);
}
}
...
for(rowNumber in dataRows){
spreadsheetFormatCell( sheet, bold, rowNumber, 1 );
}
for(rowNumber in messageRows){
spreadsheetFormatCell(sheet, {dataformat="#", alignment="center"}, rowNumber, 1);
spreadsheetFormatCell(sheet, rowNumber, rowNumber, 1, 2);
}
Here is the complete working code on TryCF.com
In honour of your perseverance, here is one that I did a couple of days ago. The visitData, headers, columns and title variables were set earlier in the program because they also applied to html output.
<cfscript>
filePath = "d:\dw\dwweb\work\";
fileName = title & " " & getTickCount() & ".xlsx";
sheet = spreadSheetNew("data", true);
HeaderFormat = {};
HeaderFormat.bold = true;
spreadSheetAddRow(sheet, headers);
SpreadSheetFormatRow(sheet, HeaderFormat, 1);
SpreadSheetAddFreezePane(sheet, 0,1);
for (queryRow = 1; queryRow <= visitData.recordcount; queryRow ++) {
rowNumber = queryRow + 1;
for (columnNumber = 1; columnNumber <= listLen(columns); columnNumber ++) {
thisColumn = listGetAt(columns, columnNumber);
thisValue = visitData[thisColumn][queryrow];
SpreadSheetSetCellValue(sheet, thisValue, rowNumber, columnNumber);
} // columns
} // rows
SpreadSheetWrite(sheet,filePath & fileName, true);
</cfscript>
<cfheader name="content-disposition" value="Attachment;filename=#fileName#">
<cfcontent file="#filePath & fileName#" type="application/vnd.ms-excel">
Note the variables that I use in the last two tags. The <cfheader> tag has only the name of the file, but not the path. A mistake I was making earlier was to use just one variable which had both. The result was undesireable filenames being sent to the user.

ColdFusion structure nested elements?

I would like to set nested elements inside of the structure. Here is example of my current code:
<cfset fnResults = StructNew()>
<cfset dateList = "HD_DATE1,HD_DATE2,HD_DATE3,HD_DATE4" />
<cfset servicesEquipment = {
1="Strongly Agree",
2="Agree",
3="Don't Know",
4="Disagree",
5="Strongly Disagree"
}>
<cfset isActive = {
1="Yes ",
0="No "
}>
<cfquery name="UserInfo" datasource="TestDB">
SELECT TOP 1
hd_yn1,
hd_active,
hd_date1,
hd_status,
hd_age1,
hd_date2,
hd_age2,
hd_date3,
hd_date4,
hd_age3,
hd_deg1,
hd_deg2,
hd_toner,
hd_cfgr,
hd_deg4,
hd_deg5,
hd_tonel,
hd_cfgl,
hd_hri,
hd_comment,
hd_rear,
hd_lear,
hd_tosound,
LTRIM(RTRIM(si_last)) + ', ' + LTRIM(RTRIM(si_first)) AS hd_staff,
(SELECT TOP 1 tm_name FROM hmMaster WHERE tm_tblid = 'HD_YN1' AND tm_code = hd_yn1) AS zhd_yn1,
(SELECT TOP 1 tm_name FROM hmMaster WHERE tm_tblid = 'HD_STATUS' AND tm_code = hd_status) AS zhd_status,
(SELECT TOP 1 tm_name FROM hmMaster WHERE tm_tblid = 'HD_AGE' AND tm_code = hd_age1) AS zhd_age1,
(SELECT TOP 1 tm_name FROM hmMaster WHERE tm_tblid = 'HD_AGE' AND tm_code = hd_age2) AS zhd_age2,
(SELECT TOP 1 tm_name FROM hmMaster WHERE tm_tblid = 'HD_AGE' AND tm_code = hd_age3) AS zhd_age3,
(SELECT TOP 1 tm_name FROM hmMaster WHERE tm_tblid = 'HEAR_IND' AND tm_code = hd_hri) AS zhd_hri,
(SELECT TOP 1 tm_name FROM hmMaster WHERE tm_tblid = 'HEAR_LOSS' AND tm_code = hd_deg1) AS zhd_deg1,
(SELECT TOP 1 tm_name FROM hmMaster WHERE tm_tblid = 'HEAR_LOSS' AND tm_code = hd_deg4) AS zhd_deg4,
(SELECT TOP 1 tm_name FROM hmMaster WHERE tm_tblid = 'HEAR_TYPE' AND tm_code = hd_deg2) AS zhd_deg2,
(SELECT TOP 1 tm_name FROM hmMaster WHERE tm_tblid = 'HEAR_TYPE' AND tm_code = hd_deg5) AS zhd_deg5,
(SELECT TOP 1 tm_name FROM hmMaster WHERE tm_tblid = 'HD_TONE' AND tm_code = hd_toner) AS zhd_toner,
(SELECT TOP 1 tm_name FROM hmMaster WHERE tm_tblid = 'HD_TONE' AND tm_code = hd_tonel) AS zhd_tonel,
(SELECT TOP 1 tm_name FROM hmMaster WHERE tm_tblid = 'HD_CFG' AND tm_code = hd_cfgr) AS zhd_cfgr,
(SELECT TOP 1 tm_name FROM hmMaster WHERE tm_tblid = 'HD_CFG' AND tm_code = hd_cfgl) AS zhd_cfgl
FROM userRec WITH (NOLOCK)
LEFT OUTER JOIN staffInfo
ON si_staff = hd_staff
WHERE hd_userid = '10051989'
</cfquery>
<cfset fnResults.recordcount = UserInfo.recordcount>
<cfif UserInfo.recordcount EQ 0>
<cfset fnResults.message = "No records were found.">
<cfelse>
<cfloop query="UserInfo">
<cfset qryRecs = StructNew()>
<cfloop array="#UserInfo.getColumnList()#" index="columnName">
<cfif listContains(dateList, columnName, ",")>
<cfset qryRecs[columnName] = URLEncodedFormat(Trim(DateFormat(UserInfo[columnName][CurrentRow],'mm/dd/yyyy')))>
<cfelseif columnName EQ 'hd_active'>
<cfset qryRecs[columnName] = URLEncodedFormat((structKeyExists(isActive, LossInfo[columnName][CurrentRow]))? isActive[UserInfo[columnName][CurrentRow]]:"No ")>
<cfelseif columnName EQ 'hd_tosound'>
<cfset qryRecs[columnName] = URLEncodedFormat((structKeyExists(servicesEquipment, UserInfo[columnName][CurrentRow]))? servicesEquipment[UserInfo[columnName][CurrentRow]]:"")>
<cfelse>
<cfset qryRecs[columnName] = URLEncodedFormat(Trim(UserInfo[columnName][CurrentRow]))>
</cfif>
</cfloop>
</cfloop>
<cfset fnResults.data = qryRecs>
</cfif>
<cfdump var="#fnResults#">
Code above use some logic to manipulate the data. Here is example of my output after I dump fncResults:
DATA
struct
HD_ACTIVE No%20
HD_AGE1 [empty string]
HD_AGE2 [empty string]
HD_AGE3 36
HD_CFGL MMO
HD_CFGR MMO
HD_COMMENT Test
HD_DATE1 09%2F22%2F1993
HD_DATE2 [empty string]
HD_DATE3 [empty string]
HD_DATE4 [empty string]
HD_DEG1 II
HD_DEG2 MM
HD_DEG4 MM
HD_DEG5 UU
HD_HRI NN
HD_LEAR [empty string]
HD_REAR [empty string]
HD_STAFF [empty string]
HD_STATUS PESS
HD_TONEL ALL
HD_TONER ALL
HD_TOSOUND [empty string]
HD_YN1 Y
ZHD_AGE1 [empty string]
ZHD_AGE2 [empty string]
ZHD_AGE3 36
ZHD_CFGL MIDDLE
ZHD_CFGR Mild
ZHD_DEG1 Mild
ZHD_DEG2 Unknown
ZHD_DEG4 Mild
ZHD_DEG5 Unknown
ZHD_HRI None
ZHD_STATUS Maybe
ZHD_TONEL All
ZHD_TONER All
ZHD_YN1 Did Not
RECORDCOUNT 1
On the front end I have to set all fields that have letter 'z' in front of 'hs' to be my data for title attribute. Because of that I want to organize my structure to look like this:
DATA
struct
HD_ACTIVE Value: No%20 Title: This is test
HD_AGE3 Value: 36 Title: Years
HD_COMMENT Value: Test Title: Test
As a side note I have tried to set nested variables in my structure. Something like this: <cfset qryRecs[columnName].value =UserInfo[columnName][CurrentRow]> This throw an error:
Element HD_YN1 is undefined in a CFML structure referenced as part of an expression.
I'm not sure if I need one more structure in order to achive that. Also I'm not sure what is the best way to organize this kind of structure. If you have any suggestions or examples please let me know . Thank you!
This code snippet is not a complete answer but should help you in the nesting structs with the Value/Title keys.
<!--- in order to nest structs, the 'nesting' parent structs must be created before assigning values to them --->
<cfif NOT structKeyExists(qryRecs, "columnName")>
<cfset qryRecs[columnName] = structNew()>
</cfif>
<!--- qryRecs[columnName] now exists as a struct so 'Value', 'Title', etc. keys can be added to it --->
<cfset qryRecs[columnName].value = UserInfo[columnName][CurrentRow]>
All the credit to #Scott-Jibben for explaining the issue in the first place. I'm posting this for a CFScript solution.
if( !structkeyexists( application, "testStruct" ) ){
dump('application.testStruct does not exist;');
application.testStruct = structNew(); // this line is key
} else {
dump('application.testStruct exists - no error;');
}
if( !structkeyexists( application.testStruct, "keyName2" ) ){
// you should only see this if you failed to include the StructNew() line above;
dump('application.testStruct.keyName2 does not exist and could produce an error;');
} else {
dump('application.testStruct.keyName2 exist - no error;');
}
For added context, I needed to load some variables into the Application scope. If the variable existed, then a database query was skipped. If it was missing, a query was needed to load site settings. The nested structure would throw an error on the first hit of the page after the web server or service was restarted. After reading Scott's solution defining the nested structure, the app is working as expected after a restart.

Set LIMIT with doctrine 2?

I trying to write a query (with subquery) but i don't know how set a limit in my subquery.
My query:
$query_ids = $this->getEntityManager()
->createQuery(
"SELECT e_.id
FROM MuzichCoreBundle:Element e_
WHERE [...]
GROUP BY e_.id")
->setMaxResults(5)
;
$query_select = "SELECT e
FROM MuzichCoreBundle:Element e
WHERE e.id IN (".$query_ids->getDql().")
ORDER BY e.created DESC, e.name DESC"
;
$query = $this->getEntityManager()
->createQuery($query_select)
->setParameters($params)
;
But ->setMaxResults(5) doesn't work. No 'LIMIT' in the SQL query. Can we do simple LIMIT with doctrine 2 ?
$query_ids = $this->getEntityManager()
->createQuery(
"SELECT e_.id
FROM MuzichCoreBundle:Element e_
WHERE [...]
GROUP BY e_.id")
->setMaxResults(5)
->setMaxResults($limit)
;
HERE in the second query the result of the first query should be passed ..
$query_select = "SELECT e
FROM MuzichCoreBundle:Element e
WHERE e.id IN (".$query_ids->getResult().")
ORDER BY e.created DESC, e.name DESC"
;
$query = $this->getEntityManager()
->createQuery($query_select)
->setParameters($params)
->setMaxResults($limit);
;
$resultCollection = $query->getResult();
I use Doctrine\ORM\Tools\Pagination\Paginator for this, and it works perfectly (doctrine 2.2).
$dql = "SELECT p, c FROM BlogPost p JOIN p.comments c";
$query = $entityManager->createQuery($dql)
->setFirstResult(0)
->setMaxResults(10);
$paginator = new Paginator($query, $fetchJoinCollection = true);
Your setMaxResults($limit) needs to be set on the object.
e.g.
$query_ids = $this->getEntityManager()
->createQuery(
"SELECT e_.id
FROM MuzichCoreBundle:Element e_
WHERE [...]
GROUP BY e_.id")
;
$query_ids->setMaxResults($limit);
$limit=5; // for exemple
$query = $this->getDoctrine()->getEntityManager()->createQuery(
'// your request')
->setMaxResults($limit);
$results = $query->getResult();
// Done
$qb = $this->getDoctrine()->getManager()->createQueryBuilder();
$qb->select('p') ->from('Pandora\UserBundle\Entity\PhoneNumber', 'p');
$qb->where('p.number = :number');
$qb->OrWhere('p.validatedNumber=:number');
$qb->setMaxResults(1);
$qb->setParameter('number',$postParams['From'] );
$result = $qb->getQuery()->getResult();
$data=$result[0];

is it possible to dynamically create a query and escape the values too using cfscript+cfquery+cfqueryparam?

I'm still new to ColdFusion. Basically I am dynamically creating a query for Oracle. I have used cfquery/cfparam in the past but I would really rather use cfscript to accomplish as that is more readable. It is intended to be a large 'INSERT ALL ... INTO.'
Here's a basic example of what I have so far:
<cfscript>
clinicNIL = structNew();
clinicNIL.ADDRESS1 = 'line 1';
clinicNIL.ADDRESS2 = 'line 2';
myFields = [
'ADDRESS1'
,'ADDRESS2'
];
query = queryNew("");
sql = "INSERT ALL";
for (i=1; i LTE ArrayLen(myFields); i=i+1) {
sql = sql & "INTO NOTINLIST (SOURCETABLE, SOURCECOLUMN, SOURCEPK, ENTEREDVALUE, INSERTDATE, UPDATEDDATE, INSERTEDBY, UPDATEDBY) VALUES(";
// [..]
// How to dynamically escape the value below?
sql = sql & EscapeTheParameterHere( clinicNIL[ myFields[i] ]);
// [..]
sql = sql & ")
";
}
WriteOutput( query );
</cfscript>
Where I have 'EscapeTheParameterHere' I want to be able to have that value escaped somehow. how can I escape the value?
while I'm here, is there any good resources or references for CF?
You can bind parameters using the addParam function of a cfscript query object just like cfqueryparam works. Had to convert your example a bit to work on my MSSQL box and a smaller version of your table but it should give you the general idea.
<cfscript>
clinicNIL = structNew();
clinicNIL.ADDRESS1 = 'line 1';
clinicNIL.ADDRESS2 = 'line 2';
myFields = [
'ADDRESS1'
,'ADDRESS2'
];
query = new query();
//you may need to use the query methods setDatasource, setUsername and setPassword to configure the query
//sql = "INSERT ALL" & chr(13) & chr(10);
sql = "";
for (i=1; i LTE ArrayLen(myFields); i=i+1) {
query.addParam(name="address"&i,value=clinicNIL[ myFields[i] ],cfsqltype="VARCHAR");
sql = sql & "INSERT INTO NOTINLIST (ADDRESS) VALUES(";
sql = sql & ":address" & i;
sql = sql & ")" & chr(13) & chr(10);
}
queryResult = query.execute(sql=sql);
</cfscript>
The magic is the :paramName in the sql string will have it's associated parameter replaced during the execute call with a properly escaped parameter.
here is the solution I came up with using cfquery/cfqueryparam. I didn't realize you could do a cfloop inside of a cfquery. By the way, I did find something called 'CF.Query' but apparently it only satisfies a subset of cfquery.
<cfscript>
clinicNIL = structNew();
clinicNIL.ADDRESS1 = 'line 1';
clinicNIL.ADDRESS2 = 'line 2';
myFields = [
'ADDRESS1'
,'ADDRESS2'
];
totalFields = ArrayLen(myFields);
</cfscript>
<cfquery name="insert" datasource="somedatasource">
INSERT ALL
<cfloop from="1" to="#totalFields#" index="i">
INTO NOTINLIST
(SOURCETABLE, SOURCEPK, SOURCECOLUMN, ENTEREDVALUE, INSERTDATE, UPDATEDATE, INSERTEDBY, UPDATEDBY)
VALUES(
'FACULTYADDRESSES'
, 123
, <cfqueryparam value = "#myFields[i]#" cfsqltype='CF_SQL_VARCHAR'>
, <cfqueryparam value = "#clinicNIL[ myFields[i] ]#" cfsqltype='CF_SQL_VARCHAR'>
, SYSDATE
, SYSDATE
, '123'
, '123'
)
</cfloop>
SELECT * FROM DUAL
</cfquery>