1

I am relatively new to Access and I have a table that has AuthorName, BookTitle, and CoAuthor. AuthorName and BookTitle are a composite key.

Currently the query pulls information like:

AuthorName---------BookTitle------CoAuthor

Steven King--------Dark Half------Peter Straub

Steven King--------Dark Half------John Doe

James Patterson----Another Time

Jeff Hanson--------Tales of Time---Joe Smith

I would like it to read (dynamically) if possible

AuthorName---------BookTitle---------CoAuthor1--------CoAuthor2

Steven King----------Dark Half--------Peter Straub-----Joe Doe

James Patterson----Another Time

Jeff Hanson----------Tales of Time----Joe Smith

So if there is another author that is later added, a third column for CoAuthor would appear.

Is this possible with VBA or SQL?

Cœur
  • 37,241
  • 25
  • 195
  • 267

2 Answers2

5

What you are asking goes against the whole point of using a relational database; in short, you should design your tables in a way that minimizes (rather eliminates) the need to redesign the tables. I suggest you read about database normalization.

Your question is, as a matter of fact, an example that I use very frequently when I teach about databases: How would you design a database to hold all the information about books and authors. The scenarios are:

  • A book can have one or more authors
  • An author may have written one or more books

So, this is a many-to-many relation, and there's a way to design such a database.

First, design a table for the authors:

  • tbl_authors
    • author_idd (primary key, numeric, preferibly autonumeric)
    • first_name (String)
    • last_name (String)

Then, design a table for the books:

  • tbl_books
    • book_id (primary key, numeric, preferibly autonumeric)
    • book_title (String)

And finally, a third table is needed to relate authors and books:

  • tbl_books_authors
    • book_id (primary key, numeric)
    • author_id (primary key, numeric)
    • main_author (boolean: "Yes" if it is the main author, "No" otherwise)

(Both fields must be part of the primary key)

And now, the main question: How to query for books and its authors?

Asuming the above design, you could write an SQL query to get the full list of books and its authors:

select book_title, first_name, last_name
from 
    tbl_authors as a
    inner join tbl_books_authors as ba on a.authorId = ba.authorId
    inner join tbl_books as b on ba.bookId = b.bookId

This way, you'll have something like this:

book_title | first_name | last_name
-----------+------------+-----------
book1      | John       | Doe
book1      | Jane       | Doe
book2      | John       | Doe
book2      | Peter      | Who
book3      | Jane       | Doe
book4      | Peter      | Who
book5      | John       | Doe
book5      | Jane       | Doe
book5      | Peter      | Who
book5      | Jack       | Black

Why is this design better than your original idea?

  • Because you won't need to alter the structure of your tables to add another author
  • Because you don't know a priori how many authors a book can have
  • Because you avoid redundancy in your database
  • Because, with this design, you'll be able to use front-end tools (like Access forms and reports, or other tools) to create almost any arraingment from this data.

Further reading:


Minor update

This kind of design will help you avoid lots and lots of headaches in the future, because you won't need to alter your tables every time you add a third or fourth author. I learned about database normalization some years ago reading "Running Access 97" by Jon Viescas (this is not a commercial, it's just a reference ;) )... an old book, yes, but it has a very good introduction on the topic.

Now, after you have normalized your database, you can use pivot queries to get what you need (as noted in the answer posted by Conrad Frix).

Community
  • 1
  • 1
Barranka
  • 20,547
  • 13
  • 65
  • 83
  • Specifically, with a structure like this it will be possible to use an Access cross-tab query to get the output you may be looking for. – Jeremy Cook Jul 02 '14 at 21:51
  • +1 for the overall message, but the purpose of [mainAuthor] is unclear. Putting it in the [tbl_authors] table implies that a given author will be "the main author" for every work in which s/he is involved. (And what does it mean if two "main authors" work on the same book?) My instincts tell me that [mainAuthor] should be in the [tbl_books_authors] table. – Gord Thompson Jul 02 '14 at 21:53
  • @Barranka excellent summary but why the switching between camelCase and snake_case? The OP framed it in PascalCase. Whatever the choice, I'd not mix multiple conventions in one application. – Jeremy Cook Jul 02 '14 at 21:57
  • @JeremyCook Maybe its just a matter of being self-taught in some topics... I just find it comfortable naming tables in snake_case and fields in camelCase... but yes, you are right (I am violating the 3rd of the [Ten Commandments of Access](http://access.mvps.org/access/tencommandments.htm))... correcting – Barranka Jul 02 '14 at 22:00
  • @JeremyCook it's possible to do a cross-tab without redesign. – Conrad Frix Jul 02 '14 at 22:12
  • @ConradFrix yes, you are right... however I intend to make the point that a normalized database can avoid lots and lots of pain in the future – Barranka Jul 02 '14 at 22:15
  • @ConradFrix true, I wasn't suggesting it didn't, only that what Barranka proposed will support its use. Guess I should have thrown the word "also" or something in there. – Jeremy Cook Jul 02 '14 at 22:15
  • @Barranka I certainly agree that this strucutre badly needs a redesign and your answer does a good job explaining why the OP should do that. Sometimes however that can't be done for various reasons. As an aside I'd use `author_sort` instead of `main_author` since it allows for "billing" order to be maintained in the case of 3 or more authors. – Conrad Frix Jul 02 '14 at 22:30
  • Thank you all for your input, I guess that I oversimplified my question, I do have multiple tables and the query results were not what I wanted. I apologize for the confusion. However the information provided is still great to remember. – user3799282 Jul 03 '14 at 17:11
3

If your table had type like below

+-----------------+---------------+--------------+-----------+
|   AuthorName    |   BookTitle   |   CoAuthor   |   Type    |
+-----------------+---------------+--------------+-----------+
| Steven King     | Dark Half     | Peter Straub | CoAuthor1 |
| Steven King     | Dark Half     | John Doe     | CoAuthor2 |
| James Patterson | Another Time  |              | CoAuthor1 |
| Jeff Hanson     | Tales of Time | Joe Smith    | CoAuthor1 |
+-----------------+---------------+--------------+-----------+

it would be a pretty simple transform

TRANSFORM First(Books.CoAuthor) AS FirstOfCoAuthor
SELECT Books.AuthorName, Books.BookTitle
FROM Books
GROUP BY Books.AuthorName, Books.BookTitle
PIVOT Books.Type;

Since it doesn't we need to create it on the fly by first assigning a number to each row simulating ROW_NUMBER OVER and then transforming. On large data sets this may be quite slow

TRANSFORM First(b.coauthor) AS firstofcoauthor 
SELECT b.authorname, 
       b.booktitle 
FROM   (SELECT authorname, 
               booktitle, 
               coauthor, 
               'CoAuthor' & k AS Type 
        FROM   (SELECT b.authorname, 
                       b.booktitle, 
                       b.coauthor, 
                       Count(*) AS K 
                FROM   books AS b 
                       LEFT JOIN books AS b1 
                              ON b.authorname = b1.authorname 
                WHERE  [b].[coauthor] <= [b1].[coauthor] 
                        OR (( ( b1.coauthor ) IS NULL )) 
                GROUP  BY b.authorname, 
                          b.booktitle, 
                          b.coauthor) AS t) AS b 
GROUP  BY b.authorname, 
          b.booktitle 
PIVOT b.type 
Community
  • 1
  • 1
Conrad Frix
  • 51,984
  • 12
  • 96
  • 155
  • 1
    This is the answer :-) The other answer is useful info for beginners and reminders for the experienced. – Jeremy Cook Jul 02 '14 at 22:18
  • Thank you for the information and great example of how to accomplish the task. I would not have thought to use a crosstab query to get this done. Thank you to all who gave their input. – user3799282 Jul 03 '14 at 17:15
  • @user3799282 If this reply answares your question coul'd you please mark it as answar by clicking the v on the left of the answar. It will become green v and mark your Q. as answared for all SO users. Thanks – Siyon DP Nov 13 '18 at 11:45