第三范式的要求是表中的非主键列必须跟主键列存在依赖,如果非主键列间存在依赖那就不符合3NF,比如我们常见的冗余就不符合。有人都觉得3NF不太符合实际需求,所以数据库设计上都遵循了2NF设计,但我觉得按3NF设计的表其实非常简洁明了,层次结构清晰,只是平常遇到的一表能解决问题需要分解到多个表,原因就是3NF的要求很柯刻。
下面我们看一下例子中要使用的按3NF设计的表(例子表使用SQLSERVER 2005制作):
按照这种设计方法,估计字典表要成百上千都有可能,那且不是要命?不急,我们稍作修改就即可:
现在又产生了一个重要的问题,什么问题呢,就是查询的时候得有多少关联SQL需要写啊,查询慢,SQL语句笨重,如果有冗余字段就不需要有这些烦琐的关联了。
不急,这些问题不需要SQLSERVER来处理,交给asp.net来做就好了。下面先讲实现,然后再说核心的解决方式。
我最近为东家编写了一个控件,命名为RSGrid,使用它即可解决问题,不过他需要依赖配置,我使用了表结构作为配置源。先看效果图:
根据上面的效果,我们需要作一翻配置:
实际查询和分页的存储过程:
USE [HisTest]
GO
/****** 对象: StoredProcedure [dbo].[sp_test] 脚本日期: 08/21/2012 11:41:37 ******/
SET ANSI_NULLS ON
GO
SET QUOTED_IDENTIFIER ON
GO
/*
* 描述:转诊系统自定义控件的测试过程。
* 参数:@BussionID 交易号
@pageIndex 页索引
@pageNumber 每页记录数
* 创建人:罗毅
* 创建日期:2012.08.15
* 测试:exec sp_test 1,1,20,''
*/
CREATE PROCEDURE [dbo].[sp_test](@BussionID int,@pageIndex int,@pageNumber int,@where varchar(100))
as
begin
if @BussionID = 1
begin
DECLARE @ID INT
--如果在第一页,则先定位到第一条记录,另外定位到本页第一条记录
DECLARE @v INT
SET @v = (@pageIndex-1)*@pageNumber + 1
SET ROWCOUNT @v
SELECT @ID=YGBH FROM YuanGong ORDER BY YGBH
SET ROWCOUNT @pageNumber
SELECT * FROM YuanGong WHERE YGBH>=@ID ORDER BY YGBH
SET ROWCOUNT 0
end
IF @BussionID = 2
BEGIN
--统计总记录数和总页数
DECLARE @PageCount INT
DECLARE @RecordCount INT
SELECT @RecordCount = COUNT(*) FROM YuanGong
SET @PageCount = @RecordCount / @pageNumber
IF @RecordCount % @pageNumber >0
BEGIN
SET @PageCount = @PageCount + 1
END
SELECT @PageCount AS PageCount,@RecordCount AS RecordCount
END
end
经过一系列的数据配置以及进行数据查询的存在过程,那后面的工作就交给asp.net了。
首先引入RSGrid.DLL,并在Default.aspx中加入RSGrid控件,
using System;
using System.Collections;
using System.Collections.Generic;
using System.Linq;
using System.Web;
using System.Web.UI;
using System.Web.UI.WebControls;
using System.Data;
using System.Data.SqlClient;
namespace WebApplication1
{
public partial class _Default : System.Web.UI.Page
{
protected void Page_Load(object sender, EventArgs e)
{
if (!IsPostBack)
{
RSGrid.GridColumnDictionary gcd = new RSGrid.GridColumnDictionary();
//清除缓存
gcd.RemoveAllDictionary();
//缓存字典表。这里可以写在程序启动的地方,只需要缓存一次。
DataSet ds = GetDataSet("select A.DLBM AS ItemCode,A.DLMC AS ItemCode,B.XLBM AS SubItemCode,B.XLMC AS SubItemName from ZiDian1 A,ZiDian2 B where A.DLBM=B.DLBM ORDER BY A.DLBM,B.XLBM");
gcd.BingDictionary(ds);
ds.Dispose();
}
}
protected void Button1_Click(object sender, EventArgs e)
{
Int32 pageIndex = RSGrid1.PageIndex;
Click(pageIndex);
}
private void Click(int pageIndex)
{
Int32 pageNumber = RSGrid1.PageNumber;
RSGrid1.PageIndex = pageIndex;
string formatSql = "exec sp_test {0},{1},{2}, ''";
string sql = string.Format(formatSql, 2, 1, pageNumber);
//获取总页数、总记录数
using (DataSet ds = GetDataSet(sql))
{
RSGrid1.PageCount = Int32.Parse(ds.Tables[0].Rows[0]["PageCount"].ToString());
RSGrid1.RecordCount = Int32.Parse(ds.Tables[0].Rows[0]["RecordCount"].ToString());
}
//获取列配置
RSGrid1.ColumnsSettingSource = GetDataSet("SELECT * FROM SYS_Columns");
//获取数据
sql = string.Format(formatSql, 1, pageIndex, pageNumber);
RSGrid1.DataSource = GetDataSet(sql);
RSGrid1.Bind();
}
private DataSet GetDataSet(string sql)
{
string connStr = System.Configuration.ConfigurationManager.ConnectionStrings["SQLConnString"].ToString();
SqlConnection conn = new SqlConnection(connStr);
SqlCommand cmd = new SqlCommand(sql, conn);
SqlDataAdapter da = new SqlDataAdapter(cmd);
DataSet ds = new DataSet();
da.Fill(ds);
conn.Close();
cmd.Dispose();
return ds;
}
//首页
protected void RSGrid1_OnFirstPageClick(object sender, ImageClickEventArgs e)
{
Click(1);
}
//上一页
protected void RSGrid1_OnPrevPageClick(object sender, ImageClickEventArgs e)
{
Int32 pageIndex = RSGrid1.PageIndex;
if (pageIndex > 1)
Click(pageIndex-1);
}
//下一页
protected void RSGrid1_OnNextPageClick(object sender, ImageClickEventArgs e)
{
Int32 pageIndex = RSGrid1.PageIndex;
if (pageIndex < RSGrid1.PageCount)
Click(pageIndex+1);
else
{
Click(pageIndex);
}
}
//末页
protected void RSGrid1_OnLastPageClick(object sender, ImageClickEventArgs e)
{
Int32 pageIndex = RSGrid1.PageCount;
Click(pageIndex);
}
//响应自定义的命令
protected void RSGrid1_CommandEvent(object sender, CommandEventArgs e)
{
if (e.CommandName.Equals("Del"))
{
Button1.Text = e.CommandArgument.ToString();
}
if (e.CommandName.Equals("View"))
{
Label1.Text = e.CommandArgument.ToString();
}
}
}
}
如果是射映成图片,比如“性别”字段需要使用图片来展示,那需要修改配置:
最终展示结果:
OK,现在已经是大功告成了,跑起来看看吧。
主要是使用了asp.net的cache,让每次的查询都100%命中字典缓存,服务器的映射工作都放到应用服务器上,这样服务器只需要关系业务表间的关联逻辑就行了,所以,在RSGrid的支持下,大胆地使用3NF来设计数据库吧,查询开销和查询速度绝对优于冗余设计的表。
欢迎大家拍砖,如果有跟我雷同的想法和控件,请大家也告诉我一下,我也学习学习。
控件已上传到我的资源中。http://download.csdn.net/detail/lllllllllluoyi/4515054