笔记02-.Net基础2
第五章:.Net常用基本类库
String 常用方法1
//bool Contains(String str):判断字符串对象是否包含给定的字符串。 //bool StartsWith(String str):判断字符串对象是否以给定的字符串开始。 //bool EndsWith(String str):判断字符串对象是否以给定的字符串结束。 /*string s1 = "Hello"; Console.WriteLine(s1.Contains("el")); Console.WriteLine(s1.Contains("abc")); */ /* Console.WriteLine(s1.StartsWith("He")); Console.WriteLine(s1.StartsWith("he")); */ /* string s = "http://www.rupeng.com"; if (s.StartsWith("https://") && (s.EndsWith(".com") || s.EndsWith(".cn"))) { Console.WriteLine("合法网址"); } else { Console.WriteLine("非法网址"); }*/ /* string email = "3333@173.com"; string username = "领导"; if (email.EndsWith("@qq.com")) { Console.WriteLine("本站不支持QQ邮箱"); Console.ReadKey(); return; } if (username.Contains("领导") || username.Contains("***")) { Console.WriteLine("用户名请勿使用敏感词汇"); Console.ReadKey(); return; }*/
int Length:获取字符串的长度属性 char ch = s[3]; int IndexOf(char ch):返回指定字符在此字符串中第一次出现的索引 int IndexOf(String str):返回指定字符串在此字符串中第一次出现的索引 LastIndexOf:最后一次出现的位置。 String Substring(int start):截取字符串。返回从指定位置开始截取后的字符串。 String substring(int start,int length)截取字符串。返回从指定位置开始截取指定长度length的字符串。 string s1 = "hellooabcdaabe"; int i = s1.IndexOf('l'); Console.WriteLine(i); Console.WriteLine(s1.IndexOf("ab")); Console.WriteLine(s1.LastIndexOf('l')); string s2 = s1.Substring(4); Console.WriteLine(s2); Console.WriteLine(s1.Substring(4,3)); 案例获取一个文件名的名称和扩展名部分: string filename = "[ADS-118]林志aaaaaa玲.avi";// int dotIndex = filename.IndexOf('.');//3 string name = filename.Substring(0, dotIndex); Console.WriteLine(name); string ext = filename.Substring(dotIndex + 1); Console.WriteLine(ext);
String ToLower():把字符串变成小写;String ToUpper():把字符串变成大写 String Replace(char oldChar,char newChar):用新的字符去替换指定的旧字符;String Replace(String oldStr,String newStr):用新的字符串去替换指定的旧字符串 String trim():去除字符串两端空格, Trim(params char[] trimChars)去掉两端的给定字符。 TrimEnd、 TrimStart String是不可变的,因此上面的操作都是生成新的字符串对象,要用返回值去取新的字符串。 String[] Split(...):重载方法很多,字符串按照分隔符切割。案例:把字符串用“,”分割
bool IsNullOrEmpty(string value):判断字符串是否为null或者是空字符串; bool Equals(string a, string b, StringComparison. OrdinalIgnoreCase):不区分大小写比较。案例:验证码 string Join(string separator, params string[] value)把一个数组(集合)用分隔符separator连接为一个字符串。
StringBuilder
1、使用反编译发现 String s7="111"+"222"+"333"+"444"+"555" 会被优化为 String s7 = "111222333444555"; 但是String s6 = s1 + s2 + s3 + s4 + s5;就没那么幸运了,每次+都会生成一个String对象,当连接字符串比较多的时候就会产生临时字符串对象。 2、使用StringBuilder拼接字符串则不会有临时字符串对象的问题: StringBuilder sb = new StringBuilder(); sb.Append(s1); sb.Append(s2); sb.Append(s3); 因为Append方法把this返回了,还可以sb.Append(s1).Append(s2).Append(s3); 最后使用String s = sb.ToString()一次性生成拼接结果字符串即可 using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Threading.Tasks; namespace CommonClassTest2 { class Program { static void Main(string[] args) { /* string s1 = "aaa" + +"aaaaa"+"bbb"; Console.WriteLine(s1); */ string s1 = "aaa"; string s2 = "bbb"; string s3 = "ccc"; string s4 = "ddd"; string s5 = "afadsf"; string s6 = s1 + s2 + s3 + s4 + s5; StringBuilder sb = new StringBuilder(); /* sb.Append(s1); sb.Append(s2); sb.Append(s3); sb.Append(s4); sb.Append(s5); sb.Append(33);*/ sb.Append(s1).Append(s2).Append(s3).Append(s4).Append(s5); sb.AppendLine("hello"); sb.AppendLine("afasdfads"); string s7 = sb.ToString(); Console.WriteLine(s7); Person p1 = new Person(); p1.Eat().Sleep().Dota();//链式编程 Jquery Console.ReadKey(); } } class Person { public Person Eat() { Console.WriteLine("吃饭"); return this; } public Person Sleep() { Console.WriteLine("睡觉"); return this; } public Person Dota() { Console.WriteLine("打Dota"); return this; } } }
可空的int(值类型)
int等是值类型,不能为null。有时想表达“不知道数字是多少”的时候,用int任何值表示都不合适。 C#中可以用“int?”表示“可空的int”。注意类型转换: int? i1=5; if(i1!=null) { int i= (int)i1; } 也可以通过i1.HasValue判断是否为null、i1.Value来获得值。 int? i = null; int? i2 = 3; if (i == null) { Console.WriteLine("i为null"); } if (i2 != null)//i2.HasValue { //i2++; i2 = i2 + 3; //int i3 = i2;//把可能为null的int?赋值给 //一定不为null的int int i3 = (int)i2;//i2.Value Console.WriteLine(i2); } long? l = null;
LED时钟
private void timer1_Tick(object sender, EventArgs e) { string imgPath = @"F:\快盘\NextBig\NET课程\5_不拖控件的.Net(.net常用基本类库)\ledclock图片\"; DateTime now = DateTime.Now; int hour = now.Hour; int min = now.Minute; int sec = now.Second; int hourGe = hour % 10;//小时的个位 int hourShi = hour / 10;//小时的十位 int minGe = min % 10; int minShi = min / 10; int secGe = sec % 10; int secShi = sec / 10; pbHourShi.Load(imgPath+hourShi+".png"); pbHourGe.Load(imgPath + hourGe + ".png"); pbMinShi.Load(imgPath + minShi + ".png"); pbMinGe.Load(imgPath + minGe + ".png"); pbSecShi.Load(imgPath+secShi+".png"); pbSecGe.Load(imgPath + secGe + ".png"); //根据奇数秒还是偶数秒来决定“:”的闪烁 if (sec % 2 == 0) { pbColon1.Load(imgPath + "colon.png"); pbColon2.Load(imgPath + "colon.png"); } else { pbColon1.Load(imgPath + "empty.png"); pbColon2.Load(imgPath + "empty.png"); } }
把阿拉伯数字转换为中文金额数字:123→壹佰贰拾三圆
//附录:阿拉伯数字转换为中文 static void Main(string[] args) { Console.WriteLine(ConvertInt("123")); Console.WriteLine(ConvertInt("1003")); Console.ReadKey(); } public static string ConvertInt(string str) { string[] hanNums={"零","壹","贰","叁","肆", "伍", "陆","柒","捌","玖"}; string[] weiStrs={"","","拾","佰","仟","萬","拾","佰","仟","億","拾","佰","仟"}; int len = str.Length; int i; string tmpstr, rstr; rstr = ""; for (i = 1; i <= len; i++) { tmpstr = str.Substring(len - i, 1); rstr = string.Concat(hanNums[Int32.Parse(tmpstr)] + weiStrs[i], rstr); } rstr = rstr.Replace("拾零", "拾"); rstr = rstr.Replace("零拾", "零"); rstr = rstr.Replace("零佰", "零"); rstr = rstr.Replace("零仟", "零"); rstr = rstr.Replace("零萬", "萬"); for (i = 1; i <= 6; i++) { rstr = rstr.Replace("零零", "零"); } rstr = rstr.Replace("零萬", "零"); rstr = rstr.Replace("零億", "億"); rstr = rstr.Replace("零零", "零"); rstr += "圆整"; rstr = rstr.Replace("零圆", "圆"); return rstr; }
异常的概念和捕获
//1、异常的根类为Exception。异常类一般都继承自Exception //2、 try { String s = null; s.ToString(); } catch (NullReferenceException ex) { Console.WriteLine("为空"+ex.Message); } //e就是发生异常的异常类对象,变量名只要不冲突就任意。 //3、在异常处理中,一旦try里面有问题了。放弃try代码块中之后的代码,直接跳到catch里面执行。如果try代码后还有代码,则处理完catch后还会继续执行。 //4、多个异常的处理 try { int a = 10; int b = 0; Console.WriteLine(a / b); int[] arr = { 1, 2, 3 }; Console.WriteLine(arr[3]); } catch (DivideByZeroException ae) { Console.WriteLine("除数不能为0"); } catch (IndexOutOfRangeException ae) { Console.WriteLine("数组越界异常"); } //可以catch住父类异常,这样就可以抓住所有子类异常了,但是强烈不建议大家这样做,特别是不要没理由的catch(Exception ex) //5、好的异常处理习惯: //不要只是把异常catch住什么都不做或者只是打印一下,这不是正常的“异常处理”。 //不知道怎么处理就不要catch,出错比“把错误藏起爱”好。这样以为“不会出错了”,其实是把异常“吃掉了”,会让程序陷入逻辑混乱状态,要科学合理的处理异常(以后经常用就明白了)。
tryfinally
try{ //有可能有问题代码 }catch(异常类型 异常变量){ //处理方式 }finally{ //无论“有可能有问题代码”是否出错都要执行的代码 } //注意:在vs调试的情况下,finally的执行可能看不到,因为vs会把异常抛出阻断代码执行,如果想验证是否都会进入finall,可以按CTRL+F5执行程序。
finally 的执行条件只有这一个。 很可能在 try-catch 里直接 return 啊 break 啊 continue ,导致跳出 try-catch 结构。你可能会想当然的认为既然我 return 了直接返回结果 finally 里的代码就不会执行。这是错误的!因为 finally 执行条件只是【try-catch 结构执行完】,即使 try-catch 里 return 了,依然还是会先执行 finally 里的代码,然后才会真的 return。 而你要是不用 finally,直接把最后要统一执行的代码放在 try-catch 外面,那 try-catch 一 return,你的代码就不会被执行了。 所以 finally 最常用的地方是在里面释放对象占用的资源的。
File类
File是静态类(无法被new)中的主要静态方法: void Delete(string path):删除文件; bool Exists(string path):判断文件是否存在; string[] ReadAllLines(string path):将文本文件中的内容读取到string数组中; string ReadAllText(string path):将文本文件读取为一个字符串 void WriteAllLines(string path, string[] contents):将string[]写入到文件中; void WriteAllText(string path, string contents):将字符串contents写入到文件path中。 AppendAllText:向文件中附加内容;Copy复制文件;Move移动文件 /* if(File.Exists(@"d:\temp\a.pnproj")) { File.Delete(@"d:\temp\a.pnproj"); }*/ /* string[] lines = File.ReadAllLines(@"d:\temp\my.txt", Encoding.Default); for (int i = 0; i < lines.Length; i++) { Console.WriteLine("第"+i+"行:"+lines[i]); }*/ /* String s = File.ReadAllText(@"d:\temp\my.txt"); Console.WriteLine(s); */ // File.WriteAllText(@"d:\temp\test.txt", "欢迎你"); //File.AppendAllText(@"d:\temp\test.txt", "afasfsafadsf");
Directory
CreateDirectory(string path):创建文件夹全路径 void Delete(string path, bool recursive):删除文件夹path, recursive表示是否也删除子文件及子文件夹。如果文件夹不为空, recursive=false,则抛异常; bool Exists(string path):判断文件夹是否存在 EnumerateFiles、 EnumerateDirectories遍历文件和文件夹; // Directory.CreateDirectory(@"d:\temp\a\b\c"); //Directory.Delete(@"d:\temp\");//目录=文件夹,只能删除空文件夹 //Directory.Delete(@"D:\temp\redissessiontest", true); //递归删除 IEnumerable<string> files1 = //Directory.EnumerateFiles(@"d:\temp"); //Directory.EnumerateFiles(@"d:\temp","*.*",SearchOption.AllDirectories); Directory.EnumerateFiles(@"d:\temp", "*.avi", SearchOption.AllDirectories); IEnumerator<string> filesEnum1 = files1.GetEnumerator(); while (filesEnum1.MoveNext())//在结果中向下移动一条 { Console.WriteLine(filesEnum1.Current);//获得当前结果 }
FileStream
FileStream fs = new FileStream(@"d:\temp\1.txt",FileMode.Create); byte[] bytes1 = Encoding.Default.GetBytes("你好\r\n"); //任何数据都是以byte为单位写入的。GetBytes获得字符串的byte表示形式 fs.Write(bytes1,0,bytes1.Length); byte[] bytes2 = Encoding.Default.GetBytes("www.baidu.com"); fs.Write(bytes2,0,bytes2.Length);//在上一个Write的结尾继续写入 fs.Close();//一定要用完了关闭 Stream写入的单位是byte(字节),char转换为byte时候,一个英文char转换为一个byte(对应的ASCII码),一个中文char转换为两个byte(*采用GBK编码时),试着输出bytes的长度。 采用Default、UTF8、UTF32得到的字符串的byte[]长度不一样,因此说明不同类型编码在计算机中存储的不一样。用什么编码写入就用什么编码读取,否则会有乱码的问题。 不要往C盘写,因为Win7、Win8默认普通程序没有权限读写C盘。
FileStream资源正确的释放Dispose
FileStream f = null; FileStream f2 = null;//避免来不及创建就已经出异常(可能初始化就失败) try { f = new FileStream("",FileMode.OpenOrCreate);//可能初始化异常 //很多代码 //如果这个过程里异常 f2还未初始化. f2 = new FileStream("",FileMode.Append); } finally { if(f!=null) { f.Dispose(); } if (f2 != null) { f2.Dispose(); } }
using简化资源释放
定义一个实现了IDisposable接口的类: using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Threading.Tasks; namespace IOTest2 { class MyFileStream:IDisposable { public MyFileStream() { //throw new Exception("初始化出错"); } public void Write(int i) { throw new Exception("Write出错"); Console.WriteLine("写入了"+i); } public void Dispose() { Console.WriteLine("Dispose被调用了,开始释放资源,虽然没什么可释放的"); } } } 使用: using (MyFileStream fs1 = new MyFileStream()) { using (MyFileStream fs2 = new MyFileStream()) { fs2.Write(333); fs1.Write(5); } } 可以同时声明多个资源: using (MyFile f1 = new MyFile()) using (MyFile f2 = new MyFile()) { } using其实就是编译器简化的try……finally操作,通过ILSpy反编译成IL可以看出来。 using只是try……finally,如果需要catch,只要catch就是了。
FileStream读取文本文件以及乱码问题(可以用StreamReader)
using (FileStream fs = new FileStream(@"D:\temp\1.txt", FileMode.Open)) { byte[] bytes = new byte[4]; //fs.Read(bytes, 0, bytes.Length);//每次读取4个字节,下次Read的时候再读取最多4个byte //返回值为真正读取的字节数 int len; /* len = fs.Read(bytes, 0, bytes.Length); Console.WriteLine(len); len = fs.Read(bytes, 0, bytes.Length); Console.WriteLine(len); len = fs.Read(bytes, 0, bytes.Length); Console.WriteLine(len); */ //判断是否已经读到了最后 while ((len = fs.Read(bytes, 0, bytes.Length)) > 0) { //Console.WriteLine(len); //把byte数组转化为字符串 //string s = Encoding.Default.GetString(bytes); string s = Encoding.Default.GetString(bytes, 0, len); Console.WriteLine(s); } }
FileStream实现文件的拷贝
Stopwatch sw = new Stopwatch(); sw.Start(); using (Stream inStream = new FileStream(@"d:\temp\DevCpp.zip", FileMode.Open)) using (Stream outStream = new FileStream(@"e:\temp\1.zip", FileMode.Create)) { //byte[] bytes = new byte[100];//100字节 //1024字节=1k,1024k=1m byte[] bytes = new byte[1024*50];//1M //缓冲区(buffer)的大小:过小:降低速度;过大:占用内存 int len; while ((len = inStream.Read(bytes, 0, bytes.Length)) > 0) { outStream.Write(bytes, 0, len);//类似于GetString } } sw.Stop(); Console.WriteLine("拷贝完成,耗时" + sw.ElapsedMilliseconds+"毫秒");
封装Copy流的方法并开发下载器
static void Copy(Stream inStream, Stream outStream) { Copy(inStream, outStream, 1024 * 1024); } static void Copy(Stream inStream,Stream outStream,int bufferSize) { byte[] bytes = new byte[bufferSize]; int len; while ((len = inStream.Read(bytes, 0, bytes.Length)) > 0) { outStream.Write(bytes, 0, len); } } using (Stream inStream = new FileStream(@"d:\temp\DevCpp.zip",FileMode.Open)) using (Stream outStream = new FileStream(@"d:\temp\1.zip", FileMode.Create)) { Copy(inStream, outStream); } 文件下载器: WebRequest req = WebRequest.Create("http://www.baidu.com/img/bd_logo1.png"); using (WebResponse res = req.GetResponse()) using (Stream inStream = res.GetResponseStream()) using(Stream outStream = new FileStream(@"d:\temp\2.png",FileMode.Create)) { //inStream.CopyTo( Console.WriteLine(inStream.GetType());//Object o = new Person(); //Copy(inStream, outStream); inStream.CopyTo(outStream); }
StreamReader和StreamWriter
直接用Stream进行文本文件的读写会比较麻烦,因为要考虑文件的编码、中文字符等的问题。 StreamReader、StreamWriter是用来读写字符流(character stream)的类,会帮着自动处理麻烦的问题。 using (Stream outStream = new FileStream(@"d:\temp\1.txt", FileMode.Create)) using(StreamWriter sw = new StreamWriter(outStream,Encoding.Default)) { sw.Write("你好a"); sw.WriteLine("hehe"); sw.WriteLine("1111"); } using (Stream inStream = new FileStream(@"d:\temp\1.txt", FileMode.Open)) using (StreamReader reader = new StreamReader(inStream, Encoding.Default)) { /* string s = reader.ReadToEnd(); Console.Write(s);*/ /* string s1 = reader.ReadLine(); Console.WriteLine(s1); string s2 = reader.ReadLine(); Console.WriteLine(s2); */ string line; while ((line = reader.ReadLine()) != null) { Console.WriteLine(line); } }
泛型List
1、数组长度是固定的,List<T>可以动态增删内容。List<T>是泛型的,可以在声明的时候指定放什么类型的数据。 List<int> list = new List<int>(); list.Add(1); list.Add(2); list.AddRange(new int[] { 1, 2, 3, 4, 5, 6 }); list.AddRange(list);//另一个list 2、如何遍历? for (int i = 0; i < list1.Count; i++) { int num = list1[i]; Console.WriteLine(num); } 3、int[] nums = list.ToArray(); //List泛型集合可以转换为数组 List<string> listStr = new List<string>(); string[] str = listStr.ToArray();
字典Dictionary
Dictionary<string, string> dict = new Dictionary<string, string>(); dict.Add("zs", "张三"); dict.Add("ls", "李四"); dict.Add("ww", "王五"); dic["ls"] = "小赵"; String s = dict["ww"]; dict.ContainsKey(); //<k,v>k是键的类型(根据谁查),v是值的类型(查什么) /* Dictionary<string, int> dict = new Dictionary<string, int>(); dict.Add("abc", 123);//往字典中放数据(k,v) dict.Add("aaa", 222); int i = dict["abc"];//根据key从字典中查数据 Console.WriteLine(i); Console.WriteLine(dict["aaa"]);*/ /* Dictionary<string, string> dict = new Dictionary<string, string>(); dict.Add("zs","张三"); dict.Add("ls", "李四"); // dict.Add("ls","李斯");// 如果key已经存在,则抛异常 dict["ls"] = "李斯";//如果不存在key,则添加;如果存在则替换 dict["ww"] = "王五"; string name = dict["ls"]; Console.WriteLine(dict.ContainsKey("ls")); Console.WriteLine(dict.ContainsKey("abc")); Console.WriteLine(name);
foreach
注意:使用MoveNext()的方法讲解IEumerable只是在讲原理,实际开发中还是使用foreach 除了使用for遍历,实现了IEumerable接口的对象还可以使用foreach遍历: string[] strs = { "fasdfa","fsadf","222"}; foreach (string s in strs) { Console.WriteLine(s); } List<T>、 Dictionary<K,V> 等一切实现了IEumerable接口的对象都可以: Dictionary<string, object> dict = new Dictionary<string, object>(); dict["rupeng"] = 888; dict["sina"] = "hehe"; dict["baidu"] = true; foreach (KeyValuePair<string, object> kv in dict) { Console.WriteLine(kv.Key+"="+kv.Value); }
ASCII介绍和char转换为int
char和int各种转换 namespace ConsoleApplication7 { class Program { static void Main(string[] args) { /* Console.WriteLine(int.MaxValue); Console.WriteLine(int.MinValue); Console.WriteLine((int)char.MaxValue);//65535,显式转换,以便于匹配WriteLine的int类型的重载 Console.WriteLine((int)char.MinValue);//0 char c='a'; Console.WriteLine(c); int i = c;//把char隐式转换为int(int范围比char大) //只要把char数据隐式或者显式转换为int,得到的int就是char的ASCII码 Console.WriteLine(i); */ /* char c = (char)97; Console.WriteLine(c); int i = 98; char c1 = (char)i; Console.WriteLine(c1);*/ /* //'1'和1的区别 char c1 = '1';//49 int i1 = 1;//1 int i2 = c1; Console.WriteLine(i2); Console.WriteLine(c1==i1); */ /* char c1 = '0'; char c2 = (char)(c1 + 2);//不能(char)c1+1 Console.WriteLine(c2); char c3 = '5'; char c4 = (char)(c3-2); Console.WriteLine(c4); */ //Console.WriteLine(intToChar(5)); //Console.WriteLine(charToInt('3')); Console.WriteLine(toUpper('d')); Console.ReadKey(); } /// <summary> /// 把1转换为'1',2转换为'2' /// </summary> /// <param name="i"></param> /// <returns></returns> static char intToChar(int i) { if (i < 0 || i > 9) { throw new Exception("i必须在0-9之间"); } return (char)('0' + i);//i=3 } /// <summary> /// 把'1'转换为1 /// </summary> /// <param name="c"></param> /// <returns></returns> static int charToInt(char c) { //'0','1'....'9' if (c < '0' || c > '9')//判断是否是数字字符 { throw new Exception("不是合法的数字字符"); } return c - '0'; } //'a'→'A' static char toUpper(char c) { if (c < 'a' || c > 'z') { throw new Exception("不是小写字符"); } //'a':97,'A':65;'c':99,'C':67 //研究发现小写字符比大写字符大32 //return c //return (char)(c - 32); return (char)(c-('a'-'A')); } //任务:写一个转换为小写的方法 } }
第六章MYSQL
安装运行
绿色版MYSQL Server的安装:
1)解压到没有中文、空格的文件夹下
2)双击mysqld.exe,如果进程中有了mysqld.exe就ok了
3)上面的方式需要每次重启电脑都要手动运行,特别是如果运行在服务器上,那么不能在登录前就启动。可以注册为系统服务:以管理员身份运行命令行,cd到mysql的bin文件夹,执行“mysqld -install”;卸载服务:mysqld -remove。
注意在“mysqld”后面有个空格,不要写成“mysqld-install”
绿色版MYSQL的用户名、密码都是:root
主键
主键有两种选用策略:业务主键和逻辑主键。业务主键是使用有业务意义的字段做主键,比如身份证号、银行账号等;逻辑主键是使用没有任何业务意义的字段做主键,完全给程序看的,业务人员不会看的数据。因为很难保证业务主键不会重复(身份证号重复)、不会变化(帐号升位),因此推荐用逻辑主键。
外键
B表中的字段指向A表的字段,叫外键.比如2个表关联主键
建数据库立表的方法(表名命名:T_Student)
1、根节点点右键“新建数据库”,数据库名字取得有意义,比如“study1”、字符集建议用"UTF-8"。复习:读写编码一致就不会乱码。
2、在study1下的“表”节点下点右键“新建表”,“栏位”其实指的就是列(Column)/列,翻译的不好。Id(主键,潜规则的名称,int、不允许为空、点右键“主键”)、Name(varchar,长度为10,不允许为空)、Gender(bit,不允许为空),保存为“T_Persons”(我习惯的表命名规则T_名词复数)
3、建表常见错误:列名/表名不要用可能的关键字、不要有空格(包括前后,一不小心输错试试)、不要有特殊字符
双击生成的表,手工插入两条数据,注意点“提交”按钮
4、MYSQL的存储引擎有很多,最常用的是InnoDB和MyISAM,MyISAM效率较高,但是不支持事务、外键约束等特性,因此一般建议用InnoDB,新版本默认也是InnoDB。怎么设:建表时“选项”→“引擎”。建好了如何看引擎:表上点右键“对象信息”。
MYSQL常用数据类型
文本:
CHAR(*):最多255个字节的定长字符串,它的长度必须在创建时指定
VARCHAR(*):最多255个字节的可变长度字符串,它的长度必须在创建时指定
TEXT:最大长度为64K字符的变长文本
TINYTEXT:最大长度为255字符的变长文本
MEDUIMTEXT:最大长度为16K字符的变长文本
LONGTEXT:最大长度为4GB字符的变长文本
整数(考虑数据取值后选择尽可能小的类型)
tinyint:1字节。有符号值:-128 到127;无符号值:0到255
smallint:2字节。有符号值:-32768 到32767;无符号值:0到65535
mediumint:3字节。
int:4字节
bigint:8字节
小数(需要指定长度和小数点,也就是显示宽度和小数位数):
decimal:精确存储的小数,在内部用字符串存储,适合金额等要求精确的类型。别名:NUMERIC
float:4字节,单精度。会近似存储(*),效率比decimal高。
double:8字节,双精度。会近似存储(*),效率比decimal高。
日期时间:
DATE:4字节。范围:1000-01-01——9999-12-31
TIME:3字节。范围:-838:59:59——838:59:59
DATETIME:8字节。范围:1000-01-01 00:00:00——9999-12-31 23:59:59
二进制大数据:
TITYBLOB:最大长度为255字节
BLOB:最大长度为64KB
MEDIUMBLOB:最大长度为16MB
LONGBLOB:最大长度为4GB
SQL简介
1、SQL语句中字符串一般用单引号。
2、SQL语句是大小写不敏感的。
3、NavCat中找到执行SQL语句的地方“查询”→“新建查询”,编写SQL后点击【运行】执行SQL语句。
补充:如果只想执行NavCat中编写的一部分代码,而不是执行全部代码,只要选中要执行的代码,点击鼠标右键,选择“运行已经选择的”即可。
4、最简单的SQL:查看一个表的全部数据:select * from T_Persons
5、简单的插入数据的SQL语句:INSERT INTO T_Persons(Id,Name,Age,Gender) VALUES(5,'Jim',20,1)
Insert语句可以省略表名后的列名,但是强烈不推荐
6、如果插入的行中有些字段的值不确定,那么Insert的时候不指定那些列即可。不“允许为空”的列在插入时不能省略。Navicat有一个bug:某些情况下没有给字段设定值,最后的结果竟然不是null,咱们不用管这个bug。
7、自动递增/自增(Auto Increment):字段自增可以避免并发等问题,不要程序员代码控制自增。用自增字段在Insert的时候不用指定值。修改表结构的方法【设计表】。
UPDATE
1、更新一个列:UPDATE T_Persons Set Age=30 2、更新多个列: UPDATE T_Persons Set Age=30,Name='tom' 3、表达式:UPDATE T_Persons Set Age=Age+1 4更新一部分数据: UPDATE T_Persons Set Age=30 where Name='tom',用where语句表示只更新Name是'tom'的行,注意SQL中等于判断用单个=,而不是==。 5、Where中还可以使用复杂的逻辑判断UPDATE T_Persons Set Age=30 where Name='tom' or Age<25,or相当于Java中的|| where (Age>20 and Age<30) or(Age=80) 6、Where中可以使用的其他逻辑运算符:or、and、not、<、>、>=、<=、!=(或<>)等
Delete
删除表中全部数据:DELETE FROM T_Persons。 Delete 也可以带where子句来删除一部分数据:DELETE FROM T_Persons WHERE Age > 20 Delete只是删除数据,表还在。(*)Drop table T_Persons
数据初始化和select语法1
T_Employees初始数据: INSERT INTO T_Employees(Number,Name,Age,Salary) VALUES('DEV001','Tom',25,8300); INSERT INTO T_Employees(Number,Name,Age,Salary) VALUES('DEV002','Jerry',28,9300.80); INSERT INTO T_Employees(Number,Name,Age,Salary) VALUES('SALES001','John',23,5000); INSERT INTO T_Employees(Number,Name,Age,Salary) VALUES('SALES002','Kerry',28,6200); INSERT INTO T_Employees(Number,Name,Age,Salary) VALUES('SALES003','Stone',22,1200); INSERT INTO T_Employees(Number,Name,Age,Salary) VALUES('HR001','Jane',23,2200.88); INSERT INTO T_Employees(Number,Name,Age,Salary) VALUES('HR002','Tina',25,5200.36); INSERT INTO T_Employees(Number,Name,Age,Salary) VALUES('IT001','Smith',28,3900); INSERT INTO T_Employees(Number,Age,Salary) VALUES('IT002',27,2800); 1、简单的数据检索 :SELECT * FROM T_Employees 2、只检索需要的列 :SELECT Number FROM T_Employees 、SELECT Name,Age FROM T_Employees 3、列别名:SELECT Number AS 编号,Name AS 姓名,Age AS Age111 FROM T_Employees 写不写AS都行 4、计算列:SELECT Number 编号,Name 姓名,Age Age111,Age+10 十年后的年龄,1+1,now() FROM T_Employees 5、使用where检索符合条件的数据:SELECT Name FROM T_Employees WHERE Salary<5000。故事:新员工的数据检索噩梦 6、还可以检索不与任何表关联的数据:select 1+1;select now();
聚合函数
1、SQL聚合函数:MAX(最大值)、MIN(最小值)、AVG (平均值)、SUM (和)、COUNT(数量) 2、大于25岁的员工的最高工资 :SELECT MAX(Salary) FROM T_Employees WHERE Age>25 3、最低工资和最高工资:SELECT MIN(Salary),MAX(Salary) FROM T_Employees 4、大于25岁的员工人数:SELECT COUNT(*) FROM T_Employees WHERE Age>25 5、全体员工的工资总和平均工资:SELECT SUM(Salary),AVG (Salary) FROM T_Employees
OrderBy
1、ORDER BY子句位于SELECT语句的末尾,它允许指定按照一个列或者多个列进行排序,还可以指定排序方式是升序(从小到大排列,ASC)还是降序(从大到小排列,DESC) 2、按照年龄升序排序所有员工信息的列表:SELECT * FROM T_Employees ORDER BY Age ASC 3、按照年龄从大到小排序,如果年龄相同则按照工资从大到小排序 :SELECT * FROM T_Employees ORDER BY Age DESC,Salary DESC 4、ORDER BY子句要放到WHERE子句之后 :SELECT * FROM T_Employees WHERE Age>23 ORDER BY Age DESC,Salary DESC
Like
通配符过滤使用LIKE 。 1、单字符匹配的通配符为半角下划线“_”,它匹配单个出现的字符。以任意字符开头,剩余部分为“erry” :SELECT * FROM T_Employees WHERE Name LIKE '_erry' 2、多字符匹配的通配符为半角百分号“%”,它匹配任意次数(零或多个)出现的任意字符。 “k%”匹配以“k”开头、任意长度的字符串。检索姓名中包含字母“n”的员工信息 :SELECT * FROM T_Employees WHERE Name LIKE '%n%' 3、Like性能较差,很容易造成全表扫描,谨慎使用。后面会讲数据库优化(索引等),项目中做搜索用全文检索。
null
1、数据库中,一个列如果没有指定值,那么值就为null,数据库中的null表示“不知道”,而不是表示没有。因此select null+1结果是null,因为“不知道”加1的结果还是“不知道”。 2、SELECT * FROM T_Employees WHERE NAME=null ; SELECT * FROM T_Employees WHERE NAME!=null ; 都没有任何返回结果,因为数据库也“不知道”。 3、提问:如果T_Employees表中Name列的值是null的话,那么下面查询结果是什么?Select Name+"a" FROM T_Employees 。 答案:还是null,因为null和任何的东西做任何的运算都是null,null+"a"还是null。 4、SQL中使用is null、is not null来进行空值判断: SELECT * FROM T_Employees WHERE NAME is null ; SELECT * FROM T_Employees WHERE NAME is not null ;
LIMIT
1、LIMIT关键字用来限制返回的结果集, LIMIT放在SELECT语句的最后位置,语法为“LIMIT 首行行号,要返回的结果集的最大数目” 。比如下面的SQL语句将返回Name不为空的、按照工资降序排列的从第二行开始(行号从0开始)的最多五条记录: SELECT * FROM T_Employees where Name is not null ORDER BY Salary DESC LIMIT 2,5 2、limit一定要放到所有的语句的最后。
Groupby分组查询
1、数据分组用来将数据分为多个逻辑组,从而可以对每个组进行聚合运算。SQL语句中使用GROUP BY子句进行分组,使用方式为“GROUP BY 分组字段”。分组语句一般和聚合函数一起使用,GROUP BY子句负责将数据分成逻辑组,而聚合函数则对每一个组进行统计计算。 2、查看公司员工有哪些年龄段的: SELECT Age FROM T_Employees GROUP BY Age 将Age相同的数据行放到一组,分组后的数据可以看作一个临时的结果集,而SELECT Age语句则取出每组的Age字段的值,这样我们就得到上表的员工年龄段表了。 3、如果SELECT语句有WHERE子句,则GROUP BY子句必须放到WHERE语句的之后。 4、GROUP BY子句将检索结果划分为多个组,每个组是所有记录的一个子集。上面的SQL例子在执行的时候数据库系统将数据分成了下面的分组: 5、 分组后就可以对组内的数据采用聚合函数进行统计了: 1)计算每个分组中的员工平均工资 SELECT Age,AVG(Salary) FROM T_Employees GROUP BY Age 2)查看每个年龄段的员工的人数: SELECT Age,COUNT(*) FROM T_Employees GROUP BY Age
left join
初始数据: INSERT INTO T_Customers(Id,Name,Age) VALUES(1,'TOM',21); INSERT INTO T_Customers(Id,Name,Age) VALUES(2,'MIKE',24); INSERT INTO T_Customers(Id,Name,Age) VALUES(3,'JACK',30); INSERT INTO T_Customers(Id,Name,Age) VALUES(4,'TOM',25); INSERT INTO T_Customers(Id,Name,Age) VALUES(5,'LINDA',30); INSERT INTO T_OrderTypes(Id,Name) VALUES(1,'现货订单'); INSERT INTO T_OrderTypes(Id,Name) VALUES(2,'预订订单'); INSERT INTO T_OrderTypes(Id,Name) VALUES(3,'预购订单'); INSERT INTO T_OrderTypes(Id,Name) VALUES(4,'内部'); INSERT INTO T_Orders(Id,Number,Price,CustomerId, TypeId) VALUES(1,'K001',100,1,1); INSERT INTO T_Orders(Id,Number,Price,CustomerId, TypeId) VALUES(2,'K002',200,1,1); INSERT INTO T_Orders(Id,Number,Price,CustomerId, TypeId) VALUES(3,'T003',300,1,2); INSERT INTO T_Orders(Id,Number,Price,CustomerId, TypeId) VALUES(4,'N002',100,2,2); INSERT INTO T_Orders(Id,Number,Price,CustomerId, TypeId) VALUES(5,'N003',500,3,4); INSERT INTO T_Orders(Id,Number,Price,CustomerId, TypeId) VALUES(6,'T001',300,4,3); INSERT INTO T_Orders(Id,Number,Price,CustomerId, TypeId) VALUES(7,'T002',100,1,1); 1、查询每张订单的订单号、价格、对应的客户姓名以及客户年龄 SELECT o.Number,o.Price,c.Name,c.Age FROM T_Orders o LEFT JOIN T_Customers c ON o.CustomerId=c.Id 2、添加where语句(显示价格>=150元的订单) SELECT o.Number,o.Price,o.CustomerId, c.Name,c.Age FROM T_Orders o LEFT JOIN T_Customers c ON o.CustomerId=c.Id WHERE o.Price>=150 3、可以join多张表: SELECT o.Number 订单号,o.Price 价格, c.Name 客户姓名,c.Age 客户年龄,t.Name 订单类型 FROM T_Orders o LEFT JOIN T_Customers c ON o.CustomerId=c.Id LEFT JOIN T_OrderTypes t ON o.TypeId=t.Id
外键约束
1、如果删除/更新T_Customers一行记录,那么就可能会导致T_Orders中存在CustomerId为非法值的数据,使得程序逻辑错误。一般不会更新主键Id的值,所以谈外键约束的时候只谈“删除T_Customers时”。 2、外键约束:当删除T_Customers中一条数据的时候,如何处理T_Orders等存在指向T_Customers外键的行。外键约束建立在外键字段***Id的表上(t_orders)。 3、建外键约束的方法:新建或者修改表的时候“外键”→“添加外键”。名:自动命名即可;栏位名:CustomerId;参考表:t_customers;外栏位名:Id;删除时、更新时:一般默认RESTRICT(CASCADE:删除T_Customers一行时把它的订单也删除了;SET NULL:删除T_Customers一行时把它的订单CustomerId设置为NULL;NO ACTION/RESTRICT:拒绝删除)。 4、有的公司不习惯建外键,而是通过程序进行数据合法性控制,对于初学者先不建议这样,都把外键加上。
第七章ADO.NET
ADO.NET是.NET中提供的标准访问数据库的接口,访问不同的DBMS的底层方法是不一样的,ADO.NET把访问数据库的方法进行了统一,访问MYSQL,Oracle,SQLServer等不同的数据库的用法几乎一模一样.
ADO.NET是规范,被不同的数据库厂商提供ADO.NET的实现,称之为ADO.NET驱动,每个厂商提供的驱动可以用来操作自己的数据库.
MYSQL的.Net驱动mysql-connector-net-***.msi下载地址: 1)https://dev.mysql.com/get/Downloads/Connector-Net/mysql-connector-net-6.9.9.msi 新建项目,添加引用→“扩展”,添加Mysql.Data;如果是直接解压版,然后直接添加对MySql.Data.dll文件的引用; using (MySqlConnection conn = new MySqlConnection("Server=localhost;Database=study1;uid=root;pwd=root;Charset=utf8")) using (MySqlCommand cmd = conn.CreateCommand()) { conn.Open();//一定要在执行前Open数据库连接 cmd.CommandText = "Insert into T_Users(UserName,Password) values('中国人','123')"; int rowCount = cmd.ExecuteNonQuery(); Console.WriteLine("受影响的行数"+rowCount); } 解释一下代码: MySqlConnection、MySqlCommand实现了IDisposable接口,因此使用using进行资源释放; "Server=localhost;Database=study1;uid=root;pwd=root;Charset=utf8"叫连接字符串,Server是Mysql服务器的地址,Database是连接的数据库,uid、pwd是用户名和密码,采用utf8编码。 conn.Open():在执行MySqlCommand之前一定要先打开数据库连接,否则会报错。 ExecuteNonQuery是执行Update、Insert、Delete等非查询语句,返回值为受影响的行数。 如果运行的时候报错AccessViolationException,一般是由于你电脑装了一些乱七八糟的软件把winsock组件搞坏了的原因,你以管理员模式运行命令行,然后执行 netsh winsock reset 然后重启电脑试试。
执行insert语句
using (MySqlConnection conn = new MySqlConnection("Server=127.0.0.1;Database=study1;uid=root;pwd=root;Charset=utf8")) using(MySqlCommand cmd = conn.CreateCommand()) { conn.Open(); cmd.CommandText = "Insert into T_Users(UserName,Password) values('aaa','123')"; int rc = cmd.ExecuteNonQuery(); Console.WriteLine("插入成功,影响行数"+rc); } ExecuteNonQuery是执行Update、Insert、Delete等非查询语句,返回值为受影响的行数。
ExecuteScalar
ExecuteScalar:执行查询,并返回查询所返回的结果集中第一行的第一列,忽略其他行列。一般用来简单的获得只有一行一列的查询结果的值。 案例1: cmd.CommandText = "Select count(*) from T_Users"; long count = (long)cmd.ExecuteScalar(); 案例2: cmd.CommandText = "Select Password from T_Users where UserName='admin'"; string pwd = (string)cmd.ExecuteScalar(); if (string.IsNullOrEmpty(pwd)) { Console.WriteLine("找不到admin"); } else { Console.WriteLine("admin的密码:" + pwd); }
ExecuteReader
cmd.CommandText = "select * from T_Users"; using (MySqlDataReader reader = cmd.ExecuteReader()) { while (reader.Read()) { long id = reader.GetInt64("Id"); string userName = reader.GetString("UserName"); string password = reader.GetString("Password"); Console.WriteLine("id=" + id + ";UserName=" + userName + ";Password=" + password); } } 注意:Reader的遍历、读取时需要Connection保持连接,如果关闭了Connection,使用Reader会出错。 也可以根据列序号获取列的值,效率略高,不过程序不容易读;通过reader.GetOrdinal("Age")获得列名对应的列序号。
SQL注入漏洞攻击,,开发一定得注意
参数化查询
/* using(MySqlConnection conn = new MySqlConnection("Server=127.0.0.1;Database=study1;uid=root;pwd=root;Charset=utf8")) using (MySqlCommand cmd = conn.CreateCommand()) { conn.Open(); cmd.CommandText = "Insert into @p(UserName,Password) values(@un,@pwd)"; cmd.Parameters.Add(new MySqlParameter { ParameterName = "@p", Value = "t_users" }); cmd.Parameters.Add(new MySqlParameter { ParameterName="@un",Value="rueng"}); cmd.Parameters.Add(new MySqlParameter { ParameterName="@pwd",Value="123456"}); cmd.ExecuteNonQuery(); }*/ /* using(MySqlConnection conn = new MySqlConnection("Server=127.0.0.1;Database=study1;uid=root;pwd=root;Charset=utf8")) using (MySqlCommand cmd = conn.CreateCommand()) { conn.Open(); cmd.CommandText = "Insert into T_Users(UserName,Password,Age) values(@un,@pwd,@Age)"; cmd.Parameters.Add(new MySqlParameter("@un","test1")); cmd.Parameters.Add(new MySqlParameter("@pwd", "321")); //int i = 0; //cmd.Parameters.Add(new MySqlParameter("@Age", i)); // cmd.Parameters.Add(new MySqlParameter("@Age", 0)); cmd.Parameters.Add(new MySqlParameter("@Age",(object)0)); cmd.ExecuteNonQuery(); }*/
读取数据库中的null值
使用IsDBNull获取指定序号的列的值是否为null int? age=null; if (!reader.IsDBNull(reader.GetOrdinal("Age"))) { age = reader.GetInt32("Age"); } using (MySqlConnection conn = new MySqlConnection("Server=127.0.0.1;Database=study1;uid=root;pwd=root;Charset=utf8")) using (MySqlCommand cmd = conn.CreateCommand()) { conn.Open(); cmd.CommandText = "select * from T_Users;"; using (MySqlDataReader reader = cmd.ExecuteReader()) { while (reader.Read()) { string username = reader.GetString("UserName"); // string password = reader.GetString("Password"); string password; if (reader.IsDBNull(reader.GetOrdinal("Password"))) { password = null; } else { //GetString/GetInt32 无法读取数据库中的null值 //需要提前用IsDBNull判断 password = reader.GetString("Password"); } int? age; if (reader.IsDBNull(reader.GetOrdinal("Age"))) { age = null; } else { age = reader.GetInt32("Age"); } // int age = reader.GetInt32("Age"); Console.WriteLine("用户名:"+username+";密码:"+ (password==null?"不知道密码":password)+";年龄:"+ (age==null?"不知道年龄":age.ToString())); } } }
离线结果集入门
DataReader是服务器结果集游标的体现,所有查询出来的数据都在MySQL服务器上。好处是:当查询结果数据量大的时候避免占用本地内存。不过大部分项目中都会避免大查询结果,因此缺点就明显了:读取的时候必须保持Connection,不仅用起来麻烦,而且会较长时间占用MySQL服务器的连接资源。 DataSet是一个离线结果集容器,它把结果数据放到本地内存中。因为查询结果可能会包含多个表,因此DataSet包含若干DataTable(ds.Tables)、DataTable包含若干DataRow(dt. Rows)。 用法1: DataSet ds = new DataSet(); MySqlDataAdapter adapter = new MySqlDataAdapter(cmd); adapter.Fill(ds); DataTable table = ds.Tables[0]; DataSet 可以盛放多个查询结果集到DataTable ;DataAdapter还可以对结果进行傻瓜化更新、删除、修改。我们一般查询结果集就一个DataTable, DataAdapter的傻瓜化更新不适合于正式的项目,因此有更简单的用法 DataTable dt = new DataTable(); dt.Load(reader); 把DataTable声明到using外,using外再使用查询结果。 遍历DataTable: for (int i = 0; i < dt.Rows.Count; i++) { DataRow row = dt.Rows[i]; string name = row.IsNull("Name")?null:(string)row["Name"];//NULL处理 Console.WriteLine("name"+name); } 案例代码: /* using (MySqlConnection conn = new MySqlConnection("Server=127.0.0.1;Database=study1;uid=root;pwd=root;Charset=utf8")) using (MySqlCommand cmd = conn.CreateCommand()) { conn.Open(); cmd.CommandText = "select * from t_users"; using (MySqlDataReader reader = cmd.ExecuteReader()) { DataTable dt = new DataTable(); dt.Load(reader); for (int i = 0; i < dt.Rows.Count; i++) { DataRow row = dt.Rows[i]; int id = (int)row["Id"];//下标方式获得是object string username = (string)row["UserName"]; //做很复杂的io操作,把username存到文件中 //这样就会长期占据Connection // object obj = row["Password"]; //if(row["Password"]==DBNull.Value)//返回的不是null,而是DBNull //string password = (string)row["Password"]; //int age = (int)row["Age"]; string password = row.IsNull("Password") ? null : (string)row["Password"]; int? age = row.IsNull("Age") ? (int?)null : (int)row["Age"]; Console.WriteLine("id=" + id + ";Username=" + username +";username="+username+";age="+age); } } }*/ /* DataTable table = new DataTable(); using (MySqlConnection conn = new MySqlConnection("Server=127.0.0.1;Database=study1;uid=root;pwd=root;Charset=utf8")) using (MySqlCommand cmd = conn.CreateCommand()) { conn.Open(); cmd.CommandText = "select * from t_users"; using (MySqlDataReader reader = cmd.ExecuteReader()) { table.Load(reader);//加载到table中 } } for (int i = 0; i < table.Rows.Count; i++) { DataRow row = table.Rows[i]; int id = (int)row["id"]; string username = (string)row["UserName"]; Console.WriteLine(id+":"+username); } */
MySqlHelper
连接字符串一般配置到App.config(网站是Web.config)中的<connectionStrings>段中 使用ConfigurationManager类(添加对System.Configuration的引用)读取 string connstr = ConfigurationManager.ConnectionStrings["connstr"].ConnectionString 注意: 1、注意不要修改App.config的名字为App1.config之类的,必须叫App.config(网站是Web.config) 2、配置文件中只能有一个connectionstrings段,不能配置多个 3、一定要保证代码中的名字和配置文件中的名字一致(初学者容易犯错的,一般提示“初始化代码错误”这个错误就是因为两者名字不一致造成的) using MySql.Data.MySqlClient; using System; using System.Collections.Generic; using System.Configuration; using System.Data; using System.Linq; using System.Text; using System.Threading.Tasks; namespace ADONETTest2 { class MySqlHelper { private static readonly string connstr = ConfigurationManager.ConnectionStrings["connstr"].ConnectionString; public static MySqlConnection CreateConnection() { //using (MySqlConnection conn = new MySqlConnection(connstr)) MySqlConnection conn = new MySqlConnection(connstr); conn.Open(); return conn; } public static int ExecuteNonQuery(MySqlConnection conn, string sql, params MySqlParameter[] parameters) { using (MySqlCommand cmd = conn.CreateCommand()) { cmd.CommandText = sql; /* foreach (MySqlParameter p in parameters) { cmd.Parameters.Add(p); }*/ cmd.Parameters.AddRange(parameters); return cmd.ExecuteNonQuery(); } } public static int ExecuteNonQuery(string sql, params MySqlParameter[] parameters) { using (MySqlConnection conn = CreateConnection()) { return ExecuteNonQuery(conn, sql, parameters); } } public static object ExecuteScalar(MySqlConnection conn, string sql, params MySqlParameter[] parameters) { using (MySqlCommand cmd = conn.CreateCommand()) { cmd.CommandText = sql; cmd.Parameters.AddRange(parameters); return cmd.ExecuteScalar(); } } public static object ExecuteScalar(string sql, params MySqlParameter[] parameters) { using (MySqlConnection conn = CreateConnection()) { return ExecuteScalar(conn, sql, parameters); } } public static DataTable ExecuteQuery(MySqlConnection conn, string sql, params MySqlParameter[] parameters) { DataTable table = new DataTable(); using (MySqlCommand cmd = conn.CreateCommand()) { cmd.CommandText = sql; cmd.Parameters.AddRange(parameters); using (MySqlDataReader reader = cmd.ExecuteReader()) { table.Load(reader); } } return table; } public static DataTable ExecuteQuery(string sql, params MySqlParameter[] parameters) { using (MySqlConnection conn = CreateConnection()) { return ExecuteQuery(conn, sql, parameters); } } } }
获取自动增长字段的值
1、使用LAST_INSERT_ID()获取“最后一次插入的自动递增列的值” 2、需要注意Insert语句和select LAST_INSERT_ID()一定要在要在同一个连接中。 using (MySqlConnection conn = MySqlHelper.CreateConnection()) { MySqlHelper.ExecuteNonQuery(conn, "insert into t_users(UserName,Password) values('我几时我','123')"); object o = MySqlHelper.ExecuteScalar(conn, "select Last_Insert_Id()"); //Last_Insert_Id()是获取当前连接中,最近一次自动递增字段的值 ulong id = (ulong)o;//无符号的long Console.WriteLine(id); } 可以Insert、LAST_INSERT_ID()在同一个连接中单独执行,也可以把LAST_INSERT_ID()放到insert语句后面用;分割(使用ExecuteScalar执行即可) ulong id = (ulong)MySqlHelper.ExecuteScalar("insert into t_users(UserName,Password) values('我几时我','123');select last_insert_id()"); Console.WriteLine(id);
事物的原子性
事务的几个关键环节: 1)要在一个连接中; 2)启动事务:MySqlTransaction tx = conn.BeginTransaction(); 3)操作结束后执行tx.Commit() 提交事务; 4)如果执行出错,则tx.Rollback()回滚(当前事务的操作全部取消)。 MySqlTransaction tx = conn.BeginTransaction(); try { MySqlHelper.ExecuteNonQuery(conn, "Update t_accounts Set Amount=Amount-1000 where Number='0001'"); string s = null; s.ToLower(); MySqlHelper.ExecuteNonQuery(conn, "Update t_accounts Set Amount=Amount+1000 where Number='0002'"); tx.Commit(); } catch (Exception ex) { tx.Rollback(); }
SQLServer2008R2
1、ADO.Net如何连接SQLServer:SQLServer驱动.Net内置(亲生的);把MySqlConnection换成SqlConnection,MySql***换成Sql***。 2、连接字符串: server=ip;user id=sa;password=密码;database=db1 3、SQLHelper:把MySql查找替换成Sql就可以了。 4、获得自动增长列的值:Insert into t1(...) output inserted.Id values(.........) 5、(*)如果基于接口编程,只要改动CreateConnection就可以(查询参数以Dictionary<string,object>传递;如果使用Provider,连代码都不用改,改配置文件即可。 6、需要特别注意:SqlServer的事务和mysql事务使用有一点不一样的地方是“需要把BeginTransaction返回的SqlTransaction对象赋值给SqlCommand的Transaction属性”
SQLHelper
using System; using System.Collections.Generic; using System.Configuration; using System.Data; using System.Data.SqlClient; using System.Linq; using System.Text; using System.Threading.Tasks; namespace SQLHelper { class SQLHelper { private static readonly string connStr = ConfigurationManager.ConnectionStrings["heiyo"].ConnectionString; public static SqlConnection CreateConnection() { SqlConnection conn = new SqlConnection(connStr); conn.Open(); return conn; } public static int ExecuteNonQuery(SqlConnection conn, string sql, params SqlParameter[] parameters) { using (SqlCommand cmd = conn.CreateCommand()) { cmd.CommandText = sql; cmd.Parameters.AddRange(parameters); return cmd.ExecuteNonQuery(); } } public static int ExecuteNonQuery(string sql, params SqlParameter[] parameters) { using (SqlConnection conn = CreateConnection()) { return ExecuteNonQuery(conn, sql, parameters); } } public static object ExecuteScalar(SqlConnection conn, string sql, params SqlParameter[] parameters) { using (SqlCommand cmd = conn.CreateCommand()) { cmd.CommandText = sql; cmd.Parameters.AddRange(parameters); return cmd.ExecuteScalar(); } } public static object ExecuteScalar(string sql, params SqlParameter[] parameters) { using (SqlConnection conn = CreateConnection()) { return ExecuteScalar(conn, sql, parameters); } } public static DataTable ExecuteQuery(SqlConnection conn, string sql, params SqlParameter[] parameters) { DataTable table = new DataTable(); using (SqlCommand cmd = conn.CreateCommand()) { cmd.CommandText = sql; cmd.Parameters.AddRange(parameters); using (SqlDataReader reader = cmd.ExecuteReader()) { table.Load(reader); } } return table; } public static DataTable ExecuteQuery(string sql, params SqlParameter[] parameters) { DataTable table = new DataTable(); using (SqlConnection conn = CreateConnection()) { return ExecuteQuery(conn, sql, parameters); } } } }
varchar,不带中文,nvarchar带中文,自增主键用bigint
DBHelper通过接口编程使用ADO.Net感受多态的强大
using MySql.Data.MySqlClient; using System; using System.Data; using System.Collections.Generic; using System.Configuration; using System.Linq; using System.Text; using System.Threading.Tasks; using System.Data.SqlClient; namespace DBHelper { class DBHelper { private static readonly string connStr = ConfigurationManager.ConnectionStrings["a1"].ConnectionString; public static IDbConnection CreateConnection() { SqlConnection conn = new SqlConnection(connStr); // MySqlConnection conn = new MySqlConnection(connStr); conn.Open(); return conn; } public static int ExecuteNonQuery(IDbConnection conn, string sql,Dictionary<string,object> parameters) { using (IDbCommand cmd = conn.CreateCommand()) { cmd.CommandText = sql; foreach (KeyValuePair<string,object> kvp in parameters) { IDbDataParameter parameter = cmd.CreateParameter(); parameter.ParameterName = kvp.Key; parameter.Value = kvp.Value; cmd.Parameters.Add(parameter); } return cmd.ExecuteNonQuery(); } } public static int ExecuteNonQuery(string sql, Dictionary<string, object> parameters) { using (IDbConnection conn = CreateConnection()) { return ExecuteNonQuery(conn, sql, parameters); } } public static object ExecuteScalar(IDbConnection conn, string sql, Dictionary<string, object> parameters) { using (IDbCommand cmd = conn.CreateCommand()) { cmd.CommandText = sql; foreach (KeyValuePair<string, object> kvp in parameters) { IDbDataParameter parameter = cmd.CreateParameter(); parameter.ParameterName = kvp.Key; parameter.Value = kvp.Value; cmd.Parameters.Add(parameter); } return cmd.ExecuteScalar(); } } public static object ExecuteScalar(string sql, Dictionary<string, object> parameters) { using (IDbConnection conn = CreateConnection()) { return ExecuteScalar(conn, sql, parameters); } } public static DataTable ExecuteQuery(IDbConnection conn, string sql, Dictionary<string, object> parameters) { DataTable table = new DataTable(); using (IDbCommand cmd = conn.CreateCommand()) { cmd.CommandText = sql; foreach (KeyValuePair<string, object> kvp in parameters) { IDbDataParameter parameter = cmd.CreateParameter(); parameter.ParameterName = kvp.Key; parameter.Value = kvp.Value; cmd.Parameters.Add(parameter); } using (IDataReader reader = cmd.ExecuteReader()) { table.Load(reader); } } return table; } public static DataTable ExecuteQuery(string sql, Dictionary<string, object> parameters) { DataTable table = new DataTable(); using (IDbConnection conn = CreateConnection()) { return ExecuteQuery(conn, sql, parameters); } } } }
Like和In中查询参数的问题
Console.WriteLine("请输入要查询的姓名"); string name = Console.ReadLine(); //'%@Name%'是一个整体,无法“部分当成查询参数” //DataTable dt = SqlHelper.ExecuteQuery("select * from T_Persons where Name like '%@Name%'", //DataTable dt = SqlHelper.ExecuteQuery("select * from T_Persons where Name like '%'+@Name+'%'", //new SqlParameter("@Name",name)); DataTable dt = SqlHelper.ExecuteQuery("select * from T_Persons where Name like @Name", new SqlParameter("@Name","%"+name+"%")); foreach (DataRow row in dt.Rows) { Console.WriteLine(row["Name"]); } string line = Console.ReadLine(); foreach(char ch in line) { if(!char.IsDigit(ch)&&ch!=',') { Console.WriteLine("输入非法"); Console.ReadKey(); return; } } //DataTable dt = SqlHelper.ExecuteQuery("select * from T_Persons where Age in(@Age)", // new SqlParameter("@Age",line)); // DataTable dt = SqlHelper.ExecuteQuery("select * from T_Persons where Age in(" + line+")"); DataTable dt = SqlHelper.ExecuteQuery("exec('select * from T_Persons where Age in('+@Age+')')", new SqlParameter("@Age",line)); foreach (DataRow row in dt.Rows) { Console.WriteLine(row["Name"]+"="+row["Age"]); }
SQLServer中事物
using (SqlConnection conn = new SqlConnection(connstr)) { conn.Open(); using (SqlTransaction tx = conn.BeginTransaction()) { try { //using是保证资源一定会被回收的,离开using范围后自动调用Dispose方法(无论是否有异常) //using==try...finally... //using不会进行异常的catch using (SqlCommand cmd1 = conn.CreateCommand()) using (SqlCommand cmd2 = conn.CreateCommand()) { cmd1.Transaction = tx;//在SQLServer中必须把BeginTransaction返回的对象赋值给SqlCommand的 //Transaction属性 cmd1.CommandText = "insert into T_Persons(Name,Age) values('a',1)"; cmd1.ExecuteNonQuery(); cmd2.Transaction = tx; cmd2.CommandText = "insert into T_Persons(Name,Age) values('b',2)"; cmd2.ExecuteNonQuery(); } tx.Commit(); } catch (Exception ex) { tx.Rollback(); } } }
SqlBulkCopy 批量插入
DataTable dt = new DataTable(); dt.Columns.Add("Name"); dt.Columns.Add("Age"); for (int i = 0; i < 80000; i++) { DataRow row = dt.NewRow(); row["Name"] = "aa" + i; row["Age"] = i * i; dt.Rows.Add(row); } using (SqlBulkCopy bulkCopy = new SqlBulkCopy(connstr)) { bulkCopy.DestinationTableName = "T_Persons"; bulkCopy.ColumnMappings.Add("Name", "Name"); bulkCopy.ColumnMappings.Add("Age", "Age"); bulkCopy.WriteToServer(dt); }
MySQL的批量数据插入
Stopwatch sw = new Stopwatch(); sw.Start(); using (MySqlConnection conn = new MySqlConnection("server=127.0.0.1;database=study1;uid=root;pwd=root")) { conn.Open(); using (MySqlCommand cmd = conn.CreateCommand()) { //一条SQL数据的长度有限 //拼接出1000条数据就批量插入一次 List<string> listSql = new List<string>(); for (int i = 0; i < 100000; i++) { listSql.Add("('test"+i+"','"+i+"')"); if (listSql.Count > 1000) { string sql = "insert into t_users(UserName,Password) values" + string.Join(",", listSql); cmd.CommandText = sql; cmd.ExecuteNonQuery(); listSql.Clear(); } } if (listSql.Count > 0) { //处理残留的数据 string sql1 = "insert into t_users(UserName,Password) values" + string.Join(",", listSql); cmd.CommandText = sql1; cmd.ExecuteNonQuery(); } } } sw.Stop(); Console.WriteLine(sw.ElapsedMilliseconds);
-------------------------------------------
个性签名:独学而无友,则孤陋而寡闻。做一个灵魂有趣的人!
如果觉得这篇文章对你有小小的帮助的话,记得在右下角点个“推荐”哦,博主在此感谢!
万水千山总是情,打赏一分行不行,所以如果你心情还比较高兴,也是可以扫码打赏博主,哈哈哈(っ•̀ω•́)っ✎⁾⁾!