項目組會(huì )議上討論的關(guān)于不定字段數目的數據庫表問(wèn)題并沒(méi)有結果,今天繼續分析之后發(fā)現問(wèn)題可能還更大。當時(shí)討論的結果是可能采用四種技術(shù):
【一】
現在我們來(lái)分析一下四種技術(shù)的優(yōu)劣,不過(guò)首先可以排除的是第一點(diǎn)動(dòng)態(tài)增加字段的方法,因為在實(shí)際操作時(shí)候幾乎是不可能的(sqlserver太慢,oracle索性不支持),基本可以不討論就排除。剩下后三點(diǎn)。
【二】
先來(lái)討論預留空白字段的方法,基本原理就是在數據庫表設計的時(shí)候加入一些多余的字段,看下面的代碼:
CREATE
}
然后看實(shí)際運行時(shí)候的需要,動(dòng)態(tài)分配字段給系統使用,也許需要一個(gè)這樣的結構來(lái)描述分配情況:
public class Available
{
}
也許某一時(shí)刻的數據狀況是這樣的: CurrentUnusedFieldNumber
現在的問(wèn)題是如果要配合Hibernate,如何來(lái)處理?以上段的數據使用狀況為例子,如果我們的類(lèi)定義是這樣:
public class Entity01
{
}
也許只需要修改一下xxx.hbm.xml,把 SomeId 和 field0做成對應就ok了。但是在運行時(shí)我們怎么知道會(huì )有這樣的類(lèi)定義?除非我們做動(dòng)態(tài)代碼生成,自動(dòng)編譯也許可以,但是問(wèn)題也許就到其他方面去了;如果我們不用動(dòng)態(tài)定義,那么類(lèi)就只能是這樣:
public class Entity01
{
}
使用的時(shí)候,用 entity01.ExtraFieldAndValues.setValue("AnyName", "boss")的方式來(lái)引用,也許這樣是修改最少的了,但是問(wèn)題是Hibernate不支持這樣的方法。
【三】
再來(lái)討論單字段存儲的方法,我們使用這樣的數據庫表定義
CREATE TABLE Sample
(
)
然后對應這樣的類(lèi)定義
public class Entity01
{
}
我們的代碼就可以這樣使用:string id =entity01.ExtraNameAndValueFromXml
是不是折衷一下,把兩種方法的優(yōu)點(diǎn)和起來(lái)?問(wèn)題有來(lái)了:怎么保持兩者之間數據的同步?難道要我們用存儲過(guò)程去解析xml內容?
所以,一個(gè)兩難的問(wèn)題,需要我們認真去解決。我們通過(guò)認真的需求分析,也許可以減少可變字段的數量,但是只要有一個(gè)可變字段或者可變的可能性存在,我們始終要去解決這個(gè)兩難的問(wèn)題。
期待繼續討論。
【四】
還有一種方法就是改列為行,用另外一個(gè)表存放擴展字段,定義可以如下:
CREATETABLE SampleFields
(
)
其中idSample關(guān)聯(lián)到Sample表的id字段(我沒(méi)有寫(xiě)出來(lái))。這樣的話(huà),Hibernate很容易支持,也可以支持Sql的查詢(xún),而且可以支持把內容放到Hashtable中去,看起來(lái)是目前最好的方式了。但是在大容量數據的時(shí)候,SampleFields表的數據會(huì )是主表數據量的N倍(看定制的字段數目多少而定),同樣存在有很?chē)乐氐男阅軉?wèn)題。
哪位高人還能再給一個(gè)方案?
--------2005-7-22新增-----------
很多朋友給出了很好的建議,其中蛙蛙池塘
圖所表示的內容簡(jiǎn)單來(lái)說(shuō)是這樣:
1。一個(gè)很寬的表ProductAttributeValues,包含用到的幾乎所有可能的類(lèi)型的值,但是每次只能用一個(gè)類(lèi)型的值
2。將可變的列轉為行,存放到上表中
3。為了存放類(lèi)型定義和一些下拉列表的內容,設計了ProductAttributeTemplate
4。ProductListItems中存放Product中所有項的說(shuō)明和順序。
這種思路其實(shí)就是把產(chǎn)品的“知識級”(設計模式用語(yǔ))和“操作級”都表現出來(lái)了,如果要劃分,則圖的左上角三個(gè)表屬于“操作級”,其余的屬于“知識級”。wljcan
怡紅公子
不過(guò)我現在覺(jué)得xml字段還是要的,作為輔助存儲手段,因為畢竟未來(lái)查詢(xún)起來(lái)可能會(huì )更方便些,然后結合“觀(guān)察模式”也就是類(lèi)似上圖的方法作為主要的存儲手段,雖然有一些冗余,結合我的使用NVelocity 解析 PowerDesigner 的cdm文件
CREATE TABLE [dbo].[ProductAttributeDataType
[DataTypeId] [int] NOT NULL ,
[Description] [varchar] (30) COLLATE SQL_Latin1_General_CP1_CI_ASNOT NULL
)
CREATE TABLE [dbo].[ProductAttributeListItem
[LookupListId] [int] NOT NULL ,
[ListItemId] [int] IDENTITY (1, 1) NOT NULL ,
[DisplayName] [varchar] (50) COLLATE SQL_Latin1_General_CP1_CI_ASNOT NULL
)
CREATE TABLE [dbo].[ProductAttributeLookupLi
[LookupListId] [int] IDENTITY (1, 1) NOT NULL ,
[Name] [varchar] (80) COLLATE SQL_Latin1_General_CP1_CI_AS NOTNULL
)
CREATE TABLE [dbo].[ProductAttributeTemplate
[PATCategoryId] [int] IDENTITY (1, 1) NOT NULL ,
[Name] [varchar] (50) COLLATE SQL_Latin1_General_CP1_CI_AS NOT NULL,
[DisplayName] [varchar] (255) COLLATE SQL_Latin1_General_CP1_CI_ASNOT NULL
)
CREATE TABLE [dbo].[ProductAttributeTemplate
[AttributeTemplateId] [int] IDENTITY (1, 1) NOT NULL ,
[Name] [varchar] (50) COLLATE SQL_Latin1_General_CP1_CI_AS NOT NULL,
[DataType] [int] NOT NULL ,
[LookupListId] [int] NULL ,
[PATCategoryId] [int] NOT NULL ,
[CustomerHelp] [varchar] (4000) COLLATESQL_Latin1_General_CP1_CI_AS NULL ,
[DisplayName] [varchar] (255) COLLATE SQL_Latin1_General_CP1_CI_ASNULL
)
CREATE TABLE [dbo].[ProductAttributeValues] (
[ProductId] [int] NOT NULL ,
[AttributeTemplateId] [int] NOT NULL ,
[valueInt] [int] NULL ,
[valueStr] [varchar] (255) COLLATE SQL_Latin1_General_CP1_CI_ASNULL ,
[valueMemo] [text] COLLATE SQL_Latin1_General_CP1_CI_AS NULL,
[valueBool] [bit] NULL
)
CREATE TABLE [dbo].[ProductListItems] (
[ProductListId] [int] NOT NULL ,
[ProductId] [int] NOT NULL ,
[ItemDescription] [varchar] (80) COLLATESQL_Latin1_General_CP1_CI_AS NULL ,
[DisplaySequence] [int] NOT NULL
)
不知道大家有沒(méi)有看懂這些表之間的關(guān)系,這是一個(gè)產(chǎn)品目錄的表,產(chǎn)品有不確定的屬性,每個(gè)屬性對應的數據類(lèi)型也不一樣,而且有的是用下拉列表選擇的,有的是是或否,有的是一段兒文字,這里用的就是數據模板來(lái)做的,這是里的一部分數據庫
以前有這樣的一個(gè)需求,不考慮像京東或者淘寶這樣分類(lèi)下有子分類(lèi)的情況,只考慮一層分類(lèi)的情況下,可以隨便添加分類(lèi),可以任意給商品添加屬性,而不需要更改表的結構.于是設計了一個(gè)這樣的結構,實(shí)現還是可以實(shí)現,一直在用,但是在操作上比較麻煩,大家討論下有沒(méi)有更好的方式.
-----------------------------以下是幾個(gè)關(guān)聯(lián)的對象 省去了 getter/setter 方法------------------
Field.java屬性
Java代碼
Java代碼
Java代碼
Java代碼
復雜商品的分類(lèi),類(lèi)似淘寶的分類(lèi)
1.每類(lèi)商品有無(wú)限級分類(lèi)
2.每個(gè)商品可能會(huì )有交叉分類(lèi)
3.每類(lèi)商品的擴展屬性不一樣
比如:
夾克的擴展屬性為
款式: 拉鏈夾克 風(fēng)格: 休閑 品牌: other/其它 適合季節: 春秋 尺碼: M L 顏色: 其它顏色 質(zhì)地:純棉
主板的擴展屬性為
品牌: 微星/MSI 類(lèi)型: Socket478 芯片組: Intel 845 平臺類(lèi)型: Intel平臺 寶貝成色:8成新
這些擴展屬性都會(huì )動(dòng)態(tài)的變化
那么問(wèn)題來(lái)了:
1.全文搜索如何合理建立?
2.可能后臺擴展屬性表是否需要動(dòng)態(tài)建立?
3.如果單件商品屬于交叉分類(lèi)的話(huà),查詢(xún)結果記錄重復是否需要?
4.高效的無(wú)限級分類(lèi)算法大家可否指點(diǎn)一二,這個(gè)困惑了我很久了?
不求完整解決,給個(gè)思路也成
每個(gè)商品都是一個(gè)xml文件每個(gè)xml文件有一個(gè)ID 每個(gè)xml文件可以采用這樣的結構
<名稱(chēng)/>
<屬性/>
<屬性/>
......
當檢索商品的時(shí)候 比如用Socket478芯片組檢索 就檢索出所有含有Socket478芯片組屬性節點(diǎn)的xml文件.用一個(gè)xql語(yǔ)句就可以搞定. 返回的結果是xml文件 可以用xsl進(jìn)行處理然后用css顯示
這類(lèi)問(wèn)題(無(wú)限級別分類(lèi),可以交叉分類(lèi))很難。
正是現代 tag,分類(lèi)時(shí)代的熱點(diǎn)問(wèn)題。我也考慮調查了很久。
如果解決得好,就可以被收購了。
xml 解決起來(lái)確實(shí)比 relation table 容易一些。xml全文檢索也比較容易做。
我想,winterwolf會(huì )跳出來(lái),終于等到了。
不過(guò),xml也有一些限制。如果能有一種專(zhuān)門(mén)描述這類(lèi)問(wèn)題的數據結構就好了。
我想到過(guò)幾種數據結構。不過(guò)都沒(méi)有想透。
Multiple Key Hashmap。多維數組。等。
xml確實(shí)是一種解決的辦法,tianxinet的子表方法有點(diǎn)繁瑣了,擴展屬性有很多表也帶來(lái)了維護的困難性了。。如果像淘寶的商品分類(lèi)那樣的話(huà),你的子表數量是很驚人的。。。。
繼續關(guān)注中
前幾天有人問(wèn)了我一個(gè)這樣的問(wèn)題,因為時(shí)間的關(guān)系,我當時(shí)嘗試做了幾種回答,比如將產(chǎn)品先分大類(lèi),為每個(gè)大類(lèi)設計一個(gè)產(chǎn)品表,在產(chǎn)品表中包括該類(lèi)的基本屬性,并預留一些字段作為擴展屬性,對于同一大類(lèi)不同的產(chǎn)品,考慮增加擴展表。不過(guò)這個(gè)答案似乎沒(méi)有得到認可。認真一想也是,如果這樣,表改有多少,查詢(xún)結構又該多復雜。
beyond_dream
方案一。
就一個(gè)產(chǎn)品表product,然后這個(gè)表里包括所有的產(chǎn)品屬性,每個(gè)屬性用一個(gè)字段表示。
方案二。
還是只用一個(gè)產(chǎn)品表product 。
與方案一不同的是,私有屬性設置為一個(gè)字段Private_Attribute ,
然后每個(gè)產(chǎn)品的多個(gè)私有屬性都放這個(gè)字段里,并且用一個(gè)分隔符號隔開(kāi)
比如書(shū)籍,就是它在 Private_Attribute 字段里 的表示就是 :
出版社||||作者||||出版日期
方案三;
產(chǎn)品表+ 私有屬性表 + 私有屬性值 表
產(chǎn)品表里 就包括一些產(chǎn)品的公共屬性
私有屬性表里 設置私有屬性的名稱(chēng) ,比如出版社 、作者 、出版日期
私有屬性值表 里就是 每個(gè)產(chǎn)品 私有屬性的值
例如:
產(chǎn)品表:
product_id= 1 ; product_name =《ajax實(shí)踐》
私有屬性表:
Attribute_id= 1 ; Attribute_name = 出版社
Attribute_id= 2 ; Attribute_name = 作者
私有屬性值表:
id =1 ; product_id = 1 ; Attribute_id = 1 ; Attribute_value =清華出版社
id =2 ; product_id = 1 ; Attribute_id = 2 ; Attribute_value =老外
方案四;
每個(gè)不同類(lèi)型的產(chǎn)品單獨設計一個(gè)數據庫,比如一個(gè)書(shū)籍的數據表product_book,一個(gè)MP3的數據表 product_mp3
1.對產(chǎn)品按照大類(lèi)進(jìn)行區分,每個(gè)大類(lèi)有一個(gè)產(chǎn)品表。產(chǎn)品表有該大類(lèi)產(chǎn)品最基本的關(guān)鍵屬性信息(應該控制在十個(gè)以?xún)?,這些屬性,也被用作關(guān)鍵字查詢(xún)索引),而接下來(lái),又有一些預定義的擴展屬性(命名如customized1,customized2,等等),這些擴展屬性有一個(gè)重要的前提,就是它們的屬性值都是列表選擇的,而不是自由輸入的(當然這些選擇項是可以在另外的表中定義的),這些屬性是用作在詳細的高級查詢(xún)中使用的,使用選擇而不是自由輸入第一是可以提高索引效率,第二是可以避免因為字符差錯引起的歧義,第三是可以使用區間值。每個(gè)不同的產(chǎn)品小類(lèi)有不同的customized定義。最后有再有幾個(gè)字段,則存放其他的由客戶(hù)特性帶來(lái)的屬性值,但是是以xml的形式存放,可以方便使用XPATH來(lái)提升效率。
http://raylinn.javaeye.com/admin/blogs/260407
ms和我以前想的這個(gè)有點(diǎn)像。。。 固定的屬性和模糊的介紹都通過(guò)lunce來(lái)搜索。。
問(wèn)題補充
我也是想用多個(gè)表,多個(gè)表有點(diǎn)不好的地方,就是如果用戶(hù)在A(yíng)表買(mǎi)了一件產(chǎn)品,又在B表買(mǎi)了一件產(chǎn)品,那么到最后統計用戶(hù)買(mǎi)過(guò)的產(chǎn)品時(shí),雖然可以從下訂單那個(gè)表讀出來(lái),但是感覺(jué)有點(diǎn)亂~~~
問(wèn)題補充:
產(chǎn)品類(lèi)別表(類(lèi)別id,名稱(chēng),...)
產(chǎn)品表(產(chǎn)品id,名稱(chēng),規格,...)
產(chǎn)品屬性表(產(chǎn)品屬性id,產(chǎn)品id,屬性id,值,...)
這種方法行不行?
問(wèn)題補充:
產(chǎn)品表(產(chǎn)品id,名稱(chēng),規格,...)
產(chǎn)品屬性表(產(chǎn)品屬性id,產(chǎn)品id,屬性id,值,...)
就說(shuō)這兩個(gè)表吧,當插入一件產(chǎn)品的時(shí)候,屬性應該是直接去到產(chǎn)品表的,如果加了一個(gè)屬性表,那么插入的時(shí)候,
難道可以插入一個(gè)產(chǎn)品id在產(chǎn)品表,然后又插入屬性在產(chǎn)品屬性表嗎?
方案一(推薦):在商品類(lèi)型已知的情況下,為每類(lèi)商品定制一個(gè)類(lèi)。class 手機,class筆記本
方案二:如果商品類(lèi)型位置,那就用一個(gè)通用的商品類(lèi),里面放個(gè)property
聯(lián)系客服