精华内容
下载资源
问答
  • 获取数据库表结构

    千次阅读 2015-03-21 10:59:29
    此例子,博主将使用SQL Server 2008 R2 做数据库,使用Winform做工具的UI...1、执行以下sql,即可从SQL server 数据库得到相关和字段的基础信息(SQL Server 2008 R2 亲测有效)  获取一个连接上的所有数据库信息

    原文:http://www.cnblogs.com/StrangeCity/p/4352753.html

    此例子,博主将使用SQL Server 2008 R2 做数据库,使用Winform做工具的UI展示。

    1、执行以下sql,即可从SQL server 数据库得到相关表和字段的基础信息(SQL Server 2008 R2 亲测有效)

    1 SELECT *
    2 FROM master..sysdatabases
    复制代码
     1 select  
     2     [表名]=c.Name, 
     3     [表说明]=isnull(f.[value],''),  
     4     [列序号]=a.Column_id,  
     5     [列名]=a.Name,  
     6     [列说明]=isnull(e.[value],''),  
     7     [数据库类型]=b.Name,    
     8     [类型]= case when b.Name = 'image' then 'byte[]'
     9                  when b.Name in('image','uniqueidentifier','ntext','varchar','ntext','nchar','nvarchar','text','char') then 'string'
    10                  when b.Name in('tinyint','smallint','int','bigint') then 'int'
    11                  when b.Name in('datetime','smalldatetime') then 'DateTime'
    12                  when b.Name in('float','decimal','numeric','money','real','smallmoney') then 'decimal'
    13                  when b.Name ='bit' then 'bool' else b.name end ,
    14     [标识]= case when is_identity=1 then '' else '' end,  
    15     [主键]= case when exists(select 1 from sys.objects x join sys.indexes y on x.Type=N'PK' and x.Name=y.Name  
    16                         join sysindexkeys z on z.ID=a.Object_id and z.indid=y.index_id and z.Colid=a.Column_id)  
    17                     then '' else '' end,      
    18     [字节数]=case when a.[max_length]=-1 and b.Name!='xml' then 'max/2G'  
    19                   when b.Name='xml' then '2^31-1字节/2G' 
    20                   else rtrim(a.[max_length]) end,  
    21     [长度]=case when ColumnProperty(a.object_id,a.Name,'Precision')=-1 then '2^31-1' 
    22                 else rtrim(ColumnProperty(a.object_id,a.Name,'Precision')) end,  
    23     [小数位]=isnull(ColumnProperty(a.object_id,a.Name,'Scale'),0),  
    24     [是否为空]=case when a.is_nullable=1 then '' else '' end,      
    25     [默认值]=isnull(d.text,'')      
    26 from  
    27     sys.columns a  
    28 left join 
    29     sys.types b on a.user_type_id=b.user_type_id  
    30 inner join 
    31     sys.objects c on a.object_id=c.object_id and c.Type='U' 
    32 left join 
    33     syscomments d on a.default_object_id=d.ID  
    34 left join 
    35     sys.extended_properties e on e.major_id=c.object_id and e.minor_id=a.Column_id and e.class=1   
    36 left join 
    37     sys.extended_properties f on f.major_id=c.object_id and f.minor_id=0 and f.class=1 
    38 
    39 获取数据库所有表字段信息
    复制代码

    2、构建自定义模板,然后替换模板中的标识符

    说到自定义模板,无非就是一堆字符串,那这一堆字符串究竟是用XML存储还是TXT文本存储还是其他方式呢?好吧,咱不纠结到底采用哪种存储介质了。基于本文的“30分钟”,博主决定短平快,直接硬编码写死吧!用字符串对象存储起来。 

    复制代码
     1 string modelTmp = @"
     2 using System;
     3 
     4 namespace #ModelNamespace#
     5 {
     6     /// <summary>
     7     /// #ModelClassDescription#
     8     /// Create By Tool #CreateDateTime#
     9     /// </summary>
    10     public class #ModelClassName#
    11     {
    12 #PropertyInfo#
    13     }
    14 }";
    15 
    16 实体类模板
    复制代码
    1 string modelPropertyTmp = @"
    2          /// <summary>
    3          /// #PropertyDescription#
    4          /// </summary>
    5          public  #PropertyType# #PropertyName# { get; set; }";
    复制代码
     1 propertyInfo += modelPropertyTmp.Replace(" #PropertyDescription#", propertyDescription)
     2                                                         .Replace(" #PropertyType#", propertyType)
     3                                                         .Replace("#PropertyName#", propertyName);
     4 
     5 
     6 modelTmp = modelTmp.Replace("#ModelClassDescription#", this.treeView1.SelectedNode.Tag.ToString())
     7                                        .Replace("#CreateDateTime#", DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss"))
     8                                        .Replace("#ModelNamespace#", modelNamespace)
     9                                        .Replace("#ModelClassName#", modelClassName)
    10                                        .Replace("#PropertyInfo#", propertyInfo);
    11 
    12 标识符替换
    复制代码

    3、采用文本控件将生成的代码打印出来

    由于本文旨在让各位读者了解代码生成的原理,故博主花了10多分钟写了一个简单的代码生成器,代码自然有些粗糙,请各位见谅!

    复制代码
      1 using System;
      2 using System.Collections.Generic;
      3 using System.Data;
      4 using System.Data.SqlClient;
      5 using System.Linq;
      6 using System.Windows.Forms;
      7 
      8 namespace WindowsTest
      9 {
     10     public partial class CodeCreate : Form
     11     {
     12         private static string S_conStr = "Data Source=127.0.0.1;Initial Catalog=master;Integrated Security=False;user=sa;password=******;";
     13 
     14         public CodeCreate()
     15         {
     16             InitializeComponent();
     17         }
     18 
     19         public static DataTable ExcuteQuery(string connectionString, string cmdText, List<SqlParameter> pars = null)
     20         {
     21             using (SqlConnection conn = new SqlConnection(connectionString))
     22             {
     23                 using (SqlCommand cmd = new SqlCommand(cmdText, conn))
     24                 {
     25                     if (pars != null && pars.Count > 0) cmd.Parameters.AddRange(pars.ToArray());
     26                     using (SqlDataAdapter adp = new SqlDataAdapter(cmd))
     27                     {
     28                         DataTable dt = new DataTable();
     29                         adp.Fill(dt);
     30                         return dt;
     31                     }
     32                 }
     33             }
     34         }
     35 
     36         private void CodeCreate_Load(object sender, EventArgs e)
     37         {
     38             #region 获取所有数据库的信息
     39             string sql = @" SELECT *
     40                             FROM master..sysdatabases
     41                             where dbid>6
     42                             ORDER BY dbid";
     43             #endregion
     44 
     45             DataTable dt = ExcuteQuery(S_conStr, sql);
     46             this.treeView1.Nodes.Add("192.168.30.69");
     47             foreach (DataRow dr in dt.Rows)
     48             {
     49                 this.treeView1.Nodes[0].Nodes.Add(dr["name"].ToString());
     50             }
     51 
     52             this.treeView1.ExpandAll();
     53         }
     54 
     55         private void treeView1_AfterSelect(object sender, TreeViewEventArgs e)
     56         {
     57             this.tabControl1.SelectedIndex = 0;
     58 
     59             if (e.Node.Level == 0) return;
     60 
     61             #region 获取数据库所有表字段信息的sql
     62             string sql = @"
     63 select  
     64     [表名]=c.Name, 
     65     [表说明]=isnull(f.[value],''),  
     66     [列序号]=a.Column_id,  
     67     [列名]=a.Name,  
     68     [列说明]=isnull(e.[value],''),  
     69     [数据库类型]=b.Name,    
     70     [类型]= case when b.Name = 'image' then 'byte[]'
     71                  when b.Name in('image','uniqueidentifier','ntext','varchar','ntext','nchar','nvarchar','text','char') then 'string'
     72                  when b.Name in('tinyint','smallint','int','bigint') then 'int'
     73                  when b.Name in('datetime','smalldatetime') then 'DateTime'
     74                  when b.Name in('float','decimal','numeric','money','real','smallmoney') then 'decimal'
     75                  when b.Name ='bit' then 'bool' else b.name end ,
     76     [标识]= case when is_identity=1 then '是' else '' end,  
     77     [主键]= case when exists(select 1 from sys.objects x join sys.indexes y on x.Type=N'PK' and x.Name=y.Name  
     78                         join sysindexkeys z on z.ID=a.Object_id and z.indid=y.index_id and z.Colid=a.Column_id)  
     79                     then '是' else '' end,      
     80     [字节数]=case when a.[max_length]=-1 and b.Name!='xml' then 'max/2G'  
     81                   when b.Name='xml' then '2^31-1字节/2G' 
     82                   else rtrim(a.[max_length]) end,  
     83     [长度]=case when ColumnProperty(a.object_id,a.Name,'Precision')=-1 then '2^31-1' 
     84                 else rtrim(ColumnProperty(a.object_id,a.Name,'Precision')) end,  
     85     [小数位]=isnull(ColumnProperty(a.object_id,a.Name,'Scale'),0),  
     86     [是否为空]=case when a.is_nullable=1 then '是' else '' end,      
     87     [默认值]=isnull(d.text,'')      
     88 from  
     89     sys.columns a  
     90 left join 
     91     sys.types b on a.user_type_id=b.user_type_id  
     92 inner join 
     93     sys.objects c on a.object_id=c.object_id and c.Type='U' 
     94 left join 
     95     syscomments d on a.default_object_id=d.ID  
     96 left join 
     97     sys.extended_properties e on e.major_id=c.object_id and e.minor_id=a.Column_id and e.class=1   
     98 left join 
     99     sys.extended_properties f on f.major_id=c.object_id and f.minor_id=0 and f.class=1 ";
    100             #endregion
    101 
    102             if (e.Node.Level == 1)
    103             {
    104                 DataTable dt = ExcuteQuery(S_conStr.Replace("master", e.Node.Text), sql);
    105                 this.dataGridView1.DataSource = dt;
    106 
    107                 if (dt.Rows.Count > 0)
    108                 {
    109                     e.Node.Nodes.Clear();
    110                     DataRow[] aryDr = new DataRow[dt.Rows.Count];
    111                     dt.Rows.CopyTo(aryDr, 0);
    112                     List<string> listTableName = aryDr.Select(a => a["表名"].ToString()).Distinct().ToList();
    113                     listTableName.ForEach(a =>
    114                     {
    115                         e.Node.Nodes.Add(a, a);
    116                     });
    117                     e.Node.ExpandAll();
    118                 }
    119             }
    120 
    121             if (e.Node.Level == 2)
    122             {
    123                 sql += "where c.Name=@TableName ";
    124                 List<SqlParameter> listSqlParameter = new List<SqlParameter>()
    125                 {
    126                     new SqlParameter("@TableName",e.Node.Text),
    127                 };
    128                 DataTable dt = ExcuteQuery(S_conStr.Replace("master", e.Node.Parent.Text), sql, listSqlParameter);
    129                 if (dt.Columns.Count > 0)
    130                 {
    131                     if (dt.Rows.Count > 0)
    132                     {
    133                         e.Node.Tag = dt.Rows[0]["表说明"].ToString();
    134                     }
    135                     dt.Columns.Remove("表名");
    136                     dt.Columns.Remove("表说明");
    137                 }
    138                 this.dataGridView1.DataSource = dt;
    139             }
    140         }
    141 
    142         private void tabControl1_SelectedIndexChanged(object sender, EventArgs e)
    143         {
    144             if (this.tabControl1.SelectedTab.Text == "Model")
    145             {
    146                 if (this.treeView1.SelectedNode.Level == 2 && this.dataGridView1.Rows.Count > 0)
    147                 {
    148                     string modelTmp = @"
    149 using System;
    150 
    151 namespace #ModelNamespace#
    152 {
    153     /// <summary>
    154     /// #ModelClassDescription#
    155     /// Create By Tool #CreateDateTime#
    156     /// </summary>
    157     public class #ModelClassName#
    158     {
    159 #PropertyInfo#
    160     }
    161 }";
    162 
    163                     string modelNamespace = "Model";
    164                     string modelClassName = this.treeView1.SelectedNode.Text;
    165 
    166                     string propertyInfo = string.Empty;
    167                     foreach (DataGridViewRow dgvr in this.dataGridView1.Rows)
    168                     {
    169                         string modelPropertyTmp = @"
    170         /// <summary>
    171         /// #PropertyDescription#
    172         /// </summary>
    173         public  #PropertyType# #PropertyName# { get; set; }";
    174 
    175                         string propertyDescription = dgvr.Cells["列说明"].Value.ToString().Trim();
    176                         string propertyName = dgvr.Cells["列名"].Value.ToString().Trim();
    177                         string propertyType = dgvr.Cells["类型"].Value.ToString().Trim();
    178 
    179                         propertyInfo += modelPropertyTmp.Replace(" #PropertyDescription#", propertyDescription)
    180                                                         .Replace(" #PropertyType#", propertyType)
    181                                                         .Replace("#PropertyName#", propertyName);
    182                         propertyInfo += Environment.NewLine;
    183                     }
    184 
    185                     modelTmp = modelTmp.Replace("#ModelClassDescription#", this.treeView1.SelectedNode.Tag.ToString())
    186                                        .Replace("#CreateDateTime#", DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss"))
    187                                        .Replace("#ModelNamespace#", modelNamespace)
    188                                        .Replace("#ModelClassName#", modelClassName)
    189                                        .Replace("#PropertyInfo#", propertyInfo);
    190 
    191                     this.richTextBox1.Text = modelTmp;
    192                     Clipboard.SetDataObject(this.richTextBox1.Text);
    193                 }
    194             }
    195         }
    196     }
    197 }
    198 
    199 主要代码
    复制代码



    展开全文
  • 数据库数据库的创建

    千次阅读 2020-05-26 18:55:50
    数据库的创建 本文使用SQLserver数据库为师范 1 目的 1、掌握利用 SSMS 及 SQL 命令两种方式管理数据库; 2、掌握利用 SSMS 及 SQL 命令两种方式管理; 3、理解数据文件、事务日志文件、文件组的概念; 4、...

    数据库和表的创建

    本文使用SQLserver数据库为示范

    1 目的

    1、掌握利用 SSMS 及 SQL 命令两种方式管理数据库;
    2、掌握利用 SSMS 及 SQL 命令两种方式管理表;
    3、理解数据文件、事务日志文件、文件组的概念;
    4、理解实体完整性、参照完整性和用户自定义完整性;
    5、掌握 PRIMARY KEY 约束,FOREIGN KEY 约束,UNIQUE 约束CHECK 约束和 DEFAULT约束的使用。

    2 要点

    1、创建数据库、修改数据库、删除数据库、数据库的分离与附加;
    2、创建表、修改表、删除表、五种数据约束;

    3 主要内容

    得到的简易图书管理数据库(假定数据库名为:BooksDB)包含 4 个关系(即:数据表)。
    关系名、属性及说明分别如下述各表所示。
    (1)读者类别表 ReaderType
    读者类别表
    (2)读者信息表 Reader
    读者信息表
    (3)图书信息表 Book

    图书信息表
    (4)借阅信息表 Borrow
    借阅信息表
    要求完成如下任务:
    1、利用 SQL 命令创建 BooksDB 数据库及表;
    2、分离与附加 BooksDB;
    3、数据导入/导出:将 BooksDB 数据库导出到 Access 数据库。

    4 详细步骤和SQL命令

    4.1 使用 SQL 命令管理数据库和表

    1、创建并使用数据库 BooksDB

    create database BookDB  --创建数据库
    go
    use BooksDB  --使用数据库
    go
    
    

    2、创建读者类别表 ReaderType

    create table ReaderType(
        rdType Int primary key,  --读者类别号
        rdTypeName varchar(20),  --读者类别名称
        canLendQty int,          --可借书天数
        canLendDay int           --可借书天数
    )
    
    

    运行后得到表格如下:
    创建的读者类别表
    3、创建读者表 Reader

    create table Reader(
        rdID char(9) primary key,  --读者编号
        rdType int references ReaderType(rdType),--读者类别
        rdName varchar(20),  --读者姓名
        rdDept varchar(40),  --读者单位
        rdQQ varchar(13),  --读者QQ
        rdBorrowQty int default 0 check(rdBorrowQty between 0 and 10)  --已借书天数
    )
    
    

    运行后得到表格如下:
    创建的读者信息表
    4、创建图书表 Book

    create table Book(
        bkID char(9) primary key,  --书号
        bkName varchar(50),  --书名
        bkAuthor varchar(50),  --作者
    	bkPress varchar(50),  --出版社
    	bkPrice decimal(5,2),  --单价
    	bkStatus int default 1  --是否在馆,1:在馆,0:不在馆   
    )
    
    

    运行后得到表格如下:
    创建的图书信息表
    5、创建借阅信息表 Borrow

    create table Borrow(
        rdID char(9) references Reader(rdID),  --读者编号
    	bkID char(9) references Book(bkID),  --书号
    	DateBorrow datetime, --借书日期
    	DateLendPlan datetime,  --应还日期
    	DateLendAct datetime,  --实际还书日期
    	primary key(rdID,bkID)
    )
    
    

    运行后得到表格如下:
    创建的借阅信息表

    4.2 向数据表中录入测试数据

    向表中录入数据时,必须严格按照创建表时给的数据类型录入。
    1、向ReaderType表中插入数据

    insert into ReaderType values('1','教师','10','60')
    insert into ReaderType values('2','本科生','5','30')
    insert into ReaderType values('3','硕士研究生','6','40')
    insert into ReaderType values('4','博士研究生','8','50')
    
    

    运行后得到表格如下:
    读者类别
    2、向Reader表中插入数据

    insert into Reader values('rd2017001','1','刘宇','计算机科学学院','2546831','1')
    insert into Reader values('rd2017002','2','邱琳','管理学院','1542368','1')
    insert into Reader values('rd2017003','3','罗美娟','外语学院','54265825','1')
    insert into Reader values('rd2017004','4','王强','电信学院','2543698','1')
    
    

    运行后得到表格如下:
    读者信息
    3、向Book表中插入数据

    insert into Book values('bk2017001','数据库原理及应用','王丽艳','机械工业出版社','33.00','1')
    insert into Book values('bk2017002','高等数学','同济大学数学系','高等教育出版社','32.00','1')
    insert into Book values('bk2017003','当代教育心理学','陈琦','北京师范大学出版社','37.20','1')
    insert into Book values('bk2017004','古代汉语','王力','中华书局','20.40','1')
    
    

    运行后得到表格如下:
    图书信息
    4、向Borrow表中插入数据

    insert into Borrow values('rd2017001','bk2017002','2017.6.1','2017.8.1','2017.6.10')
    insert into Borrow values('rd2017002','bk2017001','2017.5.2','2017.6.2','2017.5.20')
    insert into Borrow values('rd2017003','bk2017003','2017.3.15','2017.4.24','2017.4.10')
    insert into Borrow values('rd2017004','bk2017001','2017.2.8','2017.3.28','2017.2.28')
    
    

    运行后得到表格如下:
    借阅信息

    4.3 分离与附加数据库

    1、分离数据库
    (1)在对象资源管理器中,选中数据库 BooksDB,选择右键菜单“任务”->“分离”。如下图所示。
    分离数据库第一步
    (2)在弹出的“分离数据库”对象框中,单击“确定”按钮。
    分离数据库第二步
    2、附加数据库
    数据库分离成功后,利用数据文件 BookDB.mdf 及事务日志文BookDB_log.ldf 附加数据库BooksDB。
    (1)在对象资源管理器中,选中“数据库”,选择右键菜单“附加(A)…”。如下图所示。
    附加数据库第一步
    (2)在弹出的“附加数据库”对象框中,单击“添加(A)…”按钮,添加数据库的 MDF 文件。单击“确定”按钮。
    附加数据库第二步

    展开全文
  • 一、数据库操作语句 创建数据库操作 修改数据库 新增数据文件 新增日志文件 使用数据库 删除数据库 二、数据操作语句 新建数据 由其他创建新 数据新增列 三、数据操作语句 数据中数据的插入...

    目录

    一、数据库操作语句

    创建数据库操作

    修改数据库

    新增数据文件

    新增日志文件

    重命名数据库

    修改文件属性

    删除数据库文件

    使用数据库

    删除数据库

    二、数据表操作语句

    新建数据表

    由其他表创建新表

    数据表新增列

    删除数据表

    三、数据操作语句

    数据表中数据的插入

    数据表中某些数据的修改

    数据表或表内元素的删除


    Hello,你好哇,我是灰小猿!一个超会写bug的程序猿!

    最近在进行SQL server数据库的相关学习,所以对该数据库的一些常用的操作语句进行了些总结。在这里和小伙伴们一起分享一下。先从最基本的数据库的操作语句开始。

    一、数据库操作语句

    创建数据库操作

    create database 数据库名
    
    /*新建数据文件*/
    
    on(
    
    name = 数据文件逻辑名称,
    
    filename = '数据文件物理名称.mdf',
    
    size = 文件起始大小,
    
    maxsize = 文件最大大小,(不设置时默认无限大)
    
    filegrowth = 文件每次增长大小(以MB或百分比增长)
    
    )
    
    /*创建日志文件*/
    
    log on(
    
    name = 日志文件逻辑名,
    
    filename = '日志文件物理名.ldf',
    
    size = 日志文件初始大小,
    
    maxsize = 文件最大大小,(不设置时默认无限大)
    
    filegrowth = 文件每次增长大小(以MB或百分比增长)
    
    )

     

    修改数据库

    新增数据文件

    alter database 数据库名称
    
    add file(
    
    name=数据文件逻辑名,
    
    filename='物理文件名.mdf',
    
    size=数据文件初始大小,
    
    maxsize=数据文件最大值,
    
    filegrowth = 文件每次增长大小(以MB或百分比增长)
    
    )

     

    新增日志文件

    新增日志文件与数据文件的不同之处是:在add后需要加上log 同时物理文件名后缀为ldf

    alter database 数据库名称
    
    add log file(
    
    name = 日志文件逻辑名,
    
    filename = '物理文件名.ldf',
    
    size = 日志文件起始大小,
    
    maxsize=数据文件最大值,
    
    filegrowth = 文件每次增长大小(以MB或百分比增长)
    
    )

     

    重命名数据库

    /*修改数据库名称的语法:*/
    /*第一钟:*/
    alter database databaseName
    modify name = newDatabaseName
    
    /*第二种:*/`
    exec sp_renamedb '原名','新名'

     

    修改文件属性

    修改文件属性 可以修改的属性有:filename,size,filegrowth,maxsize

    alter database databaseName
    modify file(
    name=fileName,
    maxsize=120mb,
    filegrowth=20mb
    )

     

    删除数据库文件

    alter database databaseName
    remove file fileName
    

     

    使用数据库

    use database databaseName 

     

    删除数据库

    drop database databaseName

     

    二、数据表操作语句

    新建数据表

    use 数据库名
    
    create table 表名{
    
    liet1 数据类型1,
    
    liet2 数据类型2,
    
    liet3 数据类型3
    
    }

     

    由其他表创建新表

    use databaseName
    
    select list1,list2,list3 into newTableName
    
    from tableName

     

    数据表新增列

    use databaseName
    
    alter table tableName
    
    add列名 数据类型

     

    删除数据表

    use library2012
    drop table 读者信息1

     

    三、数据操作语句

    数据表中数据的插入

    /*多行插入时括号后加逗号*/
    
    use databaseName
    
    insert into tableName values ('info1','info2'...)

     

    数据表中某些数据的修改

    use databaseName
    
    update tableName
    
    set 修改列1=修改的内容1,修改列2=修改的内容2
    
    where 先决条件

     

    数据表或表内元素的删除

    use databaseName
    
    delete tableName
    
    where 先决条件

     

    好了,关于数据库创建及数据表的一些常用操作就先和小伙伴分享到这里啦,有不足的地方还希望各位大佬能够提出更正,之后也会继续更新有关数据库查询等相关操作语句的总结。

    觉得不错记得点赞关注哟!

    大灰狼陪你一起进步!

    我的博客即将同步至腾讯云+社区,邀请大家一同入驻:https://cloud.tencent.com/developer/support-plan?invite_code=389yyefwcqw4k

    展开全文
  • 数据库表设计

    万次阅读 2018-01-20 02:38:06
    范式:英文名称是 Normal Form,它是英国人 E.F.Codd(关系数据库的老祖宗)在上个世纪70年代提出关系数据库模型后总结出来的,范式是关系数据库理论的基础,也是我们在设计数据库结构过程中所要遵循的规则和指导...

    参考:

    http://blog.csdn.net/famousdt/article/details/6921622

    http://blog.csdn.net/truong/article/details/29825913

    http://www.cnblogs.com/kissdodog/p/3297894.html

    http://www.csdn.net/article/2012-04-11/2804419


    范式:英文名称是 Normal Form,它是英国人 E.F.Codd(关系数据库的老祖宗)在上个世纪70年代提出关系数据库模型后总结出来的,范式是关系数据库理论的基础,也是我们在设计数据库结构过程中所要遵循的规则和指导方法。目前有迹可寻的共有8种范式,依次是:1NF,2NF,3NF,BCNF,4NF,5NF,DKNF,6NF。通常所用到的只是前三个范式,即:第一范式(1NF),第二范式(2NF),第三范式(3NF)。下面就简单介绍下这三个范式。 
    ◆ 第一范式(1NF):强调的是列的原子性,即列不能够再分成其他几列。 
    考虑这样一个表:【联系人】(姓名,性别,电话) 
    如果在实际场景中,一个联系人有家庭电话和公司电话,那么这种表结构设计就没有达到 1NF。要符合 1NF 我们只需把列(电话)拆分,即:【联系人】(姓名,性别,家庭电话,公司电话)。1NF 很好辨别,但是 2NF 和 3NF 就容易搞混淆。 
    ◆ 第二范式(2NF):首先是 1NF,另外包含两部分内容,一是表必须有一个主键;二是没有包含在主键中的列必须完全依赖于主键,而不能只依赖于主键的一部分。 
    考虑一个订单明细表:【OrderDetail】(OrderID,ProductID,UnitPrice,Discount,Quantity,ProductName)。 
    因为我们知道在一个订单中可以订购多种产品,所以单单一个 OrderID 是不足以成为主键的,主键应该是(OrderID,ProductID)。显而易见 Discount(折扣),Quantity(数量)完全依赖(取决)于主键(OderID,ProductID),而 UnitPrice,ProductName 只依赖于 ProductID。所以 OrderDetail 表不符合 2NF。不符合 2NF 的设计容易产生冗余数据。 
    可以把【OrderDetail】表拆分为【OrderDetail】(OrderID,ProductID,Discount,Quantity)和【Product】(ProductID,UnitPrice,ProductName)来消除原订单表中UnitPrice,ProductName多次重复的情况。 
    ◆ 第三范式(3NF):首先是 2NF,另外非主键列必须直接依赖于主键,不能存在传递依赖。即不能存在:非主键列 A 依赖于非主键列 B,非主键列 B 依赖于主键的情况。 
    考虑一个订单表【Order】(OrderID,OrderDate,CustomerID,CustomerName,CustomerAddr,CustomerCity)主键是(OrderID)。 
    其中 OrderDate,CustomerID,CustomerName,CustomerAddr,CustomerCity 等非主键列都完全依赖于主键(OrderID),所以符合 2NF。不过问题是 CustomerName,CustomerAddr,CustomerCity 直接依赖的是 CustomerID(非主键列),而不是直接依赖于主键,它是通过传递才依赖于主键,所以不符合 3NF。 
    通过拆分【Order】为【Order】(OrderID,OrderDate,CustomerID)和【Customer】(CustomerID,CustomerName,CustomerAddr,CustomerCity)从而达到 3NF。 
    第二范式(2NF)和第三范式(3NF)的概念很容易混淆,区分它们的关键点在于,2NF:非主键列是否完全依赖于主键,还是依赖于主键的一部分;3NF:非主键列是直接依赖于主键,还是直接依赖于非主键列。


    3不同类型的数据应该分开管理,例如,财务数据库,业务数据库等。
    4、由于存储过程在不同的数据库中,支持方式不一样,因此不建议过多使用和使用复杂的存储过程。为数据库服务器降低压力,不要让数据库处理过多的业务逻辑,将业务逻辑处理放到应用程序中。

    2、  数据不要物理删除,应该加一个标志位,以防用户后悔时,能够恢复。还有就是比如人员登记加入某系统,采用登记卡的方式,一段时间后该人员退出了该系统,那么此时我们将该人员对应的记录标记为不可用,那么这个人就从系统中移除了,一段时间后这个人又回来了,那么我们直接修改标记字段为可用,这个人就从新加入系统,避免了其它信息的录入。

    6、  增加备注字段,虽然我们考虑了很多用户需要输入信息的需求,但是无论何时我们都不可能考虑全,因此可以定义一个备注字段,允许用户将其它的信息填写在这里。无论表设计的再神奇,那么还是加一个备注字段。

     (1) 在数据库物理设计时,降低范式,增加冗余, 少用触发器, 多用存储过程。
       (2) 当计算非常复杂、而且记录条数非常巨大时(例如一千万条),复杂计算要先在数据库外面,以

    文件系统方式用C++语言计算处理完成之后,最后才入库追加到表中去。这是电信计费系统设计的经验。
       (3) 发现某个表的记录太多,例如超过一千万条,则要对该表进行水平分割。水平分割的做法是,

    以该表主键PK的某个值为界线,将该表的记录水平分割为两个表。若发现某个表的字段太多,例如超过

    八十个,则垂直分割该表,将原来的一个表分解为两个表。



      相信有过开发经验的朋友都曾碰到过这样一个需求。假设你正在为一个新闻网站开发一个评论功能,读者可以评论原文甚至相互回复。

      这个需求并不简单,相互回复会导致无限多的分支,无限多的祖先-后代关系。这是一种典型的递归关系数据。

      对于这个问题,以下给出几个解决方案,各位客观可斟酌后选择。

    一、邻接表:依赖父节点

      邻接表的方案如下(仅仅说明问题):

    复制代码
      CREATE TABLE Comments(
        CommentId  int  PK,
        ParentId   int,    --记录父节点
        ArticleId  int,
        CommentBody nvarchar(500),
        FOREIGN KEY (ParentId)  REFERENCES Comments(CommentId)   --自连接,主键外键都在自己表内
        FOREIGN KEY (ArticleId) REFERENCES Articles(ArticleId)
      )
    复制代码

      由于偷懒,所以采用了书本中的图了,Bugs就是Articles:

      

      这种设计方式就叫做邻接表。这可能是存储分层结构数据中最普通的方案了。

      下面给出一些数据来显示一下评论表中的分层结构数据。示例表:

      

      图片说明存储结构:

      

      邻接表的优缺分析

      对于以上邻接表,很多程序员已经将其当成默认的解决方案了,但即便是这样,但它在从前还是有存在的问题的。

      分析1:查询一个节点的所有后代(求子树)怎么查呢?

      我们先看看以前查询两层的数据的SQL语句:

      SELECT c1.*,c2.*
      FROM Comments c1 LEFT OUTER JOIN Comments2 c2
      ON c2.ParentId = c1.CommentId

      显然,每需要查多一层,就需要联结多一次表。SQL查询的联结次数是有限的,因此不能无限深的获取所有的后代。而且,这种这样联结,执行Count()这样的聚合函数也相当困难。

      说了是以前了,现在什么时代了,在SQLServer 2005之后,一个公用表表达式就搞定了,顺带解决的还有聚合函数的问题(聚合函数如Count()也能够简单实用),例如查询评论4的所有子节点:

    复制代码
    WITH COMMENT_CTE(CommentId,ParentId,CommentBody,tLevel)
    AS
    (
        --基本语句
        SELECT CommentId,ParentId,CommentBody,0 AS tLevel FROM Comment
        WHERE ParentId = 4
        UNION ALL  --递归语句
        SELECT c.CommentId,c.ParentId,c.CommentBody,ce.tLevel + 1 FROM Comment AS c 
        INNER JOIN COMMENT_CTE AS ce    --递归查询
        ON c.ParentId = ce.CommentId
    )
    SELECT * FROM COMMENT_CTE
    复制代码

      显示结果如下:

      

      那么查询祖先节点树又如何查呢?例如查节点6的所有祖先节点:

    复制代码
    WITH COMMENT_CTE(CommentId,ParentId,CommentBody,tLevel)
    AS
    (
        --基本语句
        SELECT CommentId,ParentId,CommentBody,0 AS tLevel FROM Comment
        WHERE CommentId = 6
        UNION ALL
        SELECT c.CommentId,c.ParentId,c.CommentBody,ce.tLevel - 1  FROM Comment AS c 
        INNER JOIN COMMENT_CTE AS ce  --递归查询
        ON ce.ParentId = c.CommentId
        where ce.CommentId <> ce.ParentId
    )
    SELECT * FROM COMMENT_CTE ORDER BY CommentId ASC
    复制代码

      结果如下:

      

      再者,由于公用表表达式能够控制递归的深度,因此,你可以简单获得任意层级的子树。

      OPTION(MAXRECURSION 2)

      看来哥是为邻接表平反来的。

       分析2:当然,邻接表也有其优点的,例如要添加一条记录是非常方便的。

      INSERT INTO Comment(ArticleId,ParentId)...    --仅仅需要提供父节点Id就能够添加了。

       分析3:修改一个节点位置或一个子树的位置也是很简单.

    UPDATE Comment SET ParentId = 10 WHERE CommentId = 6  --仅仅修改一个节点的ParentId,其后面的子代节点自动合理。

      分析4:删除子树

      想象一下,如果你删除了一个中间节点,那么该节点的子节点怎么办(它们的父节点是谁),因此如果你要删除一个中间节点,那么不得不查找到所有的后代,先将其删除,然后才能删除该中间节点。

      当然这也能通过一个ON DELETE CASCADE级联删除的外键约束来自动完成这个过程。

       分析5:删除中间节点,并提升子节点

      面对提升子节点,我们要先修改该中间节点的直接子节点的ParentId,然后才能删除该节点:

      SELECT ParentId FROM Comments WHERE CommentId = 6;    --搜索要删除节点的父节点,假设返回4
      UPDATE Comments SET ParentId = 4 WHERE ParentId = 6;  --修改该中间节点的子节点的ParentId为要删除中间节点的ParentId
      DELETE FROM Comments WHERE CommentId = 6;          --终于可以删除该中间节点了

      由上面的分析可以看到,邻接表基本上已经是很强大的了。

    二、路径枚举

      路径枚举的设计是指通过将所有祖先的信息联合成一个字符串,并保存为每个节点的一个属性。

      路径枚举是一个由连续的直接层级关系组成的完整路径。如”/home/account/login”,其中home是account的直接父亲,这也就意味着home是login的祖先。

      还是有刚才新闻评论的例子,我们用路径枚举的方式来代替邻接表的设计:

    复制代码
      CREATE TABLE Comments(
        CommentId  int  PK,
        Path      varchar(100),    --仅仅改变了该字段和删除了外键
        ArticleId  int,
        CommentBody nvarchar(500),
        FOREIGN KEY (ArticleId) REFERENCES Articles(ArticleId)
      )
    复制代码

       简略说明问题的数据表如下:

      CommentId  Path    CommentBody

      1       1/        这个Bug的成因是什么

      2       1/2/     我觉得是一个空指针

      3       1/2/3     不是,我查过了

      4       1/4/     我们需要查无效的输入

      5       1/4/5/    是的,那是个问题

      6       1/4/6/    好,查一下吧。

      7       1/4/6/7/   解决了

      路径枚举的优点:

      对于以上表,假设我们需要查询某个节点的全部祖先,SQL语句可以这样写(假设查询7的所有祖先节点):

    SELECT * FROM Comment AS c
    WHERE '1/4/6/7/' LIKE c.path + '%'

      结果如下:

      

      假设我们要查询某个节点的全部后代,假设为4的后代:

    SELECT * FROM Comment AS c
    WHERE c.Path LIKE '1/4/%'

      结果如下:

      

      一旦我们可以很简单地获取一个子树或者从子孙节点到祖先节点的路径,就可以很简单地实现更多查询,比如计算一个字数所有节点的数量(COUNT聚合函数)

      

       插入一个节点也可以像和使用邻接表一样地简单。可以插入一个叶子节点而不用修改任何其他的行。你所需要做的只是复制一份要插入节点的逻辑上的父亲节点路径,并将这个新节点的Id追加到路径末尾就可以了。如果这个Id是插入时由数据库生成的,你可能需要先插入这条记录,然后获取这条记录的Id,并更新它的路径。

      路径枚举的缺点:

      1、数据库不能确保路径的格式总是正确或者路径中的节点确实存在(中间节点被删除的情况,没外键约束)。

      2、要依赖高级程序来维护路径中的字符串,并且验证字符串的正确性的开销很大。

      3、VARCHAR的长度很难确定。无论VARCHAR的长度设为多大,都存在不能够无限扩展的情况。

      路径枚举的设计方式能够很方便地根据节点的层级排序,因为路径中分隔两边的节点间的距离永远是1,因此通过比较字符串长度就能知道层级的深浅。

    三、嵌套集

      嵌套集解决方案是存储子孙节点的信息,而不是节点的直接祖先。我们使用两个数字来编码每个节点,表示这个信息。可以将这两个数字称为nsleft和nsright。

      还是以上面的新闻-评论作为例子,对于嵌套集的方式表可以设计为:

    复制代码
      CREATE TABLE Comments(
        CommentId  int  PK,
        nsleft    int,  --之前的一个父节点
           nsright   int,  --变成了两个
        ArticleId  int,
        CommentBody nvarchar(500),
        FOREIGN KEY (ArticleId) REFERENCES Articles(ArticleId)
      )
    复制代码

      nsleft值的确定:nsleft的数值小于该节点所有后代的Id。

      nsright值的确定:nsright的值大于该节点所有后代的Id。

      当然,以上两个数字和CommentId的值并没有任何关联,确定值的方式是对树进行一次深度优先遍历,在逐层入神的过程中依次递增地分配nsleft的值,并在返回时依次递增地分配nsright的值。

      采用书中的图来说明一下情况:

      

      一旦你为每个节点分配了这些数字,就可以使用它们来找到给定节点的祖先和后代。

      嵌套集的优点:

      我觉得是唯一的优点了,查询祖先树和子树方便。

      例如,通过搜索那些节点的ConmentId在评论4的nsleft与nsright之间就可以获得其及其所有后代:

      SELECT c2.* FROM Comments AS c1
       JOIN Comments AS c2  ON cs.neleft BETWEEN c1.nsleft AND c1.nsright
      WHERE c1.CommentId = 1;

      结果如下:

      

      通过搜索评论6的Id在哪些节点的nsleft和nsright范围之间,就可以获取评论6及其所有祖先:

      SELECT c2.* FROM Comment AS c1
      JOIN Comment AS c2 ON c1.nsleft BETWEEN c2.nsleft AND c2.nsright
      WHERE c1.CommentId = 6;

      

      这种嵌套集的设计还有一个优点,就是当你想要删除一个非叶子节点时,它的后代会自动地代替被删除的节点,称为其直接祖先节点的直接后代。

      嵌套集设计并不必须保存分层关系。因此当删除一个节点造成数值不连续时,并不会对树的结构产生任何影响。

      嵌套集缺点:

      1、查询直接父亲。

      在嵌套集的设计中,这个需求的实现的思路是,给定节点c1的直接父亲是这个节点的一个祖先,且这两个节点之间不应该有任何其他的节点,因此,你可以用一个递归的外联结来查询一个节点,它就是c1的祖先,也同时是另一个节点Y的后代,随后我们使y=x就查询,直到查询返回空,即不存在这样的节点,此时y便是c1的直接父亲节点。

      比如,要找到评论6的直接父节点:老实说,SQL语句又长又臭,行肯定是行,但我真的写不动了。

      2、对树进行操作,比如插入和移动节点。

      当插入一个节点时,你需要重新计算新插入节点的相邻兄弟节点、祖先节点和它祖先节点的兄弟,来确保它们的左右值都比这个新节点的左值大。同时,如果这个新节点是一个非叶子节点,你还要检查它的子孙节点。

      够了,够了。就凭查直接父节点都困难,这个东西就很冷门了。我确定我不会使用这种设计了。

    四、闭包表

      闭包表是解决分层存储一个简单而又优雅的解决方案,它记录了表中所有的节点关系,并不仅仅是直接的父子关系。
      在闭包表的设计中,额外创建了一张TreePaths的表(空间换取时间),它包含两列,每一列都是一个指向Comments中的CommentId的外键。

    复制代码
    CREATE TABLE Comments(
      CommentId int PK,
      ArticleId int,
      CommentBody int,
      FOREIGN KEY(ArticleId) REFERENCES Articles(Id)
    )
    复制代码

      父子关系表:

    复制代码
    CREATE TABLE TreePaths(
      ancestor    int,
      descendant int,
      PRIMARY KEY(ancestor,descendant),    --复合主键
      FOREIGN KEY (ancestor) REFERENCES Comments(CommentId),
      FOREIGN KEY (descendant) REFERENCES Comments(CommentId)
    )
    复制代码

      在这种设计中,Comments表将不再存储树结构,而是将书中的祖先-后代关系存储为TreePaths的一行,即使这两个节点之间不是直接的父子关系;同时还增加一行指向节点自己,理解不了?就是TreePaths表存储了所有祖先-后代的关系的记录。如下图:

      

      Comment表:

      

      TreePaths表:

      

      优点:

      1、查询所有后代节点(查子树):

    SELECT c.* FROM Comment AS c
        INNER JOIN TreePaths t on c.CommentId = t.descendant
        WHERE t.ancestor = 4

      结果如下:

      

      2、查询评论6的所有祖先(查祖先树):

    SELECT c.* FROM Comment AS c
        INNER JOIN TreePaths t on c.CommentId = t.ancestor
        WHERE t.descendant = 6

      显示结果如下:

      

       3、插入新节点:

      要插入一个新的叶子节点,应首先插入一条自己到自己的关系,然后搜索TreePaths表中后代是评论5的节点,增加该节点与要插入的新节点的”祖先-后代”关系。

      比如下面为插入评论5的一个子节点的TreePaths表语句:

    复制代码
    INSERT INTO TreePaths(ancestor,descendant)
        SELECT t.ancestor,8
        FROM TreePaths AS t
        WHERE t.descendant = 5
        UNION ALL
        SELECT 8,8
    复制代码

      执行以后:

      

      至于Comment表那就简单得不说了。

      4、删除叶子节点:

      比如删除叶子节点7,应删除所有TreePaths表中后代为7的行:

      DELETE FROM TreePaths WHERE descendant = 7

      5、删除子树:

      要删除一颗完整的子树,比如评论4和它的所有后代,可删除所有在TreePaths表中的后代为4的行,以及那些以评论4的后代为后代的行:

      DELETE FROM TreePaths
      WHERE descendant 
      IN(SELECT descendant FROM TreePaths WHERE ancestor = 4)

      另外,移动节点,先断开与原祖先的关系,然后与新节点建立关系的SQL语句都不难写。

      另外,闭包表还可以优化,如增加一个path_length字段,自我引用为0,直接子节点为1,再一下层为2,一次类推,查询直接自子节点就变得很简单。

    总结

      其实,在以往的工作中,曾见过不同类型的设计,邻接表,路径枚举,邻接表路径枚举一起来的都见过。

      每种设计都各有优劣,如果选择设计依赖于应用程序中哪种操作最需要性能上的优化。 

      下面给出一个表格,来展示各种设计的难易程度:

    设计 表数量 查询子 查询树 插入 删除 引用完整性
    邻接表 1 简单 简单 简单 简单
    枚举路径 1 简单 简单 简单 简单
    嵌套集 1 困难 简单 困难 困难
    闭包表 2 简单 简单 简单 简单

      1、邻接表是最方便的设计,并且很多软件开发者都了解它。并且在递归查询的帮助下,使得邻接表的查询更加高效。

      2、枚举路径能够很直观地展示出祖先到后代之间的路径,但由于不能确保引用完整性,使得这个设计比较脆弱。枚举路径也使得数据的存储变得冗余。

      3、嵌套集是一个聪明的解决方案,但不能确保引用完整性,并且只能使用于查询性能要求较高,而其他要求一般的场合使用它。

      4、闭包表是最通用的设计,并且最灵活,易扩展,并且一个节点能属于多棵树,能减少冗余的计算时间。但它要求一张额外的表来存储关系,是一个空间换取时间的方案。






    展开全文
  • SQL数据库查询和多查询

    千次阅读 2018-11-03 16:57:42
    –(一)以数据库系统student数据库为基础,请使用T-SQL 语句实现进行以下操作: --1. 查询以‘DB_’开头,且倒数第3个字符为‘s’的课程的详细情况; --(1) select * from dbo.course where dbo.course.cname ...
  • 也许很多读者朋友都经历过这样的事情:要在开发数据库基础上清理一个空库,但由于对数据库结构缺乏整体了解,在删除一个的记录时,删除不了,因为可能有外键约束,一个常见的数据库结构是一个主,一个子,这种...
  • 数据库字典设计

    万次阅读 2018-04-28 13:55:21
    数据库字典设计村长大神 发表于2年前 在稍大一些的项目中,我们总是需要管理各种各样的类型类型数据(如商品类型、游戏类型。。。)。对于这些类型的管理类似,如果为每 一种类型都建立一张去维护(而在项目...
  • camunda数据库表结构介绍

    千次阅读 多人点赞 2020-10-13 14:21:59
    文章目录一、Camunda数据清单二、流程引擎主要1.act_ge_bytearray(二进制数据)2.act_ge_property(流程引擎配置)3.act_ge_schema_log(数据库脚本执行日志)4.act_hi_actinst(历史的活动实例)5.act...
  •  也许很多读者朋友都经历过这样的事情:要在开发数据库基础上清理一个空库,但由于对数据库结构缺乏整体了解,在删除一个的记录时,删除不了,因为可能有外键约束,一个常见的数据库结构是一个主,一个子...
  • 数据库结构)设计技巧及注意事项

    万次阅读 多人点赞 2017-12-01 16:51:39
    当您在决定开发一个数据库管理项目时,最先着手的工作就应是数据库表结构的设计了。可以这么说,结构的设计是开发数据库管理项目的基石,一个糟糕的结构设计,可能会严重延误您的项目开发周期,使您大量的劳动...
  • 第一章:SQL Server 数据库环境搭建与使用 第二章:SQL Server2019 数据库的基本使用之图形化界面操作 第三章:SQL Server2019 数据库 之 开启 SQL 语言之旅 第四章:SQL Server2019 数据库 之 综合案例练习、 使用...
  • 也许很多读者朋友都经历过这样的事情:要在开发数据库基础上清理一个空库,但由于对数据库结构缺乏整体了解,在删除一个的记录时,删除不了,因为可能有外键约束,一个常见的数据库结构是一个主,一个子...
  • 数据库表结构设计

    千次阅读 2015-10-15 11:01:30
    1. 原始单据与实体之间的关系   可以是一对一、一对多、多对多的关系。在一般情况下,它们是一对一的关系:即一张原始单据对应且只对应一个实体。...这里的实体可以理解为基本。明确这种对应关系后,对我们
  • Java获取数据库详细信息

    千次阅读 2019-07-04 10:59:37
    最近有同事问我怎么获取数据库中某数据的某一列字段名称及数据类型,这种问题我还是第一次听别人问起,不过既然有,就应该得到解决。 当你使用和学习JDK的时候,可以查看并学习它所提供给你的两个...
  • 总之,报告这个错误很有可能是存储元数据的关系型数据库有问题,很可能是没有启动,或者连接配置错误。 注意:hive默认的日志路径为/tmp/root/hive.log,可以使用tail -F /tmp/root/hive.log来监控hive的运行。 ...
  • 数据库表添加唯一性约束

    千次阅读 2020-10-28 16:36:51
    数据库表添加唯一约束(就是一个字段不能重复) ALTER TABLE t_admin STRAINT uq_loginAcct UNIQUE (loginAcct) t_admin :表名 uq_loginAcct :约束名(随便起) loginAcct:添加约束的字段 给数据库表撤销唯一...
  • sql server 数: select count(1) from sysobjects where xtype=’U’数视图: select count(1) from sysobjects where xtype=’V’数存储过程 select count(1) from sysobjects where xtype=’P’
  • 1.数据库设计14规则 1. 原始单据与实体之间的关系   可以是一对一、一对多、多对多的关系。在一般情况下,它们是一对一的关系:即一张原始单据对应且只对应一个实体。  在特殊情况下,它们可能是一对多或多对...
  • 本文只包含部分代码,读者需要结合自己的数据库弹性公网、数据库用户及其密码等自身信息做出相应的修改。一、实验环境使用程序:putty.exe; IntelliJ IDEA 2021.1.1; apache-tomcat-9.0.46 服务器名称:ecs-d8b3 ...
  • 数据库表的设计基础

    2011-12-16 10:43:22
    数据库设计的三少原则: 1、 数据的数目要少 2、 数据的字段要少 3、 组合主键的字段要少 正确认识数据冗余: 数据冗余可以简单的分为低级冗余和高级冗余。低级冗余就是字段的重复出现;对于一个已经...
  • 本系列文章是作者暑假给学生进行实训分享的笔记,主要介绍MyEclipse环境下JSP网站开发,包括JAVA基础、网页布局、数据库基础、Servlet、前端后台数据库交互、DAO...基础性文章,希望对读者有所帮助 ,尤其是我的学生。
  • 是防止读者利用打补丁技术,不断地对数据库进行增删改,使企业数据库变成了随意设计数据库表的“垃圾堆”,或数据库表的“大杂院”,最后造成数据库中的基本、代码、中间、临时杂乱无章,不计其数,导致企...
  • 数据库课程设计

    万次阅读 多人点赞 2019-01-06 10:00:32
    图书管理系统 1.概述 项目背景 2.需求分析 ...2.3.1 书籍信息表 2.3.2 库存信息表 2.3.4 顾客信息表 2.3.5 管理员信息表 2.3.6 图书类型信息表 2.3.7 订单详细信息表 3.数据库设计 3.1 ...
  • 数据库

    千次阅读 2010-04-20 13:30:00
    数据库编程总结 收藏 此文于2010-04-12被推荐到CSDN首页此文于2010-04-16被推荐到CSDN首页如何被推荐?数据库编程总结当前各种主流数据库有很多,包括Oracle, MS SQL Server, Sybase, Informix, MySQL, DB2, ...
  • 数据库表结构设计方法及原则

    千次阅读 2020-12-01 22:30:43
    在目前的企业信息系统中,数据库还是最佳的数据存储方式,虽然已经有很多的书籍在指导我们进行数据库设计,但应该那种方式是设计数据库结构的最好方法、设计时应遵从什么样的原则、四个范式如何能够用一种方式...

空空如也

空空如也

1 2 3 4 5 ... 20
收藏数 83,521
精华内容 33,408
关键字:

数据库读者信息表