I'm doing the following query of queries. The first dump of the query shows the entire excel sheet, the second dump shows the results of the second query.
I'm doing a validation check of the excel sheet to make sure there are no more pit bulls breeds brought into the system and I need to be able to tell the user which row on the excel sheet has the pit-bull.
How do I get the row numbers from the first row to appear in the second row? So the user can make changes on the excel sheet.
Do I have to resort to editing the excel document when it was first uploaded into the server and add a row number column to it? There is probably a better way to accomplish this.
<cffunction name="validateExcelSheet" access="public" output="yes" returnType="void"
hint="check dogs">
<cfspreadsheet
action="read"
src="#SESSION.theFile#"
headerrow= "1"
excludeHeaderRow = "true"
query = "allData"
rows = "1-#lastRow#" />
<cfscript>
pitBullcheck = new Query(
sql ="SELECT * FROM allData where breed like 'Pit%' ",
dbtype = "query",
allData = allData);
pitBullresult = pitBullcheck.execute().getResult();
</cfscript>
</cffunction>
Here's a tag based version of cfquery
<cfquery name="pitBullresult" dbtype="query">
SELECT *
FROM allData
WHERE breed LIKE 'Pit'
</cfquery>
That is not something you can do with cfspreadsheet. CFSpreadsheet only returns the cell values. It does not provide the physical row numbers, within the spreadsheet, that contained those values.
Also, something else to keep in mind is that CFSpreadsheet only returns "logical" (ie populated) rows/cells. That is not the same as the "physical" row numbers 1,2,3,... and column headers A,B.C.... that you see in Excel. Since users can enter values anywhere within a spreadsheet, logical and physical are not always the same thing.
For example, create a blank spreadsheet. Then enter values in cells
A2 and A25. Now run your code above. While you might expect the
resulting query to contain twenty-five (25) records, it will only
contain two (2), because only two cells were populated.
query
Row | COL_1
1 | Value in cell A2 (physical row 2)
2 | Value in cell 25 (physical row 25)
I think best you could do with cfspreadsheet is to loop through the query and display the relative row number within the results ie query.currentRow. If the populated data always starts within the first few rows, that might be good enough for your purposes. If not, it could get a bit confusing ...
That said, technically you could get the real physical row numbers. However, it requires much lower level code, which quite honestly ... seems like a lot of work, for very little gain.
Related
Copy and Filter out selected fields from 2 tabs in the Same Gsheet to another Tab with Fraud = 'Yes'
I have 2 sets Tabs in the Same Gsheet with different information. I would like to copy them into another tab with Fraud ="Yes". I have an example with formulas in Combine Example from Stall A Example and Stall B Example with some help previously. When I tried to replicated into my actual data (Combine NSU and ACH ) set I can't do it.
Can someone please help and guide on this.
https://docs.google.com/spreadsheets/d/1N35wUB-a7hDHFTzdhajlaCTJf_Ce34Ql3Miwq51JCqY/edit?usp=sharing
Whenever it is Fraud = "Yes", it extracts the necessary information from NSU Tab and ACH Tab with has Fraud = Yes into Combine NSU and ACH Tab
#=SORT(
LAMBDA(DATA,
LAMBDA(DATE,TYPE,AMOUNT,FRAUD,ID,ERP
FILTER({TEXT(DATE,"dd/mm/yyyy"),TYPE,AMOUNT,FRAUD,ID,ERP},FRAUD="YES")
)(INDEX(DATA,,1),INDEX(DATA,,2),INDEX(DATA,,8),INDEX(DATA,,13),INDEX(DATA,,15),INDEX(DATA,,16))
)({'NSU'!A2:P27;{'ACH'!A2:A8, 'ACH'!B2:B8,'ACH'!N2:N8,'ACH'!L2:L8,'ACH'!F2:F8,'ACH'!H2:H8 }})
,1,TRUE)
Code:
Output:
Get all data of sheet NSU and ASH, and re-arrange their orders by QUERY.
Group them up with another QUERY to filter the data you want such as Col4 = 'Yes'.
All the formating and sort can also be done with query.
=ArrayFormula(
LAMBDA(NSU,ACH,
QUERY({NSU;ACH},
" WHERE Col1 IS NOT NULL "
&" AND Col4 = 'Yes' "
&" ORDER BY Col1 ASC"
&" LABEL Col1 'Date',Col2 'Type',Col3 'Amount',Col4 'Fraud',Col5 'ID',Col6 'ERP' "
&" FORMAT Col1 'yyyy-mm-dd' "
)
)(
LAMBDA(COLS,
QUERY({NSU!$A:$P},
" SELECT "&JOIN(",","Col"&COLS)
&" LABEL "&JOIN(",","Col"&COLS&" '"&REPT(" ",COLS)&"'"),1)
)({1,2,8,13,15,16}),
LAMBDA(COLS,
QUERY({ACH!$A:$N},
" SELECT "&JOIN(",","Col"&COLS)
&" LABEL "&JOIN(",","Col"&COLS&" '"&REPT(" ",COLS)&"'"),1)
)({1,2,14,12,6,8})
)
)
Well, in-case you really want to have the ability to add some input fields in-between the output QUERY.
The answer is you can't, but also you can.
Basically, you cannot insert anything in-between any kind of array outputs in google spreadsheets, the array-formula will return an error '#ref' mentioning that there are other values inside the output range which fail it from showing the output, BUT...
you can always get around this issue by simply seperate the output array by an other QUERY, such as:
This simple array in google sheet will reference range A1:C10 and place the data into where-ever you type this formula into, which output a 10 rows by 3 columns array.
={A1:C10}
We assumne that you put this in cell 'E1', that makes the output covers range 'E1:G10'.
If you what to have a column of fields which allow you to input new data in-between the output range, for example, you want to add a new column in F:F.
In that case, you can put 2 formula seperatly into cell 'E1' and cell 'G1', which contains the following formulas:
in cell 'E1':
=QUERY({A1:C10},"SELECT Col1")
in cell 'G1':
=QUERY({A1:C10},"SELECT Col2,Col3")
Since the output arrays are seperated, that makes the column(s) between the column E and column G a normal empty column, which allows you to input anything into the cells.
The beauty of QUERY function is that you can select any column(s) of a given reference array as an output.
In this case the output data will be seperated into 2 parts, one contains only the first column of the reference, the second one contains the rest of them.
It doesn't matter how many empty columns you insert between the 2 outputs since they are 2 outputs of one identical reference.
The draw back is, if your reference data are results of calculations, the sheet will have to do all those calculations two times, even if they gives the same results, which is why I say this can and will slow things down and are not very recommended.
If an object, such as an Array or Struct is used as the column value of a row in a CF query object. Can properties of that object be used in the WHERE clause of a query of queries to limit the result set?
Given:
<cfset local.exampleArray=[
{ id:1,
nestedArray:["Tom","Dick","Harry"],
nestedStruct:{nid:42,name:"unknown"}
},
{ id:2,
nestedArray:["John","Paul","Ringo","George"],
nestedStruct:{nid:12,name:"rockstars"}
},
{ id:3,
nestedArray:["Bonny","Clyde"],
nestedStruct:{nid:43,name:"criminals"}
},
]>
<cfset local.exampleQuery=queryNew("id,nestedArray,nestedStruct","integer,object,object",local.exampleArray)>
The queries of queries:
<cfquery dbtype="query" name="local.exampleQoQ">
SELECT *
FROM [local].exampleQuery
WHERE nestedStruct.nid=12
</cfquery>
<cfquery dbtype="query" name="local.exampleQoQ2">
SELECT *
FROM [local].exampleQuery
WHERE nestedArray.length=3
</cfquery>
Results in the query of queries runtime error:
nestedStruct.nid/nestedArray.length does not match any table in FROM table list
When not using the object type columns in the WHERE clause, the objects are returned correctly when queried and behave as expected:
<cfquery dbtype="query" name="local.exampleQoQ">
SELECT *
FROM [local].exampleQuery
WHERE id=1
</cfquery>
<cfoutput query="local.exampleQoQ">
#local.exampleQoQ.id#:#ArrayLen(local.exampleQoQ.nestedArray)#:#local.exampleQoQ.nestedStruct.nid#
</cfoutput>
Will result in "1:3:42"
Is this just an issue where the QoQ implementation doesn't support accessing the properties of a column value object?
As I mentioned earlier, a database query can have a column with array/structure-ish data, but that's not really what a database is for. As you've seen, it makes querying for the data you want more difficult than it should be, and is really treating a database as little more than a place to store data.
Anyway, you seem to want to filter your query records by a specific value that's contained inside one column's structure data and also filter those results if another columns array data contains a certain number of records.
You don't want Query of Query for this. It's already a highly limited "query" aspect of CF, and should be used only when necessary. If you are using ColdFusion 2016+, you can use a function that was added: queryFilter().
Using your above setup under "Given:", you can use the following:
<cfscript>
/* Instead of QoQ, limit your Query with queryFilter() */
filteredQuery = queryFilter( exampleQuery
,function(o){ return o.nestedStruct.NID == 12 ;
}
) ;
</cfscript>
Which will give you a variable filteredQuery that contains:
Then you can just address filteredQuery.nestedArray to get your array of "John, Paul, George and Ringo".
But you also want to filter for the array in nestedArray to be 3 elements. So you can just add another condition to your callback return:
local.filteredQueryForLength = queryFilter(
local.exampleQuery2,
function(o){ return o.nestedStruct.NID == 12 && arrayLen(o.nestedArray) == 3 ; }
) ;
Which then gives you an empty Query Object, since there are 4 elements to the filteredQuery.nestedArray that you selected.
Finally, queryFilter has a member function that is simply filter(), so you can be even shorter and use this:
local.filteredQueryForLength2 = local.exampleQuery3.filter(
function(o){ return o.nestedStruct.NID == 12 && o.nestedArray.len() == 3 ; }
) ;
Also remember that ColdFusion Query Objects are Pass-By-Reference, so if you do anything (like filter()) that modifies the object, it will change that base object so it will be different if you use it again. Which also means that you don't have to assign it to a variable. You can just call queryFilter and then reference your original query object.
And another note: when using CF Script syntax (which I much prefer), don't forget that=is assignment and==is comparison. I forgot that initially and all of the records were returning withnestedStruct.NIDas12`. :-/
Last note: I created a Fiddle at https://trycf.com/gist/031a090059a46cd471aa44627fc7ee12/acf2016?theme=monokai. I added one extra element to your mocked query, so that you could see what your return object looks like with multiple elements matching the filters.
Take the following query example:
<cfquery name="Test" Datasource = "TestDB">
Select * from Table_Test
</cfquery>
Assume that the "Test" query returns 10 rows. I want to show single row on current time.
Note: I do not want to change the SQL statement.
If you know your row number, Test.columnName[RowNumber] will show you the value of the columnName in specified row number.
If you want one random row from the query:
<cfset start = randRange(1, Test.recordCount)>
<cfoutput>
#Test.name[start]# #Test.email[start]#<br>
</cfoutput>
No need to loop.
NOTE: It is more efficient to modify the query to get a random row.
How to request a random row in SQL?
Using CF10 Standard to create a spreadsheet from a query. No matter what I've tried so far, the formatting for a specific column stops at row 32 (1 header row, 31 data), even though the entire sheet is populated to 186 rows.
<cfscript>
dfStyle=StructNew();
dfStyle.fgcolor="pale_blue";
dfStyle.dataformat="mm/dd/yyyy";
theSheet = SpreadSheetNew('mysheet');
SpreadSheetAddRow(theSheet,'SID,FIRST,LAST,ADDRESS1,ADDRESS2,CITY,STATE,ZIP,EMAIL,ADDED,PID');
SpreadSheetAddRows(theSheet,qry);
SpreadSheetFormatColumn(theSheet,dfStyle,10);
</cfscript>
I'm trying to get a consistent mm/dd/yyyy format on the 'ADDED' column. Instead, I'm getting that through row 31, and then getting dates like 41937.56594 on the of the rows.
In the formula bar I show "10/20/2014 12:25:23 PM" as the first value and "41932.552037037" as the 2nd value.
If I format the date in the query (i.e. date_format(sp_add,'%c/%e/%Y') AS spadd) I do get a nice date format all the way down the column, but the blue still stops at row 32.
Here is the cfoutput of the anonymized query - top row is formatted, bottom row loses formatting (background color - formatting dates in query). I also dropped the ADDRESS2 column from the query for now.
I'm not sure what the problem is, but try using the XML format option (the option to produce an xlsx file instead of the older style xls file). In your code add the "true" as a second argument to your spreadsheetnew() function call.
<cfscript>
dfStyle=StructNew();
dfStyle.fgcolor="pale_blue";
dfStyle.dataformat="mm/dd/yyyy";
theSheet = SpreadSheetNew('mysheet',TRUE);
SpreadSheetAddRow(theSheet,'SID,FIRST,LAST,ADDRESS1,ADDRESS2,CITY,STATE,ZIP,EMAIL,ADDED,PID');
SpreadSheetAddRows(theSheet,qry);
SpreadSheetFormatColumn(theSheet,dfStyle,10);
</cfscript>
This will solve the problem - though we don't know why :)
I have a column name and, for the sake of SpreadsheetSetCellFormula, I want to get the index of that column from its name. This is important for expandability, as columns may be added or taken away in the future.
When I use queryName.ColumnList, ColdFusion automatically alphabetizes the list. However, passing it into SpreadsheetAddRows dumps the columns in original order. How can I get the index of a column from its name?
<!--- get the column list, in the original order, as a coldfusion compatible array --->
<cfset variables.columnArray = createObject("java","java.util.Vector").init(
createObject("java","java.util.Arrays").asList(
query.getColumnList()
)
)/>
<!--- get the index of the column. note that this is case sensitive. --->
<cfset variables.myColumnIndex = variables.columnArray.indexOf("MY_COLUMN")/>
No looping required.
This doesn't directly answer your question, however this may be VERY useful in your situation (I have used it in a similar situation)
This is taken from:
http://existdissolve.com/2010/11/quick-coldfusion-goodness/
columns = arrayToList(myquery.getMeta().getcolumnlabels())
gives you a list of the columns in their original order, with their original case sensitivity (not all upper case)
You can use the getMetaData() function, which you can read about here. This function will return an array of Columns in the correct order, including some other information. You would use it as follows:
getMetaData(queryName)