我设计了一个以短链接行为中心的数据库。
我有以下架构:
create_table "students", force: :cascade do |t|
t.string "first_name"
t.string "last_name"
t.string "email"
t.boolean "recruit", default: true
t.boolean "archive", default: false
t.datetime "created_at"
t.datetime "updated_at"
end
create_table "fields", force: :cascade do |t|
t.string "name"
t.integer "index"
t.integer "group_id"
t.string "description"
t.string "options"
t.boolean "hidden"
t.boolean "locked", default: false
t.datetime "created_at"
t.datetime "updated_at"
end
create_table "groups", force: :cascade do |t|
t.string "name"
t.datetime "created_at"
t.datetime "updated_at"
end
create_table "texts", force: :cascade do |t|
t.integer "student_id"
t.integer "field_id"
t.string "content", default: ""
t.datetime "created_at"
t.datetime "updated_at"
end
create_table "options", force: :cascade do |t|
t.integer "student_id"
t.integer "field_id"
t.string "choice", default: ""
t.datetime "created_at"
t.datetime "updated_at"
end
create_table "addresses", force: :cascade do |t|
t.integer "student_id"
t.integer "field_id"
t.string "address_1", default: ""
t.string "address_2", default: ""
t.string "city", default: ""
t.integer "state_id", default: 1
t.string "zip", default: ""
t.datetime "created_at"
t.datetime "updated_at"
end
该组织背后的基础是一种完全灵活的形式。您只需在字段表中添加一个新行,该条目就会在生成学生的表单上定义一个新问题。组指的是选项字段,文本字段或地址。保存数据后,与给定字段关联的组会告诉我的应用程序将哪个表放入其中。此系统运行良好。它非常灵活,我可以很容易地看到学生有哪些选项的数据,我几乎可以根据任何事情进行查询。
问题是当我开始展示学生时。我首先询问我要展示的学生,然后我必须搜索每个学生的每个领域。这意味着对于500名学生,我在基本表单上有大约2500个查询。这会杀死表现。
我想知道将查询加在一起并加快这个系统的最佳方法是什么?
部分问题是我不能100%确定学生有一个给定的字段,所以当我现在查询时,如果数据库没有返回任何结果,我可以创建一个。
答案 0 :(得分:0)
似乎没有在此处定义任何外键。使用FK并确保它们在每个表上点击索引的组合应允许快速连接。
我假设您在问题中使用基于 Rails 和 DSL 的 ActiveRecord 。我建议您在添加FK后使用joins
。 ActiveRecord Querying doc
如果您担心某些JOIN
列不存在,可以在LEFT OUTER JOIN
的调用中使用joins
(在上面的链接中提及),这样您仍然可以使用即使连接条件没有匹配,左侧表中的行也只是获得右侧的NULL
列值。那些NULL
会告诉你哪些不存在,可能需要随后创建。
根据OP的评论进行编辑:
我自己没有使用 SchemaPlus gem,但我觉得如果你愿意的话可以使用外键,因为它支持的是其中一个(根据its doc )。
关于这个问题:
就像是因为认为它是外键而引起的问题 某些原因数据不再存在于一端?
外键对于执行referential integrity至关重要。
例如,如果field_id
表中有texts
作为外键,则无法从field
表中删除相应的记录(不使用cascade
还要删除下游记录),因为它指的是field
中的记录。
另一方面,如果texts
中不存在field_id
,您将无法插入field_id
行,其中fields
无效serial
, Postgres 会抛出外键约束违规。
外键是表设计的关键部分,绝对应该使用。它使您不必将所有逻辑放在应用程序代码中的所有位置,例如:在尝试插入任何东西之前,必须检查您要插入的密钥是否有效;相反,如果它们不是, Postgres 将违反约束,并作为可以处理的异常返回到您的应用程序,例如回滚并返回错误。)
基本上,只要你在一个表中有一个引用另一个表中的记录的id,就应该在外键约束中定义。
此外,可能值得为每个表添加主键。我经常喜欢使用{{1}}变量来确保每一行都可以通过单个键识别,即使存在构成隐含主键的元组也是如此。主键还隐式地根据这些键创建索引。