- android - 多次调用 OnPrimaryClipChangedListener
- android - 无法更新 RecyclerView 中的 TextView 字段
- android.database.CursorIndexOutOfBoundsException : Index 0 requested, 光标大小为 0
- android - 使用 AppCompat 时,我们是否需要明确指定其 UI 组件(Spinner、EditText)颜色
我希望就哪种数据库模式最适合我的情况以在表中存储小部件的“类型”信息达成共识。一个 Widget 只能有一种类型,但该类型可以是预设类型或自定义类型。显然,我会创建预设类型,而用户会创建自定义类型。
我将在服务器上使用 MySQL 和 INNODB。我还将使用 SQLite 在应用程序上存储相同的信息。但我们在这里只讨论服务器。我是一名应用程序程序员,而不是数据库管理员,但希望在第一时间为这个项目获得正确的数据库,并在合理范围内进行规范化。
在我搜索是否应该对外键使用 null 时,我从比我拥有更多 DB 经验的人那里得到了以下答案。
我需要知道在模型 #2 的特定情况下使用 Null 是否是不好的做法,哪个模型更可取以及为什么。或者可能建议一个更好的模型。感谢您的任何输入。
预设和自定义类型都有一个“类型”表。我通过使用预设类型预填充“类型”表并为我以后可以添加的 future 预设类型留出大约 1500 个保留空间来实现这一点。
优点:简单,没有额外的表,没有连接,可能是最快的选择,并且从长远来看可能更少的数据库空间(4 字节 type_id)。并且 widgets 表 type_id FK 永远不会为 NULL。
缺点:将预设和自定义类型混合在一起可能不是很好的规范化做法,因为预设不需要一些字段,如“account_id”等。如果我想要超过 1500 个预设(极不可能),我需要计算别的东西了。该模型还在类型表中使用标记/占位符值来表示预设和预留预设点。
CREATE TABLE accounts (
account_id INT UNSIGNED AUTO_INCREMENT NOT NULL,
# Other Columns...,
PRIMARY KEY (account_id)
);
CREATE TABLE widgets (
widget_id INT UNSIGNED AUTO_INCREMENT NOT NULL,
account_id INT UNSIGNED NOT NULL,
type_id INT UNSIGNED NOT NULL,
PRIMARY KEY (widget_id),
FOREIGN KEY (account_id) REFERENCES accounts(account_id) ON DELETE CASCADE,
FOREIGN KEY (type_id) REFERENCES types(type_id)
);
CREATE TABLE types (
type_id INT UNSIGNED AUTO_INCREMENT NOT NULL,
account_id INT UNSIGNED NOT NULL,
name VARCHAR(100) NOT NULL,
PRIMARY KEY (type_id),
FOREIGN KEY (account_id) REFERENCES accounts(account_id)
);
预设和自定义类型的单独小部件类型表。 'widgets' 表具有用于预设类型和自定义类型的可为空的 FK 字段。 Check 约束确保其中一个为空,另一个不为空。
优点:数据库中只有 1 个额外的表。除了可能为空的 FK 之外,没有标记/占位符值。无需预留预置值空间, future 预置类型添加无限制。
缺点:对于 preset_type_id 或 custom_type_id,widgets 表中的每条记录都使用一个 FK null。
CREATE TABLE accounts (
account_id INT UNSIGNED AUTO_INCREMENT NOT NULL,
# Other Columns...,
PRIMARY KEY (account_id)
);
CREATE TABLE widgets (
widget_id INT UNSIGNED AUTO_INCREMENT NOT NULL,
account_id INT UNSIGNED NOT NULL,
preset_type_id INT UNSIGNED DEFAULT NULL,
custom_type_id INT UNSIGNED DEFAULT NULL,
PRIMARY KEY (widget_id),
FOREIGN KEY (account_id) REFERENCES accounts(account_id) ON DELETE CASCADE,
FOREIGN KEY (preset_type_id) REFERENCES preset_types(preset_type_id),
FOREIGN KEY (custom_type_id) REFERENCES custom_types(custom_type_id),
CHECK ((preset_type_id IS NOT NULL AND custom_type_id IS NULL) OR (preset_type_id IS NULL AND custom_type_id IS NOT NULL) )
);
CREATE TABLE preset_types (
preset_type_id INT UNSIGNED AUTO_INCREMENT NOT NULL,
name VARCHAR(100) NOT NULL,
PRIMARY KEY (preset_type_id)
);
CREATE TABLE custom_types (
custom_type_id INT UNSIGNED AUTO_INCREMENT NOT NULL,
account_id INT UNSIGNED NOT NULL,
name VARCHAR(100) NOT NULL,
PRIMARY KEY (custom_type_id),
FOREIGN KEY (account_id) REFERENCES accounts(account_id)
);
使用中间表 widget_preset_types 和 widget_custom_types。如果小部件具有预设类型,它将在 widget_preset_types 表中引用,或者如果小部件具有自定义类型,它将在 widget_custom_types 表中引用。
优点:可能是最规范化的模型。从不使用空值或 FK 空值。没有使用 sentinal/placehodler 值。
缺点:在数据库中添加 3 个额外的表只是为了确定小部件类型。除了带有自定义/预设类型的小部件之外,我的数据库中还有其他东西,这意味着我可以使用此模型向我的数据库添加至少 12 个额外的表。它是否过度标准化?我将不得不使用某种类型的联接来同时从 3 个表中获取所有小部件信息和类型信息。我将不得不检查 custom_type_id 或 preset_type_id 是否在连接中返回,可能使用的代码比我在 Model#2 中用于检查空值的代码要多。可能比模型 1 和 2 慢。更多表意味着更多索引意味着更多内存。
CREATE TABLE accounts (
account_id INT UNSIGNED AUTO_INCREMENT NOT NULL,
# Other Columns...,
PRIMARY KEY (account_id)
);
CREATE TABLE widgets (
widget_id INT UNSIGNED AUTO_INCREMENT NOT NULL,
account_id INT UNSIGNED NOT NULL
PRIMARY KEY (widget_id),
FOREIGN KEY (account_id) REFERENCES accounts(account_id) ON DELETE CASCADE
);
CREATE TABLE preset_types (
preset_type_id INT UNSIGNED AUTO_INCREMENT NOT NULL,
name VARCHAR(100) NOT NULL,
PRIMARY KEY (preset_type_id)
);
CREATE TABLE custom_types (
custom_type_id INT UNSIGNED AUTO_INCREMENT NOT NULL,
account_id INT UNSIGNED NOT NULL,
name VARCHAR(100) NOT NULL,
PRIMARY KEY (custom_type_id),
FOREIGN KEY (account_id) REFERENCES accounts(account_id) ON DELETE CASCADE
);
CREATE TABLE widget_preset_types (
widget_id INT UNSIGNED NOT NULL UNIQUE,
preset_type_id INT UNSIGNED NOT NULL,
PRIMARY KEY (widget_id),
FOREIGN KEY (widget_id) REFERENCES widgets(widget_id) ON DELETE CASCADE,
FOREIGN KEY (preset_type_id) REFERENCES preset_types(preset_type_id)
);
CREATE TABLE widget_custom_types (
widget_id INT UNSIGNED NOT NULL UNIQUE,
custom_type_id INT UNSIGNED NOT NULL,
PRIMARY KEY (widget_id),
FOREIGN KEY (widget_id) REFERENCES widgets(widget_id) ON DELETE CASCADE,
FOREIGN KEY (custom_type_id) REFERENCES custom_types(custom_type_id)
);
最佳答案
一些非常优秀的设计者在外键中使用 NULL 而没有产生不良后果。我自己就是这样倾斜的。可为空的 FK 表示可选关系。在实体没有关系的情况下,FK 包含 NULL。空间开销很小。当跨两个表进行连接(更准确地说是等值连接)时,FK 中包含 NULL 的实例将从连接中删除,这是合适的。
说到这里,我要向你推荐第四种方法。这涉及到总共4张表、accounts、widgets、types和custom_types。 custom_types 表使用一种称为 Shared-primary-key 的技术,概述如下。
CREATE TABLE accounts (
account_id INT UNSIGNED AUTO_INCREMENT NOT NULL,
# Other Columns...,
PRIMARY KEY (account_id)
);
CREATE TABLE widgets (
widget_id INT UNSIGNED AUTO_INCREMENT NOT NULL,
account_id INT UNSIGNED NOT NULL,
type_id INT UNSIGNED NOT NULL,
PRIMARY KEY (widget_id),
FOREIGN KEY (account_id) REFERENCES accounts(account_id) ON DELETE CASCADE,
FOREIGN KEY (type_id) REFERENCES types(type_id)
);
CREATE TABLE types (
type_id INT UNSIGNED AUTO_INCREMENT NOT NULL,
account_id INT UNSIGNED NOT NULL,
name VARCHAR(100) NOT NULL,
PRIMARY KEY (type_id),
FOREIGN KEY (account_id) REFERENCES accounts(account_id)
CREATE TABLE custom_types (
type_id INT NOT NULL,
account_id INT UNSIGNED NOT NULL,
PRIMARY KEY (type_id),
FOREIGN KEY (type_id) REFERENCES types(type_id),
FOREIGN KEY (account_id) REFERENCES accounts(account_id)
);
custom_types 中的 type_id 列是共享主键。请注意,它既被声明为主键又被声明为外键,并且它不使用自动编号。它是相应条目类型中主键的副本。自定义类型表包含自定义类型中存在但预设类型中不存在的所有数据。
对于预设类型,在types中有一个条目,但在custom_types中没有条目。对于 custom_types,首先在 types 中创建一个条目,然后将 type_id 的结果值与 account_id 一起复制到 custom_types 中。
如果您使用 INNER JOIN 类型和 custom_types,则预设类型会退出连接。如果您希望在单个联接中同时使用自定义类型和预设类型,则必须使用 LEFT JOIN 或 RIGHT JOIN 来获得该效果。请注意,LEFT 或 RIGHT JOIN 的结果将包含一些 NULL,即使这些 NULL 未存储在数据库中。
点击这个shared-primary-key将为您提供共享主键技术的更详细描述。
关于mysql - 关于可空外键和规范化的数据库设计问题,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/57905620/
关闭。这个问题是off-topic .它目前不接受答案。 想要改进这个问题? Update the question所以它是on-topic用于堆栈溢出。 关闭 12 年前。 Improve thi
我有一个动态网格,其中的数据功能需要正常工作,这样我才能逐步复制网格中的数据。假设在第 5 行中,我输入 10,则从第 6 行开始的后续行应从 11 开始读取,依此类推。 如果我转到空白的第一行并输入
我有一个关于我的按钮消失的问题 我已经把一个图像作为我的按钮 用这个函数动画 function example_animate(px) { $('#cont
我有一个具有 Facebook 连接和经典用户名/密码登录的网站。目前,如果用户单击 facebook_connect 按钮,系统即可运行。但是,我想将现有帐户链接到 facebook,因为用户可以选
我有一个正在为 iOS 开发的应用程序,该应用程序执行以下操作 加载和设置注释并启动核心定位和缩放到位置。 map 上有很多注释,从数据加载不会花很长时间,但将它们实际渲染到 map 上需要一段时间。
我被推荐使用 Heroku for Ruby on Rails 托管,到目前为止,我认为我真的会喜欢它。只是想知道是否有人可以帮助我找出问题所在。 我按照那里的说明在该网站上创建应用程序,创建并提交
我看过很多关于 SSL 错误的帖子和信息,我自己也偶然发现了一个。 我正在尝试使用 GlobalSign CA BE 证书通过 Android WebView 访问网页,但出现了不可信错误。 对于大多
我想开始使用 OpenGL 3+ 和 4,但我在使用 Glew 时遇到了问题。我试图将 glew32.lib 包含在附加依赖项中,并且我已将库和 .dll 移动到主文件夹中,因此不应该有任何路径问题。
我已经盯着这两个下载页面的源代码看了一段时间,但我似乎找不到问题。 我有两个下载页面,一个 javascript 可以工作,一个没有。 工作:http://justupload.it/v/lfd7不是
我一直在使用 jQuery,只是尝试在单击链接时替换文本字段以及隐藏/显示内容项。它似乎在 IE 中工作得很好,但我似乎无法让它在 FF 中工作。 我的 jQuery: $(function() {
我正在尝试为 NDK 编译套接字库,但出现以下两个错误: error: 'close' was not declared in this scope 和 error: 'min' is not a m
我正在使用 Selenium 浏览器自动化框架测试网站。在测试过程中,我切换到特定的框架,我们将其称为“frame_1”。后来,我在 Select 类中使用了 deselectAll() 方法。不久之
我正在尝试通过 Python 创建到 Heroku PostgreSQL 数据库的连接。我将 Windows10 与 Python 3.6.8 和 PostgreSQL 9.6 一起使用。 我从“ht
我有一个包含 2 列的数据框,我想根据两列之间的比较创建第三列。 所以逻辑是:第 1 列 val = 3,第 2 列 val = 4,因此新列值什么都没有 第 1 列 val = 3,第 2 列 va
我想知道如何调试 iphone 5 中的 css 问题。 我尝试使用 firelite 插件。但是从纵向旋转到横向时,火石占据了整个屏幕。 有没有其他方法可以调试 iphone 5 中的 css 问题
所以我有点难以理解为什么这不起作用。我正在尝试替换我正在处理的示例站点上的类别复选框。我试图让它做以下事情:未选中时以一种方式出现,悬停时以另一种方式出现(选中或未选中)选中时以第三种方式出现(而不是
Javascript CSS 问题: 我正在使用一个文本框来写入一个 div。我使用以下 javascript 获取文本框来执行此操作: function process_input(){
你好,我很难理解 P、NP 和多项式时间缩减的主题。我试过在网上搜索它并问过我的一些 friend ,但我没有得到任何好的答案。 我想问一个关于这个话题的一般性问题: 设 A,B 为 P 中的语言(或
你好,我一直在研究 https://leetcode.com/problems/2-keys-keyboard/并想到了这个动态规划问题。 您从空白页上的“A”开始,完成后得到一个数字 n,页面上应该
我正在使用 Cocoapods 和 KIF 在 Xcode 服务器上运行持续集成。我已经成功地为一个项目设置了它来报告每次提交。我现在正在使用第二个项目并收到错误: Bot Issue: warnin
我是一名优秀的程序员,十分优秀!