CFspreadsheet looping - excel

I have a question regarding cfspreadsheet....So I'm using cfspreadshseet to create excel spreadsheets for reporting purposes. My page allows a user to select whatever columns from the database to include in the report. So here is an example:
The spreadsheet could look like this:
First Name---Last Name---Organization---Address---City---State---Zip---Concern
Joe Smith Sample 12 main denver co 80513 concerns go here
My question is this, if Joe has more than 1 concern I get multiple rows with joe's info...is there a way I can loop the concerns and only have 1 row for joe?
Thanks,
Steve

Using the "group" feature of cfoutput is perfectly fine for this task. Just to throw out another possibility, you could also generate the list within your database query.
For example, MySQL has the GROUP_CONCAT function. I do not know the structure of your tables, but say you have two tables User and UserConcern, you could use GROUP_CONCAT like so to concatenate the "Concern" values into a single string:
SQLFiddle
SELECT
u.UserID
, u.FirstName
, ... other columns
, GROUP_CONCAT( uc.Concern ) AS ConcernList
FROM UserTable u INNER JOIN UserConcern uc
ON uc.UserID = u.UserID
GROUP BY
u.UserID
, u.FirstName
, ... other columns
For SQL Server, a standard trick is to use XML Path:
SQLFiddle
SELECT
u.UserID
, u.FirstName
, ... other columns
, STUFF( ( SELECT ',' + uc.Concern
FROM UserConcern uc
WHERE uc.UserID = u.UserID
ORDER BY uc.Concern
FOR XML PATH('')
)
, 1, 1, ''
) AS ConcernList
FROM UserTable u
GROUP BY
u.UserID
, u.FirstName
, ... other columns
Then simply generate your spreadsheet as usual.

You need a unique row ID to do this safest, the outer group working with lastname, or something, could cause conflict. UserID is a placeholder variable. Make sure to replace it with an accurate ID name. Of course, a few of these variable names are just guessed.
<cfoutput query ="thequery" group="UserID">
<cfset cList="">
<cfoutput group="concern">
<cfset cList=ListAppend(cList,Concern)>
</cfoutput>
<cfset temp = spreadsheetAddRow(my_spreadsheet,"'#fn#','#ln#',...,'#cList#'">
</cfoutput>

Assuming you want separate lines for each comment, something like this would work:
<cfset current_id = "">
<cfloop query = "my_query">
<cfset next_id = user_id>
<!--- or whatever else forms the primary key --->
<cfif next_id neq current_id>
<cfset current_id = next_id>
<cfset SpreadsheetAddRow(my_spreadsheet, "#first_name#,#last_name#,etc, #comment#">
<cfelse>
<cfset SpreadsheetAddRow(my_spreadsheet, ",,#comment#">
</cfif>
</cfloop>
This is based on the information provided. If you have a unique ID the group attribute would work better.

Related

using continue on a loop over a query with the group attribute breaks the loop

The goal is to use cfcontinue within a nested loop over a query that uses the group attribute.
The expected result is that the loop will continue with the next iteration after calling cfcontinue.
The actual result is that calling cfcontinue behaves like cfbreak. It will stop the inner loop completely and move on the the next iteration in the outer loop.
The situation is as follows:
I have a query result containing something like the following:
testQuery = queryNew( "id , LOJVal" , "numeric , varchar" ,
{
id: [ 1, 1, 1, 2, 2, 2, 3, 3, 3 ] ,
LOJVal: [ "val1" , "", "","val1" , "val2", "val3","val1" , "", "val3" ]
});
this query is a simplification of the result from a query like:
SELECT table.id, leftOuterJoinTable.LOJVal
FROM table
LEFT OUTER JOIN leftOuterJoinTable ON leftOuterJoinTable.id = table.id
where leftOuterJoinTable can contain multiple records for one record in table
when processing this query we want to loop over the records and group by id. We also want to validate the records, and if the record is not valid, we want to continue to the next. This looks something like the following:
<cfset var resultArr = []>
<cfset var outerLoopArr = [1,2,3]>
<cfset var outerLoopVal = "">
<cfloop array="#outerLoopArr#" item="outerLoopVal">
<cfloop query="testQuery" group="id">
<!--- this should iterate once per id --->
<cfset var newVal = {}>
<writeOutput>test</writeOutput>
<cfif true><!--- record invalid --->
<cfcontinue>
</cfif>
<!--- record valid --->
<cfloop>
<!--- do stuff with LOJVal --->
</cfloop>
<cfset arrayAppend(resultArr, newVal)>
</cfloop>
</cfloop>
this code outputs the following:
test test test
yet the expected output is: test test test test test test test test test
for every iteration in the outer loop (3 iterations total) we have 3 iterations in the inner loop (one per group on id) so we'd expect 9 ''test''
When we remove the group attribute of the inner loop, the code behaves as expected: 3 * 9=27 * "test"
At first I thought maybe continue just triggers on the outer loop, so I added a label to the inner loop and continue like so:
<cfset var resultArr = []>
<cfset var outerLoopArr = [1,2,3]>
<cfset var outerLoopVal = "">
<cfloop array="#outerLoopArr#" item="outerLoopVal">
<cfloop query="testQuery" group="id" label="innerloop">
<!--- this should iterate once per id --->
<cfset var newVal = {}>
<writeOutput>test</writeOutput>
<cfif true><!--- record invalid --->
<cfcontinue innerloop>
</cfif>
<!--- record valid --->
<cfloop>
<!--- do stuff with LOJVal --->
</cfloop>
<cfset arrayAppend(resultArr, newVal)>
</cfloop>
</cfloop>
as can be found in the docs https://docs.lucee.org/reference/tags/continue.html. But this doesn't seem to work eiter.
The only thing we can think of that could cause this is that continue will go to index + 1, but that doesnt exist because its grouped, so the next index would be + 3. And then the loop would be considered done because the index doesn't exist.
I haven't found any other information about this.

Copy one field to another: "Subquery returns more than 1 row"

here we have the query:
UPDATE ps_product_lang_copy
SET description =
( SELECT description
FROM ( SELECT description
FROM `ps_product_lang_copy`
WHERE id_lang = 5
) t
)
WHERE id_lang IN (1,2,3,4);
I'm trying to do this thing:
i've a field on a table, called 'description'
every row in this table has an 'id_lang'
i wish to copy, for every row and not only one, the field 'description' from id_lang = 5 to 'description' of others rows with id_lang IN (1,2,3,4) but the system, with that query told me: Subquery returns more than 1 row
i read a lot here on stack overflow but really can't fix my code :(
EDIT:
SOLVED by myself, i'll post here to help each others
UPDATE ps_product_lang
INNER JOIN ps_product_lang_copy
ON ps_product_lang.id_product = ps_product_lang_copy.id_product
SET ps_product_lang.description = ps_product_lang_copy.description
WHERE ps_product_lang_copy.id_lang = 5
ciuss
SOLVED
UPDATE ps_product_lang
INNER JOIN ps_product_lang_copy
ON ps_product_lang.id_product = ps_product_lang_copy.id_product
SET ps_product_lang.description = ps_product_lang_copy.description
WHERE ps_product_lang_copy.id_lang = 5

ColdFusion: Object with duplicate values (removing duplicates)

I have a query object (SQL) with some records, the problem is that some of the records contain duplicate values. :( (I can't use DISTINCT in my SQL Query, so how to remove in my object?)
categories[1].id = 1
categories[2].id = 1
categories[3].id = 2
categories[4].id = 3
categories[5].id = 2
Now I want to get a list with 1, 2, 3
Is that possible?
I'm not quite sure why you say you can't use DISTINCT, even given the qualification you offered. It doesn't matter were a query came from (<cfquery>, <cfldap>, <cfdirectory>, built by hand) by the time it's exposed to your CFML code, it's just "a query", so you can definitely use DISTINCT on it:
<cfquery name="distinctCategories" dbtype="query">
SELECT DISTINCT id
FROM categories
</cfquery>

how to select only one particular field from model in zend framework

How to select only one field from table inside a model.
For example: table1(field1, field2 , ..) and the select only field 1
You want only one value or a rowset with one column?
In first case, you can use fetchOne():
$result = $db->fetchOne('SELECT bug_status FROM bugs WHERE bug_id = 2');
Or you can make a select only with the fields you want to retreive:
$select = $db->select()
->from(array('t' => 'table1'),
array('field1'));

How to reference one foreign key column with multiple primary key column

I am creating BOOK_Issue table which will contain id of person to whom the book is issued.
i have a column name user_id witch will contain ids from tbl_student as well as tbl_faculty. so how to set user_id field of book_issue table with reference to two primary key columns.
Your database schema is not correct.
If you expect unique IDs then they should be in one table.
You can create a table with all the users, and have a column to set their type (student, faculty). Then create 2 different tables for each type that has the proper information for each user based on their type.
Create a "person" superclass that can be either of type "student" or type "faculty". Reference this from the BOOK_Issue table instead.
Basically to create this relationship, you'll need one unique ID that spans both "student" and "faculty". Put this in a table (tbl_person?) and have each row in tbl_student and tbl_faculty reference this new table. It's probably also best to then pull out the fields present in both tbl_student and tbl_faculty and put them in this new supertable instead.
You can solve the problem by either having an extra column in BOOK_Issue table, next to user_id, which indicates if this is a Student ID or a Faculty ID.
Alternatively, the IDs themselves may readily include some pattern which indicate their nature (for example no all faculty Ids may start with say "UC", and none of the student Id are so).
The two solutions above then allow using queries similar to the following
SELECT B.*,
CASE B.BorrowerType -- could be LEFT(user_id, 2) etc...
WHEN 'S' THEN S.Name
WHEN 'F' Then F.Name
END As Name,
CASE B.BorrowerType
WHEN 'S' THEN S.PhoneNumber
WHEN 'F' Then F.Phone -- Note that these constructs allow
-- mapping distinct columns names etc.
END As PhoneNr
FROM BOOK_Issue B
LEFT JOIN tbl_student S ON B.BorrowerType = 'S' AND B.user_id = S.id
LEFT JOIN tbl_faculty F ON B.BorrowerType = 'F' AND B.user_id = F.id
WHERE B.DueDate < '11/23/2009' -- or some other condition
This can get a bit heavy when we need to get multiple columns from the student/faculty tables. A possible alternative is a UNION, but this would then cause the repeating of the search clause.
Finally, the best solution but not avaible on all DBMS is a sub-query driven by an "IF B.BorrowerType = 'S' " condition.
This should be your table design:
FacultyTable (FacultyID, FacultyName)
StudentsTable (StudentID, StudentName, FacultlyID, ...)
BookTable (BookID, BookName, ...)
UsersTable(UserID, UserName, UserPassword, StudentID, LastLogin, ...)
Now this is the main thing:
BookIssedTable(BookIssedID, BookID, UserID)
//This table tells me that a book of "BookID was issued to a user of "UserID"
//this can be better for this is certainly a great improvement from the initial design.

Resources