MySQL - Table 中的很多列 - 需要建议

MySQL - A lot of columns in a Table - needs suggestions

我想询问有关我的数据库之一的建议 table。下面是我的 table 名为 io_generatedcreate 语句,其中包含 30+ 列 。我的问题是拥有这样的列是否好,或者我应该将一些数据分隔在不同的 table 中?我正在使用 VB.net 在我的数据库中插入数据并且我正在使用 OOP。这是一个好习惯吗?还是有什么建议或建议可以创造一个美丽的table?

table 包含 8 logs、4 次输入和 4 次输出日志。每条日志都有branch_id(branch1...branch8),我还有一列判断是否log is edited(in1_edited...out4_edited),还有LateUndertimetotal working hours 的计算。所有这些都在 table.

提前致谢!非常感谢您的帮助。

CREATE TABLE `io_generated` (
  `id` int(11) unsigned NOT NULL AUTO_INCREMENT,
  `employee_id` int(11) unsigned DEFAULT NULL,
  `date` date DEFAULT NULL,
  `branch_id` mediumint(8) unsigned DEFAULT NULL,
  `in1` datetime DEFAULT NULL,
  `out1` datetime DEFAULT NULL,
  `in2` datetime DEFAULT NULL,
  `out2` datetime DEFAULT NULL,
  `in3` datetime DEFAULT NULL,
  `out3` datetime DEFAULT NULL,
  `in4` datetime DEFAULT NULL,
  `out4` datetime DEFAULT NULL,
  `branch1` mediumint(8) unsigned DEFAULT NULL,
  `branch2` mediumint(8) unsigned DEFAULT NULL,
  `branch3` mediumint(8) unsigned DEFAULT NULL,
  `branch4` mediumint(8) unsigned DEFAULT NULL,
  `branch5` mediumint(8) unsigned DEFAULT NULL,
  `branch6` mediumint(8) unsigned DEFAULT NULL,
  `branch7` mediumint(8) unsigned DEFAULT NULL,
  `branch8` mediumint(8) unsigned DEFAULT NULL,
  `in1_edited` tinyint(1) unsigned NOT NULL DEFAULT '0',
  `out1_edited` tinyint(1) unsigned NOT NULL DEFAULT '0',
  `in2_edited` tinyint(1) unsigned NOT NULL DEFAULT '0',
  `out2_edited` tinyint(1) unsigned NOT NULL DEFAULT '0',
  `in3_edited` tinyint(1) unsigned NOT NULL DEFAULT '0',
  `out3_edited` tinyint(1) unsigned NOT NULL DEFAULT '0',
  `in4_edited` tinyint(1) unsigned NOT NULL DEFAULT '0',
  `out4_edited` tinyint(1) unsigned NOT NULL DEFAULT '0',
  `late1` decimal(10,2) NOT NULL DEFAULT '0.00',
  `late2` decimal(10,2) NOT NULL DEFAULT '0.00',
  `late3` decimal(10,2) NOT NULL DEFAULT '0.00',
  `late4` decimal(10,2) NOT NULL DEFAULT '0.00',
  `total_late` decimal(10,2) NOT NULL DEFAULT '0.00',
  `hrs1` decimal(10,2) NOT NULL DEFAULT '0.00',
  `hrs2` decimal(10,2) NOT NULL DEFAULT '0.00',
  `total_whrs` decimal(10,2) NOT NULL DEFAULT '0.00',
  `ot_hrs` decimal(10,2) NOT NULL DEFAULT '0.00',
  `ut1` decimal(10,2) NOT NULL DEFAULT '0.00',
  `ut2` decimal(10,2) NOT NULL DEFAULT '0.00',
  `ut3` decimal(10,2) NOT NULL DEFAULT '0.00',
  `ut4` decimal(10,2) NOT NULL DEFAULT '0.00',
  `total_ut` decimal(10,2) NOT NULL DEFAULT '0.00',
  `day_count` double(10,2) NOT NULL DEFAULT '0.00',
  PRIMARY KEY (`id`),
  UNIQUE KEY `employee_id` (`employee_id`,`id`,`date`,`branch_id`),
  KEY `branch_id` (`branch_id`),
  KEY `branch1` (`branch1`),
  KEY `branch2` (`branch2`),
  KEY `branch3` (`branch3`),
  KEY `branch4` (`branch4`),
  KEY `branch5` (`branch5`),
  KEY `branch6` (`branch6`),
  KEY `branch7` (`branch7`),
  KEY `branch8` (`branch8`),
  CONSTRAINT `io_generated_ibfk_1` FOREIGN KEY (`employee_id`) REFERENCES `employee` (`Employee_ID`) ON DELETE CASCADE ON UPDATE CASCADE,
  CONSTRAINT `io_generated_ibfk_10` FOREIGN KEY (`branch8`) REFERENCES `branch` (`Branch_ID`),
  CONSTRAINT `io_generated_ibfk_2` FOREIGN KEY (`branch_id`) REFERENCES `branch` (`Branch_ID`) ON UPDATE CASCADE,
  CONSTRAINT `io_generated_ibfk_3` FOREIGN KEY (`branch1`) REFERENCES `branch` (`Branch_ID`),
  CONSTRAINT `io_generated_ibfk_4` FOREIGN KEY (`branch2`) REFERENCES `branch` (`Branch_ID`),
  CONSTRAINT `io_generated_ibfk_5` FOREIGN KEY (`branch3`) REFERENCES `branch` (`Branch_ID`),
  CONSTRAINT `io_generated_ibfk_6` FOREIGN KEY (`branch4`) REFERENCES `branch` (`Branch_ID`),
  CONSTRAINT `io_generated_ibfk_7` FOREIGN KEY (`branch5`) REFERENCES `branch` (`Branch_ID`),
  CONSTRAINT `io_generated_ibfk_8` FOREIGN KEY (`branch6`) REFERENCES `branch` (`Branch_ID`),
  CONSTRAINT `io_generated_ibfk_9` FOREIGN KEY (`branch7`) REFERENCES `branch` (`Branch_ID`)
) ENGINE=InnoDB DEFAULT CHARSET=latin1;

In1 到 Out4 与 Branch1 到 Branch 8 和 In1_edited 到 Out4_edited 相关。每个 In/Out 都有相应的分支和编辑标识符。

您的数据库应该规范化。 尽量减少列数,增加表数。 例如: 为 Employee、Branches、Timings 制作单独的表格 使用外键添加数据 那会比这个更容易理解

您不应在 table 中存储计算值。这违反了数据库规范化,因为您冗余地存储了值。示例:如果 table 包含列 x 和 y 以及 x_plus_y 并且它们在 table 行中的值为 10、12 和 13,则某些人或进程插入了无效数据,因为 10+12=22,而不是 13。也许一开始这些值是正确的,但后来其中一个值被更新了,更新的人或过程不知道他们也必须更新从属列。无论如何,现在一些查询可能使用 x_plus_y 而其他查询可能会从 x 和 y 计算结果,因此它们会给出不同的结果。那一定不是。

解决方案:不要存储值,因为您总是可以临时计算它们。但是,您可以编写视图或将生成的列添加到 table。生成的列只是在查询时或在其基值更改时完成的计算。例如

create table io_generated
(
  ...
  total_late decimal(10,2) generated always as (late1 + late2 + late3 + late4) virtual;
  ...
);

(规则的例外:在数据仓库中我们经常接受冗余。我们通常从没有冗余的数据库中获取数据并引入冗余以提高访问速度。)

除此之外,您的 table 看起来还不错。但是,我们无法知道它的设计是否合适,因为我们对您的数据知之甚少。一个更典型的设计是:

CREATE TABLE io_generated 
(
  io_generated_id INT(11) UNSIGNED NOT NULL AUTO_INCREMENT,
  employee_id INT(11) UNSIGNED NOT NULL,
  date DATE NOT NULL,
  branch_id MEDIUMINT(8) UNSIGNED NOT NULL
);

CREATE TABLE io_detail
(
  io_detail_id INT(11) UNSIGNED NOT NULL AUTO_INCREMENT,
  io_generated_id INT(11) NOT NULL,
  in_datetime DATETIME NOT NULL,
  out_datetime DATETIME NOT NULL,
  in_branch_id MEDIUMINT(8) UNSIGNED NOT NULL,
  out_branch_id MEDIUMINT(8) UNSIGNED NOT NULL,
  in_edited TINYINT(1) UNSIGNED NOT NULL DEFAULT 0,
  out_edited TINYINT(1) UNSIGNED NOT NULL DEFAULT 0
);

与您的设计相比,此设计既有优点也有缺点。

  • 例如判断10p.m之后是否有IOs是很容易的,因为它只是我们必须看的一个数据库列。另一方面,很难判断在 10 p.m 之后是否还有 IOs。对于第三个 IO,因为我们首先必须确定哪个详细信息行是第三个。
  • 很容易扩展它并有朝一日拥有五个 IOs 而不是只有四个。只需添加一行;我们根本不需要更改 table 设计。另一方面,很难或不可能保证恰好有四个 IOs.
  • 计算涉及多少不同的内部分支非常容易 (COUNT(DISTINCT in_branch_id))。
  • 不可能保证所有详细信息行的日期都与父行的日期匹配。然而,这可以通过从代理键切换到自然复合键轻松解决。

我希望这可以帮助您了解在决定一种设计或另一种设计时要考虑的因素。