资料
在ASP.Net中两种利用CSS实现多界面的方法通过使页面动态加载不同CSS实现多界面
方法一: <%@page language="C#"%> <%@import namespace="System.Data"%> <script language="c#" runat="server"> public void page_load(Object obj,EventArgs e) { //创建服务器端控件. //指定的标记"LINK"初始化此类的新实例. HtmlGenericControl objLink=new HtmlGenericControl("LINK"); objLink.ID=ID; objLink.Attributes["rel"]="stylesheet"; objLink.Attributes["type"]="text/css"; objLink.Attributes["href"]="portal.css"; //此控件不产生任何可见输出,仅作为其他控件的容器,可在其中添加,插入或移除控件. MyCSS.Controls.Add(objLink); } </script> <html> <head> <title>c#</title> <asp:placeholder id="MyCSS" runat="server"></asp:placeholder> </head> <body bgColor="#ffcc66" style="FONT:9pt"> <form runat="server"> </form> </body> </html> 通过动态设置页面所有同类型控件的样式来该变界面: 方法二: 可以通过改变WEB控件的CssClass属性,可方便地设置和修改控件的样式。 但在实际开发过程中,一个个地设置控件的CssClass属性,非常繁琐,所以此思路应用不广. 但下面的代码段演示了一次性改变页面所有同类型控件的样式的方法,可以实现简单的SKIN等功能。 代码如下: public void page_load(Object obj,EventArgs e) { if(!Page.IsPostBack){ //为页面的所有控件设置样式. SetCSS(Page.Controls); } } private void SetCSS(System.Web.UI.ControlCollection vControls) { for(int i=0;i<vControls.Count;i++) { System.Web.UI.Control vControl=vControls[i]; //得到控件的类型 //可增加控件类型及相应处理方法 string PType=vControl.GetType().Name; switch (PType) { case "TextBox": TextBox_CSS ((TextBox) vControl); break; case "Button": //Button_CSS ((Button) vControl); break; case "DataGrid": //DataGrid_CSS ((DataGrid) vControl); break; } if(vControl.Controls.Count>0) SetCSS(vControl.Controls); } } private void TextBox_CSS(TextBox tb){ tb.CssClass="TextBox_show"; } <form runat="server"> <asp:textbox id="Search1" runat="server"/> <asp:textbox id="Search2" CssClass="INPUT" runat="server"/> </form> 运行后,查看页面源码.可发现文本框的样式已统一修改为"TextBox_show". 转换函数大全1、DateTime 数字型
System.DateTime currentTime=new System.DateTime();; 1.1 取当前年月日时分秒 currentTime=System.DateTime.Now;; 1.2 取当前年 int 年=currentTime.Year;; 1.3 取当前月 int 月=currentTime.Month;; 1.4 取当前日 int 日=currentTime.Day;; 1.5 取当前时 int 时=currentTime.Hour;; 1.6 取当前分 int 分=currentTime.Minute;; 1.7 取当前秒 int 秒=currentTime.Second;; 1.8 取当前毫秒 int 毫秒=currentTime.Millisecond;; (变量可用中文) 2、Int32.Parse(变量) Int32.Parse("常量") 字符型转换 转为32位数字型 3、 变量.ToString() 字符型转换 转为字符串 12345.ToString("n");; //生成 12,345.00 12345.ToString("C");; //生成 ¥12,345.00 12345.ToString("e");; //生成 1.234500e+004 12345.ToString("f4");; //生成 12345.0000 12345.ToString("x");; //生成 3039 (16进制) 12345.ToString("p");; //生成 1,234,500.00% 4、变量.Length 数字型 取字串长度: 如: string str="中国";; int Len = str.Length ;; //Len是自定义变量, str是求测的字串的变量名 5、System.Text.Encoding.Default.GetBytes(变量) 字码转换 转为比特码 如:byte[] bytStr = System.Text.Encoding.Default.GetBytes(str);; 然后可得到比特长度: len = bytStr.Length;; 6、System.Text.StringBuilder("") 字符串相加,(+号是不是也一样?) 如:System.Text.StringBuilder sb = new System.Text.StringBuilder("");; sb.Append("中华");; sb.Append("人民");; sb.Append("共和国");; 7、变量.Substring(参数1,参数2);; 截取字串的一部分,参数1为左起始位数,参数2为截取几位。 如:string s1 = str.Substring(0,2);; 8、String user_IP=Request.ServerVariables["REMOTE_ADDR"].ToString();; 取远程用户IP地址 9、穿过代理服务器取远程用户真实IP地址: if(Request.ServerVariables["HTTP_VIA"]!=null){ string user_IP=Request.ServerVariables["HTTP_X_FORWARDED_FOR"].ToString();; }else{ string user_IP=Request.ServerVariables["REMOTE_ADDR"].ToString();; } 10、 Session["变量"];; 存取Session值; 如,赋值: Session["username"]="小布什";; 取值: Object objName=Session["username"];; String strName=objName.ToString();; 清空: Session.RemoveAll();; 11、String str=Request.QueryString["变量"];; 用超链接传送变量。 如在任一页中建超链接:〈a href=Edit.aspx?fbid=23〉点击〈/a〉 在Edit.aspx页中取值:String str=Request.QueryString["fdid"];; 12、DOC对象.CreateElement("新建节点名");; 创建XML文档新节点 13、父节点.AppendChild(子节点); 将新建的子节点加到XML文档父节点下 14、 父节点.RemoveChild(节点);; 删除节点 15、Response Response.Write("字串"); Response.Write(变量); 向页面输出。 Response.Redirect("URL地址"); 跳转到URL指定的页面 16、char.IsWhiteSpce(字串变量,位数)——逻辑型 查指定位置是否空字符; 如: string str="中国 人民";; Response.Write(char.IsWhiteSpace(str,2));; //结果为:True, 第一个字符是0位,2是第三个字符。 17、char.IsPunctuation('字符') --逻辑型 查字符是否是标点符号 如:Response.Write(char.IsPunctuation('A'));; //返回:False 18、(int)'字符' 把字符转为数字,查代码点,注意是单引号。 如: Response.Write((int)'中');; //结果为中字的代码:20013 19、(char)代码 把数字转为字符,查代码代表的字符。 如: Response.Write((char)22269);; //返回;国”字。 20、 Trim() 清除字串前后空格 21 、字串变量.Replace("子字串","替换为") 字串替换 如: string str="中国";; str=str.Replace("国","央");; //将国字换为央字 Response.Write(str);; //输出结果为;中央” 再如:(这个非常实用) string str="这是〈script〉脚本";; str=str.Replace("〈","〈font〉〈〈/font〉");; //将左尖括号替换为〈font〉 与 〈 与 〈/font〉 (或换为〈,但估计经XML存诸后,再提出仍会还原) Response.Write(str);; //显示为:;这是〈script〉脚本” 如果不替换,〈script〉将不显示,如果是一段脚本,将运行;而替换后,脚本将不运行。 这段代码的价值在于:你可以让一个文本中的所有HTML标签失效,全部显示出来,保护你的具有交互性的站点。 具体实现:将你的表单提交按钮脚本加上下面代码: string strSubmit=label1.Text;; //label1是你让用户提交数据的控件ID。 strSubmit=strSubmit.Replace("〈","〈font〉〈〈/font〉");; 然后保存或输出strSubmit。 用此方法还可以简单实现UBB代码。 22、Math.Max(i,j) 取i与j中的最大值 如 int x=Math.Max(5,10);; // x将取值 10 23、字串对比一般都用: if(str1==str2){ } , 但还有别的方法: (1)、 string str1;; str2 //语法: str1.EndsWith(str2);; __检测字串str1是否以字串str2结尾,返回布尔值.如: if(str1.EndsWith(str2)){ Response.Write("字串str1是以"+str2+"结束的");; } (2)、 //语法:str1.Equals(str2);; __检测字串str1是否与字串str2相等,返回布尔值,用法同上. (3)、 //语法 Equals(str1,str2);; __检测字串str1是否与字串str2相等,返回布尔值,用法同上. 24、IndexOf() 、LastIndexOf() 查找字串中指定字符或字串首次(最后一次)出现的位置,返回索引值,如: str1.IndexOf("字"); //查找;字”在str1中的索引值(位置) str1.IndexOf("字串");//查找;字串”的第一个字符在str1中的索引值(位置) str1.IndexOf("字串",3,2);//从str1第4个字符起,查找2个字符,查找;字串”的第一个字符在str1中的索引值(位置) 25、Insert() 在字串中指定索引位插入指定字符。如: str1.Insert(1,"字");;在str1的第二个字符处插入;字”,如果str1="中国",插入后为;中字国”; 26、PadLeft()、PadRight() 在字串左(或右)加空格或指定char字符,使字串达到指定长度,如: 〈% string str1="中国人";; str1=str1.PadLeft(10,'1');; //无第二参数为加空格 Response.Write(str1);; //结果为;1111111中国人” , 字串长为10 %〉 27、Remove() 从指定位置开始删除指定数的字符 字串对比一般都用: if(str1==str2){ } , 但还有别的方法: 1、 string str1;; str2 //语法: str1.EndsWith(str2);; __检测字串str1是否以字串str2结尾,返回布尔值.如: if(str1.EndsWith(str2)){ Response.Write("字串str1是以"+str2+"结束的");; } 2、 //语法:str1.Equals(str2);; __检测字串str1是否与字串str2相等,返回布尔值,用法同上. 3、 //语法 Equals(str1,str2);; __检测字串str1是否与字串str2相等,返回布尔值,用法同上. IndexOf() 查找字串中指定字符或字串首次出现的位置,返首索引值,如: str1.IndexOf("字"); //查找;字”在str1中的索引值(位置) str1.IndexOf("字串");//查找;字串”的第一个字符在str1中的索引值(位置) str1.IndexOf("字串",3,2);//从str1第4个字符起,查找2个字符,查找;字串”的第一个字符在str1中的索引值(位置) 1.9 取中文日期显示——年月日时分 string strY=currentTime.ToString("f");; //不显示秒 1.10 取中文日期显示_年月 string strYM=currentTime.ToString("y");; 1.11 取中文日期显示_月日 string strMD=currentTime.ToString("m");; 1.12 取当前年月日,格式为:2003-9-23 string strYMD=currentTime.ToString("d");; 1.13 取当前时分,格式为:14:24 string strT=currentTime.ToString("t");; 2007/7/6
转载(正则表达式)正则表达式用于字符串处理,表单验证等场合,实用高效,但用到时总是不太把握,以致往往要上网查一番。我将一些常用的表达式收藏在这里,作备忘之用。本贴随时会更新。 匹配中文字符的正则表达式: [\u4e00-\u9fa5] 匹配双字节字符(包括汉字在内):[^\x00-\xff] 应用:计算字符串的长度(一个双字节字符长度计2,ASCII字符计1) String.prototype.len=function(){return this.replace([^\x00-\xff]/g,"aa").length;} 匹配空行的正则表达式:\n[\s| ]*\r 匹配HTML标记的正则表达式:/<(.*)>.*<\/\1>|<(.*) \/>/ 匹配首尾空格的正则表达式:(^\s*)|(\s*$)
String.prototype.trim = function() 利用正则表达式分解和转换IP地址: 下面是利用正则表达式匹配IP地址,并将IP地址转换成对应数值的Javascript程序: function IP2V(ip) 不过上面的程序如果不用正则表达式,而直接用split函数来分解可能更简单,程序如下: var ip="10.100.20.168" 匹配Email地址的正则表达式:\w+([-+.]\w+)*@\w+([-.]\w+)*\.\w+([-.]\w+)* 匹配网址URL的正则表达式:http://([\w-]+\.)+[\w-]+(/[\w- ./?%&=]*)?
var s="abacabefgeeii" 得用正则表达式从URL地址中提取文件名的javascript程序,如下结果为page1 s="http://www.9499.net/page1.htm" 利用正则表达式限制网页表单里的文本框输入内容: 用正则表达式限制只能输入中文:onkeyup="value=value.replace(/[^\u4E00-\u9FA5]/g,'')" onbeforepaste="clipboardData.setData('text',clipboardData.getData('text').replace(/[^\u4E00-\u9FA5]/g,''))" 用正则表达式限制只能输入全角字符: onkeyup="value=value.replace(/[^\uFF00-\uFFFF]/g,'')" onbeforepaste="clipboardData.setData('text',clipboardData.getData('text').replace(/[^\uFF00-\uFFFF]/g,''))" 用正则表达式限制只能输入数字:onkeyup="value=value.replace(/[^\d]/g,'') "onbeforepaste="clipboardData.setData('text',clipboardData.getData('text').replace(/[^\d]/g,''))" 用正则表达式限制只能输入数字和英文:onkeyup="value=value.replace(/[\W]/g,'') "onbeforepaste="clipboardData.setData('text',clipboardData.getData('text').replace(/[^\d]/g,''))"
正则表达式,相关链接 正则表达式 regular expression 补充: 利用正则表达式去除字串中重复的字符的算法程序: 1.确认有效电子邮件格式
一、正则表达式基础知识 ![]() 1.1句点符号 假设你在玩英文拼字游戏,想要找出三个字母的单词,而且这些单词必须以“t”字母开头,以“n”字母结束。另外,假设有一本英文字典,你可以用正则表达式搜索它的全部内容。要构造出这个正则表达式,你可以使用一个通配符??句点符号“.”。这样,完整的表达式就是“t.n”,它匹配“tan”、“ten”、“tin”和“ton”,还匹配 “t#n”、“tpn”甚至“tn”,还有其他许多无意义的组合。这是因为句点符号匹配所有字符,包括空格、Tab字符甚至换行符: ![]() 1.2方括号符号 为了解决句点符号匹配范围过于广泛这一问题,你可以在方括号(“[]”)里面指定看来有意义的字符。此时,只有方括号里面指定的字符才参与匹配。也就是说,正则表达式“t[aeio]n”只匹配“tan”、“Ten”、“tin”和“ton”。但“Toon”不匹配,因为在方括号之内你只能匹配单个字符: ![]() 1.4表示匹配次数的符号 表一显示了表示匹配次数的符号,这些符号用来确定紧靠该符号左边的符号出现的次数: ![]() 假设我们要在文本文件中搜索美国的社会安全号码。这个号码的格式是999-99-9999。用来匹配它的正则表达式如图一所示。在正则表达式中,连字符(“-”)有着特殊的意义,它表示一个范围,比如从0到9。因此,匹配社会安全号码中的连字符号时,它的前面要加上一个转义字符“\”。 ![]() 图一:匹配所有123-12-1234形式的社会安全号码 假设进行搜索的时候,你希望连字符号可以出现,也可以不出现??即,999-99-9999和999999999都属于正确的格式。这时,你可以在连字符号后面加上“?”数量限定符号,如图二所示: ![]() 图二:匹配所有123-12-1234和123121234形式的社会安全号码 下面我们再来看另外一个例子。美国汽车牌照的一种格式是四个数字加上二个字母。它的正则表达式前面是数字部分“[0-9]{ 4}”,再加上字母部分“[A-Z]{ 2}”。图三显示了完整的正则表达式。 ![]() 图三:匹配典型的美国汽车牌照号码,如8836KV 1.5“否”符号 “^”符号称为“否”符号。如果用在方括号内,“^”表示不想要匹配的字符。例如,图四的正则表达式匹配所有单词,但以“X”字母开头的单词除外。 ![]() 图四:匹配所有单词,但“X”开头的除外 1.6圆括号和空白符号 假设要从格式为“June26,1951”的生日日期中提取出月份部分,用来匹配该日期的正则表达式可以如图五所示: ![]() 图五:匹配所有MothDD,YYYY格式的日期 新出现的“\s”符号是空白符号,匹配所有的空白字符,包括Tab字符。如果字符串正确匹配,接下来如何提取出月份部分呢?只需在月份周围加上一个圆括号创建一个组,然后用OROAPI(本文后面详细讨论)提取出它的值。修改后的正则表达式如图六所示: ![]() 图六:匹配所有MonthDD,YYYY格式的日期,定义月份值为第一个组 1.7其它符号 为简便起见,你可以使用一些为常见正则表达式创建的快捷符号。如表二所示: 表二:常用符号 ![]() 例如,在前面社会安全号码的例子中,所有出现“[0-9]”的地方我们都可以使用“\d”。修改后的正则表达式如图七所示: ![]()
|
SQL的自动生成卡号和密码
;
set @i = 10200001;
while (@i<=10250000)
begin
set @j = 0;
set @str='';
while (@j<=16)
begin
set @int_temp=rand()*9;
set @str_temp=str(@int_temp);
set @str=@str+@str_temp;
set @j=@j+1;
end
set @Card_NO=str(@i);
set @str=replace(@str,' ','');
insert into IN_Card (Card_No,[Password],[Value])values(@Card_NO,@str,100);
set @i=@i+1;
Asp.Net防止刷新重复提交数据
网上最多的解决此类问题的方法就是不保存缓存,即提交后表单上的数据不会被浏览器的缓存保存,如果此时再遇到刷新或者后退请求时, 就会显示“网页已过期”,数据也就不会重复提交了,这就起到了阻止刷新重复提交的效果。
下面以简单的提交一篇帖子为例,介绍禁用缓存防止刷新重复提交的方法,表单数据包括“标题”和“正文”两个部分。
以下是该方法的代码(post.aspx):
protected void Page_Load(object sender, EventArgs e)
{
//可以在页面加载时设置页面的缓存为“SetNoStore()”,即无缓存
Response.Cache.SetNoStore();
//Session中存储的变量“IsSubmit”是标记是否提交成功的
if ((bool)Session["IsSubmit"])
{
//如果表单数据提交成功,就设“Session["IsSubmit"]”为false
Session["IsSubmit"] = false;
//显示提交成功信息
ShowMsg.Text = " * 提交成功!";
}
else
//否则的话(没有提交,或者是页面刷新),不显示任何信息
ShowMsg.Text = "";
}
//提交按钮(btnOK)单击事件
protected void btnOK_Click(object sender, EventArgs e)
{
if (txtTitle.Text.ToString().Trim() == "")
//ShowMsg是用来显示提示信息的
ShowMsg.Text = " * 标题不能为空!";
else if (txtText.Text.ToString().Trim() == "")
ShowMsg.Text = " * 内容不能为空!";
else
{
//这里是将数据提交到数据库中,省略
/*
string sql = "insert into tab...values(...)";
MyConn.ExecQuery(sql);
*/
//提交成功后,设“Session["IsSubmit"]”为true
Session["IsSubmit"] = true;
//强制转换页面(不可少,否则刷新仍会重复提交,仍转到本页),
通过页面的转换将缓存中的提交的数据都释放了,即提交的标单数据不会被保存到缓存里,
如果后退的话,将会出现该页无法显示
Response.Redirect("post.aspx");
}
}
上面这个方法非常简单也很实用,推荐大家使用。
下面是我自己研究出来的另一种方法,该方法不同于“不保存缓存的方法”,它是让浏览器保存所有页面缓存的。该方法通过随机码的方式 来判断是正常提交还是“刷新”或“后退”的。
首先(提交页面是post.aspx)在 Session 中 增加变量 Rnd 用来存放随机码,同时在提交表单数据时不做处理,而是让页面转到 post.aspx?r=x,这里“x”等于Session["Rnd"],这个时候在页面加载时,通过判断r的值和Session["Rnd"]的值是否相同,如果相同就处理提 交的数据,否则即可认为是“刷新”或者是“后退”操作了,最后再次付给Session["Rnd"]一个随机码。
以下是该方法代码(post.aspx):
public class MyRnd
{
public static string Rnd()
{
//随机码是由 0-9 a-z A-Z 之间的数字或字母组成的
//下面是生成的20位随机码
//0..9 A..Z a..z
//48-57 65-90 97-122
string rst = "";
Random rr = new Random();
for (int i = 0; i < 20; i++)
{
int ir = 0;
do
{
ir = rr.Next(123);
if((ir >= 48) && (ir <= 57)) break;
else if((ir >= 65) && (ir <= 90)) break;
else if ((ir >= 97) && (ir <= 122)) break;
}
while (true);
rst += ((char)ir).ToString();
}
return rst;
}
}
//页面加载
protected void Page_Load(object sender, EventArgs e)
{
//获取URL中请求的“r”值,如果“r”不存在则 r=""
string r = "";
if(Request.QueryString["r"] != null)
r = Request.QueryString["r"].ToString().Trim();
string t;
//获取 “Session” 中的 “Rnd” 值,用于和“r”比较
t = Session["Rnd"].ToString().Trim();
//如果“r=t”则为提交操作,即可对表单的数据进行处理
if(r == t)
{
if (txtTitle.Text.ToString().Trim() == "")
ShowMsg.Text = " * 标题不能为空!";
else if (txtText.Text.ToString().Trim() == "")
ShowMsg.Text = " * 内容不能为空!";
else {
//这里是将数据提交到数据库中,省略
/*
string sql = "insert into tab...values(...)";
MyConn.ExecQuery(sql);
*/
//提交成功后清空表单数据
txtTitle.Text = "";
txtText.Text = "";
//显示提交成功信息
ShowMsg.Text = " * 提交成功!";
}
}
//否则可以认为是“刷新”或者“后退”操作
else
{
txtTitle.Text = "";
txtText.Text = "";
}
//最后要重新获得“Session["Rnd"]”的值,并将“btnOK.PostBackUrl”设为“Session["Rnd"]”的值
Session["Rnd"] = MyRnd.Rnd();
btnOK.PostBackUrl ="post.aspx?r=" + Session["Rnd"].ToString().Trim();
}
//这里提交按钮(btnOK)单击事件就不需要写任何代码了
通过这种方法,每次加载页面时“Session["Rnd"]”都将得到一个新的值,而在刷新或后退时就不会得到相同的“r”和“t”值,数据也就 不会被重复提交,只有通过“btnOK”来提交的操作才会得到“r==t”,数据才会被提交处理的,通过判断随机码的方式来阻止刷新重复提交就 可以实现了。
提高SQL Server性能
为了解决这些问题,重要的是找到问题的根源。那么,从哪里开始呢?根本原因通常在于数据库设计和访问它的查询。我将讲述四项技术,这些技术可用于提高基于SQL Server的应用程序的性能或改善其可伸缩性。我将仔细说明 LEFT JOIN、CROSS JOIN 的使用以及IDENTITY 值的检索。请记住,根本没有神奇的解决方案。调整您的数据库及其查询需要占用时间、进行分析,还需要大量的测试。这些技术都已被证明行之有效,但对您的应用程序而言,可能其中一些技术比另一些技术更适用。
从 INSERT 返回 IDENTITY
我决定从遇到许多问题的内容入手:如何在执行SQL INSERT后检索IDENTITY值。通常,问题不在于如何编写检索值的查询,而在于在哪里以及何时进行检索。在SQL Server中,下面的语句可用于检索由最新在活动数据库连接上运行的 SQL 语句所创建的 IDENTITY 值:
SELECT @@IDENTITY
这个 SQL 语句并不复杂,但需要记住的一点是:如果这个最新的 SQL 语句不是 INSERT,或者您针对非 INSERT SQL 的其他连接运行了此 SQL,则不会获得期望的值。您必须运行下列代码才能检索紧跟在 INSERT SQL 之后且位于同一连接上的 IDENTITY,如下所示:
INSERT INTO Products (ProductName) VALUES ('Chalk')
SELECT @@IDENTITY
在一个连接上针对 Northwind 数据库运行这些查询将返回一个名称为 Chalk 的新产品的 IDENTITY 值。所以,在使用ADOVisual Basic应用程序中,可以运行以下语句:
Set oRs = oCn.Execute("SET NOCOUNT ON;INSERT INTO Products _
(ProductName) VALUES ('Chalk');SELECT @@IDENTITY")
lProductID = oRs(0)
此代码告诉 SQL Server 不要返回查询的行计数,然后执行 INSERT 语句,并返回刚刚为这个新行创建的 IDENTITY 值。SET NOCOUNT ON 语句表示返回的记录集有一行和一列,其中包含了这个新的 IDENTITY 值。如果没有此语句,则会首先返回一个空的记录集(因为 INSERT 语句不返回任何数据),然后会返回第二个记录集,第二个记录集中包含 IDENTITY 值。这可能有些令人困惑,尤其是因为您从来就没有希望过 INSERT 会返回记录集。之所以会发生此情况,是因为 SQL Server 看到了这个行计数(即一行受到影响)并将其解释为表示一个记录集。因此,真正的数据被推回到了第二个记录集。当然您可以使用 ADO 中的 NextRecordset 方法获取此第二个记录集,但如果总能够首先返回该记录集且只返回该记录集,则会更方便,也更有效率。
此方法虽然有效,但需要在 SQL 语句中额外添加一些代码。获得相同结果的另一方法是在 INSERT 之前使用 SET NOCOUNT ON 语句,并将 SELECT @@IDENTITY 语句放在表中的 FOR INSERT 触发器中,如下面的代码片段所示。这样,任何进入该表的 INSERT 语句都将自动返回 IDENTITY 值。
CREATE TRIGGER trProducts_Insert ON Products FOR INSERT AS
SELECT @@IDENTITY
GO
触发器只在 Products 表上发生 INSERT 时启动,所以它总是会在成功 INSERT 之后返回一个 IDENTITY。使用此技术,您可以始终以相同的方式在应用程序中检索 IDENTITY 值。
内嵌视图与临时表
某些时候,查询需要将数据与其他一些可能只能通过执行 GROUP BY 然后执行标准查询才能收集的数据进行联接。例如,如果要查询最新五个定单的有关信息,您首先需要知道是哪些定单。这可以使用返回定单 ID 的 SQL 查询来检索。此数据就会存储在临时表(这是一个常用技术)中,然后与 Products 表进行联接,以返回这些定单售出的产品数量:
CREATE TABLE #Temp1 (OrderID INT NOT NULL, _
OrderDate DATETIME NOT NULL)
INSERT INTO #Temp1 (OrderID, OrderDate)
SELECT TOP 5 o.OrderID, o.OrderDate
FROM Orders o ORDER BY o.OrderDate DESC
SELECT p.ProductName, SUM(od.Quantity) AS ProductQuantity
FROM #Temp1 t
INNER JOIN [Order Details] od ON t.OrderID = od.OrderID
INNER JOIN Products p ON od.ProductID = p.ProductID
GROUP BY p.ProductName
ORDER BY p.ProductName
DROP TABLE #Temp1
这些 SQL 语句会创建一个临时表,将数据插入该表中,将其他数据与该表进行联接,然后除去该临时表。这会导致此查询进行大量 I/O 操作,因此,可以重新编写查询,使用内嵌视图取代临时表。内嵌视图只是一个可以联接到 FROM 子句中的查询。所以,您不用在 tempdb 中的临时表上耗费大量 I/O 和磁盘访问,而可以使用内嵌视图得到同样的结果:
SELECT p.ProductName,
SUM(od.Quantity) AS ProductQuantity
FROM (
SELECT TOP 5 o.OrderID, o.OrderDate
FROM Orders o
ORDER BY o.OrderDate DESC
) t
INNER JOIN [Order Details] od ON t.OrderID = od.OrderID
INNER JOIN Products p ON od.ProductID = p.ProductID
GROUP BY
p.ProductName
ORDER BY
p.ProductName
此查询不仅比前面的查询效率更高,而且长度更短。临时表会消耗大量资源。如果只需要将数据联接到其他查询,则可以试试使用内嵌视图,以节省资源。
避免 LEFT JOIN 和 NULL
当然,有很多时候您需要执行 LEFT JOIN 和使用 NULL 值。但是,它们并不适用于所有情况。改变 SQL 查询的构建方式可能会产生将一个花几分钟运行的报告缩短到只花几秒钟这样的天壤之别的效果。有时,必须在查询中调整数据的形态,使之适应应用程序所要求的显示方式。虽然 TABLE 数据类型会减少大量占用资源的情况,但在查询中还有许多区域可以进行优化。SQL 的一个有价值的常用功能是 LEFT JOIN。它可以用于检索第一个表中的所有行、第二个表中所有匹配的行、以及第二个表中与第一个表不匹配的所有行。例如,如果希望返回每个客户及其定单,使用 LEFT JOIN 则可以显示有定单和没有定单的客户。
此工具可能会被过度使用。LEFT JOIN 消耗的资源非常之多,因为它们包含与 NULL(不存在)数据匹配的数据。在某些情况下,这是不可避免的,但是代价可能非常高。LEFT JOIN 比 INNER JOIN 消耗资源更多,所以如果您可以重新编写查询以使得该查询不使用任何 LEFT JOIN,则会得到非常可观的回报。
加快使用 LEFT JOIN 的查询速度的一项技术涉及创建一个 TABLE 数据类型,插入第一个表(LEFT JOIN 左侧的表)中的所有行,然后使用第二个表中的值更新 TABLE 数据类型。此技术是一个两步的过程,但与标准的 LEFT JOIN 相比,可以节省大量时间。一个很好的规则是尝试各种不同的技术并记录每种技术所需的时间,直到获得用于您的应用程序的执行性能最佳的查询。
测试查询的速度时,有必要多次运行此查询,然后取一个平均值。因为查询(或存储过程)可能会存储在 SQL Server 内存中的过程缓存中,因此第一次尝试耗费的时间好像稍长一些,而所有后续尝试耗费的时间都较短。另外,运行您的查询时,可能正在针对相同的表运行其他查询。当其他查询锁定和解锁这些表时,可能会导致您的查询要排队等待。例如,如果您进行查询时某人正在更新此表中的数据,则在更新提交时您的查询可能需要耗费更长时间来执行。
避免使用 LEFT JOIN 时速度降低的最简单方法是尽可能多地围绕它们设计数据库。例如,假设某一产品可能具有类别也可能没有类别。如果 Products 表存储了其类别的 ID,而没有用于某个特定产品的类别,则您可以在字段中存储 NULL 值。然后您必须执行 LEFT JOIN 来获取所有产品及其类别。您可以创建一个值为“No Category”的类别,从而指定外键关系不允许 NULL 值。通过执行上述操作,现在您就可以使用 INNER JOIN 检索所有产品及其类别了。虽然这看起来好像是一个带有多余数据的变通方法,但可能是一个很有价值的技术,因为它可以消除 SQL 批处理语句中消耗资源较多的 LEFT JOIN。在数据库中全部使用此概念可以为您节省大量的处理时间。请记住,对于您的用户而言,即使几秒钟的时间也非常重要,因为当您有许多用户正在访问同一个联机数据库应用程序时,这几秒钟实际上的意义会非常重大。
灵活使用笛卡尔乘积
对于此技巧,我将进行非常详细的介绍,并提倡在某些情况下使用笛卡尔乘积。出于某些原因,笛卡尔乘积 (CROSS JOIN) 遭到了很多谴责,开发人员通常会被警告根本就不要使用它们。在许多情况下,它们消耗的资源太多,从而无法高效使用。但是像 SQL 中的任何工具一样,如果正确使用,它们也会很有价值。例如,如果您想运行一个返回每月数据(即使某一特定月份客户没有定单也要返回)的查询,您就可以很方便地使用笛卡尔乘积。
虽然这看起来好像没什么神奇的,但是请考虑一下,如果您从客户到定单(这些定单按月份进行分组并对销售额进行小计)进行了标准的 INNER JOIN,则只会获得客户有定单的月份。因此,对于客户未订购任何产品的月份,您不会获得 0 值。如果您想为每个客户都绘制一个图,以显示每个月和该月销售额,则可能希望此图包括月销售额为 0 的月份,以便直观标识出这些月份。如果使用 Figure 2(最后一页) 中的 SQL,数据则会跳过销售额为 0 美元的月份,因为在定单表中对于零销售额不会包含任何行(假设您只存储发生的事件)。
Figure 3(最后一页)中的代码虽然较长,但是可以达到获取所有销售数据(甚至包括没有销售额的月份)的目标。首先,它会提取去年所有月份的列表,然后将它们放入第一个 TABLE 数据类型表 (@tblMonths) 中。下一步,此代码会获取在该时间段内有销售额的所有客户公司的名称列表,然后将它们放入另一个 TABLE 数据类型表 (@tblCus-tomers) 中。这两个表存储了创建结果集所必需的所有基本数据,但实际销售数量除外。 第一个表中列出了所有月份(12 行),第二个表中列出了这个时间段内有销售额的所有客户(对于我是 81 个)。并非每个客户在过去 12 个月中的每个月都购买了产品,所以,执行 INNER JOIN 或 LEFT JOIN 不会返回每个月的每个客户。这些操作只会返回购买产品的客户和月份。
笛卡尔乘积则可以返回所有月份的所有客户。笛卡尔乘积基本上是将第一个表与第二个表相乘,生成一个行集合,其中包含第一个表中的行数与第二个表中的行数相乘的结果。因此,笛卡尔乘积会向表 @tblFinal 返回 972 行。最后的步骤是使用此日期范围内每个客户的月销售额总计更新 @tblFinal 表,以及选择最终的行集。
如果由于笛卡尔乘积占用的资源可能会很多,而不需要真正的笛卡尔乘积,则可以谨慎地使用 CROSS JOIN。例如,如果对产品和类别执行了 CROSS JOIN,然后使用 WHERE 子句、DISTINCT 或 GROUP BY 来筛选出大多数行,那么使用 INNER JOIN 会获得同样的结果,而且效率高得多。如果需要为所有的可能性都返回数据(例如在您希望使用每月销售日期填充一个图表时),则笛卡尔乘积可能会非常有帮助。但是,您不应该将它们用于其他用途,因为在大多数方案中 INNER JOIN 的效率要高得多。
拾遗补零
这里介绍其他一些可帮助提高 SQL 查询效率的常用技术。假设您将按区域对所有销售人员进行分组并将他们的销售额进行小计,但是您只想要那些数据库中标记为处于活动状态的销售人员。您可以按区域对销售人员分组,并使用 HAVING 子句消除那些未处于活动状态的销售人员,也可以在 WHERE 子句中执行此操作。在 WHERE 子句中执行此操作会减少需要分组的行数,所以比在 HAVING 子句中执行此操作效率更高。HAVING 子句中基于行的条件的筛选会强制查询对那些在 WHERE 子句中会被去除的数据进行分组。
另一个提高效率的技巧是使用 DISTINCT 关键字查找数据行的单独报表,来代替使用 GROUP BY 子句。在这种情况下,使用 DISTINCT 关键字的 SQL 效率更高。请在需要计算聚合函数(SUM、COUNT、MAX 等)的情况下再使用 GROUP BY。另外,如果您的查询总是自己返回一个唯一的行,则不要使用 DISTINCT 关键字。在这种情况下,DISTINCT 关键字只会增加系统开销。
您已经看到了,有大量技术都可用于优化查询和实现特定的业务规则,技巧就是进行一些尝试,然后比较它们的性能。最重要的是要测试、测试、再测试。
Figure 2 Returning All Customers and Their Sales
set nocount on
DECLARE @dtStartDate DATETIME,
@dtEndDate DATETIME,
@dtDate DATETIME
SET @dtEndDate = '5/5/1997'
SET @dtEndDate = DATEADD(DD, -1, CAST(CAST((MONTH(@dtEndDate) + 1)
AS VARCHAR(2)) + '/01/' + CAST(YEAR(@dtEndDate) AS VARCHAR(4)) + '
23:59:59' AS DATETIME))
SET @dtStartDate = DATEADD(MM, -1 * 12, @dtEndDate)
SELECT CAST(YEAR(o.OrderDate) AS VARCHAR(4)) + '-' +
CASE
WHEN MONTH(o.OrderDate) < 10
THEN '0' + CAST(MONTH(o.OrderDate) AS VARCHAR(2))
ELSE CAST(MONTH(o.OrderDate) AS VARCHAR(2))
END AS sMonth,
c.CustomerID,
c.CompanyName,
c.ContactName,
SUM(od.Quantity * od.UnitPrice) AS mSales
FROM Customers c
INNER JOIN Orders o ON c.CustomerID = o.CustomerID
INNER JOIN [Order Details] od ON o.OrderID = od.OrderID
WHERE o.OrderDate BETWEEN @dtStartDate AND @dtEndDate
GROUP BY
CAST(YEAR(o.OrderDate) AS VARCHAR(4)) + '-' +
CASE
WHEN MONTH(o.OrderDate) < 10
THEN '0' + CAST(MONTH(o.OrderDate) AS VARCHAR(2))
ELSE CAST(MONTH(o.OrderDate) AS VARCHAR(2))
END,
c.CustomerID,
c.CompanyName,
c.ContactName
ORDER BY
c.CompanyName,
sMonth-------------------------------------------------------------------------------------------------------------------------------------
Figure 3 Cartesian Product at Work
DECLARE @tblMonths TABLE (sMonth VARCHAR(7))
DECLARE @tblCustomers TABLE ( CustomerID CHAR(10),
CompanyName VARCHAR(50),
ContactName VARCHAR(50))
DECLARE @tblFinal TABLE ( sMonth VARCHAR(7),
CustomerID CHAR(10),
CompanyName VARCHAR(50),
ContactName VARCHAR(50),
mSales MONEY)
DECLARE @dtStartDate DATETIME,
@dtEndDate DATETIME,
@dtDate DATETIME,
@i INTEGER
SET @dtEndDate = '5/5/1997'
SET @dtEndDate = DATEADD(DD, -1, CAST(CAST((MONTH(@dtEndDate) + 1) AS
VARCHAR(2)) + '/01/' + CAST(YEAR(@dtEndDate) AS VARCHAR(4)) + '
23:59:59' AS DATETIME))
SET @dtStartDate = DATEADD(MM, -1 * 12, @dtEndDate)
— Get all months into the first table
SET @i = 0
WHILE (@i < 12)
BEGIN
SET @dtDate = DATEADD(mm, -1 * @i, @dtEndDate)
INSERT INTO @tblMonths SELECT CAST(YEAR(@dtDate) AS VARCHAR(4)) + '-' +
CASE
WHEN MONTH(@dtDate) < 10
THEN '0' + CAST(MONTH(@dtDate) AS VARCHAR(2))
ELSE CAST(MONTH(@dtDate) AS VARCHAR(2))
END AS sMonth
SET @i = @i + 1
END
— Get all clients who had sales during that period into the "y" table
INSERT INTO @tblCustomers
SELECT DISTINCT
c.CustomerID,
c.CompanyName,
c.ContactName
FROM Customers c
INNER JOIN Orders o ON c.CustomerID = o.CustomerID
WHERE o.OrderDate BETWEEN @dtStartDate AND @dtEndDate
INSERT INTO @tblFinal
SELECT m.sMonth,
c.CustomerID,
c.CompanyName,
c.ContactName,
0
FROM @tblMonths m CROSS JOIN @tblCustomers c
UPDATE @tblFinal SET
mSales = mydata.mSales
FROM @tblFinal f INNER JOIN
(
SELECT c.CustomerID,
CAST(YEAR(o.OrderDate) AS VARCHAR(4)) + '-' +
CASE WHEN MONTH(o.OrderDate) < 10
THEN '0' + CAST(MONTH(o.OrderDate) AS VARCHAR(2))
ELSE CAST(MONTH(o.OrderDate) AS VARCHAR(2))
END AS sMonth,
SUM(od.Quantity * od.UnitPrice) AS mSales
FROM Customers c
INNER JOIN Orders o ON c.CustomerID = o.CustomerID
INNER JOIN [Order Details] od ON o.OrderID = od.OrderID
WHERE o.OrderDate BETWEEN @dtStartDate AND @dtEndDate
GROUP BY
c.CustomerID,
CAST(YEAR(o.OrderDate) AS VARCHAR(4)) + '-' +
CASE WHEN MONTH(o.OrderDate) < 10
THEN '0' + CAST(MONTH(o.OrderDate) AS VARCHAR(2))
ELSE CAST(MONTH(o.OrderDate) AS VARCHAR(2))
END
) mydata on f.CustomerID = mydata.CustomerID AND f.sMonth =
mydata.sMonth
SELECT f.sMonth,
f.CustomerID,
f.CompanyName,
f.ContactName,
f.mSales
FROM @tblFinal f
ORDER BY
f.CompanyName,
f.sMonth
SQL Server存储过程
请大家来了解一下存储过程的语法。
CREATE PROC [ EDURE ] procedure_name [ ; number ]
[ { @parameter data_type }
[ VARYING ] [ = default ] [ OUTPUT ]
] [ ,...n ]
[ WITH
{ RECOMPILE | ENCRYPTION | RECOMPILE , ENCRYPTION } ]
[ FOR REPLICATION ]
AS sql_statement [ ...n ]
参数:
procedure_name
新存储过程的名称。过程名必须符合标识符规则,且对于数据库及其所有者必须唯一。
要创建局部临时过程,可以在 procedure_name 前面加一个编号符 (#procedure_name),要创建全局临时过程,可以在 procedure_name 前面加两个编号符 (##procedure_name)。完整的名称(包括 # 或 ##)不能超过 128 个字符。指定过程所有者的名称是可选的。
;number
是可选的整数,用来对同名的过程分组,以便用一条 DROP PROCEDURE 语句即可将同组的过程一起除去。例如,名为 orders 的应用程序使用的过程可以命名为 orderproc;1、orderproc;2 等。DROP PROCEDURE orderproc 语句将除去整个组。如果名称中包含定界标识符,则数字不应包含在标识符中,只应在 procedure_name 前后使用适当的定界符。
@parameter
过程中的参数。在 CREATE PROCEDURE 语句中可以声明一个或多个参数。用户必须在执行过程时提供每个所声明参数的值(除非定义了该参数的默认值)。存储过程最多可以有 2100 个参数。
使用@符号作为第一个字符来指定参数名称。参数名称必须符合标识符的规则。每个过程的参数仅用于该过程本身;相同的参数名称可以用在其它过程中。默认情况下,参数只能代替常量,而不能用于代替表名、列名或其它数据库对象的名称。
data_type
参数的数据类型。所有数据类型(包括 text、ntext 和 image)均可以用作存储过程的参数。不过,cursor 数据类型只能用于 OUTPUT 参数。如果指定的数据类型为 cursor,也必须同时指定 VARYING 和 OUTPUT 关键字。
说明:对于可以是cursor 数据类型的输出参数,没有最大数目的限制。
VARYING
指定作为输出参数支持的结果集(由存储过程动态构造,内容可以变化)。仅适用于游标参数。
default
参数的默认值。如果定义了默认值,不必指定该参数的值即可执行过程。默认值必须是常量或 NULL。如果过程将对该参数使用 LIKE 关键字,那么默认值中可以包含通配符(%、_、[] 和 [^])。
OUTPUT
表明参数是返回参数。该选项的值可以返回给 EXEC[UTE]。使用 OUTPUT 参数可将信息返回给调用过程。Text、ntext 和 image 参数可用作 OUTPUT 参数。使用 OUTPUT 关键字的输出参数可以是游标占位符。
n
表示最多可以指定 2100 个参数的占位符。
{RECOMPILE | ENCRYPTION | RECOMPILE, ENCRYPTION}
RECOMPILE 表明 SQL Server 不会缓存该过程的计划,该过程将在运行时重新编译。在使用非典型值或临时值而不希望覆盖缓存在内存中的执行计划时,请使用 RECOMPILE 选项。
ENCRYPTION 表示 SQL Server 加密 syscomments 表中包含 CREATE PROCEDURE 语句文本的条目。使用 ENCRYPTION 可防止将过程作为 SQL Server 复制的一部分发布。
说明:在升级过程中,SQL Server 利用存储在 syscomments 中的加密注释来重新创建加密过程。
FOR REPLICATION
指定不能在订阅服务器上执行为复制创建的存储过程。.使用 FOR REPLICATION 选项创建的存储过程可用作存储过程筛选,且只能在复制过程中执行。本选项不能和 WITH RECOMPILE 选项一起使用。
AS
指定过程要执行的操作。
sql_statement
过程中要包含的任意数目和类型的 Transact-SQL 语句。但有一些限制。
n
是表示此过程可以包含多条 Transact-SQL 语句的占位符。
注释
存储过程的最大大小为 128 MB。
存储过程的优点都有哪些呢?
1.存储过程只在创造时进行编译即可,以后每次执行存储过程都不需再重新编译,而我们通常使用的SQL语句每执行一次就编译一次,所以使用存储过程可提高数据库执行速度。
2.经常会遇到复杂的业务逻辑和对数据库的操作,这个时候就会用SP来封装数据库操作。当对数据库进行复杂操作时(如对多个表进行Update, Insert,Query,Delete时),可将此复杂操作用存储过程封装起来与数据库提供的事务处理结合一起使用。可以极大的提高数据库的使用效率,减少程序的执行时间,这一点在较大数据量的数据库的操作中是非常重要的。在代码上看,SQL语句和程序代码语句的分离,可以提高程序代码的可读性。
3.存储过程可以设置参数,可以根据传入参数的不同重复使用同一个存储过程,从而高效的提高代码的优化率和可读性。
4.安全性高,可设定只有某此用户才具有对指定存储过程的使用权存储过程的种类:
(1)系统存储过程:以sp_开头,用来进行系统的各项设定.取得信息.相关管理工作,如 sp_help就是取得指定对象的相关信息。
(2)扩展存储过程 以XP_开头,用来调用操作系统提供的功能
exec master..xp_cmdshell 'ping 10.8.16.1'
(3)用户自定义的存储过程,这是我们所指的存储过程常用格式
模版:Create procedure procedue_name [@parameter data_type][output]
[with]{recompile|encryption} as sql_statement
解释:output:表示此参数是可传回的
with {recompile|encryption} recompile:表示每次执行此存储过程时都重新编译一次;encryption:所创建的存储过程的内容会被加密。
实例1:只返回单一记录集的存储过程。
表银行存款表(bankMoney)的内容如下
|
Id |
userID |
Sex |
Money |
|
001 |
Zhangsan |
男 |
30 |
|
002 |
Wangwu |
男 |
50 |
|
003 |
Zhangsan |
男 |
40 |
要求1:查询表bankMoney的内容的存储过程
create procedure sp_query_bankMoney
as
select * from bankMoney
go
exec sp_query_bankMoney
注* 在使用过程中只需要把中的SQL语句替换为存储过程名,就可以了很方便吧!
实例2(向存储过程中传递参数):
加入一笔记录到表bankMoney,并查询此表中userID= Zhangsan的所有存款的总金额。
Create proc insert_bank @param1 char(10),@param2 varchar(20),@param3 varchar(20),@param4 int,@param5 int output
with encryption ---------加密
as
insert bankMoney (id,userID,sex,Money)
Values(@param1,@param2,@param3, @param4)
select @param5=sum(Money) from bankMoney where userID='Zhangsan'
go
在SQL Server查询分析器中执行该存储过程的方法是:
declare @total_price int
exec insert_bank '004','Zhangsan','男',100,@total_price output
print '总余额为'+convert(varchar,@total_price)
go
在这里再啰嗦一下存储过程的3种传回值(方便正在看这个例子的朋友不用再去查看语法内容):
1.以Return传回整数
2.以output格式传回参数
3.Recordset
传回值的区别:
output和return都可在批次程式中用变量接收,而recordset则传回到执行批次的客户端中。
实例3:使用带有复杂 SELECT 语句的简单过程
下面的存储过程从四个表的联接中返回所有作者(提供了姓名)、出版的书籍以及出版社。该存储过程不使用任何参数。
USE pubs
IF EXISTS (SELECT name FROM sysobjects
WHERE name = 'au_info_all' AND type = 'P')
DROP PROCEDURE au_info_all
GO
CREATE PROCEDURE au_info_all
AS
SELECT au_lname, au_fname, title, pub_name
FROM authors a INNER JOIN titleauthor ta
ON a.au_id = ta.au_id INNER JOIN titles t
ON t.title_id = ta.title_id INNER JOIN publishers p
ON t.pub_id = p.pub_id
GO
au_info_all 存储过程可以通过以下方法执行:
EXECUTE au_info_all
-- Or
EXEC au_info_all
如果该过程是批处理中的第一条语句,则可使用:
au_info_all
实例4:使用带有参数的简单过程
CREATE PROCEDURE au_info
@lastname varchar(40),
@firstname varchar(20)
AS
SELECT au_lname, au_fname, title, pub_name
FROM authors a INNER JOIN titleauthor ta
ON a.au_id = ta.au_id INNER JOIN titles t
ON t.title_id = ta.title_id INNER JOIN publishers p
ON t.pub_id = p.pub_id
WHERE au_fname = @firstname
AND au_lname = @lastname
GO
au_info 存储过程可以通过以下方法执行:
EXECUTE au_info 'Dull', 'Ann'
-- Or
EXECUTE au_info @lastname = 'Dull', @firstname = 'Ann'
-- Or
EXECUTE au_info @firstname = 'Ann', @lastname = 'Dull'
-- Or
EXEC au_info 'Dull', 'Ann'
-- Or
EXEC au_info @lastname = 'Dull', @firstname = 'Ann'
-- Or
EXEC au_info @firstname = 'Ann', @lastname = 'Dull'
如果该过程是批处理中的第一条语句,则可使用:
au_info 'Dull', 'Ann'
-- Or
au_info @lastname = 'Dull', @firstname = 'Ann'
-- Or
au_info @firstname = 'Ann', @lastname = 'Dull'
实例5:使用带有通配符参数的简单过程
CREATE PROCEDURE au_info2
@lastname varchar(30) = 'D%',
@firstname varchar(18) = '%'
AS
SELECT au_lname, au_fname, title, pub_name
FROM authors a INNER JOIN titleauthor ta
ON a.au_id = ta.au_id INNER JOIN titles t
ON t.title_id = ta.title_id INNER JOIN publishers p
ON t.pub_id = p.pub_id
WHERE au_fname LIKE @firstname
AND au_lname LIKE @lastname
GO
au_info2 存储过程可以用多种组合执行。下面只列出了部分组合:
EXECUTE au_info2
-- Or
EXECUTE au_info2 'Wh%'
-- Or
EXECUTE au_info2 @firstname = 'A%'
-- Or
EXECUTE au_info2 '[CK]ars[OE]n'
-- Or
EXECUTE au_info2 'Hunter', 'Sheryl'
-- Or
EXECUTE au_info2 'H%', 'S%'
= 'proc2'
Ajax Validate
<head runat="server">
<title>Untitled Page</title>
<link href="App_Themes/AnsonWebShop/test.css" type="text/css" rel="stylesheet" />
</head>
<body>
<form id="form1" runat="server">
<asp:Label ID="Label1" runat="server" Text="User:"></asp:Label>
<input id="un" type="text" style="width: 150px" runat="server" onblur="checkEmailAddress(this.value, true);" /><div id="divE"></div><br />
<asp:Label ID="Label2" runat="server" Text="Pass:"></asp:Label>
<input id="pw" style="width: 150px" type="password" runat="server" /><br />
<input id="Button1" style="width: 184px" runat="server" onclick="loginin()" value="OK" />
<br />
<br />
function checkEmailAddress(strValue, isCheck)
{
var str = strValue.trim();
var uu = document.getElementById("un");
var d = document.getElementById("divEmail");
if(str.length < 1)
{
d.innerHTML = "<div class=\"check\">电子邮件地址不能为空</div>";
return false;
}
else
{
var reg = /^\w+([-+.]\w+)*@\w+([-.]\w+)*\.\w+([-.]\w+)*$/;
if(checkReg(reg, str))
{
if(isCheck)
{
var url = "CEE.aspx";
var pars = "EMail=" + StrCode(strValue);
var acheck = new Ajaxcheck.check({success: "divE"}, url, {method: "post", parameters: pars, onLoading: function(){$("divE").innerHTML = "<div class=\"loading\">检测中,请稍后……</div>";}});
}
return true;
}
else
{
d.innerHTML = "<div class=\"checkerror\">电子邮件地址不合法</div>";
return false;
}
}
}
{
if(checkEmailAddress("un", false))
{
}
else
{
return false;
}
}
</script>
</body>
</html>
html代码
<html xmlns="http://www.w3.org/1999/xhtml" >
<head id="Head1" runat="server">
<title>Ajax Tree</title>
<link type="text/css" href="../../Styles/tree_css/tree.css" rel="stylesheet">
</head>
<body>
<form id="Form1" runat="server">
<div class="TreeMenu" id="CategoryTree" style="width: 100%; height: 489px">
</div>
<script language="javascript" type="text/javascript">
function el(id)
{
return document.getElementById(id);
}
function ExpandSubCategory(iCategoryID)
{
var li_father = el("li_" + iCategoryID);
if (li_father.getElementsByTagName("li").length > 0) //分类已下载
{
ChangeStatus(iCategoryID);
return;
}
li_father.className = "Opened";
switchNote(iCategoryID, true);
AjaxMethod.GetSubCategory(iCategoryID, GetSubCategory_callback);
}
function GetSubCategory_callback(response)
{
var dt = response.value.Tables[0];
if (dt.Rows.length > 0)
{
var iCategoryID = dt.Rows[0].FatherID;
}
var li_father = el("li_" + iCategoryID);
var ul = document.createElement("ul");
for (var i = 0;i < dt.Rows.length;i++)
{
if (dt.Rows[i].IsChild == 1) //叶子节点
{
var li = document.createElement("li");
li.className = "Child";
li.id = "li_" + dt.Rows[i].CategoryID;
var img = document.createElement("img");
img.id = dt.Rows[i].CategoryID;
img.className = "s";
img.src = "../../Styles/tree_css/s.gif";
var a = document.createElement("a");
var id = dt.Rows[i].CategoryID;
a.onmouseover = function()
{
PreviewImage(id);
};
a.href = "javascript:OpenDocument('" + dt.Rows[i].CategoryID + "');";
a.innerHTML = dt.Rows[i].CategoryName;
}
else
{
var li = document.createElement("li");
li.className = "Closed";
li.id = "li_" + dt.Rows[i].CategoryID;
var img = document.createElement("img");
img.id = dt.Rows[i].CategoryID;
img.className = "s";
img.src = "../../Styles/tree_css/s.gif";
img.onclick = function () {
ExpandSubCategory(this.id);
};
img.alt = "展开/折叠";
var a = document.createElement("a");
a.href = "javascript:ExpandSubCategory(" +
dt.Rows[i].CategoryID + ");";
a.innerHTML = dt.Rows[i].CategoryName;
}
li.appendChild(img);
li.appendChild(a);
ul.appendChild(li);
}
li_father.appendChild(ul);
switchNote(iCategoryID, false);
}
// 叶子节点的单击响应函数
function OpenDocument(iCategoryID)
{
// 预加载信息
PreloadFormUrl(iCategoryID);
}
function PreviewImage(iCategoryID)
{
}
function ChangeStatus(iCategoryID)
{
var li_father = el("li_" + iCategoryID);
if (li_father.className == "Closed")
{
li_father.className = "Opened";
}
else
{
li_father.className = "Closed";
}
}
function switchNote(iCategoryID, show)
{
var li_father = el("li_" + iCategoryID);
if (show)
{
var ul = document.createElement("ul");
ul.id = "ul_note_" + iCategoryID;
var note = document.createElement("li");
note.className = "Child";
var img = document.createElement("img");
img.className = "s";
img.src = "../../Styles/tree_css/s.gif";
var a = document.createElement("a");
a.href = "javascript:void(0);";
a.innerHTML = "请稍候";
note.appendChild(img);
note.appendChild(a);
ul.appendChild(note);
li_father.appendChild(ul);
}
else
{
var ul = el("ul_note_" + iCategoryID);
if (ul)
{
li_father.removeChild(ul);
}
}
}
// 加载根节点
var tree = el("CategoryTree");
var root = document.createElement("li");
root.id = "li_0";
tree.appendChild(root);
// 加载页面时显示第一级分类
ExpandSubCategory(0);
function PreloadFormUrl(iCategoryID)
{
// 采用同步调用的方式获取图片的信息
var ds = AjaxMethod.GetFormsList(iCategoryID).value;
// 如果返回了结果
if (ds)
{
// 判断数据表是否不为空
if (ds.Tables[0].Rows.length > 0)
{
// 返回的信息数据表
dt = ds.Tables[0];
el("furl").src = dt.Rows[0].FormUrl;
}
else // 分类下没有
{
}
}
}
</script>
</form>
</body>
</html>
2.cs代码
using System;
using System.Data;
using System.Configuration;
using System.Collections;
using System.Web;
using System.Web.Security;
using System.Web.UI;
using System.Web.UI.WebControls;
using System.Web.UI.WebControls.WebParts;
using System.Web.UI.HtmlControls;
using AjaxPro;
public partial class Pages_Home_HomePage : System.Web.UI.Page
{
protected void Page_Load(object sender, EventArgs e)
{
AjaxPro.Utility.RegisterTypeForAjax(typeof(AjaxMethod));
}
}
3.建立一个tree.css的css样式
a
{
text-decoration:none;
}
a,a:visited
{
color:#000;
background:inherit;
}
body
{
margin:0;
padding:20px;
font:12px tahoma,宋体,sans-serif;
}
dt
{
font-size:22px;
font-weight:bold;
margin:0 0 0 15px;
}
dd
{
margin:0 0 0 15px;
}
h4
{
margin:0;
padding:0;
font-size:18px;
text-align:center;
}
p
{
margin:0;
padding:0 0 0 18px;
}
p a,p a:visited
{
color:#00f;
background:inherit;
}
.TreeMenu img.s
{
cursor:hand;
vertical-align:middle;
}
.TreeMenu ul
{
padding:0;
}
.TreeMenu li
{
list-style:none;
padding:0;
}
.Closed ul
{
display:none;
}
.Child img.s
{
background:none;
cursor:default;
}
#CategoryTree ul
{
margin:0 0 0 17px;
}
#CategoryTree img.s
{
width:34px;
height:18px;
}
#CategoryTree .Opened img.s
{
background:url(skin3/opened.gif) no-repeat 0 1px;
}
#CategoryTree .Closed img.s
{
background:url(skin3/closed.gif) no-repeat 0 1px;
}
#CategoryTree .Child img.s
{
background:url(skin3/child.gif) no-repeat 13px 2px;
}
#CategoryTree
{
float:left;
width:249px;
border:1px solid #99BEEF;
background:#D2E4FC;
color:inherit;
margin:3px;
padding:3px;
height:600px;
}
4.建立一个类AjaxMethod
using System;
using System.Data;
using System.Data.SqlClient;
using System.Configuration;
using System.Web;
using System.Web.Security;
using System.Web.UI;
using System.Web.UI.WebControls;
using System.Web.UI.WebControls.WebParts;
using System.Web.UI.HtmlControls;
using AjaxPro;
/**//// <summary>
/// Summary description for AjaxMethod
/// </summary>
public class AjaxMethod
{
public AjaxMethod()
{
//
// TODO: Add constructor logic here
//
}
[AjaxMethod(HttpSessionStateRequirement.ReadWrite)]
public static DataSet GetSubCategory(int iCategoryID)
{
string sql = string.Format("SELECT CategoryID, CategoryName, FatherID, dbo.IsLeaf(CategoryID) as IsChild FROM Category WHERE FatherID = {0}", iCategoryID);
return GetDataSet(sql);
}
[AjaxMethod(HttpSessionStateRequirement.ReadWrite)]
public static DataSet GetFormsList(int iCategoryID)
{
string sql = string.Format("SELECT * FROM forms WHERE form_category_id = {0}", iCategoryID);
return GetDataSet(sql);
}
public static string ConnectionString = ConfigurationSettings.AppSettings["ConnectionString"].ToString();
public static DataSet GetDataSet(string sql)
{
SqlDataAdapter sda = new SqlDataAdapter(sql, ConnectionString);
DataSet ds = new DataSet();
sda.Fill(ds);
if (ds != null)
return ds;
else
return null;
}
}
5.sql脚本
if exists (select * from dbo.sysobjects where id = object_id(N'[dbo].[Category]') and OBJECTPROPERTY(id, N'IsUserTable') = 1)
drop table [dbo].[Category]
GO
if exists (select * from dbo.sysobjects where id = object_id(N'[dbo].[Forms]') and OBJECTPROPERTY(id, N'IsUserTable') = 1)
drop table [dbo].[Forms]
GO
CREATE TABLE [dbo].[Category] (
[CategoryID] [int] IDENTITY (1, 1) NOT NULL ,
[CategoryName] [varchar] (20) COLLATE Chinese_PRC_CI_AS NULL ,
[FatherID] [int] NULL
) ON [PRIMARY]
GO
CREATE TABLE [dbo].[Forms] (
[FormID] [int] IDENTITY (1, 1) NOT NULL ,
[FormName] [nvarchar] (50) COLLATE Chinese_PRC_CI_AS NULL ,
[FormUrl] [nvarchar] (50) COLLATE Chinese_PRC_CI_AS NULL ,
[Form_category_id] [int] NULL ,
[target] [char] (10) COLLATE Chinese_PRC_CI_AS NULL
) ON [PRIMARY]
GO
CREATE FUNCTION IsLeaf (@cat_id int)
RETURNS int AS
BEGIN
declare @count int
select @count = (select count(*) from Category where FatherID=@cat_id)
if (@count=0)
return 1
return 0
END
Ajax DataGrid
1. 在引用中添加引用Ajax.dll
<add verb="POST,GET" path="ajax/*.ashx" type="Ajax.PageHandlerFactory, Ajax" />
</httpHandlers>
{
Ajax.Utility.HandlerPath = "ajax";
}
public int AddAjaxTable(string name)
{
//输入一个字符串,然后更新
SqlConnection conn = new SqlConnection( System.Configuration.ConfigurationSettings.AppSettings["connectionString"] );
SqlCommand cmd = new SqlCommand("insert into ajaxTable(name) values(’"+name+"’)", conn);
cmd.Connection.Open();
int result = cmd.ExecuteNonQuery();
conn.Dispose();
cmd.Dispose();
return result;
}
[Ajax.AjaxMethod]
public string GetAjaxTable()
{
//这个方法就是拿到datagrid生成出来的html
SqlConnection conn = new SqlConnection(System.Configuration.ConfigurationSettings.AppSettings["connectionString"]);
SqlCommand cmd = new SqlCommand("select * from ajaxTable order by id", conn);
SqlDataAdapter ap = new SqlDataAdapter( cmd );
DataSet ds = new DataSet();
ap.SelectCommand.Connection.Open();
ap.Fill( ds, "db" );
conn.Dispose();
cmd.Dispose();
//实例化一个datagird类并设置好数据源
DataGrid dg = new DataGrid();
dg.DataSource = ds.Tables["db"];1
//实例化一个HtmlTextWriter的类
System.Text.StringBuilder strb = new System.Text.StringBuilder();
System.IO.StringWriter sw = new System.IO.StringWriter( strb );
System.Web.UI.HtmlTextWriter htw = new HtmlTextWriter( sw );
//执行控件的render并输出到HtmlTextWriter里
dg.RenderControl( htw );
string s = strb.ToString();
return s;//最后就是返回这个html啦
}
{
DemoMethods.AddAjaxTable(name);
LoadGrid();
}
{
var cc=document.getElementById("UCtd");
cc.innerHTML=DemoMethods.GetAjaxTable().value;
}
{
Ajax.Utility.RegisterTypeForAjax(typeof(AjaxTestPrjLib.DemoMethods));
}
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0 Transitional//EN" >
<HTML>
<HEAD>
<title>default</title>
<meta name="GENERATOR" Content="Microsoft Visual Studio .NET 7.1">
<meta name="CODE_LANGUAGE" Content="C#">
<meta name="vs_defaultClientScript" content="JavaScript">
<meta name="vs_targetSchema" content="http://schemas.microsoft.com/intellisense/ie5">
<script language="javascript" src="default.js"></script>
</HEAD>
<body onload="LoadGrid()">
<form id="Form1" method="post" runat="server">
<INPUT type="text" id="AddTextBox" maxlength="10"><INPUT type="button" value="添加" onclick="javascript:AddAjax(form.AddTextBox.value);">
<table>
<tr>
<td id="UCtd"></td>
</tr>
</table>
</form>
</body>
</HTML>
ASP.NET中利用DataGrid的自定义分页功能和存储过程结合实现高效分页
Ajax Login Sample
1、在工程中引入Ajax.dll文件。
Ajax.dll实现XmlHttpRequest请求服务器的实现细节。.net项目中,添加上对其的引用,就可以进
<httpHandlers>
<add verb="POST,GET" path="ajax/*.ashx" type="Ajax.PageHandlerFactory, Ajax"/>
</httpHandlers>
3、在 <HEAD>与</HEAD>间加入一些引用如下:
<script src=js/Xml.js></script>
<link href="css/myStyle.css" type="text/css" rel="stylesheet">
<script src="/HttpForAjax/ajax/common.ashx" type="text/javascript"></script>
<script src="/HttpForAjax/ajax/Ttyu.AjaxData,HttpForAjax.ashx" type="text/javascript"></script>
1、前台Html:
<form id="Form1" method="post" runat="server" action="" onsubmit="login.GetLogin();return false;">
<TABLE id="Table1" cellSpacing="1" cellPadding="1" width="300" border="1">
<TR>
<TD></TD>
<TD><INPUT type="text" id="txtUsername">usename</TD>
</TR>
<TR>
<TD></TD>
<TD><INPUT type="password" id="txtPassword">pwd</TD>
</TR>
<TR>
<TD></TD>
<TD><INPUT type="submit" value="登陆"></TD>
</TR>
</TABLE>
</form>
<script language="javascript">
window.onload = function()
{
login=new Login(testAjax);
}
</script>
// 提取控件值
function getValueById(pObjID){
var obj=document.getElementById(pObjID);
try{
return obj.value;
}catch(e){
alert("控件:"+pObjID+" 不存在,或没有value属性");
}
}
{
this.OBJ = obj;
this.GetLogin=function()
{
var returnValue;
var username=getValueById('txtUsername');
var password=getValueById('txtPassword');
if(!username||!password)
{
alert('请输入用户名与密码!');
return;
}
try
{
returnValue=this.OBJ.Login(username,password).value;
}catch(e)
{
alert('登录出错,请稍后再试或与管理员联系');
}
switch(returnValue)
{
case 1:
alert('对不起,您输入的用户名或密码不正确或者不是管理员!');
break;
case 0:
alert('管理员登录成功!');
window.document.location.href('../Error.aspx');
break;
default:
alert('登录失败,请稍后再试或与管理员联系'+returnValue);
break;
}
}
}
{
Ajax.Utility.RegisterTypeForAjax(typeof(testAjax));
}
public int Login(string username,string password)
{
// 管理员登陆入口
Action.Common.CDB cdb = new Action.Common.CDB();
if("admin"==cdb.ExeScalar("select upower from users where
return 0;
else
return 1;
}
Ajax Shopping Cart(2)
using System.Data;
using System.Configuration;
using System.Collections;
using System.Web;
using System.Web.Security;
using System.Web.UI;
using System.Web.UI.WebControls;
using System.Web.UI.WebControls.WebParts;
using System.Web.UI.HtmlControls;
using AW.Components;
using AjaxPro;
using System.Collections.Generic;
using System.Collections.Specialized;
using System.Text;
protected string Key = "ORDERDETAIL_" + CContext.Current.SiteOwnerUserID.ToString();
protected void Page_Load(object sender, EventArgs e) {
Utility.RegisterTypeForAjax(typeof(_Default));
int id;
action = "list";
if (action == "add") {
CProduct prodcut = CProducts.GetProduct(id, CContext.Current.SiteOwnerUserID);
if (prodcut == null) {
CJS.AlertAndGoUrl("此商品已经被删除!", Request.Url.AbsolutePath);
return;
}
AddProductToOrder(id, 1);
Response.Redirect(Request.Url.AbsolutePath, true);
return;
}
}
rptView.DataSource = GetOrders();
rptView.DataBind();
}
HttpCookie cookie;
cookie = Request.Cookies[Key] == null ? new HttpCookie(Key) : Request.Cookies[Key];
NameValueCollection nvc = CHttpHelper.Base64Decode(cookie.Value);
List<ORDERDETAIL> orders = new List<ORDERDETAIL>();
int totalNum = 0;
double totalMoney = 0.00;
double totalVipMoney = 0.00;
for (int i = 0; i < nvc.Count; i++) {
ORDERDETAIL ord = new ORDERDETAIL(int.Parse(nvc.Keys[i]), int.Parse(nvc[i]));
orders.Add(ord);
totalNum += int.Parse(nvc[i]);
totalMoney += (ord.Number * (ord.Product==null?0:ord.Product.Price));
totalVipMoney += (ord.Number * (ord.Product==null?0:ord.Product.VipPrice));
}
lbTotalNum.Text = totalNum.ToString();
lbTotalMoney.Text = totalVipMoney.ToString("0.00");
lbDiscount.Text = (totalMoney - totalVipMoney).ToString("0.00");
return orders;
}
HttpCookie cookie;
cookie = Request.Cookies[Key] == null ? new HttpCookie(Key) : Request.Cookies[Key];
cookie.Path = Request.ApplicationPath;
cookie.Domain = "." + Request.Url.Host;
NameValueCollection nvc = CHttpHelper.Base64Decode(cookie.Value);
if (nvc[pID.ToString()] != null)
nvc[pID.ToString()] = (int.Parse(nvc[pID.ToString()]) + num).ToString();
else
nvc.Add(pID.ToString(), num.ToString());
Response.AppendCookie(cookie);
}
public RESUTLINFO ModifyProductNumber(int pID, int num, string cookieStr) {
NameValueCollection nvc = CHttpHelper.Base64Decode(cookieStr);
nvc[pID.ToString()] = num.ToString();
return new RESUTLINFO(pID, int.Parse(nvc[pID.ToString()]),CHttpHelper.Base64Encode(nvc));
}
public RESUTLINFO RemoveProductFromOrder(int pID, string cookieStr) {
NameValueCollection nvc = CHttpHelper.Base64Decode(cookieStr);
nvc.Remove(pID.ToString());
return new RESUTLINFO(pID, CHttpHelper.Base64Encode(nvc));
}
}
using System.Collections.Generic;
using System.Text;
using System.Collections.Specialized;
using System.Web;
public class CHttpHelper {
private CHttpHelper() { }
public static string GetString(NameValueCollection nvc) {
string rtn = string.Empty;
for (int i = 0; i < nvc.Count; i++) {
rtn += nvc.Keys[i] + "=" + nvc[i];
if (i < nvc.Count - 1) rtn += "&";
}
return rtn;
}
byte[] bytes = Convert.FromBase64String(base64String);
return Encoding.Default.GetString(bytes);
}
#endregion
return Convert.ToBase64String(Encoding.Default.GetBytes(GetString(nvc)), Base64FormattingOptions.None);
}
if (string.IsNullOrEmpty(base64String))
return new NameValueCollection();
}
}
public ORDERDETAIL(int pID, int num) {
Product = CProducts.GetProduct(pID, CContext.Current.SiteOwnerUserID);
Number = num;
}
public int Number;
get {
return Product == null ? 0 : Product.ID;
}
}
return ProudctID + ":" + Number.ToString();
}
}
public string NVC;
public int PID;
public int TOTAL;
PID = pID;
NVC = nvc;
TOTAL = 0;
}
PID = pID;
NVC = nvc;
TOTAL = total;
}
}
}
Ajax Shopping Cart(1)
CodeFile="Default.aspx.cs" Inherits="_Default" Title="购物车" %>
<%@ Import Namespace="AW.Components.Helpers" %>
<asp:MultiView ID="MultiView1" runat="server">
<asp:View ID="View1" runat="server">
var key = '<%= Key %>';
function remove(pID){
var v = getCookie(key)==null?"":getCookie(key);
_Default.RemoveProductFromOrder(pID,v,remove_Callback);
}
function remove_Callback(res){
if(res.error!=null)alert(res.error);
else{
var tr = document.getElementById("tr_" + res.value.PID.toString());
tr.style.display = "none";
deleteCookie(key,"/","." + location.host);
setCookie(key,res.value.NVC,null,"/","."+location.host);
calcMoney();
}
}
function modify(pID,num){
var v = getCookie(key)==null?"":getCookie(key);
_Default.ModifyProductNumber(pID,num,v,modify_Callback);
}
function modify_Callback(res){
deleteCookie(key,"/","." + location.host);
setCookie(key,res.value.NVC,null,"/","."+location.host);
var total = document.getElementById("txtSubTotal_" + res.value.PID.toString());
var price = document.getElementById("txtVipPrice_" + res.value.PID.toString());
total.value = Round(parseFloat(parseInt(res.value.TOTAL) * parseFloat(price.value)),2);
calcMoney();
}
function modifyAll(){
}
function clearCart(){
if (!confirm('确定要清空购物车吗?')) return;
var tb = document.getElementById("tbOrder");
for(var i=1;i<tb.rows.length;i++){
var tr = tb.rows.item(i);
if(tr.id.indexOf("_")!=-1 && tr.style.display!="none"){
tr.style.display = 'none';
}
}
deleteCookie(key,"/","." + location.host);
var ctlTotalNum = document.getElementById("<%=lbTotalNum.ClientID %>");
var ctlTotalMoney = document.getElementById("<%=lbTotalMoney.ClientID %>");
var ctlDiscount = document.getElementById("<%=lbDiscount.ClientID %>");
ctlTotalNum.innerHTML = "0";
ctlTotalMoney.innerHTML = "0";
ctlDiscount.innerHTML = "0";
}
function calcMoney(){
var ctlNums = document.getElementsByName("txtNumber");
var ctlPrices = document.getElementsByName("txtPrice");
var ctlVipPrices = document.getElementsByName("txtVipPrice");
var ctlSubTotal = document.getElementsByName("txtSubTotal");
var ctlTotalNum = document.getElementById("<%=lbTotalNum.ClientID %>");
var ctlTotalMoney = document.getElementById("<%=lbTotalMoney.ClientID %>");
var ctlDiscount = document.getElementById("<%=lbDiscount.ClientID %>");
var totalNum = 0;
var subTotal = 0;
var subTotalVip = 0;
var totalMoney = 0;
var totalVipMoney = 0;
for(var i=0;i<ctlNums.length;i++){
totalNum += parseInt(ctlNums[i].value);
subTotal = parseFloat(totalNum * parseFloat(ctlPrices[i].value));
subTotalVip = parseFloat(totalNum * parseFloat(ctlVipPrices[i].value));
totalMoney += subTotal;
totalVipMoney += subTotalVip;
ctlSubTotal.value = subTotalVip.toString();
}
ctlTotalNum.innerHTML = Round(totalNum,2);
ctlTotalMoney.innerHTML = Round(totalVipMoney,2);
ctlDiscount.innerHTML = Round((totalMoney - totalVipMoney),2);
}
function Round(a_Num , a_Bit){
return (Math.round(a_Num * Math.pow (10 , a_Bit)) / Math.pow(10 , a_Bit)).toString() ;
}
</script>
<table width="778" border="0" align="center" cellpadding="0" cellspacing="0" background="../../Icon/ShopCart/shopC_syp_5.gif">
<tbody>
<tr>
<td width="12">
<img height="72" src="../../Icon/ShopCart/shopC_syp_1.gif" width="12" /></td>
<td>
<img height="72" src="../../Icon/ShopCart/shopC_6.gif" width="176" /></td>
<td valign="bottom" align="right">
<img height="29" src="../../Icon/ShopCart/shopC_4.gif" width="324" /></td>
<td width="10" align="right" valign="bottom">
<img height="72" src="../../Icon/ShopCart/shopC_syp_3.gif" width="10" /></td>
</tr>
</tbody>
</table>
<table class="meun_bg" cellspacing="0" cellpadding="0" width="540" align="center"
border="0">
<tbody>
<tr>
<td align="right">
<img height="28" src="../../Icon/ShopCart/shopC_syx_01.gif" width="93" border="0" /></td>
<td>
<img height="32" src="../../Icon/ShopCart/shopC_sy_8.gif" width="55" /></td>
<td>
<img height="28" src="../../Icon/ShopCart/shopC_syx_02.gif" width="112" border="0" /></td>
<td>
<img height="32" src="../../Icon/ShopCart/shopC_sy_8.gif" width="55" /></td>
<td>
<img height="22" src="../../Icon/ShopCart/shopC_syx_03.gif" width="109" border="0" /></td>
</tr>
</tbody>
</table>
<table cellspacing="0" cellpadding="0" width="778" align="center" bgcolor="#fafafa"
border="0">
<tbody>
<tr>
<td>
<img height="41" hspace="8" src="../../Icon/ShopCart/shopc_z1.gif" width="143" /></td>
</tr>
<tr>
<td>
<table cellspacing="0" cellpadding="0" width="96%" align="center" border="0">
<tbody>
<tr align="right" bgcolor="#fafafa">
<td>
<img height="15" src="../../Icon/ShopCart/shopc_syx_j.gif" width="771" /></td>
</tr>
<tr align="right" bgcolor="#fafafa">
<td class="boder_center_bg">
<table cellspacing="1" width="95%" align="center" border="0" id="tbOrder">
<tbody>
<tr class="boder_topbg" align="center">
<td>
商品名称</td>
<td>
单 位</td>
<td>
数 量</td>
<td>
市场价(元)</td>
<td>
会员价(元)</td>
<td>
小 计(元)</td>
<td width="8%">
操作</td>
</tr>
<asp:Repeater ID="rptView" runat="server">
<ItemTemplate>
<tr align="center" bgcolor="#f9f9f9" id="tr_<%# ((ORDERDETAIL)Container.DataItem).Product.ID %>">
<td align="left">
<asp:HyperLink Target="_blank" NavigateUrl='<%# "~/" + ((ORDERDETAIL)Container.DataItem).Product.ChannelID.ToString() + "_" + ((ORDERDETAIL)Container.DataItem).Product.ID.ToString() + ".aspx" %>'
Text='<%# ((ORDERDETAIL)Container.DataItem).Product.Name %>' runat="server" /></td>
<td>
<%# String.IsNullOrEmpty(((ORDERDETAIL)Container.DataItem).Product.Unit) ? "个" : ((ORDERDETAIL)Container.DataItem).Product.Unit %>
</td>
<td>
<input style="text-align: right;" onbeforepaste="clipboarddata.setdata('text',clipboarddata.getdata('text').replace(/[^\d]/g,''))"
id="txtNum_<%# ((ORDERDETAIL)Container.DataItem).Product.ID %>" onkeyup="value=value.replace(/[^\d]/g,'');"
onblur="if(this.value.length==0)this.value=0;modify(<%# ((ORDERDETAIL)Container.DataItem).Product.ID %>,value);"
maxlength="2" size="4" value="<%# ((ORDERDETAIL)Container.DataItem).Number %>"
name="txtNumber" /></td>
<td style="padding-left: 20px" align="right">
<del>
<%# ((ORDERDETAIL)Container.DataItem).Product.Price %>
</del>
<input name='txtPrice' id="txtPrice_<%# ((ORDERDETAIL)Container.DataItem).Product.ID %>"
type="hidden" value='<%# ((ORDERDETAIL)Container.DataItem).Product.Price %>' />
</td>
<td style="padding-left: 20px" align="right">
<input style="text-align: right;" name='txtVipPrice' id="txtVipPrice_<%# ((ORDERDETAIL)Container.DataItem).Product.ID %>"
type="hidden" value='<%# ((ORDERDETAIL)Container.DataItem).Product.VipPrice %>' />
<%# ((ORDERDETAIL)Container.DataItem).Product.VipPrice %>
</td>
<td style="padding-left: 20px" align="right">
<input style="text-align: right;border: none 0px white; background-color: White;" size="4" name='txtSubTotal' id="txtSubTotal_<%# ((ORDERDETAIL)Container.DataItem).Product.ID %>"
type="text" readonly="readonly" value='<%# ((ORDERDETAIL)Container.DataItem).Product.VipPrice * ((ORDERDETAIL)Container.DataItem).Number %>' /></td>
<td>
<a href="javascript:remove(<%# ((ORDERDETAIL)Container.DataItem).Product.ID %>);"><font
color="#ff0000">删除</font></a></td>
</tr>
</ItemTemplate>
</asp:Repeater>
<tr align="right" bgcolor="#f9f9f9">
<td colspan="7">
购物车中共有
<asp:Label ID="lbTotalNum" ForeColor="red" runat="server" />
件商品,
共计 <font class="font_red">¥<asp:Label ID="lbTotalMoney" runat="server" />
元</font>(不包括邮费) 您节省了<font class="font_red">¥<asp:Label ID="lbDiscount" runat="server" /></font>
</td>
</tr>
<tr align="left" bgcolor="#f9f9f9">
<td colspan="7">
<img style="width: 104px; height: 27px; cursor: pointer;" onclick="modifyAll();"
height="23" alt="单击这里保存您所修改的商品数量" width="85" src="../../Icon/ShopCart/shopc_13_02.gif"
border="0" />
<img style="cursor: pointer" onclick="clearCart();" height="27" alt="单击这里清空所有购买的商品"
src="../../Icon/ShopCart/shopc_13_03.gif" width="104" /> </td>
</tr>
</tbody>
</table>
</td>
</tr>
<tr align="right" bgcolor="#fafafa">
<td>
<img height="15" src="../../Icon/ShopCart/shopc_20.gif" width="771" /></td>
</tr>
</tbody>
</table>
</td>
</tr>
<tr>
<td align="right">
<img style="cursor: hand" onclick="javascript:window.close();" height="42" alt="点击继续购买您喜欢的其它商品"
src="../../Icon/ShopCart/shopc_13_01.gif" width="143" vspace="10" />
<img style="cursor: hand" onclick="location.href='./ClientInfo.aspx';" height="42"
alt="点击去收银台结帐" hspace="10" src="../../Icon/ShopCart/shopc_13.gif" width="143"
vspace="10" id="IMG1" /></td>
</tr>
<tr>
<td class="style2" align="center">
</td>
</tr>
</tbody>
<tr>
<td>
</td>
</tr>
</table>
</div>
</asp:View>
</asp:MultiView>
<br />
<br />
</asp:Content>
ASP.NET Cache
By Peter A. Bromberg, Ph.D.
[C#]
ASP.NET 2.0 SQL Cache
Microsoft Corporation
适用于:
Microsoft ASP.NET 2.0
Microsoft ASP.NET Framework
Microsoft SQL Server
Microsoft Visual Studio .NET
摘要:本文中,Stephen Walther 将重点介绍 ASP.NET 2.0 中新增的缓存功能,以及如何使用这些新功能改进 ASP.NET 应用程序的性能和可扩展性。(本文包含一些指向英文站点的链接。)
本页内容
更轻松的数据缓存
使用 SQL Cache Invalidation
使用 Post-Cache Substitution
结论
对于由数据库驱动的 Web 应用程序来说,要改进其性能,最好的方法就是使用缓存。从数据库中检索数据可能是您在 Web 站点上执行的最慢的操作之一。如果能够将数据库中的数据缓存到内存中,就无需在请求每个页面时都访问数据库,从而可以大大提高应用程序的性能。
缓存有一个且只有一个缺点,那就是数据过期的问题。如果将数据库表的内容缓存到内存中,当基础数据库表中的记录发生更改时,您的 Web 应用程序将显示过期的、不准确的数据。对于某些类型的数据,即便显示的数据稍微有些过期,影响也不会太大;但对于诸如股票价格和竞拍出价之类的数据,即使显示的数据稍微有些过期也是不可接受的。
Microsoft ASP.NET 1.0 Framework 没有针对此问题提供一个完善的解决方案。使用 ASP.NET 1.0 Framework 时,您不得不在性能和数据过期之间作出权衡。幸运的是,Microsoft ASP.NET 2.0 Framework 提供了一项新功能,称为 SQL Cache Invalidation,可以解决这一棘手的问题。
在本文中,您将进一步了解 ASP.NET 2.0 Framework 中许多新的缓存改进功能。首先,您将了解到如何在新增的 DataSource 控件中集成缓存支持。然后,您将了解到如何配置和使用 SQL Cache Invalidation。最后,您将了解到随 ASP.NET 2.0 Framework 引入的一个新控件:Substitution 控件,使用该控件可以向已缓存的页面中插入动态内容。
更轻松的数据缓存
在 ASP.NET 2.0 Framework 中,最大的变化之一就是在 ASP.NET 页面上访问数据库数据的方式发生了变化。ASP.NET 2.0 Framework 包含一组新的控件,统称为 DataSource 控件。您可以使用这些控件来表示数据源,例如数据库或 XML 文件。
在 ASP.NET 1.0 Framework 中,是通过将控件绑定到 DataSet 或 DataReader,使用控件来显示数据库数据的。而在 ASP.NET 2.0 Framework 中,通常是将控件绑定到 DataSource 控件。通过 DataSource 控件,您可以创建显示数据库数据的 ASP.NET 页面,而不用为访问数据库编写任何代码。
在处理数据库数据时,通常使用下列三个 DataSource 控件中的一个控件:
• SqlDataSource — 表示 SQL 数据源,例如 Microsoft SQL Server 或 Oracle 数据库。
• AccessDataSource — 一个专用的 SqlDataSource 控件,用于 Microsoft Access 数据库。
• ObjectDataSource — 表示充当数据源的自定义业务对象。
例如,假设您要在 DropDownList 控件中显示从数据库中检索到的书目列表(参见图 1)。列表 1 中的页面说明了如何将 DropDownList 控件绑定到 SqlDataSource 控件。
图 1:使用 SqlDataSource 控件检索数据
列表 1:DisplayTitles.aspx
<html>
<head runat ="server">
<title>Display Titles</title>
</head>
<body>
<form id="form1" runat ="server">
<asp:DropDownList
ID="DropDownList1"
DataSourceId="SqlDataSource1"
DataTextField="Title"
Runat="server" />
<asp:SqlDataSource
ID="SqlDataSource1"
ConnectionString="Server=localhost;database=Pubs"
SelectCommand="SELECT Title FROM Titles"
Runat="server" />
</form>
</body>
</html>
请注意,列表 1 中的 SqlDataSource 控件用于提供连接字符串,SQL SELECT 命令用于从数据库中检索记录。DropDownList 控件通过其 DataSourceID 属性绑定到 SqlDataSource 控件。
使用 DataSource 控件缓存数据
使用 DataSource 控件,不仅可以更轻松地连接数据库,还使缓存数据库数据变得更容易。只需在 SqlDataSource 控件上设置一两个属性,就可以自动在内存中缓存由 DataSource 控件表示的数据。
例如,如果要将 Titles 数据库表在内存中缓存至少 10 分钟,可以按照以下方式声明 SqlDataSource 控件。
<asp:SqlDataSource
ID="SqlDataSource1"
EnableCaching="true"
CacheDuration="600"
ConnectionString="Server=localhost;database=Pubs"
SelectCommand="SELECT Title FROM Titles"
Runat="server" />
如果 EnableCaching 属性的值为 true,SqlDataSource 将自动缓存通过 SelectCommand 检索到的数据。使用 CacheDuration 属性,可以指定从数据库中刷新数据之前缓存数据的时间(以秒为单位)。
默认情况下,SqlDataSource 使用绝对过期策略来缓存数据,即每隔指定的秒数就从数据库中刷新一次。此外,您还可以选择使用可变过期策略。如果将 SqlDataSource 配置为使用可变过期策略,那么只要持续访问数据,数据就不会过期。如果需要缓存大量项目,使用可变过期策略将非常有用,因为这种过期策略将只在内存中保留访问最频繁的项目。
例如,下面的 SqlDataSourceControl 被配置为使用可变过期策略,过期时间为 10 分钟。
<asp:SqlDataSource
ID="SqlDataSource1"
EnableCaching="true"
CacheExpirationPolicy="Sliding"
CacheDuration="600"
ConnectionString="Server=localhost;database=Pubs"
SelectCommand="SELECT Title FROM Titles"
Runat="server" />
由于 CacheExpirationPolicy 属性的值被设置为 Sliding,CacheDuration 属性的值被设置为 600,因此,只要在 10 分钟内持续访问,此 SqlDataSource 表示的数据就会一直保留在内存中。
返回页首
使用 SQL Cache Invalidation
SQL Cache Invalidation 是 ASP.NET 2.0 Framework 最值得期待的新增功能之一。使用 SQL Cache Invalidation 可以获得缓存的全部性能优势,而不用担心数据过期的问题。SQL Cache Invalidation 使您可以在基础数据库中的数据发生更改时自动更新缓存中的数据。
SQL Cache Invalidation 通过在后台不断轮询数据库来检查数据更改。每隔一定的时间(毫秒),ASP.NET Framework 就会检查数据库中是否存在更新。如果 ASP.NET Framework 检测到任何更改,将从缓存中删除从数据库中添加的、依赖于数据库的任何项目(即,这些项目将过期)。
注意:Microsoft SQL Server 2005 支持一种截然不同的 SQL Cache Invalidation 方法。您可以配置 SQL Server 2005,使其在数据库、数据库表或数据库行发生变化时通知 ASP.NET 应用程序。这样,ASP.NET Framework 就不需要通过不断轮询 SQL Server 2005 数据库来检查数据更改了。
需要注意的是,SQL Cache Invalidation 只能用于 Microsoft SQL Server 7 及更高版本,不能用于其他数据库,例如 Microsoft Access 或 Oracle。
在缓存整个页面的输出、使用 DataSource控件或直接使用 Cache 对象时,都可以使用 SQL Cache Invalidation。下面将分别介绍这三种情况。
配置 SQL Cache Invalidation
在 Web 应用程序中使用 SQL Cache Invalidation 之前,首先必须执行一些配置步骤。必须将 Microsoft SQL Server 配置为支持 SQL Cache Invalidation,还必须在应用程序的 Web 配置文件中添加必要的配置信息。
可以按照以下两种方法配置 SQL Server:使用 aspnet_regsql 命令行工具,或者使用 SqlCacheDependencyAdmin 类。
使用 ASPNET_REQSQL 启用 SQL Cache Invalidation
使用 aspnet_regsql 工具,您可以通过命令行来配置 SQL Cache Invalidation。aspnet_regsql 工具位于 Windows\Microsoft.NET\Framework\[版本] 文件夹中。要使用此工具,必须打开命令提示符窗口并浏览到此文件夹。
要在使用 Pubs 数据库时支持 SQL Cache Invalidation,需要执行以下命令。
aspnet_regsql -E -d Pubs -ed
-E 选项使 aspnet_regsql 工具在连接到数据库服务器时使用集成的安全设置。-d 选项用于选择 Pubs 数据库。最后,-ed 选项用于为数据库启用 SQL Cache Invalidation。
执行此命令时,将在数据库中添加一个名为 AspNet_SqlCacheTablesForChangeNotification 的新数据库表。此表包含启用了 SQL Cache Invalidation 的所有数据库表的列表。此命令还将在数据库中添加一组存储过程。
为数据库启用 SQL Cache Invalidation 后,必须从数据库中选择要启用 SQL Cache Invalidation 的特定表。以下命令将为 Titles 数据库表启用 SQL Cache Invalidation。
aspnet_regsql -E -d Pubs -t Titles -et
-t 选项用于选择数据库表。-et 选项为数据库表启用 SQL Cache Invalidation。当然,您可以通过对每个数据库表重复执行此命令,为多个表启用 SQL Cache Invalidation。
执行此命令时,将在数据库表中添加一个触发器。只要您对表进行了修改,此触发器就将触发并更新 AspNet_SqlCacheTablesForChangeNotification 表。
最后,要获取某个特定数据库中当前启用了 SQL Cache Invalidation 的表的列表,可以使用以下命令。
aspnet_regsql -E -d Pubs -lt
此方法将从 AspNet_SqlCacheTablesForChangeNotification 中选择表的列表。此外,您也可以通过直接在该数据库表中执行查询来检索此信息。
使用 SqlCacheDependencyAdmin 类
aspnet_regsql 工具在后台使用 SqlCacheDependencyAdmin 类的方法来配置 Microsoft SQL Server。如果您愿意,可以直接从 ASP.NET 页面中使用此类的方法。
SqlCacheDependencyAdmin 类具有五个重要的方法:
• DisableNotifications — 为特定数据库禁用 SQL Cache Invalidation。
• DisableTableForNotifications — 为数据库中的特定表禁用 SQL Cache Invalidation。
• EnableNotifications — 为特定数据库启用 SQL Cache Invalidation。
• EnableTableForNotifications — 为数据库中的特定表启用 SQL Cache Invalidation。
• GetTablesEnabledForNotifications — 返回启用了 SQL Cache Invalidation 的所有表的列表。
例如,使用列表 2 中的 ASP.NET 页面,您可以为 Pubs 数据库中的任何表配置 SQL Cache Invalidation(参见图 2)。
图 2:从 ASP.NET 页面中启用 SQL Cache Invalidation
列表 2:EnableSCI.aspx (C#)
<%@ Page Language="c#" %>
<%@ Import Namespace="System.Web.Caching" %>
<script runat ="server">
const string connectionString = "Server=localhost;Database=Pubs";
void Page_Load()
{
if (!IsPostBack)
{
SqlCacheDependencyAdmin.EnableNotifications(
connectionString);
SqlDataSource1.SelectParameters.Add("connectionString",
connectionString);
}
}
void EnableTable(Object s, EventArgs e)
{
try
{
SqlCacheDependencyAdmin.EnableTableForNotifications(
connectionString, txtTableName.Text);
}
catch (Exception ex)
{
lblErrorMessage.Text = ex.Message;
}
txtTableName.Text = "";
}
</script>
<html>
<head runat ="server">
<title>Enable SQL Cache Invalidation</title>
</head>
<body>
<form id="form1" runat ="server">
<h1>SQL Cache Invalidation</h1>
以下表格已启用 SQL Cache Invalidation:
<p>
<asp:GridView id="grdTables"
DataSourceID="SqlDataSource1" CellPadding="10"
ShowHeader="false" Runat="Server" />
</p>
<asp:ObjectDataSource
ID="SqlDataSource1"
TypeName="System.Web.Caching.SqlCacheDependencyAdmin"
SelectMethod="GetTablesEnabledForNotifications"
Runat="Server" />
<p>
<asp:Label ID="lblErrorMessage" EnableViewState="false"
ForeColor="red" Runat="Server" />
</p>
<asp:TextBox ID="txtTableName" Runat="Server" />
<asp:Button Text="Enable Table" OnClick="EnableTable"
Runat="Server" />
</form>
</body>
</html>
列表 2:EnableSCI.aspx (Visual Basic .NET)
<%@ Page Language="vb" %>
<%@ Import Namespace="System.Web.Caching" %>
<script runat ="server">
Const connectionString As String = "Server=localhost;Database=Pubs"
Sub Page_Load()
If Not IsPostBack Then
SqlCacheDependencyAdmin.EnableNotifications( _
connectionString)
SqlDataSource1.SelectParameters.Add("connectionString", _
connectionString)
End If
End Sub
Sub EnableTable(ByVal s As Object, ByVal e As EventArgs)
Try
SqlCacheDependencyAdmin.EnableTableForNotifications( _
connectionString, txtTableName.Text)
Catch ex As Exception
lblErrorMessage.Text = ex.Message
End Try
txtTableName.Text = ""
End Sub
</script>
<html>
<head id="Head1" runat ="server">
<title>ConfigureSCI</title>
</head>
<body>
<form id="form1" runat ="server">
<h1>SQL Cache Invalidation</h1>
以下表格已启用 SQL Cache Invalidation:
<p>
<asp:GridView id="grdTables" DataSourceID="SqlDataSource1"
CellPadding="10" ShowHeader="false" Runat="Server" />
</p>
<asp:ObjectDataSource
ID="SqlDataSource1"
TypeName="System.Web.Caching.SqlCacheDependencyAdmin"
SelectMethod="GetTablesEnabledForNotifications"
Runat="Server" />
<p>
<asp:Label ID="lblErrorMessage" EnableViewState="false"
ForeColor="red" Runat="Server" />
</p>
<asp:TextBox ID="txtTableName" Runat="Server" />
<asp:Button ID="Button1" Text="Enable Table"
OnClick="EnableTable" Runat="Server" />
</form>
</body>
</html>
在列表 2 中,connectionString 常量用于选择启用了 SQL Cache Invalidation 的数据库(如果要为 Pubs 数据库以外的数据库启用 SQL Cache Invalidation,可以更改此常量的值)。在 Page_Load 方法中,调用 SqlCacheDependencyAdmin 类上的 EnableNotifications 方法,为由 connectionString 常量指定的数据库启用 SQL Cache Invalidation。
列表 2 中的 GridView 显示了当前启用了 SQL Cache Invalidation 的所有数据库表。GridView 被绑定到 ObjectDataSource 控件上,该控件为其 SelectMethod 调用 GetTablesneabledForNotifications 方法。
最后,您可以使用列表 2 中的页面为其他表启用 SQL Cache Invalidation。在文本框中输入表的名称并单击“Enable Table”按钮时,将调用 EnableTableForNotifications 方法。
SQL Cache Invalidation 的 Web 配置设置
在 ASP.NET 应用程序中使用 SQL Cache Invalidation 之前,下一步要做的是更新您的 Web 配置文件。您需要配置 ASP.NET Framework,以便轮询启用了 SQL Cache Invalidation 的数据库。
列表 3 中的 Web 配置文件包含轮询 Pubs 数据库所必需的配置信息。
列表 3:Web.Config
<configuration>
<connectionStrings>
<add name="mySqlServer"
connectionString="Server=localhost;Database=Pubs" />
</connectionStrings>
<system.web>
<caching>
<sqlCacheDependency enabled="true">
<databases>
<add
name="Pubs"
connectionStringName="mySqlServer"
pollTime="60000" />
</databases>
</sqlCacheDependency>
</caching>
</system.web>
</configuration>
列表 3 中的 Web 配置文件包含两部分。<connectionStrings> 部分用于创建数据库连接字符串,以连接到名为 mySqlServer 的 Pubs 数据库。
caching 部分用于配置 SQL Cache Invalidation 轮询。在 <databases> 子部分中,您可以列出要对其进行轮询以检查数据更改的一个或多个数据库。在列表 3 中,mySqlServer 表示的数据库每分钟(每 60000 毫秒)轮询一次。
您可以为不同的数据库指定不同的轮询间隔。每次轮询数据库以检查数据更改时,服务器都必须执行一些操作。如果您认为数据库中的数据不会频繁地更改,可以增加轮询间隔。
在页面输出缓存中使用 SQL Cache Invalidation
现在,我们已经完成了 SQL Cache Invalidation 的所有配置步骤,可以在 ASP.NET 页面中使用它了。一种方法是在页面输出缓存中使用 SQL Cache Invalidation。页面输出缓存允许您在内存中缓存页面所显示的所有内容。通过使用 SQL Cache Invalidation,您可以在(且只在)数据库表发生更改时自动更新缓存的页面。
例如,列表 4 中的页面在 GridView 控件中显示了 Titles 数据库表的内容。在该页面的顶部,OutputCache 指令用于在内存中缓存页面内容。如果 Titles 数据库表发生更改,SqlDependency 属性将使页面更新。
列表 4:OutputCacheTitles.aspx
<%@ OutputCache SqlDependency="Pubs:Titles"
Duration="6000" VaryByParam="none" %>
<html>
<head runat ="server">
<title>Output Cache Titles</title>
</head>
<body>
<form id="form1" runat ="server">
<%= DateTime.Now %>
<asp:GridView
ID="grdTitles"
DataSourceID="SqlDataSource1"
Runat="Server" />
<asp:SqlDataSource
ID="SqlDataSource1"
SelectCommand="Select * FROM Titles"
ConnectionString="<%$ ConnectionStrings:mySqlServer %>"
Runat="Server" />
</form>
</body>
</html>
请注意,SqlDependency 属性引用了 Web 配置文件中定义的数据库的名称。由于我们指定了每分钟轮询一次 Pubs 数据库以检查数据更改,因此如果对该数据库进行了更改,列表 4 中的页面将在一分钟之内进行更新。
您可以为 SqlDependency 属性值列出多个数据库和/或多个数据库表。要创建多个依赖关系,只需用分号分隔每个依赖关系即可。
在 DataSource 控件中使用 SQL Cache Invalidation
除了在页面输出缓存中使用 SQL Cache Invalidation 之外,还可以直接在 DataSource 控件中使用 SQL Cache Invalidation。如果要在多个页面中使用相同的数据库数据,请考虑在 DataSource 控件中使用 SQL Cache Invalidation。SqlDataSource、AccessDataSource 和 ObjectDataSource 控件都支持 SqlCacheDependency 属性。
例如,列表 5 中的页面在 SqlDataSource 控件中使用了 SQL Cache Invalidation。
列表 5:SqlDataSourceCaching.aspx
<html>
<head id="Head1" runat ="server">
<title>SqlDataSource Caching</title>
</head>
<body>
<form id="form1" runat ="server">
<%= DateTime.Now %>
<asp:GridView
ID="grdTitles"
DataSourceId="SqlDataSource1"
Runat="server" />
<asp:SqlDataSource
ID="SqlDataSource1"
EnableCaching="true"
SqlCacheDependency="Pubs:Titles"
SelectCommand="select * from titles"
ConnectionString="<%$ ConnectionStrings:mySqlServer %>"
Runat="server" />
</form>
</body>
</html>
在列表 5 中,SqlDataSource 控件是使用 EnableCaching 和 SqlCacheDependency 这两个属性声明的。SqlCacheDependency 属性使用的语法与 OutputCache 指令的 SqlDependency 属性相同。您需要列出数据库的名称,后跟数据库表的名称。
在 Cache 对象中使用 SQL Cache Invalidation
最后,您还可以在 Cache 对象中使用 SQL Cache Invalidation。此选项使您可以最大程度地对 SQL Cache Invalidation 进行编程控制。
要在 Cache 对象中使用 SQL Cache Invalidation,您需要创建一个 SqlCacheDependency 对象实例。使用 Insert 方法在 Cache 中插入新对象时,可以使用 SqlCacheDependency 对象。
例如,列表 6 中的页面显示了 Titles 数据库表中的记录数。计数是基于对基础数据库表的依赖关系进行缓存的。
列表 6:DisplayTitleCount.aspx (C#)
<%@ Page Language="c#" %>
<%@ Import Namespace="System.Data.SqlClient" %>
<script runat ="server">
void Page_Load()
{
int count = 0;
if (Cache["TitleCount"] != null)
{
count = (int)Cache["TitleCount"];
}
else
{
string connectionString =
ConfigurationSettings.ConnectionStrings[
"mySqlServer"].ConnectionString;
SqlConnection con = new SqlConnection(connectionString);
SqlCommand cmd = new
SqlCommand("SELECT Count(*) FROM Titles", con);
con.Open();
count = (int)cmd.ExecuteScalar();
con.Close();
Cache.Insert("TitleCount", count,
new SqlCacheDependency("Pubs", "Titles"));
}
lblTitleCount.Text = count.ToString();
}
</script>
<html>
<head runat ="server">
<title>Display Title Count</title>
</head>
<body>
<form id="form1" runat ="server">
<asp:Label ID="lblTitleCount" Runat="Server" />
</form>
</body>
</html>
列表 6:DisplayTitleCount.aspx (Visual Basic .NET)
<%@ Page Language="vb" %>
<%@ Import Namespace="System.Data.SqlClient" %>
<script runat ="server">
Sub Page_Load()
Dim count As Integer = 0
If Not Cache("TitleCount") Is Nothing Then
count = Convert.ToInt32(Cache("TitleCount"))
Else
Dim connectionString As String = _
ConfigurationSettings.ConnectionStrings( _
"mySqlServer").ConnectionString
Dim con As New SqlConnection(connectionString)
Dim cmd As New _
SqlCommand("SELECT Count(*) FROM Titles", con)
con.Open()
count = Convert.ToInt32(cmd.ExecuteScalar())
con.Close()
Cache.Insert("TitleCount", count, _
new SqlCacheDependency("Pubs", "Titles"))
End If
lblTitleCount.Text = count.ToString()
End Sub
</script>
<html>
<head id="Head1" runat ="server">
<title>Display Titles Count</title>
</head>
<body>
<form id="form1" runat ="server">
<asp:Label ID="lblTitleCount" Runat="Server" />
</form>
</body>
</html>
返回页首
使用 Post-Cache Substitution
在许多情况下,您需要缓存页面的一部分,而不是整个页面。例如,在您的 Web 站点主页上,您可能希望同时显示随机的标题广告和数据库表中的记录。如果缓存整个页面,每个用户都将在每次请求的页面上看到同一个标题广告。
要处理这种同时混有动态内容和缓存内容的问题,一种方法是使用 Web 用户控件。因为可以为 Web 用户控件添加 OutputCache 指令,所以即使不缓存包含页面的内容,也可以缓存 Web 用户控件的内容。
但有时候可能会事与愿违。虽然您可以使用 Web 用户控件在动态页面上添加缓存的内容,但很多情况下,您实际上是想在缓存的页面中添加动态内容。例如,假设您要缓存整个页面的内容,只留一小块区域用于显示当前用户的用户名。这种情况下最好使用 Post-Cache Substitution。
ASP.NET 2.0 Framework 引入了一种新控件,称为 Substitution 控件。您可以使用 Substitution 控件在缓存的页面中插入动态内容。列表 7 中的页面使用 Substitution 控件将用户名插入到缓存的内容中(参见图 3)。
图 3:使用 Substitution 控件显示用户名
列表 7:PostCacheSubstitution.aspx (C#)
<%@ Page Language="C#" %>
<%@ OutputCache Duration="6000" VaryByParam="none" %>
<script runat ="server">
static string DisplayUsername(HttpContext context)
{
if (!context.Request.IsAuthenticated)
return "Anonymous";
else
return context.User.Identity.Name;
}
</script>
<html>
<head runat ="server">
<title>Post Cache Substitution</title>
</head>
<body>
<form id="form1" runat ="server">
Welcome <asp:Substitution
MethodName="DisplayUsername" Runat="Server" />!
<p>
此页已缓存, 因为时间
<%= DateTime.Now.ToString("t") %> 并无改变。
</p>
</form>
</body>
</html>
列表 7:PostCacheSubstitution.aspx (Visual Basic .NET)
<%@ Page Language="vb" %>
<%@ OutputCache Duration="6000" VaryByParam="none" %>
<script runat ="server">
Shared Function DisplayUsername(ByVal context As HttpContext) _
As String
If Not context.Request.IsAuthenticated Then
Return "Anonymous"
Else
Return context.User.Identity.Name
End If
End Function
</script>
<html>
<head id="Head1" runat ="server">
<title>Post Cache Substitution</title>
</head>
<body>
<form id="form1" runat ="server">
Welcome <asp:Substitution
ID="Substitution1"
MethodName="DisplayUsername"
Runat="Server" />!
<p>
此页已缓存, 因为时间
<%= DateTime.Now.ToString("t") %> 并无改变。
</p>
</form>
</body>
</html>
Substitution 控件有一个非常重要的属性:即 MethodName 属性。MethodName 属性用于表示为返回动态内容而调用的方法。由 Substitution 控件调用的方法必须是静态方法(在 Visual Basic .NET 中共享的方法)。此外,该方法还必须具有一个表示当前 HttpContext 的参数。
在 ASP.NET 2.0 Framework 中,已对 AdRotator 控件进行了修改以支持 Post-Cache Substitution。如果在使用 OutputCache 指令的页面中添加 AdRotator 控件,AdRotator 控件将自动从其包含页面的缓存策略中排除出去。
返回页首
结论
缓存对由数据库驱动的 Web 应用程序的性能具有非常大的影响。幸运的是,ASP.NET 2.0 Framework 提供了许多新的重要增强功能,使您可以在应用程序中更轻松地使用缓存功能。
新增的 DataSource 控件中包含的属性使得在内存中缓存数据库数据变得非常容易。通过使用 DataSource 控件,您可以检索并缓存数据库数据,而无需编写任何代码。
新增的 SQL Cache Invalidation 支持使您可以在基础数据库中的数据发生更改时自动在缓存中重新加载数据库数据。此功能为您提供了缓存的所有性能优势,而不用担心数据过期的问题。
最后,使用新增的 Substitution 控件,您可以更轻松地在缓存的页面中混合动态内容。Substitution 控件为您在缓存的页面中插入动态内容提供了一个独立的空间。
作者简介
Stephen Walther 编写了有关 ASP.NET 的畅销书 ASP.NET Unleashed。此外,他还是 ASP.NET Community Starter Kit(Microsoft 开发的 ASP.NET 示例应用程序)的体系结构设计师和主要开发人员。他还通过自己的公司 Superexpert (http://www.superexpert.com/) 为美国的公司(包括 NASA 和 Microsoft)提供 ASP.NET 培训。
CSS
使用缩写可以帮助减少你CSS文件的大小,更加容易阅读。css缩写的主要规则请参看《常用css缩写语法总结》,这里就不展开描述。
忘记定义尺寸的单位是CSS新手普遍的错误。在HTML中你可以只写width=100,但是在CSS中,你必须给一个准确的单位,比如:width: 100px width:100em。只有两个例外情况可以不定义单位:行高和0值。除此以外,其他值都必须紧跟单位,注意,不要在数值和单位之间加空格。
当在XHTML中使用CSS,CSS里定义的元素名称是区分大小写的。为了避免这种错误,我建议所有的定义名称都采用小写。
当你写给一个元素定义class或者id,你可以省略前面的元素限定,因为ID在一个页面里是唯一的,鴆las s可以在页面中多次使用。你限定某个元素毫无意义。例如:
fieldset.details { /* declarations */ }
.details { /* declarations */ }
通常padding的默认值为0,background-color的默认值是transparent。但是在不同的浏览器默认值可能不同。如果怕有冲突,可以在样式表一开始就先定义所有元素的margin和padding值都为0,象这样:
margin:0;
padding:0;
}
CSS中,子元素自动继承父元素的属性值,象颜色、字体等,已经在父元素中定义过的,在子元素中可以直接继承,不需要重复定义。但是要注意,浏览器可能用一些默认值覆盖你的定义。
如果对同一个元素的定义有多种,以最接近(最小一级)的定义为最优先,例如有这么一段代码
margin:1em 0;
font-size:1em;
color:#333;
}
.update {
font-weight:bold;
color:#600;
}
八.多重class定义
.two{border:10px solid #F00;}
九.使用子选择器(descendant selectors)
<ul>
<li class=subnavitem> <a href=# class=subnavitem>Item 1</a></li>>
<li class=subnavitemselected> <a href=# class=subnavitemselected> Item 1</a> </li>
<li class=subnavitem> <a href=# class=subnavitem> Item 1</a> </li>
</ul>
</div>
div#subnav ul li.subnavitem { /* Some styling */ }
div#subnav ul li.subnavitem a.subnavitem { /* Some styling */ }
div#subnav ul li.subnavitemselected { /* Some styling */ }
div#subnav ul li.subnavitemselected a.subnavitemselected { /* Some styling */ }
<li> <a href=#> Item 1</a> </li>
<li class=sel> <a href=#> Item 1</a> </li>
<li> <a href=#> Item 1</a> </li>
</ul>
#subnav li { /* Some styling */ }
#subnav a { /* Some styling */ }
#subnav .sel { /* Some styling */ }
#subnav .sel a { /* Some styling */ }
为了节省字节,我建议不要给背景图片路径加引号,因为引号不是必须的。例如:
当一些元素类型、class或者id都有共同的一些属性,你就可以使用组选择器来避免多次的重复定义。这可以节省不少字节。
font-family:Lucida Grande,Lucida,Arial,Helvetica,sans-serif;
color:#333;
margin:1em 0;
}
h2 { font-size:1.6em; }
当你用CSS来定义链接的多个状态样式时,要注意它们书写的顺序,正确的顺序是::link :visited :hover :active。抽取第一个字母是LVHA,你可以记忆成LoVe HAte(喜欢讨厌)。为什么这么定义,可以参考Eric Meyer的《Link Specificity》。
一个非常常见的CSS问题,定位使用浮动的时候,下面的层被浮动的层所覆盖,或者层里嵌套的子层超出了外层的范围。
这是一个简单的技巧,但是值得再说一遍,因为我看见太多的新手问题都是问这个:CSS如何横向居中?你需要定义元素的宽,并且定义横向的margin,如果你的布局包含在一个层(容器)中,就象这样:
<!-- 你的布局这里开始 -->
width:760px; /* 修改为你的层的宽度 */
margin:0 auto;
}
text-align:center;
}
#wrap {
width:760px; /* 修改为你的层的宽度 */
margin:0 auto;
text-align:left;
}
因为老版本浏览器不支持CSS,一个通常的做法是使用@import技巧来把CSS隐藏起来。例如:
有些时候,你需要对IE浏览器的bug定义一些特别的规则,这里有太多的CSS技巧(hacks),我只使用其中的两种方法,不管微软在即将发布的IE7 beta版里是否更好的支持CSS,这两种方法都是最安全的。
o (a)在IE中隐藏一个CSS定义,你可以使用子选择器(child selector):
html>body p {
/* 定义内容 */
}
o (b)下面这个写法只有IE浏览器可以理解(对其他浏览器都隐藏)
* html p {
/* declarations */
}
o (c)还有些时候,你希望IE/Win有效而IE/Mac隐藏,你可以使用反斜线技巧:
/* */
* html p {
declarations
}
/* */
* 2.条件注释(conditional comments)的方法
<!--[if IE]>
<link rel=stylesheet type=text/css href=ie.css />
<![endif]-->
当调试CSS发生错误,你就要象排版工人,逐行分析CSS代码。我通常在出问题的层上定义一个背景颜色,这样就能很明显看到层占据多大空间。有些人建议用 border,一般情况也是可以的,但问题是,有时候border 会增加元素的尺寸,border-top和boeder-bottom会破坏纵向margin的值,所以使用background更加安全些。
在写CSS代码的时候,对于缩进、断行、空格,每个人有每个人的书写习惯。在经过不断实践后,我决定采用下面这样的书写样式:
selector2 {
property:value;
}
longhorn server Key
Windows Server 2003 IIS6 PHP
PHP: 推荐PHP-4.4.0-Win32的ZIP解压版本
4.4.0版本下载:http://cn.php.net/get/php-4.4.0-Win32.zip/from/a/mirror
5.1.2版本下载:http://cn.php.net/get/php-5.1.2-Win32.zip/from/a/mirror
5.1.4版本下载:http://download.discuz.net/env/php-5.1.4-Win32.zip
MySQL:配合PHP4推荐MySQL5.0.22的WIN系统安装版本
5.0.22版本下载:http://download.discuz.net/env/mysql-essential-5.0.22-win32.msi
官方下载地址:http://dev.mysql.com/downloads/mysql/5.0.html
ZendOptimizer:版本越新越好啊(目前最新是2.6.2)
官方下载地址:https://www.zend.com/store/free_download.php?pid=13
(虽然下载是免费的,但是要注册用户,才可以下载,为了方便大家的使用,公开我注册好的用户名:ideacmblog密码:chm.ideacm.cn,方便大家使用,希望大家在使用这个帐号的时候不要修改密码和用于其它非正当途径)
phpMyAdmin:这个当然也是越新越好啊
官方下载地址:http://www.phpmyadmin.net/home_page/downloads.php
国内下载地址:http://www.crsky.com/soft/4190.html
http://www.skycn.com/soft/10687.html
假设你的系统在C:\\,如果你的系统不是C:\\那么下面请自己对应修改。同时,为了避免C:\\因为重装系统等等各种原因,对C盘进行操作,和在系统盘下备份、转移也很不方便,所以不建议将PHP相关软件安装在C:\\,这里我把它安装在D:\\php目录下(这个目录你也可以自己设定,如果你要安装到不同墓立涉及到路径修改的请修改下面对应的路径即可)
第二步:安装PHP(我取PHP安装路径取为D:\\php\\php4\\(为避混淆,PHP5.1.x版本安装路径取为D:\\php\\php5\\)
(1)、下载得到的 php-4.4.0-Win32.zip ,解压至D:\\php目录,将得到二级目录php-4.4.0-Win32,改名为 php4,也即得到PHP文件存放目录D:\\php\\php4\\[如果是PHP5.1.2,得到的文件是php-5.1.2-Win32.zip,直接全部接压至D:\\php\\php5目录即可得PHP文件存放目录D:\\php\\php5\\];
(2)、再将D:\\php\\php4目录和D:\\php\\php4\\dlls目录[PHP5为D:\\php\\php5\\]下的所有dll文件拷到c:\\Windows\\system32(win2000系统为 c:/winnt/system32/)下,如果目录下有这些dll文件就覆盖已有的dll文件;
(3)、将D:\\php\\php4\\php.ini-dist用记事本打开,利用记事本的查找功能(Ctrl+F)搜索并修改:
register_globals = Off
把 Off 改成 On ,即得到 register_globals = On
注:这个对应PHP的全局变量功能,考虑有很多PHP程序需要全局变量功能故打开,打开后请注意-PHP程序的严谨性,如果不需要推荐不修改保持默认Off状态
extension_dir =
,并将其路径指到你的 PHP 目录下的 extensions 目录,比如:修改extension_dir = "./"为extension_dir = "D:/php/php4/extensions/" ;
PHP扩展功能目录[PHP5对应修改为extension_dir = "D:/php/php5/ext/"]
在D:\\php下建立文件夹并命名为tmp
;upload_tmp_dir =
将;upload_tmp_dir该行的注释符,即前面的分号“;”去掉,使该行在php.ini文档中起作用。upload_tmp_dir是用来定义上传文件存放的临时路径,在这里你还可以修改并给其定义一个绝对路径,这里设置的目录必须有读写权限。
这里我设置为upload_tmp_dir = D:/php/tmp (即前面建立的这个文件夹呵)
搜索找到
将下面一些常用的项前面的;去掉
这个是用来支持GD库的,一般需要,必选
去掉前面的";"
对于PHP5的版本还需要查找
并同样去掉前面的";"
这个是用来支持MYSQL的,由于PHP5将MySQL作为一个独立的模块来加载运行的,故要支持MYSQL必选
查找
去掉前面;号,本文这里将其设置置为
session.save_path = D:/php/tmp
其他的你可以选择需要的去掉前面的;然后将该文件另存为为php.ini到C:\\Windows ( Windows 2000 下为 C:\\WINNT)目录下,注意更改文件后缀名为ini,得到C:\\Windows\\php.ini ( Windows 2000 下为 C:\\WINNT\\php.ini)
一些朋友经常反映无法上传较大的文件或者运行某些程序经常超时,那么可以找到C:\\Windows ( Windows 2000 下为 C:\\WINNT)目录下的PHP.INI以下内容修改:
max_execution_time = 30 ; 这个是每个脚本运行的最长时间,可以自己修改加长,单位秒
max_input_time = 60 ; 这是每个脚本可以消耗的时间,单位也是秒
memory_limit = 8M ; 这个是脚本运行最大消耗的内存,也可以自己加大
upload_max_filesize = 2M ; 上载文件的最大许可大小 ,自己改吧,一些图片论坛需要这个更大的值
IIS如何安装,那就不用说了吧!
PHP 支持 CGI 和 ISAPI 两种安装模式,CGI 更消耗资源,容易因为超时而没有反映,但是实际上比较安全,负载能力强,节省资源,但是安全性略差于CGI,本人推荐使用 ISAPI 模式。故这里只解介绍 ISAPI 模式安装方法:
在“控制面板”的“管理工具”中选择“Internet 服务管理器”,打开 IIS 后停止服务,对于Win2003系统展开”Internet 服务管理器“的下级树一般为你的”计算机名“选择”网站“并单击右键选择“属性”,在弹出的属性窗口上选择“ISAPI 筛选器”选项卡找到并点击“添加”按钮,在弹出的“筛选器属性”窗口中的“筛选器名称”栏中输入:PHP ,再将浏览可执行文件使路径指向 php4isapi.dll 所在路径,如本文中为:D:\\php\\php4\\sapi\\php4isapi.dll[PHP5对应路径为D:\\php\\php5\\php5isapi.dll]。
打开“站点属性”窗口的“主目录”选项卡,找到并点击“配置”按钮
在弹出的“应用程序配置”窗口中的”应用程序映射“选项卡找到并点击“添加”按钮新增一个扩展名映射,在弹出的窗口中单击“浏览”将可执行文件指向 php4isapi.dll 所在路径,如本文中为:D:\\php\\php4\\sapi\\php4isapi.dll[PHP5对应路径为D:\\php\\php5\\php5isapi.dll],扩展名为 .php ,动作限于”GET,HEAD,POST,TRACE“,将“脚本引擎”“确认文件是否存在”选中,然后一路确定即可。如果还想支持诸如 .php3 ,.phtml 等扩展名的 PHP 文件,可以重复“添加”步骤,对应扩展名设置为需要的即可如.PHPX。
此步操作将使你服务器IIS下的所有站点都支持你所添加的PHP扩展文件,当然如果你只需要部分站点支持PHP,只需要在“你需要支持PHP的Web站点”比如“默认Web站点”上单击右键选择“属性”,在打开的“ Web 站点属性”“主目录”选项卡,编辑或者添加PHP的扩展名映射即可或者将你步需要支持PHP的站点中的PHP扩展映射删除即可
再打开“站点属性”窗口的“文档”选项卡,找到并点击“添加”按钮,向默认的 Web 站点启动文档列表中添加 index.php 项。您可以将 index.php 升到最高优先级,这样,访问站点时就会首先自动寻找并打开 index.php 文档。
确定 Web 目录的应用程序设置和执行许可中选择为纯脚本,然后关闭 Internet 信息服务管理器
对于2003系统还需要在“Internet 服务管理器”左边的“WEB服务扩展”中设置ISAPI 扩展允许,Active Server Pages 允许
完成所有操作后,重新启动IIS服务。
在CMD命令提示符中执行如下命令:
net stop w3svc
net stop iisadmin
net start w3svc
检查方法是,在 IIS 根目录下新建一个文本文件存为 php.php ,内容如下:
phpinfo();
?>
打开浏览器,输入:http://localhost/php.php,将显示当前服务器所支持 PHP 的全部信息,可以看到 Server API的模式为:ISAPI 。
或者利用PHP探针检测,到http://depoch.net/download.htm下载后解压到你的站点根目录下并访问即可
第三步:安装MySQL
对于MySQL4.0.26下载得到的是mysql-4.0.26-win32.zip,解压到mysql-4.0.26-win32目录双击执行 Setup.exe 一路Next下一步,选择安装目录为D:\\php\\MySQL和安装方式为Custom自定义安装,再一路Next下一步即可。
安装完毕后,在CMD命令行中输入并运行:
D:\\php\\MySQL\\bin\\mysqld-nt -install
新建一文本文件存为MY.INI,编辑配置MY.INI,这里给出一个参考的配置
[mysqld]
basedir=D:/php/MySQL
#MySQL所在目录
datadir=D:/php/MySQL/data
#MySQL数据库所在目录,可以更改为其他你存放数据库的目录
#language=D:/php/MySQL/share/your language directory
#port=3306
set-variable = max_connections=800
skip-locking
set-variable = key_buffer=512M
set-variable = max_allowed_packet=4M
set-variable = table_cache=1024
set-variable = sort_buffer=2M
set-variable = thread_cache=64
set-variable = join_buffer_size=32M
set-variable = record_buffer=32M
set-variable = thread_concurrency=8
set-variable = myisam_sort_buffer_size=64M
set-variable = connect_timeout=10
set-variable = wait_timeout=10
server-id = 1
[isamchk]
set-variable = key_buffer=128M
set-variable = sort_buffer=128M
set-variable = read_buffer=2M
set-variable = write_buffer=2M
set-variable = key_buffer=128M
set-variable = sort_buffer=128M
set-variable = read_buffer=2M
set-variable = write_buffer=2M
Server=D:/php/MySQL/bin/mysqld-nt.exe
保存后复制此MY.INI文件到C:\\Windows ( Windows 2000 下为 C:\\WINNT)目录下
回到CMD命令行中输入并运行:
net start mysql
MySQL 服务正在启动 .
MySQL 服务已经启动成功。
将启动 MySQL 服务;
DOS下修改ROOT密码:当然后面安装PHPMYADMIN后修改密码也可以通过PHPMYADMIN修改
格式:mysqladmin -u用户名 -p旧密码 password 新密码
例:给root加个密码ideacmblog
首先在进入CMD命令行,转到MYSQL目录下的bin目录,然后键入以下命令
mysqladmin -uroot password ideacmblog
注:因为开始时root没有密码,所以-p旧密码一项就可以省略了。
D:\\php\\MySQL\\bin>mysqladmin -uroot password ideacmblog回车后ROOT密码就设置为ideacmblog了
Next下一步后选择Standard Configuration
Next下一步,钩选Include .. PATH
Next下一步,设置ROOT密码,建议社设置复杂点,确保服务器安全!
Apply完成后将在D:\\php\\MySQL目录下生成MY.INI配置文件,添加并启动MySQL服务
第四步:安装 Zend Optimizer
下载后得到 ZendOptimizer-2.6.2-Windows-i386.exe ,直接双击安装即可,安装过程要你选择 Web Server 时,选择 IIS ,然后提示你是否 Restart Web Server,选择是,完成安装之前提示是否备份 php.ini ,点确定后安装完成。我这里安装到D:\\php\\Zend
zend_extension_ts="D:\\php\\Zend\\lib\\ZendExtensionManager.dll"
;Zend Optimizer 模块在硬盘上的安装路径。
zend_extension_manager.optimizer_ts="D:\\php\\Zend\\lib\\Optimizer-2.6.2"
;优化器所在目录,默认无须修改。
zend_optimizer.optimization_level=1023
;优化程度,这里定义启动多少个优化过程,默认值是 15 ,表示同时开启 10 个优化过程中的 1-4 ,我们可以将这个值改为 1023 ,表示开启全部10个优化过程。
调用phpinfo()函数后显示:
Zend Engine v1.3.0, Copyright (c) 1998-2004 Zend Technologies with Zend Extension Manager v1.0.9, Copyright (c) 2003-2006, by Zend Technologies with Zend Optimizer v2.6.2, Copyright (c) 1998-2006, by Zend Technologies 则表示安装成功。
这一步在前面PHP.INI配置中去掉“;extension=php_gd2.dll”前面的;实际上已经安装好了~
[在php.ini里找到"extension=php_gd2.dll"这一行,并且去掉前面的分号,gd库安装完成,用echophpinfo();测试是否成功! ]
下载得到 phpMyAdmin-2.8.2.zip ,将其解压到D:\\php\\或者 IIS 根目录,改名phpMyAdmin-2.8.2为phpMyAdmin,并在IIS中建立新站点或者虚拟目录指向该目录以便通过WEB地址访问,这里建立默认站点的phpMyAdmin虚拟目录指向D:\\php\\phpMyAdmin目录通过http://localhost/phpmyadmin/访问
找到并打开D:\\php\\phpMyAdmin\\libraries(phpMyAdmin2.7.0和以前的版本在D:\\php\\phpMyAdmin\\目录下)目录下的 config.default.php ,做以下修改:
查找
设置你的phpmyadmin的WEB访问URL,如本文中:$cfg[\'PmaAbsoluteUri\'] = \'http://localhost/phpmyadmin/\'; 注意这里假设phpmyadmin在默认站点的根目录下
搜索
设置COOKIES加密密匙,如ideacmblog则设置为$cfg[\'blowfish_secret\'] = \'ideacmblog\';
搜索$cfg[\'Servers\'][$i][\'auth_type\'],默认为config,是不安全的,不推荐,推荐使用cookie,将其设置为$cfg[\'Servers\'][$i][\'auth_type\'] = \'cookie\';
注意这里如果设置为config请在下面设置用户名和密码!例如:
$cfg[\'Servers\'][$i][\'user\'] = \'root\'; // MySQL user-----MySQL连接用户
$cfg[\'Servers\'][$i][\'password\'] = \'ideacmblog\';
搜索$cfg[\'DefaultLang\'] ,将其设置为 zh-gb2312 ;
搜索$cfg[\'DefaultCharset\'] ,将其设置为 gb2312 ;
在进http://localhost/phpmyadmin/输入用户名和密码后,如果出现 Client does not support authentication protocol requested by server; consider upgrading MySQL client 错误提示,
这是由于MySQL 4.1 及其后版本验证协议使用的密码哈希算法与老的客户端不兼容,因此需要在MySQL Command Line Client里面用命令同步兼容密码:
进入MySQL Command Line Client后MYSQL的提示符是:mysql>
然后执行:
SET PASSWORD FOR \'root\'@\'localhost\' = OLD_PASSWORD(\'你的ROOT管理密码\');
再可以进入phpmyadmin管理,每新建一个用户的时候都要这样执行命令一次,注意把root替换成你新建的用户名和对应的密码。
首先点击权限进入用户管理,删除除ROOT和主机不为localhost的用户并重新读取用户权限表,这里同样可以修改和设置ROOT的密码,添加其他用户等。
phpMyAdmin 的具体功能,请慢慢熟悉,这里就不赘述。
第七步:目录结构以及NTFS格式下安全的目录权限设置:
当前目录结构为
D:\\php
|
+—————+——————+———————+———————+
php4(php5) tmp MySQL Zend phpMyAdmin
D:\\php设置为管理员和SYSTEM完全权限即可,其他用户均无权限
对于其下的二级目录
php4(或者php5)、 tmp 给EVERYONE完全权限
MySQL 、Zend 管理员和SYSTEM完全权限
phpMyAdminWEB匿名用户读取运行权限
来源:http://www.cnblogs.com/zpq521/archive/2007/08/14/854648.html












浙公网安备 33010602011771号