Results 1 to 6 of 6

Thread: [RESOLVED] Handling result of inner join query

  1. #1

    Thread Starter
    PowerPoster
    Join Date
    Apr 2007
    Location
    The Netherlands
    Posts
    5,070

    Resolved [RESOLVED] Handling result of inner join query

    Hi,

    I am kinda stumped on this one... I am using an Access database to store (filepaths to) pictures as well as comments people can leave on them. One picture can have multiple comments, so the tables look like this:
    Code:
    _____________      _____________
    |Photos      |    |Comments     |
    |------------|    |-------------|
    |Id (PK)     |    |Id (PK)      | 
    |PhotoPath   |    |PhotoId (FK) |
    |Description |    |Comment      |
    |____________|    |_____________|
    What I need to do now is retrieve a list of all Photos along with their Comments. So I have a class Photo with a collection of Comments:
    vb.net Code:
    1. Public Class Photo
    2.    
    3.     Public Property Id As Integer
    4.     Public Property PhotoPath As String
    5.     Public Property Description As String
    6.    
    7.     Public Property Comments As List(Of Comment)
    8.  
    9. End Class
    10.  
    11. Public Class Comment
    12.  
    13.     Public Property Id As Integer
    14.     Public Property PhotoId As Integer
    15.     Public Property Comment As String
    16.    
    17. End Class

    I can retrieve the records using an INNER JOIN query (right?), as such;
    Code:
    SELECT Photos.*, Comments.*
    FROM (Photos INNER JOIN Comments ON Photos.Id = Comments.PhotoId)
    From the result of that, for each row in the result I can create a new Photo instance, set its properties and put it in a List(Of Photo) for further processing (displaying). However, how do I handle the Comments? With this query I would get a lot of duplicate Photos. I'd get a row for each comment, but most of these rows will represent the same photos...

    How do I handle this the best way? I have used inner join queries before, but only for a one-to-one relationship so I never had this problem before. Now when I finally need a one-to-many relationship I realize I've no idea how to handle this appropriately...

    There must be some standard way of handling this?


    When I previously handled cases like this I didn't use an inner join, I just retrieved all Photos first, then iterate through them and run another query to retrieve the Comments that belong to that Photo (SELECT * FROM Comments WHERE PhotoId = ?). That works fine, but when the tables are getting larger it is getting noticeably slower. So I want to do it in a single query, I definitely want to avoid running hundreds of queries if I can

    Thanks!

  2. #2
    Super Moderator jmcilhinney's Avatar
    Join Date
    May 2005
    Location
    Sydney, Australia
    Posts
    111,221

    Re: Handling result of inner join query

    If you want to create two different types of objects then you wouldn't use a join. You'd execute two separate queries. The reason that your original code was so slow was because you were querying the Comments table over and over again. Just query it once and get all the data. You've got a various options:
    vb.net Code:
    1. Public Function GetPhotosWithComments() As List(Of Photo)
    2.     Dim photoTable As New DataTable
    3.     Dim commentsTable As New DataTable
    4.  
    5.     Using connection As New OleDbConnection("connection string here"),
    6.           photoCommand As New OleDbCommand("SELECT * FROM Photos", connection),
    7.           commentsCommand As New OleDbCommand("SELECT * FROM Comments", connection)
    8.         connection.Open()
    9.  
    10.         Using photoReader = photoCommand.ExecuteReader()
    11.             photoTable.Load(photoReader)
    12.         End Using
    13.  
    14.         Using commentsReader = commentsCommand.ExecuteReader()
    15.             commentsTable.Load(commentsReader)
    16.         End Using
    17.     End Using
    18.  
    19.     Dim photoList As New List(Of Photo)
    20.     Dim photosById As New Dictionary(Of Integer, Photo)
    21.  
    22.     For Each row In photoTable.AsEnumerable()
    23.         Dim photo As New Photo With {.Id = row.Field(Of Integer)("Id"),
    24.                                      .PhotoPath = row.Field(Of String)("PhotoPath"),
    25.                                      .Description = row.Field(Of String)("Description")}
    26.  
    27.         photoList.Add(photo)
    28.         photosById.Add(photo.Id, photo)
    29.     Next
    30.  
    31.     For Each row In commentsTable.AsEnumerable()
    32.         Dim comment As New Comment With {.Id = row.Field(Of Integer)("Id"),
    33.                                          .PhotoId = row.Field(Of Integer)("PhotoId"),
    34.                                          .Comment = row.Field(Of String)("Comment")}
    35.         Dim photo = photosById(comment.PhotoId)
    36.  
    37.         photo.Comments.Add(comment)
    38.     Next
    39.  
    40.     Return photoList
    41. End Function
    You could LINQ that up a lot more and do away with the Dictionary:
    vb.net Code:
    1. Public Function GetPhotosWithComments() As List(Of Photo)
    2.     Dim photoTable As New DataTable
    3.     Dim commentsTable As New DataTable
    4.  
    5.     Using connection As New OleDbConnection("connection string here"),
    6.           photoCommand As New OleDbCommand("SELECT * FROM Photos", connection),
    7.           commentsCommand As New OleDbCommand("SELECT * FROM Comments", connection)
    8.         connection.Open()
    9.  
    10.         Using photoReader = photoCommand.ExecuteReader()
    11.             photoTable.Load(photoReader)
    12.         End Using
    13.  
    14.         Using commentsReader = commentsCommand.ExecuteReader()
    15.             commentsTable.Load(commentsReader)
    16.         End Using
    17.     End Using
    18.  
    19.     Dim photoList = (From row In photoTable.AsEnumerable()
    20.                      Select New Photo With {.Id = row.Field(Of Integer)("Id"),
    21.                                             .PhotoPath = row.Field(Of String)("PhotoPath"),
    22.                                             .Description = row.Field(Of String)("Description")}).ToList()
    23.  
    24.     For Each photo In photoList
    25.         photo.Comments.AddRange(From row In commentsTable.AsEnumerable()
    26.                                 Where row.Field(Of Integer)("PhotoId") = photo.Id
    27.                                 Select New Comment With {.Id = row.Field(Of Integer)("Id"),
    28.                                                          .PhotoId = row.Field(Of Integer)("PhotoId"),
    29.                                                          .Comment = row.Field(Of String)("Comment")})
    30.     Next
    31.  
    32.     Return photoList
    33. End Function
    Like I said, you have various options and those are just two of them. Whatever variation you choose to use though, you should be performing exactly two queries. Query the tables separately and query each only once.
    Why is my data not saved to my database? | MSDN Data Walkthroughs
    VBForums Database Development FAQ
    My CodeBank Submissions: VB | C#
    My Blog: Data Among Multiple Forms (3 parts)
    Beginner Tutorials: VB | C# | SQL

  3. #3
    Super Moderator jmcilhinney's Avatar
    Join Date
    May 2005
    Location
    Sydney, Australia
    Posts
    111,221

    Re: Handling result of inner join query

    By the way, here's the "proper" implementation of your Photo class:
    vb.net Code:
    1. Public Class Photo
    2.  
    3.     Private _comments As New List(Of Comment)
    4.  
    5.     Public Property Id As Integer
    6.     Public Property PhotoPath As String
    7.     Public Property Description As String
    8.  
    9.     Public ReadOnly Property Comments As List(Of Comment)
    10.         Get
    11.             Return Me._comments
    12.         End Get
    13.     End Property
    14.  
    15. End Class
    Properties that expose collections should pretty much always be read-only.

    You could conceivably add a corresponding Photo property to your Comment class, if there was ever going to be a need to go back that way.
    Why is my data not saved to my database? | MSDN Data Walkthroughs
    VBForums Database Development FAQ
    My CodeBank Submissions: VB | C#
    My Blog: Data Among Multiple Forms (3 parts)
    Beginner Tutorials: VB | C# | SQL

  4. #4

    Thread Starter
    PowerPoster
    Join Date
    Apr 2007
    Location
    The Netherlands
    Posts
    5,070

    Re: Handling result of inner join query

    Ok, for some reason I think I could do it in one query, so I never even considered loading all the comments in one go and dividing them up later. That makes sense, thanks.

    As for the read-only property, I actually have that, this was just a quick example of the classes in question, I didn't want to post all of them cause that would include alot of irrelevant information, and I was too lazy to type out the 'full' property (hence the use of auto properties).

    I'm going to try this soon, but I'm pretty sure it'll work just fine, thanks!

  5. #5
    Super Moderator jmcilhinney's Avatar
    Join Date
    May 2005
    Location
    Sydney, Australia
    Posts
    111,221

    Re: [RESOLVED] Handling result of inner join query

    You could actually do it with one query, but you'd then be retrieving and discarding a lot of the Photo data repeatedly.

    If you weren't using Access, another variation would to use a single DbCommand that contained two queries. You could then call ExecuteReader once and your DataReader would contain both result sets, or you could use a DataAdapter and call Fill once to populate two DataTables in one DataSet. You could add a DataRelation to that DataSet and use that to get all the Comments rows related to a particular Photos row.
    Why is my data not saved to my database? | MSDN Data Walkthroughs
    VBForums Database Development FAQ
    My CodeBank Submissions: VB | C#
    My Blog: Data Among Multiple Forms (3 parts)
    Beginner Tutorials: VB | C# | SQL

  6. #6

    Thread Starter
    PowerPoster
    Join Date
    Apr 2007
    Location
    The Netherlands
    Posts
    5,070

    Re: [RESOLVED] Handling result of inner join query

    Sounds good, unfortunately this is for a website and as far as I know it only supports Access for free, you have to pay extra for a MsSQL server and I'm not doing that unless I have a very good reason. But I'll keep it in mind for other projects, thanks.

Posting Permissions

  • You may not post new threads
  • You may not post replies
  • You may not post attachments
  • You may not edit your posts
  •  



Click Here to Expand Forum to Full Width