My Struggle with SLQ Queries

This last week I have spent a significant amount of time dealing with mildly complex SQL queries for our game. At least they are complex by my standards (your mileage may vary). My first problem I needed to tackle is the structure of our database. In the grand scheme of things, the database is rather straightforward but a certain many-to-many relationship has definitely been causing havoc on my brain.

Our current database. Not too shabby.

With the structure out of the way, I moved on to problem number two. The problem being, how do we retrieve all of the card entries from the cards table that belong to a specific user AND deck so that we can feed that into our script that handles creating decks to play the game. My first stab at it worked after a few tries however it makes you want to puke at first sight.

SELECT
  cards.cardID,
  cards.cardName,
  cards.description,
  cards.portrait,
  cards.summon,
  cards.elementString,
  cards.cost,
  cards.life,
  cards.attack,
  cards.defense,
  cards.cardMax,
  cards.spellValue,
  cards.spellType,
  cardInDeck.cardCount
FROM cards
JOIN users ON
  users.userID = cards.userID
JOIN decks ON
  users.userID = decks.userID
JOIN cardInDeck ON
  decks.deckID = cardInDeck.deckID
WHERE
  users.userID = '$userID' AND
  decks.deckID = '$deckID' AND
  cardInDeck.cardID = cards.cardID;

Yuck!

Turns out I really only need information from two tables and following the foreign key lines from table to table to accomplish my joins was a terrible idea. One single join should solve this issue.

SELECT
  cards.cardID,
  cards.cardName,
  cards.description,
  cards.portrait,
  cards.summon,
  cards.elementString,
  cards.cost,
  cards.life,
  cards.attack,
  cards.defense,
  cards.cardMax,
  cards.spellValue,
  cards.spellType,
  cardInDeck.cardCount
FROM cards
JOIN cardInDeck 
  USING (cardID)
WHERE
  userID = '$userID' AND
  deckID = '$deckID'

Much better. Only one JOIN needed!

But wait, this code is still pretty smelly. That is one long list of columns to select from the cards table. In fact, it is every column in the cards table!

SELECT
  cards.*,
  cardInDeck.cardCount
FROM cards
JOIN cardInDeck
  USING (cardID)
WHERE
  userID = '$userID' AND
  deckID = '$deckID'

Now we are finally getting somewhere. This query retrieves all the cards that are in the deck along with their associated info and the count of how many should be in the deck from the cardInDeck table.

The Next Trouble

Following that, I needed to create a query that would do something very similar but indeed quite different. In order to edit a deck that a user has created we need to retrieve every card they have ever made as well as the number of individual cards they have included in the deck so far. So in this case we need ALL the user’s cards and the number of each assigned to the deck. Let us bring in the magic of LEFT JOIN and separating conditionals. For my first few attempts I could not figure out a way to do different conditionals on my selected table and the JOIN. However, it is possible! We simply must use conditionals in the JOIN statement and then another conditional on the WHERE statement that refers to SELECT.

SELECT
  cards.*,
  cardInDeck.cardCount
FROM cards
LEFT JOIN cardInDeck
  ON cards.cardID = cardInDeck.cardID AND
  deckID = '$deckID'
WHERE
  userID = '$userID'

So in this case, I select everything from cards where userID matches the desired userID. Then, perform the join for matching cardID’s AND the desired deckID. So userID applies to the SELECT and deckID applies only to the JOIN. Obviously this is some fairly rudimentary SQL work but figuring it out was quite satisfying. Here’s to the next inevitable SQL query that must be resolved for our game. Cheers!

Leave a comment

Your email address will not be published. Required fields are marked *