数据库设计的5种常见关系,其中本文主要讲“多态”关系结构,以手机为例。 1,配置关系 --和其他表无任何关系的表。
例如:webConfig里的东西你存储到表里。 2,一对多关系 ,一张表包含另外一个表的主键作为外键。
例如:手机.品牌id=2, 这里的2是[品牌名称表]的id字段为2的纪录,品牌名称是"Nokia"。一个手机只能有一个品牌。 3,多对多,需要2张表,有一个包含两个外键的关系表。
例如: 手机1即属于"智能" 又属于"滑盖"组的, 一个组包含多个手机,一个手机可以属于多个组。 4,树型结构,常见的两钟:父ID设计和001002编码设计。
例如:手机的经销商分为 省/市/县 5,“多态”结构和多对多略有不同,如果需求中某表字段多少类型有非常大的不确定性,可以采用3个表来完成: 一个[主表](ID),
一个[属性名称表](属性ID.属性名称),
一个[属性值表],包括3个字段:
属性值(属性Value varchar(500))
主表ID
属性ID 这样可以作到最小冗余度。
(和常见的多对多关系不同的是:值统一用varchar来存储,因为这类型的值一般不会用来计算)。 比如:手机型号有几千种,除了共同属性外还有不同属性有几百个,属性名和值类型都不一样,有的手机有这属性,有的没有。
对于这样的“多态”,我们就采用上面的设计结构。
其效果相当于: 某奇怪手机.属性集合["某某奇怪属性名"]="某某奇怪值";
某变态手机.属性集合["某某变态属性名"]="某某变态值";
例如:webConfig里的东西你存储到表里。 2,一对多关系 ,一张表包含另外一个表的主键作为外键。
例如:手机.品牌id=2, 这里的2是[品牌名称表]的id字段为2的纪录,品牌名称是"Nokia"。一个手机只能有一个品牌。 3,多对多,需要2张表,有一个包含两个外键的关系表。
例如: 手机1即属于"智能" 又属于"滑盖"组的, 一个组包含多个手机,一个手机可以属于多个组。 4,树型结构,常见的两钟:父ID设计和001002编码设计。
例如:手机的经销商分为 省/市/县 5,“多态”结构和多对多略有不同,如果需求中某表字段多少类型有非常大的不确定性,可以采用3个表来完成: 一个[主表](ID),
一个[属性名称表](属性ID.属性名称),
一个[属性值表],包括3个字段:
属性值(属性Value varchar(500))
主表ID
属性ID 这样可以作到最小冗余度。
(和常见的多对多关系不同的是:值统一用varchar来存储,因为这类型的值一般不会用来计算)。 比如:手机型号有几千种,除了共同属性外还有不同属性有几百个,属性名和值类型都不一样,有的手机有这属性,有的没有。
对于这样的“多态”,我们就采用上面的设计结构。
其效果相当于: 某奇怪手机.属性集合["某某奇怪属性名"]="某某奇怪值";
某变态手机.属性集合["某某变态属性名"]="某某变态值";
解决方案 »
- 关于SqlServer2005统计sql的求助
- 请教各位前辈一查询语句,急!
- 请教一个导入Access的问题?
- 囡囡求助:谁能帮我解释下这些词啥意思哦!
- FILLFACTOR 根据什么而设置最合适
- 如何将decimal类型的数据转换成特殊的数字表示形式?
- 如何 把 约束定义成 表级
- SQL.Text := 'select top 1 * from zhu';//为什么这个模块需要把头一位的资料拉出来
- 我使用VB+ado向ms SQl 2000 评估版更改某一纪录的一个字段,结果所有的纪录的这一字段都给该了,语句在企业管理器中时没有任何问题的,到
- 数据库链接选用
- 求一SQL语句,在线等,急~!!!!!
- 可以帮我看看这个sql语句是什么意思么?
2,依赖性 (必须依赖主键)
3,非依赖性(除主键外都不依赖)比你拷贝来罗嗦一堆话经济实用。
<head>
<style type="text/css">
v\:*
{
behavior: url(#default#VML);
}
td
{
font-size: 12px;
cursor: move;
background-color: white;
}
th
{
font-size: 12px;
background-color: #cccccc;
cursor: move;
}
table
{
border-collapse: collapse;
border: 4px #999999 double;
position: absolute;
}
body
{
cursor: move;
}
span #aaa
{
}
</style> <title>察看表结构</title>
</head>
<body onmousedown='BodyMouseDown()' onmousemove='BodyMouseMove()' onmouseup='BodyMouseUp()'>
<%=SBTable %>
<span id="aaa" style='position: absolute; top: <%=WorkAreaNum*WorkAreaWidth %>; left: <%=WorkAreaNum*WorkAreaWidth %>;'>
超级大笨狼</span> <script type="text/jscript" language="javascript">
//定义区开始
var workPlaceSize=<%=WorkAreaWidth %>;
var workPlaceHeight= (<%=WorkAreaNum %> +1)* workPlaceSize ;//工作区大小
var workPlaceWidth= workPlaceHeight;
var currrentViewX= workPlaceWidth/2;
var currrentViewY=workPlaceHeight/2;
var lastMouseX=-1;
var lastMouseY=-1;
window.scrollTo(currrentViewX,currrentViewY);
document.onselectionchange=function(){document.selection.empty();}
var Obj;
var isBodyCapture=false;
// 定义区结束
function BodyMouseDown(){
if(event.srcElement.tagName=="BODY")
{
isBodyCapture=true;
}
lastMouseX=event.x;
lastMouseY=event.y;
}
//alert(window.screen.availWidth);
function BodyMouseMove(){
window.status=event.srcElement.tagName;
if(event.srcElement.tagName!="BODY" )
{
lostCaptrue();
}
if(isBodyCapture)
{
currrentViewX+=lastMouseX-event.x;
currrentViewY+=lastMouseY- event.y;
window.scrollTo(currrentViewX ,currrentViewY);
}
lastMouseX=event.x;
lastMouseY=event.y;
}
function BodyMouseUp(){
lostCaptrue();
}
function lostCaptrue(){
isBodyCapture=false;
}
var badTB=0;
function drawLine( tb1, td1,tb2,td2)
{
var table1=document.getElementById("Tb" + tb1);
var table2=document.getElementById("Tb" + tb2);
if(table1==undefined || table2==undefined)
{ badTB++;
return;}
var x1 = parseInt(table1.style.left);
var x2 = parseInt(table2.style.left);
var y1= parseInt(table1.style.top) +18+td1*18;
var y2= parseInt(table2.style.top)+18+td2*18;
var w1=parseInt(table1.offsetWidth);
var w2=parseInt(table2.offsetWidth);
var x3;var x4;
//如果两者差距不超过一个表格
if(Math.abs(x1-x2)<Math.min(w1,w2))
{
//向后深出50;
x1+=w1;
x2+=w2;
x3=x4=x1+50;
}
else
{
//如果从左往右。
if ((x1+w1) < x2)
{
x1 += w1;
}
if ((x2+w2) < x1)
{
x2 += w2;
}
//计算两个中间点的位置;各8分之一处。
x3 = parseInt((7 * x1 + x2) /8);
x4 = parseInt((x1 + 7 * x2) / 8);
}
var HTML="<v:PolyLine filled='false' strokeWeight=2pt Points='";
HTML+=x1 + "," + y1 + " " ;
HTML+=x3 + "," + y1 + " " ;
HTML+= x4 + "," + y2 + " " ;
HTML+=x2 + "," + y2;
HTML+="' StrokeColor='"+rndColor()+"'>" ;
HTML+="<v:stroke EndArrow='Classic' StartArrow='Oval' dashstyle='ShortDot' />" ;
HTML+="</v:PolyLine>" ;
//alert(HTML);
document.body.insertAdjacentHTML("beforeEnd",HTML);
}
function rndColor(){
var r=Math.floor((Math.random()*256)).toString(16);
var g=Math.floor((Math.random()*256)).toString(16);
var b=Math.floor((Math.random()*256)).toString(16);
return( colorString="#"+r+g+b);
}
function MouseDown(obj){
window.status=event.srcElement.tagName;
Obj=obj;
Obj.setCapture();
Obj.l=event.x-Obj.style.pixelLeft;
Obj.t=event.y-Obj.style.pixelTop;
}
function MouseMove(){
if(Obj!=null && (event.srcElement.tagName=="TH" || event.srcElement.tagName=="TABLE" ) ){
Obj.style.left = event.x-Obj.l;
Obj.style.top = event.y-Obj.t;
}
}
function MouseUp(){
lostCaptrue();
if(Obj!=null){
Obj.releaseCapture();
Obj=null;
}
}
<%=SBJs.ToString()%>
alert(badTB);
</script></body>
</html>
using System.Collections;
using System.Collections.Generic;
using System.Data;
using System.Web;
using System.Web.Security;
using System.Web.UI;
using System.Web.UI.HtmlControls;
using System.Web.UI.WebControls;
using System.Web.UI.WebControls.WebParts;
using System.Text;
using Kuqu.Component;
public partial class dataBaseInfo : BasePage
{
#region protected string SQL1 = @"SELECT O.object_id AS TableId,TableName=O.name,TableDesc= PTB.[value] FROM sys.objects O LEFT JOIN sys.extended_properties PTB ON PTB.class=1 AND PTB.minor_id=0 AND O.object_id=PTB.major_id WHERE O.type='U' ORDER BY TableName"; protected string SQL2 = @"
SELECT
TableName=CASE WHEN C.column_id=1 THEN O.name ELSE N'' END,
TableDesc=ISNULL(CASE WHEN C.column_id=1 THEN PTB.[value] END,N''),
Column_id=C.column_id,
ColumnName=C.name,
PrimaryKey=ISNULL(IDX.PrimaryKey,N''),
[IDENTITY]=CASE WHEN C.is_identity=1 THEN N'√'ELSE N'' END,
Computed=CASE WHEN C.is_computed=1 THEN N'√'ELSE N'' END,
Type=T.name,
Length=C.max_length,
Precision=C.precision,
Scale=C.scale,
NullAble=CASE WHEN C.is_nullable=1 THEN N'√'ELSE N'' END,
[Default]=ISNULL(D.definition,N''),
ColumnDesc=ISNULL(PFD.[value],N''),
IndexName=ISNULL(IDX.IndexName,N''),
IndexSort=ISNULL(IDX.Sort,N''),
Create_Date=O.Create_Date,
Modify_Date=O.Modify_date
FROM sys.columns C
INNER JOIN sys.objects O
ON C.[object_id]=O.[object_id]
AND O.type='U'
AND O.is_ms_shipped=0
INNER JOIN sys.types T
ON C.user_type_id=T.user_type_id
LEFT JOIN sys.default_constraints D
ON C.[object_id]=D.parent_object_id
AND C.column_id=D.parent_column_id
AND C.default_object_id=D.[object_id]
LEFT JOIN sys.extended_properties PFD
ON PFD.class=1
AND C.[object_id]=PFD.major_id
AND C.column_id=PFD.minor_id
AND PFD.name='MS_Description' -- 字段说明对应的描述名称(一个字段可以添加多个不同name的描述)
LEFT JOIN sys.extended_properties PTB
ON PTB.class=1
AND PTB.minor_id=0
AND C.[object_id]=PTB.major_id
AND PTB.name='MS_Description' -- 表说明对应的描述名称(一个表可以添加多个不同name的描述) LEFT JOIN -- 索引及主键信息
(
SELECT
IDXC.[object_id],
IDXC.column_id,
Sort=CASE INDEXKEY_PROPERTY(IDXC.[object_id],IDXC.index_id,IDXC.index_column_id,'IsDescending')
WHEN 1 THEN 'DESC' WHEN 0 THEN 'ASC' ELSE '' END,
PrimaryKey=CASE WHEN IDX.is_primary_key=1 THEN N'√'ELSE N'' END,
IndexName=IDX.Name
FROM sys.indexes IDX
INNER JOIN sys.index_columns IDXC
ON IDX.[object_id]=IDXC.[object_id]
AND IDX.index_id=IDXC.index_id
LEFT JOIN sys.key_constraints KC
ON IDX.[object_id]=KC.[parent_object_id]
AND IDX.index_id=KC.unique_index_id
INNER JOIN -- 对于一个列包含多个索引的情况,只显示第1个索引信息
(
SELECT [object_id], Column_id, index_id=MIN(index_id)
FROM sys.index_columns
GROUP BY [object_id], Column_id
) IDXCUQ
ON IDXC.[object_id]=IDXCUQ.[object_id]
AND IDXC.Column_id=IDXCUQ.Column_id
AND IDXC.index_id=IDXCUQ.index_id
) IDX
ON C.[object_id]=IDX.[object_id]
AND C.column_id=IDX.column_id WHERE O.name=N'{0}' -- 如果只查询指定表,加上此条件
ORDER BY O.name,C.column_id
";
#endregion
protected List<Table> MyTables = new List<Table>();
protected List<Relation> MyRelations = new List<Relation>();
protected WorkPlace[,] MyWorkPlace;
protected int WorkAreaNum;//工作区数量
protected int WorkAreaWidth = 400;//工作区大小
protected System.Text.StringBuilder SBJs = new StringBuilder();
protected System.Text.StringBuilder SBTable = new StringBuilder(); protected void Page_Load(object sender, EventArgs e)
{ //得到全部表信息,加入内存
makeTables();
//得到关系
getCoreRelation();
//放置表
placeTables(); //显示表
for (int i = 0; i < MyTables.Count; i++)
{
if (MyTables[i].WorkPlace != null)
{
RenderTable(MyTables[i]); }
}
//DrawLine( MyTables[7].findFiledByName("userid"), MyTables[0].PK);
//写脚本
}
private void getCoreRelation()
{
//得到核心关系,把所有表按照外键关系多少排序。
//得到表的外键集合PK_FKs for (int i = 0; i < MyTables.Count; i++)
{
if (MyTables[i].PK != null)//如果包含主键
{
//遍历所有字段
for (int j = 0; j < MyTables.Count; j++)
{
//排除掉自己表本身
if (MyTables[j].Id != MyTables[i].Id)
{
for (int k = 0; k < MyTables[j].Fileds.Count; k++)
{
//如果不是主键也不是ID字段
if ((!MyTables[j].Fileds[k].IsID) && (!MyTables[j].Fileds[k].IsPK) && MyTables[j].Fileds[k].Name == MyTables[i].PK.Name)
{
//加入主-外键集合
MyTables[i].PK_FKs.Add(MyTables[j].Fileds[k]);
MyTables[j].Fileds[k].FK = MyTables[i].PK;
//加入关系集合。
MyRelations.Add(new Relation(MyTables[j].Fileds[k], MyTables[i].PK));
}
}
}
}
} }
//按照关系数量拓扑排序。
MyTables.Sort(delegate(Table tb1, Table tb2)
{
return (tb2.PK_FKs.Count - tb1.PK_FKs.Count);
}
);
//纪录顺序;
for (int i = 0; i < MyTables.Count; i++)
{
MyTables[i].Order = i + 1;
//便历工作区。
}
for (int i = 0; i < MyRelations.Count; i++)
{
////drawLine(i,0,1,0);
SBJs.Append("drawLine(");
SBJs.Append(MyRelations[i].FromField.Table.Order);
SBJs.Append(",");
SBJs.Append(MyRelations[i].FromField.Id);
SBJs.Append(",");
SBJs.Append(MyRelations[i].ToField.Table.Order);
SBJs.Append(",");
SBJs.Append(MyRelations[i].ToField.Id);
SBJs.Append(");\n");
} }
{
//放置表格,设置X,Y
//得到单数个工作区数量,110-11-21
WorkAreaNum = 2 * (Convert.ToInt32(Math.Sqrt(MyTables.Count))) - 1; MyWorkPlace = new WorkPlace[WorkAreaNum, WorkAreaNum];
for (int x = 0; x < WorkAreaNum; x++)
{
for (int y = 0; y < WorkAreaNum; y++)
{
MyWorkPlace[x, y] = new WorkPlace(x, y);
}
}
//找到中间那个区
int centerArea = getHalfInt(1, WorkAreaNum);
//从中间开始放,按照9宫格的位置
//得到核心的9个
WorkPlace[] WorkPlaceCenter9 = new WorkPlace[9];
WorkPlaceCenter9[0] = new WorkPlace(centerArea, centerArea);
WorkPlaceCenter9[1] = new WorkPlace(getHalfInt(centerArea, WorkAreaNum), centerArea);
WorkPlaceCenter9[2] = new WorkPlace(getHalfInt(1, centerArea), centerArea);
WorkPlaceCenter9[3] = new WorkPlace(centerArea, getHalfInt(1, centerArea));
WorkPlaceCenter9[4] = new WorkPlace(centerArea, getHalfInt(centerArea, WorkAreaNum));
WorkPlaceCenter9[5] = new WorkPlace(getHalfInt(centerArea, WorkAreaNum), getHalfInt(1, centerArea));
WorkPlaceCenter9[6] = new WorkPlace(getHalfInt(1, centerArea), getHalfInt(1, centerArea));
WorkPlaceCenter9[7] = new WorkPlace(getHalfInt(1, centerArea), getHalfInt(centerArea, WorkAreaNum));
WorkPlaceCenter9[8] = new WorkPlace(getHalfInt(centerArea, WorkAreaNum), getHalfInt(centerArea, WorkAreaNum));
//先放9个
for (int i = 0; i < 9; i++)
{
if (MyTables.Count > i)
{
placeTable(MyTables[i], WorkPlaceCenter9[i]);
}
} for (int j = 0; j < 9; j++)
{
for (int k = 1; k < 3; k++)
{
//寻找9个表每个中间表周围的空地
List<WorkPlace> WorkPlaceAroundCenter = WorkPlaceCenter9[j].getNearWorkPlace(this.MyWorkPlace, k);
for (int i = 0; i < WorkPlaceAroundCenter.Count; i++)
{
//找到一个可以放的表。
Table t = WorkPlaceCenter9[0].Table.findTableWithoutPlace();
if (!string.IsNullOrEmpty(t.Name))
{
placeTable(t, WorkPlaceAroundCenter[i]);
}
else
{
break;
} }
}
} } private void placeTable(Table tb, WorkPlace w)
{
//放一个表格
tb.X = w.X * WorkAreaWidth;
tb.Y = w.Y * WorkAreaWidth;
w.Table = tb;
tb.WorkPlace = w; }
private int getHalfInt(int min, int max)
{
if (max > min)
{
return (Convert.ToInt32(Math.Ceiling((double)((max + min) / 2))));
}
else
{
return 0;
}
}
private void makeTables()
{
//得到全部表
DataTable dt1 = SqlHelper.ExecuteDataset(DataHelper.ConnectionString, CommandType.Text, SQL1).Tables[0]; int tableId = 1;
for (int i = 0; i < dt1.Rows.Count; i++)
{
string tableName = dt1.Rows[i]["TableName"].ToString();
Table newTable = new Table(tableName);
newTable.Id = tableId;
newTable.Description = dt1.Rows[i]["TableDesc"].ToString();
//得到字段
string sql = string.Format(SQL2, tableName);
DataTable dt2 = SqlHelper.ExecuteDataset(DataHelper.ConnectionString, CommandType.Text, sql).Tables[0];
for (int j = 0; j < dt2.Rows.Count; j++)
{
Filed newFiled = new Filed(dt2.Rows[j]);
if (newFiled.IsID || newFiled.IsPK)
{
newTable.PK = newFiled;
}
newTable.Fileds.Add(newFiled);
newFiled.Id = j;
newFiled.Table = newTable; }
newTable.Width = 250;
//18 * n +26
newTable.Height = newTable.Fileds.Count * 18 + 26;
MyTables.Add(newTable);
tableId++;
//RenderTable( newTable); }
if (MyTables.Count < 1)
{
Response.Write("没有表,请重新设置数据库连接");
Response.End();
} }
private void RenderTable(Table tb)
{ SBTable.Append(@"
<TABLE id='Tb" + tb.Order + "' title='" + tb.Id + "'");
string str = " border='1' bordercolor='#999999'style='top:{0};left:{1}' onmousedown='MouseDown(this)' onmousemove='MouseMove()' onmouseup='MouseUp()'>";
int X = tb.X;
int Y = tb.Y;
str = string.Format(str, Y, X);
SBTable.Append(str);
SBTable.Append(@"<TR ><th colspan=3 >");
SBTable.Append("[" + tb.Id + "]" + tb.Name + "(" + tb.Description + tb.Fileds.Count + "个字段" + tb.PK_FKs.Count + "个关系");
SBTable.Append(@"</th></TR>");
for (int i = 0; i < tb.Fileds.Count; i++)
{
tb.Fileds[i].X = tb.X;
tb.Fileds[i].Y = tb.Y + 26 + 18 * i - 9;
RenderField(tb.Fileds[i]);
}
SBTable.Append(@"</TABLE>");
}
private void RenderField(Filed f)
{
SBTable.Append(@"<TR>");
SBTable.Append(@"<TD style='padding-left:20px;");
if (f.IsID || f.IsPK)
{
SBTable.Append("color:red;");
//font-family:wingdings;
SBTable.Append("' onmousedown='return(false);'>");
SBTable.Append("<B>");
SBTable.Append(f.Name);
SBTable.Append("</B>");
}
else
{
SBTable.Append("' onmousedown='return(false);'>" + f.Name);
} SBTable.Append(@"</TD>");
SBTable.Append(@"<TD>" + f.Type);
SBTable.Append(@"</TD>");
SBTable.Append(@"<TD>" + f.Description);
SBTable.Append(@"</TD>");
SBTable.Append(@"</TR>"); }}
{
public Point(int x, int y) { this.X = x; this.Y = y; }
public int X;
public int Y;
}
public sealed class WorkPlace
{
public WorkPlace(int x, int y) { this.X = x; this.Y = y; }
public int X;
public int Y;
public Table Table;
public List<WorkPlace> getNearWorkPlace(WorkPlace[,] ws, int d)
{
//得到周围邻近的空地
List<WorkPlace> re = new List<WorkPlace>();
for (int x = this.X - d; x < this.X + d; x++)
{
for (int y = this.Y - d; y < this.Y + d; y++)
{
if (x >= 0 && y >= 0 && x != this.X && y != this.Y && x < ws.GetUpperBound(1) && y < ws.GetUpperBound(1))
{
if (ws[x, y].Table == null)
{
re.Add(ws[x, y]);
}
}
}
}
return (re);
}
}
public sealed class Relation
{
public Relation(Filed fromField, Filed toField)
{
this.FromField = fromField;
this.ToField = toField; }
public Filed FromField;
public Filed ToField;}
public sealed class Table
{
public Table(string name) { this.Name = name; }
public int Id;
public int Order;
public int X;
public int Y;
public int Width;
public int Height;
public string Name;
public Filed PK;
public string Description;
public WorkPlace WorkPlace;
public List<Filed> Fileds = new List<Filed>();
//我的主键有多少外键,XX表的xx字段
public List<Filed> PK_FKs = new List<Filed>();
public Table findTableWithoutPlace()
{
//找到一个没放置的表;
Table re=new Table("");
for (int i = 0; i < PK_FKs.Count; i++)
{
if (PK_FKs[i].Table.WorkPlace == null)
{
re = PK_FKs[i].Table;
break;
}
}
return re;
}
public List<Table> getTablesWithoutPlace()
{
List<Table> re = new List<Table>();
for (int i = 0; i < PK_FKs.Count; i++)
{ if (PK_FKs[i].Table.WorkPlace == null)
{
re.Add(PK_FKs[i].Table);
}
}
return re;
}
public Filed findFiledByName(string FiledName)
{
for (int i = 0; i < this.Fileds.Count; i++)
{
if (this.Fileds[i].Name.ToLower() == FiledName.ToLower())
{
return this.Fileds[i];
}
}
return null;
}
}
public sealed class Filed
{
public Filed(DataRow row)
{
this.Name = row["ColumnName"].ToString();
this.IsPK = (row["PrimaryKey"].ToString() == "√") ? true : false;
this.IsID = (row["IDENTITY"].ToString() == "√") ? true : false;
this.Type = row["Type"].ToString();
this.Length = row["Length"].ToString();
this.NullAble = (row["NullAble"].ToString() == "√") ? true : false;
this.Default = row["Default"].ToString();
this.Description = row["ColumnDesc"].ToString(); } public string Name;
public int Id;
public bool IsPK;
public bool IsID;
public string Type;
public string Length;
public bool NullAble;
public string Default;
public string Description;
public int X;
public int Y;
public Filed FK;
public Table Table;
}
2命名不规范:
stuNo这样的缩写是不提倡的
ZZMM 政治面貌,KSLB 考生类别 这样的命名是被鄙视的
3,nationality 民族 这个一对多关系建议用一个表来存储名字字典,采用NationId关联
政治面貌 也同上class(班级表)4,classID varchar行政班级ID 这个应该用int类型
5,录取专业,学制,辅导员姓名,班主任姓名 这些一对多关系都应该另外建表,用ID字段关联department (院(系)/部表)
6,departHeadName 系主任,应该用ID字段和老师表关联 course(课程表)
7,classID 行政班级ID 课程和班级是“多对多”的关系,应该加一个关系表(班级选课表2个主要字段,包含另外两个表的主健),这个字段不应该出现在这里。
8,teacherID 任课教师和课程也是“多对多”关系,应该加一个关系表(教师任课表2个主要字段,包含另外两个表的主健),这个字段不应该出现在这里。score (成绩表)
9,关系字段都应该用int型teacher(教师表)
10,教师学历 ,教师职称 应该另外建表,用ID字段关联
例如:手机的经销商分为 省/市/县 5,“多态”结构和多对多略有不同,如果需求中某表字段多少类型有非常大的不确定性,可以采用3个表来完成:
这两种关系结构还是第一次看到,又孤陋寡闻了一次。lz
前言:多对多关系至少需要3个表,我们把一个表叫做主表,一个叫做关系表,另外一个叫做字典表或者副表(字典表是纪录比较少,而且基本稳定的,例如:版块名称;副表是内容比较多,内容变化的,例如)。
按照数据库的增删查改操作,多对多关系的查找都可以用inner join或者select * from 主表 where id in (select 主表id from 关系表)1,角色任命型特点:关系表两外键组合无重复纪录,关系表一般不需要时间字段和主键,有一个表是字典类型的表。
界面特点:显示主表,用checkbox或多选select设置多选关系。
例如:任命版主(用户表-关系表-版块名称表),角色权限控制等,用户是5个版块版主,只要关系表5行纪录就可以确立,关系表的两个外键具有联合主键性质。
增加关系:如果没有组合纪录,insert之。
删除关系:如果有组合纪录,删除之。2,集合分组型特点:同角色任命型类似,关系表两外键组合无重复纪录,关系表一般不需要时间字段和主键。区别是主副表都不是字典表,可能都很大不固定。
界面特点:显示主表,用搜索代替简单的checkbox或多选select,或者一条一条的添加。
例如:歌曲专集(专集表-关系表-歌曲表)。手机分组(分组表-关系表-手机表)。用户圈子(圈子表-关系表-用户表)。文章标签(文章表-关系表-标签表)
增加关系:同版主任命型。
删除关系:同版主任命型。
3,明细帐型特点:关系表可以有重复纪录,关系表一般有时间字段,有主键,可能还有文字型的字段用来说明每次发生关系的原因(消费)。
界面特点:显示关系表,用radio或下拉设置单选关系。
例如:现金消费明细帐或订单(用户表-订单表-消费原因表),用户可能多次在同一事情上重复消费。积分变化纪录也属于这类。
增加关系:不管有没有组合纪录,insert之,纪录时间。
删除关系:根据关系表PK删除。
4,评论回复型特点:同明细帐型关系表一般有时间字段,有主键,区别是重点在文字型的字段用来说明每次发生关系的内容(评论回复)。
界面特点:回复文本框。
例如:论坛回复(用户表-回复表-帖子表),用户可能多次在不同帖子上评论回复费。
增加关系:不管有没有组合纪录,insert之,纪录时间和文字。
删除关系:根据关系表(回复表)PK删除。5,站内短信型特点:主副表是同一个,关系表一般有时间字段,有主键,重点在关系表文字型的字段用来说明每次发生关系的内容(消息)或者其他标记位来表示文字已读状态时间等。
界面特点:回复文本框。
例如:站内短信(用户表-短信表-用户表),用户可能给用户群发或者单发,有标记位来表示文字已读状态时间等。
增加关系:不管有没有组合纪录,insert之,纪录时间和文字。
删除关系:根据关系表(回复表)PK删除。6,用户好友型特点:主副表是同一个,同集合分组型,关系表两外键组合无重复纪录,关系表一般不需要时间字段和主键。
界面特点:同集合分组型,显示主表,用搜索代替简单的checkbox或多选select,或者一条一条的添加。
例如:下载站点的文件,(文件表-关系表-文件表)可以被软件工具打开,软件工具本身也是一种文件,可以被下载。用户的好友,也是用户(用户表-好友关系表-用户表)
增加关系:同版主任命型。
删除关系:同版主任命型。
7,未知属性型特点:在设计初期,主表的某些字段类型和名称是不确定的时候,关系表实际上是主表的可扩展字段,
一个[主表](ID),
一个[属性名称表](属性ID.属性名称),
一个[属性值表],包括3个字段:
属性值(属性Value varchar(500))
主表ID
属性ID这样可以作到最小冗余度。
(和常见的多对多关系不同的是:值统一用varchar来存储,因为这类型的值一般不会用来计算)。比如:军队的数据库设计中有种物资叫做“战缴物资”,就是打仗的时候缴获的,军队自己都不知道这些物资有什么属性。 比如缴获的化学品有化学名,通用名,是否有辐射,计量单位,包装规格,数量等等,或者不是化学品是其他任何未知的东西。
这样东西就可以 某奇怪东西.属性集合["某某奇怪属性名"]="某某奇怪值";
某变态东西.属性集合["某某变态属性名"]="某某变态值"; 这样存储。再比如:手机型号有几千种,除了共同属性外还有不同属性有几百个,属性名和值类型都不一样,有的手机有这属性,有的没有。
对于这样的“多态”,我们就采用上面的设计结构。
其效果相当于:某奇怪手机.属性集合["某某奇怪属性名"]="某某奇怪值";
某变态手机.属性集合["某某变态属性名"]="某某变态值";界面特点:设置主表一行纪录的属性时候,要列出所有可能的属性名称,每个对应一个文本框。