views:

67

answers:

3

I have the following Tables:

Players

  • id
  • playerName

Weapons

  • id
  • type
  • otherData

Weapons2Player

  • id
  • playersID_reference
  • weaponsID_reference

That was nice and simple.

Now I need to SELECT items from the Weapons table, according to some of their characteristics that i previously just packed into the otherData column (since it was only needed on the client side). The problem is, that the types have varying characteristics - but also a lot of similar data.

So I'm trying to decide on the following possibilities, all of which have their pros and cons.

Solution A

Kill the Weapons table, and create a new table for each Weapon-Type:

Weapons_Swords

  • id
  • bladeType
  • damage
  • otherData

Weapons_Guns

  • id
  • accuracy
  • damage
  • ammoType
  • otherData

But how will i Link these to the Players ?

  • create Weapons_Swords2Players, Weapons_Guns2Players for each weapon-type? (Will result in a lot more JOINS when loading the player with all his weapons...and it's also more complicated to insert a new player)

or

  • add another column to Weapons2Players called WeaponsTypeTable, then do sub-selects to the correct Weapons sub-table (seems easier, but not really right, slightly easier insert i guess)

Solution B

Keep the Weapons table, and add all the fields i need to it. The Problem is that then there will be NULL fields, since not all Weapon-Types use all fields (can't be right)

Weapons

  • id
  • type
  • accuracy
  • damage
  • ammoType
  • bladeType
  • otherData

This seems to be pretty basic stuff, but i just can't decide what's best. Or is there a correct Solution C?

many thanks.

A: 

I would take a hybrid of your two approaches. Keep the weapons table for the common stats (id, type, damage, otherData). Then create gun_stats (weapon_id, accuracy, ammoType) and sword_stats (weapon_id, bladeType) tables that link to a weapon's id and have the remaining data pieces. You'll need just two queries to get all weapons of both types for a player, and there isn't any data duplication.

Mark B
+2  A: 

One solution is to create a master weapons table to which the Weapons_Swords and Weapons_Guns have foreign key references like so:

Create Table Weapons
{
    Id ...
    , ...
}

Create Table Weapons_Swords
{
    Id...
    , ...
    , Constraint FK_Weapons_Swords_Weapons
        Foreign Key ( Id )
        References Weapons( Id )
}
Create Table Weapons_Guns
{
    Id...
    , ...
    , Constraint FK_Weapons_Guns_Weapons
        Foreign Key ( Id )
        References Weapons( Id )
}

You would then have your standard PlayerWeapons table:

Create Table PlayerWeapons
{
    PlayerId ..
    , WeaponId ..
    , Constraint FK_PlayerWeapons_Players
        Foreign Key ( PlayerId )
        References Players( Id )
    , Constraint FK_PlayerWeapons_Weapons
        Foreign Key ( WeaponId )
        References Weapons( Id )
}

The downside to this approach is that you can have a weapon that does not point to a specific type and you do have additional joins. The upside is that you can add attributes common to all weapons into the Weapons table.

Your solution b involves denormalizing the Weapons table. The advantage is that it is significantly simpler to get data and ensure that a weapons does not point to nothing.

If the attributes of the various weapon types differ significantly, then I'd recommend creating a master weapons table. If there is a lot of similarities, then you could consider the denormalized solution. If I had no idea how much variance I would get, my inclination would be to make it as normalized as possible and thus use the master Weapons table.

Thomas
ok, so i do a big SELECT, joining all those tables. Now, i'll get a result with some rows that have NULL in the Weapons_Guns.Ammo field. What's the correct way to sort this before sending it back to the client. Simply looping through the rows and checking if(Weapons_Guns.Ammo == "NULL") then 'is Weapons_Sword...'? thanks.
iddqd
@iddqd - I wouldn't necessarily do this in a single query although you could depending on the number of weapons types. First, as you mentioned, I would keep a WeaponType column in the Weapons table to indicate where to go look for the details of the weapons. Next, for a user, I'd query for their list of weapons and use the WeaponType to determine which other tables to query. An alternate solution would be to use a sp and a series of `if`s and return multiple resultsets. So, if they had both a sword and a gun, you would get three resultsets: Weapons, Swords and Guns in a single query.
Thomas
@iddqd - Keep in mind that the first design assumes that there are few shared attributes. If instead most weapons shared a large number of attributes, then your second design might be better. It should also be noted that your code will still need to know which attributes to populate based on the weapon type. I.e., it will need to know to populate the "BulletCapacity" attribute on a Gun and not on a Sword. So now matter how you slice it, there will likely be some code forks. However, in the first design, you could populate those weapons instances by passing them the correct resultset.
Thomas
@Thomas, many thanks for the detailed suggestions. I guess my assumption of "no matter how you slice it, there will likely be some code forks" was what I needed to have confirmed ;-)
iddqd
A: 

Modern databases are pretty darn good at handling sparse data. Adding a few extra columns that are blank will not cause any trouble as long as the data type is small or variable width. This would allow you to extract some of the information out without complicating your table structures.

Another mechanism would be to add some extra index tables for the weapons that would help you search without modifying your Weapons table or your character loading code. Depending on the kinds of searches you are doing, that information should be easy to calculate, store, and update.

For the clean and truly scalable approach, I would have to go with the normalized tables @Thomas. If you just need to get a little further, one or both of the above approaches might help get you there.

Jacob

TheJacobTaylor