Created
June 5, 2012 12:02
-
-
Save dwilliamson/2874586 to your computer and use it in GitHub Desktop.
DB Notes
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Create tables of simple types; don't try to replicate C++ entity structures in a DB as they don't map well. For example, a C++ structure like this: | |
struct Entity | |
{ | |
PositionComponent* position; | |
AIComponent* ai; | |
}; | |
Can be considered a "object-oriented database model" and is hard to extend. Generalising this in C++ a little, we get: | |
struct Entity | |
{ | |
vector<Component*> components; | |
}; | |
While this is easy to extend in C++ land, a direct DB mapping is pretty awful. | |
Instead of parent objects referencing child objects, having child objects reference parents works well for extensible database designs. In C++ this would like like: | |
struct Entity { }; | |
struct PositionComponent { Entity* parent; } | |
struct AIComponent { Entity* parent; } | |
While not ideal for C++ (you lose low-cost O(1) pointer dereferencing), this allows for extensible database designs for entities. | |
CREATE TABLE Entities (id INT PRIMARY_KEY); | |
CREATE TABLE PositionComponents (parent_entity INT PRIMARY_KEY, x DECIMAL, y DECIMAL, z DECIMAL); | |
CREATE TABLE AIComponents (parent_entity INT PRIMARY_KEY, ...); | |
You can add as many different components as you like by adding new tables, you can easily change the format of individual components and transform the necessary tables and lookups are quick due to the entity ID being the primary key in all tables. | |
How this scales to very large Zynga-style databases, I have no idea. I would guess it doesn't. |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Need a bunch of tables, so:
Remove:
Unique ID - what's the point? To help remove redundant data, see below...
Constructor/destructor info in base types and enums
Need a way of querying: give me all fields belonging to type X.
Could store a Parent field that gives the full name, but then we'd have:
which is an awful lot of duplicate data. Of course, Parent could be an ID but you'd then need to do two queries:
SQL subqueries should help here:
In order to achieve this, the full names of each table entry need to be stored. This is, again, redundant data, but it's necessary
to prevent queries from getting more complex.
Joins can also be used to achieve this:
In the case of inheritance, how would you achieve this? We would need a recursive search, which is a bit of a pain in SQL.
SQL doesn't represent tree hierarchies very well. For each type you could have a table that stores a list of all types it
inherits from. That way your query would be:
Note that this table would also have to mark that a type inherits from itself, in order to be included in the results.
Alternatively we could use a table union to merge with the types unique ID.
The ClassNameInherits table only exists if a type does inherit from something and its existence is purely to aid construction
of simpler SQL queries. This will work but it might put too much noise in the database.
Is there a way to dynamically build up the ClassNameInherits table? Probably. But SQLite is quite primitive in this scenario.
Rather than using the adjacency model, we could use the nested set model:
This would actually work wonders and give so many benefits, but it limits the representation to a tree. Even
though I don't like the idea of multiple inheritance, we have to support it in some minimal form; diamond inheritance,
and all.
Of course, when queries get this complicated, it might be worth simply switching to Python.
There is another approach: Ancestor (closure) tables:
This is a generalisation of the ClassNameInherits approach, requiring only one table. This is exactly what's needed!
Only in this case we're not worried about having to rebuild the table from scratch so it's much simpler.
To find a class and all its bases, do:
Note that DISTINCT is not used here because we can do some post-processing on the ancestor table before commit that
removes duplicate references due to diamond inheritance.
Finally, to find all fields that belong to a given type or its bases, do:
LISP, anyone?
Note also the same requirement as above: there must be an entry in the ancestor table where a class references itself
as an ancestor. If at a later date the database design needs to be adjusted for proper ancestor behaviour, the query
would become:
Not great but I'm sure further research will turn up a simpler solution. Let's not worry about that now, though.
Call this the ClassInheritance table and there's no need to store inheritance information in the class table itself.