需要有关SQL哲学的建议

时间:2012-12-21 21:16:29

标签: sql database-design data-modeling

在我提出有关编码的更多问题之前,我想首先找出制作数据库的最佳方法。我遇到了一个问题,我应该如何构建它以保持一切最小化,并且由于它的本质,我有很多重新发生的数据,我必须代表。

我设计定制衬衫,并有各种不同类型的衬衫供人们选择,既有成人和儿童两种尺码。例如,我有男士,女士,男士,女童和幼儿可以买到的圆领衬衫,插肩袖,铃铛袖和连帽衫。每种衬衫的价格相同,从幼儿尺寸到成人尺码的1x,然后2x,3x,4x和5x各自不同的价格。然后是每种衬衫的颜色选择有所不同,有些可能有4种颜色可供选择,有些则有32种。

所以,让我们以圆领衬衫为例。男性s-1x,女性s-1x,男孩xs-1x,女孩xs-1x和幼儿NB-18个月共有22行,将在表格中显示并且价格相同。 2X及以上仅适用于男性和女性,因此仅增加8行,仅为圆领衬衫共30行。当它进入颜色选项时,它们有32种不同的颜色可供选择。如果我要为他们所有人做各种尺寸,那么仅仅为单独的圆领衬衫提供960行,主要是重复数据只有一个小的变化。

我想到了并且想到最好将桌子上的这些物品当作库房中的实际物品,因为他们真的在库房里......你没有一盒衬衫给你你可以在侧面按一个按钮转到任何大小的颜色,你必须处理实际的衬衫和把它们放在某个地方的繁琐任务,所以我决定不要试图用一堆外键和索引来做得离谱,除此之外它变得单调乏味,当你只需要将它链接到的数据放在第一个表格中时,你最终需要表示同样多的表格。

如果我们只选择其他3种衬衫,并且仅使用所有颜色和尺寸的相同逻辑,那么仅有4件衬衫就会有3,840排,剩下的其他衬衫我不指望你可以说我在一张表中查看大约10,000行数据。随着时间的推移,这些数据将不断增长,我想知道它可能会变成什么样才能保持整齐有序。因此,我认为最好的逻辑可能就是像实际零售商店那样将其分解,即将部门分为男性,女性,男性,女性和婴儿。这样我就有5个单独的表,只有在用户决定“去那个部门”时才会调用,所以如果有男人想要男士衬衫,他就不会有7,000多行额外数据存在,甚至连适用于他正在寻找的东西。

这是一种更好的设置方法吗?或者最好将它全部保存为一个巨大的表格,只是从男性部分的表格中查询php中的“男士”衬衫,对女性和孩子一样?

我的下一个问题是可用的所有颜色选项,正如我之前所说的,一些衬衫只有4个,一些将有多达32个,所以其中一些是足够的数据形成一个表全部在他们的拥有,所以我真的可以为每种衬衫都有一张单独的桌子。我将在php中使用查询从表中填充我的项目,所以我不必在html和javascript中编写这么多代码。通过这种方式,我可以将其设置为SELECT ALL * table WHERE type=men,并且所有男士衬衫和自动填充每个人的编码。这样,当我添加并从表中取出内容时,它将自动更新。我已经知道我将如何做到这一点,但我只能想到这一点,因为我还没有决定一个好的方法来设置表格,这是我必须将它构建到来自。

例如,如果我将每件衬衫的所有颜色选项全部放在同一张桌子上,而不是将其分解,并将外键链接到其他表格来表示它们。这将是两种完全不同的方式来调用它,所以我坚持这一点,并不知道该去哪里。有什么建议吗?

3 个答案:

答案 0 :(得分:4)

零售组织通常由SKU(库存单位)提供。部门和颜色是服装的属性,而不是您为了会计或库存而识别服装的方式。

CREATE TABLE Skus (
  sku BIGINT UNSIGNED PRIMARY KEY,
  description TEXT,
  department VARCHAR(10) NOT NULL,
  color VARCHAR(10) NOT NULL,
  qty_in_stock INT UNSIGNED NOT NULL DEFAULT 0,
  unit_price NUMERIC(9,2) NOT NULL,
  FOREIGN KEY (department) REFERENCES Departments(department),
  FOREIGN KEY (color) REFERENCES Colors(color)
);

这比分成五个表更好,因为:

  • 您可以快速获得所有库存总价值的总和。
  • 您可以轻松切换给定SKU的部门。
  • 当有人购买一些服装时,他们的订单行项目会引用一个表而不是五个不同的表(这对于关系数据库来说是无效的。)

如果类似的实体存储在一个表中,还有很多其他任务示例会更容易。

答案 1 :(得分:2)

我知道你不想将它分解成单独的表,但我认为走多表路线是最好的。但是,我认为它并不像你想象的那么糟糕。我的建议如下。显然,您想要更改字段的名称,但这是一个快速表示:

<强>衬衫

  - id (primary key)
  - description
  - men (Y/N)
  - women (Y/N)
  - boy (Y/N)
  - girl (Y/N)
  - toddlers (Y/N)

尺寸

  - id (primary key)
  - shirt_id (foreign key)
  - Size

<强>颜色

  - id (primary key)
  - shirt_id (foreign key)
  - Color

<强>价格

  - id (primary key)
  - shirt_id (foreign key)
  - size_id (foreign key)
  - price

拥有这三个表使得您不必将所有10,000行存储在一个表中并进行维护,但数据仍然存在。保持数据分离到适当的位置不会复制不必要的信息。

想要拉所有男士衬衫吗?

SELECT * FROM shirts WHERE men = '1'

说实话,你应该至少有5或6张桌子。一个/两个包含大小和颜色的标签(一个表包含所有,或每个一个),另一个包含实际数据。这样可以使您的数据在所有内容中保持一致(例如:Blue vs blue)。你知道他们说的是什么,有一种方法可以给猫皮肤。

答案 2 :(得分:1)

您需要考虑一个名为“规范化”的数据库术语。规范化意味着一切都在数据库中存在,不应该列出两次,而是根据需要重复使用。人们最常犯的错误就是不要问或思考将来会发生什么,他们建立了一个几乎没有规范化的数据库,大量数据类型消耗大量内存,没有播种,并且完全没有灵活性以后改变成本很高,因为它是在不考虑未来的情况下制造的。

有很多级别的规范化,但最一致的是考虑一个简单的例子,我可以给你解释一些可以在以后应用于更大的事情的简单概念。这假设您可以访问SQL管理工作室,SSMS,但如果您使用的是MYSQL或Oracle,原则仍然非常相似,评论部分将显示我所获得的内容。如果您有SSMS,可以自行运行此示例,只需将其粘贴并按F5即可。如果你不只是看评论部分,虽然这些概念在行动中看起来比试图想象他们的意思更好。

Declare @Everything table (PersonID int, OrderID int, PersonName varchar(8), OrderName varchar(8) );

insert into @Everything values (1, 1, 'Brett', 'Hat'),(1, 2, 'Brett', 'Shirt'),(1, 3, 'Brett', 'Shoes'),(2,1,'John','Shirt'),(2,2,'John','Shoes');

-- very basic normalization level in that I did not even ATTEMPT to seperate entities into different tables for reuse.
-- I just insert EVERYTHING as I get in one place.  This is great for just getting off the ground or testing things.
-- but in the future you won't be able to change this easily as everything is here and if there is a lot of data it is hard 
-- to move it.  When you insert if you keep adding more and more and more columns it will get slower as it requires memory 
-- for the rows and the columns
Select Top 10 * from @Everything

declare @Person table ( PersonID int identity, PersonName varchar(8));

insert into @Person values ('Brett'),('John');

declare @Orders table ( OrderID int identity, PersonID int, OrderName varchar(8));

insert into @Orders values (1, 'Hat'),(1,'Shirt'),(1, 'Shoes'),(2,'Shirt'),(2, 'Shoes');

-- I now have tables storing two logic things in two logical places.  If I want to relate them I can use the TSQL language
-- to do so.  I am now using less memory for storage of the individual tables and if one or another becomes too large I can 
-- deal with them isolated.  I also have a seeding record (an ever increasing number) that I could use as a primary key to 
-- relate row position and for faster indexing
Select *
from @Person p 
    join @Orders o on p.PersonID = o.PersonID

declare @TypeOfOrder table ( OrderTypeID int identity, OrderType varchar(8));

insert into @TypeOfOrder values ('Hat'),('Shirt'),('Shoes')

declare @OrderBridge table ( OrderID int identity, PersonID int, OrderType int)

insert into @OrderBridge values (1, 1),(1,2),(1,3),(2,2),(2,3);


-- Wow I have a lot more columns but my ability to expand is now pretty flexible I could add even MORE products to the bridge table
-- or other tables I have not even thought of yet.  Now that I have a bridge table I have to list a product type ONLY once ever and 
-- then when someone orders it again I just label the bridge to relate a person to an order, hence the name bridge as it on it's own
-- serves nothing but relating two different things to each other.  This method takes more time to set up but in the end you need 
-- less rows of your database overall as you are REUSING data efficiently and effectively.
Select Top 10 *
from @Person p 
    join @OrderBridge o on p.PersonID = o.PersonID
    join @TypeOfOrder t on o.OrderType = t.OrderTypeID