Skip to main content
Inspiring
November 9, 2007
Answered

cfinclude query: return result for specific row

  • November 9, 2007
  • 17 replies
  • 3074 views
I've been trying to crack this all day and I feel like if I just had a nudge in the right direction I might be successful.

I have a query that returns the records for some textbooks that are at a specific school. I've normalized the textbook database so that the primary, secondary and tertiary subject categories are represented by the primary key of that category in its own table. As the query loops through the records, I want it to return the (varchar) subject category that goes with that (numerical) key in the table.

Since Coldfusion won't allow nested cfoutput, I've tried using cfinclude to grab the specific name for the subject category that goes with the current textbook - but what I've got so far returns all the subjects for the school id in each row - they're the right subjects! but I don't want all of them, just the right one with the right book/record. Can anyone suggest what I need to do? I'm attaching the code for the main page and for one of the cfincludes (they're identical except for "primary", "secondary" and "tertiary").

I realize I must be leaving something out but I can't figure out what. Thanks much in advance.

    This topic has been closed for replies.
    Correct answer
    Holy cow, I got it to work! Code attached... and thank you so much, everyone!!! *whew*.

    17 replies

    Inspiring
    November 9, 2007
    Hi William -

    I'm using Access on my workstation, then uploading it to SQL Server 05 on my webspace.

    Okay - new and interesting error:

    [Macromedia][SQLServer JDBC Driver][SQLServer]The multi-part identifier "ts.PrimarySubjectID" could not be bound.

    I thought it said "could not be found" at first and was racking my brains trying to figure out why that would happen :\
    November 9, 2007
    A quick note too, whenever you're using user data in a query, make sure you encapsulate it in a <cfqueryparam> like so:

    WHERE ts.SchoolID = <cfqueryparam cfsqltype="cf_sql_integer" value="#URL.SchoolID#" />

    This has two main benefits - number one is it helps protect against SQL injection or other malicious data, number two it uses a bind parameter which will generally speed performance.
    Inspiring
    November 9, 2007
    William At FAA wrote:
    > Does that make sense?
    >
    > - William

    Yes it makes sense, but my suggestion was that one associates the book
    to the most specific subject. From that relationship it would be
    possible to determine the chain from specific to general subjects to
    which a book belongs.

    To use your example, if one created the singe relationship that a book's
    subject is "Operating Systems" then we also know it belongs to the
    larger "Computer Science" subject and any other subjects up the tree to
    the highest ancestor. Is there a benefit to also including records in
    the book_subject join table describing the relationship between this
    book and the "Computer Science" record and a presumed parent record of
    this subject of say "Engineering"?




    November 9, 2007
    quote:

    Yes it makes sense, but my suggestion was that one associates the book
    to the most specific subject.


    That's a good point - sorry I misinterpreted your original statement. That would be the least redundant way to associate a book to multiple hierachical subjects. However, there would be no way to easily retrieve a book and all its subjects as very few DBMS support recursion. You could of course write three subqueries (if you are assuming you only ever have three levels of subject) to retrieve those subjects, but that removes the scalability from the approach, as more subjects would mean more subqueries.

    The many-to-many relationship also allows the possibility of multiple top-level subjects to be associated to a book (although that is currently beyond the scope of the requirements as I understand them).

    Basically I'm saying either approach would work, although my feeling is the many-to-many approach is easier to read in a scalable fashion without writing some sort of stored procedure.
    November 9, 2007
    Ian, I think I understand your question - you're saying associate a single subject to the book, and then through the relationship we can determine the other subjects?

    I don't see how that would be possible (aside from in my limited example) because in general I would expect each parent subject as multiple child subjects. Such as "Computer Science" might have "Internet and Web," "Desktop Applications," "Operating Systems," and more just to name a few.

    If we associate "Computer Science" to a book, we know the domain of the secondary subject (between those three mentioned), but we do not implicitly know which of those three subjects to associate to the book as a secondary object. Hence, the child relationship can determine the parent subject (as it only has one), but the parent subject alone cannot determine the child subject (since it can have many).

    Does that make sense?

    - William
    Inspiring
    November 9, 2007
    Marianne

    You just exposed a new requirement of your system where the levels of
    categories are hierarchal. I.E. your categories are not independant
    such as a books subject being Science, Einstein, Biography or something.
    Rather they have a category, sub-category relationship such as
    Science, Biology, Roses.

    You may want to look into how you can modify your category table to
    store this relationship so that you do not need your extra tables. A
    common way to do this is have a column in the subjects table that says
    what subject is the higher category (parent) to this subject (child).

    I.E
    SubjectsTable
    ID NAME ParentID
    1 Science
    2 Biology 1
    3 Chemistry 1
    4 Roses 2
    5 Physics 1
    6 Arts

    Now it is fairly easy to get our top level groups, they have no value in
    the parentID column. Also it is easy to get the sub-categories of any
    subject by finding any with that parents subjects ID in the parentID
    column. Like before this allows for any depth of sub-subjects without
    haveing to go in and modify the database or the code.

    Inspiring
    November 9, 2007
    William - thank you, thank you! This is very helpful and I learned a great deal too!

    I do already have a master Subject table but I thought it would be more efficient to link to the individual tables, since I'm planning on creating an interface to allow the teachers to enter new books, with a series of drop-downs to enter the subject by increasingly more precise categories. For example: Primary - Language Arts; Secondary - Reading; Tertiary - Phonics.

    I'm guessing that, incorporating what you've offered as a solution (which does seem a lot better than what I've been trying to do! :D) is to go ahead and use the master Subject table here, and then when I create the interface, use the individual tables, but insert the selection results as a three- (or four-, or five-, in the future) part string - does that seem like it would work?

    Okay - I'm going to go try this out - hold your thumbs!
    November 9, 2007
    Marianne,

    Glad I could help. To meet your requirement it seems like you would add one field to my schema in the Subjects table:

    parentSubjectId number (FK)

    That would simply be a recursive reference to another subject's ID to which it falls under. Maybe that doesn't make sense so here's some example data:

    subjectId, subjectName, parentSubjectId
    1, "Language Arts", null
    2, "Reading", 1
    3, "Phonics", 2
    4, "Other Subject with no parent", null

    So you can see we have built ourselves a nice hierarchy, where subjects which fall under other subjects can reference them. When you go to allow subjects to be chosen you can then query after each drop-down selection by refreshing the page (or via AJAX) to get its subjects. In this case we could also possibly do away with the "Ranking" field in our linking table since our hierarchy is captured in the relationships we now have, but for the sake of making our lives easy, let's keep it.

    Like let's say we select "Language arts" with a subjectId of 1, we can get its child subjects like:

    <cfquery name="getChildSubjects" datasource="#Request.dsn#">
    SELECT s.subjectId, s.subjectName
    FROM subjects s
    WHERE s.parentSubjectId = <cfqueryparam cfsqltype="cf_sql_integer" value="#URL.subjectId#" />
    ORDER BY s.subjectName
    </cfquery>

    Obviously, modifications would need to be made to fit your schema, variable names, etc. but that's the general principles.

    As for how to associate textbooks to subjects you will actually be inserting one record into the linking table for each subject selected. So let's say you have a book "XSS for dummies."

    To insert the book you would add a single record in your textbook table, maybe something like:

    INSERT INTO textbook (
    textbookId,
    textbookName,
    <!--- Other fields go here, etc... --->
    ) VALUES (
    1,
    'XSS for dummies',
    <!--- Other fields go here, etc... --->
    )

    Now, the user has selected three subjects: Computer Science (ID: 5), Internet and Web Topics (ID: 11), and Security (ID:98). We will thus insert three records into our linking table like:

    INSERT INTO textbookSubjects (
    textbookId,
    subjectId,
    ranking
    ) VALUES (
    1,
    5,
    'Primary'
    )

    INSERT INTO textbookSubjects (
    textbookId,
    subjectId,
    ranking
    ) VALUES (
    1,
    11,
    'Secondary'
    )

    INSERT INTO textbookSubjects (
    textbookId,
    subjectId,
    ranking
    ) VALUES (
    1,
    98,
    'Tertiary'
    )

    So now when we query to get all our textbooks and related subjects, we have something like this:

    SELECT t.textbookId, t.textbookName, s.subjectId, s.subjectName, ts.ranking
    FROM textbook t
    LEFT JOIN textbookSubjects ts
    ON t.textbookId = ts.textbookId
    LEFT JOIN subject s
    ON t.subjectId = s.subjectId
    ORDER BY t.textbookName

    An example dataset might look like:

    textbookId, textbookName, subjectId, subjectName, ranking
    1, "XSS for Dummies", 5, "Computer Science", "Primary"
    1, "XSS for Dummies", 11, "Internet and Web Topics", "Secondary"
    1, "XSS for Dummies", 98, "Security", "Tertiary"
    2, "Some Other Book With No Subjects", null, null, null

    Now if we iterate over this dataset, you will end up outputting the textbook 3 times, which is probably not what you wanted. So to control this we can do something like:

    <cfoutput query="myBooks" group="textbookId">
    <tr>
    <td>#myBooks.textbookName#</td>
    <!--- Show all the subjects --->
    <td>
    <!--- These cfoutput tags with no group will give you all the values for the current group --->
    <cfoutput>
    #myBooks.subjectName#<br />
    </cfoutput>
    </td>
    </tr>
    </cfoutput>

    These examples are obviously brief but I hope they get the point across and help you accomplish what you're trying to do.
    Inspiring
    November 9, 2007
    William - I really appreciate your examples - this will certainly come in handy as I venture into the next steps of this little application! I'm really under a tight schedule so it's especially helpful.

    Okay - I think I did this wrong, because I'm getting an error:

    [Macromedia][SQLServer JDBC Driver][SQLServer]Incorrect syntax near the keyword 'JOIN'.

    The error occurred in C:\Inetpub\wwwroot\textbooks\textbook_list.cfm: line 9

    7 : AND LEFT JOIN ss.SubjectCategorySecondary ON ts.SecondarySubjectId = ss.SecondarySubjectID
    8 : AND LEFT JOIN st.SubjectCategoryTertiary ON ts.TertiarySubjectId = st.TertiarySubjectID
    9 : AND ts.SchoolID = #URL.SchoolID#
    10 : AND s.SchoolID = ts.SchoolID
    11 : ORDER BY ts.PrimarySubjectID, ts.Grade1

    Here's what I did - I'm planning on incorporating the changes you suggested but I thought I'd try to get this working correctly first! (By the way - thanks for the more efficient table abbreviations :D)
    Also - I somehow misread the datatypes in the table structure - in all tables the ID's for the subjects are numbers. I guess I needed to have more coffee!
    Inspiring
    November 9, 2007
    I think I know what you are after, but not 100%

    can you post the table structures ?

    basically I think if you just add the table that contains the information you want and join by the common key.

    Also, yes you can nest cfoutput (need to use the group attribute)

    Ken
    Inspiring
    November 9, 2007
    Thanks Ken - I'll post the table structures tomorrow when I get to work.
    I'm still pretty new to this stuff so I might need some hand holding