sql|sql server数据库如何存储数组,int[]float[]double[]数组存储到数据库方法

原文地址:https://www.zhaimaojun.top/Note/5475296 将数组存储到数据库的方法 (本人平时同csharp编写代码,所以本文中代码都是csharp代码,有些地方java和csharp有所不同,文中会有提示) 方法一: 现在的电脑或者手机代码运行速度已经相当快了,而且各种语言,尤其是python,csharp,java等解析类语言的运行效率也大大提升,所以对字符串的操作很简单快速了,所以很多人都喜欢将数据作为字符串进行操作。本方法就是将数组拼接后存储到数据库。基本的代码逻辑如下:

var source = new int[100]; //new double[100]; //new string[100]; //等等各种类型的数组 var result = string.Empty; //最后要存储到数据库中的字符串 foreach(var item in source){result += item + ","; //这里要注意,一般都用,号,当然不限于,号,但是要注意,这个符号不能出现在item中,一般情况下,int,dobule,float,等等数组元素tostring之后都不会出现,号,但是string类型的数组很有可能就会包含逗号,所以,需要用一个确保不会出现在item中的符号来作为每个元素的分隔符,这样才能在下次读取数据后能重新分割,分开每个数组成员而不会出错! } new SqlCommand("insert into XX(XX) value(" + result + ")",new SqlConnection("XX")).ExectueNoQuery(); //将字符串类型的数据插入数据库

当需要将数据读取出来时(反序列化):
var sourcestring = "XXX,XXX,XXX,XXX"; //从数据库读出的数据 var sourcedatas = sourcestring.Split(','); //当初用了什么符号拼接的就用什么符号分割! var result = new int[sourcedatas.Lenth]; //new double[]; //new string[]; //等等你当初存进去时的数组类型 for(int i = 0; i < sourcedatas.Lenth; i++){if(string.IsNullOrEmpty(sourcedatas[i])) continue; result[i] = int.TryParse(sourcedatas[i], out var v) ? v : default; //double.TryParse//按照当初存进去时的数据类型反向解析出原始数据!如果是string类型的,这个for循环不必要,sourcedatas就是result。 }

此方法的优缺点:
易读,数据库管理员查库的时候可以直观的看到数据内容,因为都是string,直接可以显示,而且对于很小的标号,比如999以下的标号存储来说,每个标号加一个分隔符都是2-4字符,占用空间还是比较少的。缺点当然也很明显,对于string数组,分隔符不好取,对于double,long类型的数组,如果数据量很大时,就非常浪费空间,因为每个元素可能要占用十多个字符。
方法二: 在这个方法之前先讲一下数据库中的一种数据类型:binary/varbinary:
这是一种将数据按照8bit直接进行存储的数据类型,数据内容不限制,可以是任何形式的数据。最大空间为8000*8bit。
如果对数据有所了解的同学应该知道,计算机中的所有数据都是binary(二进制)形式存储的,不管是手机还是电脑,只不过数据的意义有所不同。
一般来说,1bit就是1个二进制,8bit就是8个二进制,常常我们将8个二进制用byte表示一个字符,16个二进制用short表示,32个二进制用int表示,64个二进制用long表示,当然这是整形的情况,还有float,是32bit来表示一个float数,用64bit来表示double,等等,这些其实都是计算机的基本知识。
而数组,他其实就是连续存放的一堆数据,只不过给他进行了按大小的分组而已。比如,int[8];他的大小其实就是8个int,也就是8*4个byte,也就是8*4*8个bit,所以我们要把这些数据存储到数据库中,其实基本上就是直接内存拷贝就可以了,但是数据库他不支持内存拷贝啊~~
数据库也没有int[]这种数据类型啊,那怎么办呢,其实很简单,将int[]转化成byte[],再传递给数据库就可以了,数据库中的数据类型binary(8000)/varbinary(8000)就是为此而生的。
但是这里需要考虑一个历史遗留问题:大小端模式问题,不同的操作系统中对数据保存时,大小端可能是不同的,不能盲目的用位移操作和强制类型转换可能导致数据错误的情况,即:int a = 888; byte[] b = new byte[2]; b[0] = (byte)(a>>8); b[1] = (byte)a; 这种序列化后反向序列化时,可能导致数据出错的情况。
还要考虑语言的问题,java环境中对数据的正负要求非常严格,如果代码不正确,序列化和反序列化时可能导致数据的异常,这就是考验程序员能力的时候了。
下面看序列化过程:(将int数组转为byte数组)
var values = new int[100]; //new double[100]; //new float[100]; //一些基本的数组类型,但是不能为string,除非所有的string都在GetBytes之后等长,如果不等长,反序列化时会造成困难,当然,也不是不行,在这部分代码中不行,下面还有一种方法,用于介绍不等长string的存储。 var result = new byte[values.Length * 8]; //byte类型的需要存储到数据库中的结果数据 for (int i = 0; i < values.Length; i++) { var bts = BitConverter.GetBytes(values[i]); //这里不同的数据类型他的长度是不一样的。需要注意 Array.Copy(bts, 0, gds, i * bts.Lenth, bts.Lenth); //这里的长度要注意,不是固定的8,应该用bts.Lenth为准!不同的数据类型转化为byte后长度是不同的!但是所有的相同数据类型的数据转化后的byte是相同的,比如:int a=9; int b=900999;他们都是int,所以转化成byte后都是4个byte。(4个byte就不要抬杠了,如果非要抬杠,请自己换成sizeof(int)去计算长度)再比如,int a=9; double b = 9; 他们是不同的数据类型,转化后a的长度是4,而b的长度是8,他们是不一样的! }

在看反序列化过程:(将数据库中取出来的byte转化成int数组)
var soucedata= https://www.it610.com/article/new byte[8000]; //数据库中读取出来的binary类型的数据 var result = new int[soucedata.Length / sizeof(int)]; //这里数据类型的就是当初存储时的原始类型,一般int的大小是4,如果不确定,可以用sizeof计算,不同的类型这里的类型是不同的 for (int i = 0; i < soucedata.Length; i++) { result[i] = BitConverter.ToInt(soucedata[i],i*sizeof(int)); //这里就需要考虑大小端的问题,BitConverter默认是小端模式,你可以自己百度如何设置大小端。 }

此方法的优缺点:
对于不等长不规则的数组无能为力,数据的不直观,数据库管理者看到的都是一堆16进制的码,易读性差,几乎不可改错,但是,对大数据的存储效果相当可观,能大大降低存储的数据量,而且几乎是将数据进行了内存拷贝,对于数据的使用来说提高了效率。
方法三: 这是基于方法二的一种扩展方法,针对一些不规则数组,比如string[]; struct等,与方法二同样,都是使用数据库的binary/varbinary数据类型存储数据的,主要解决了方法二中数据不能可变长度的问题。
下面看序列化过程:(不规则数租转化为byte数组)
var sourcedata = https://www.it610.com/article/new string[]{"ff", "aaaa", "dddddddddddd"}; //不规则长度的字符串数组 var ms = new MemoryStream(); //用到了一个流,便于操作byte foreach(var item in sourcedata){ var bt = Encoding.Utf8.GetBytes(item); //这是一种字符的编码形式,编码方式决定了反序列化后看到的内容是不是乱码,所以很关键!一般网络上都用的utf-8编码,这种编码支持多国语言,但是只是支持常用的词,有些生僻词是不支持的,根据自己的使用环境自己决定编码方式。 var lbt = BitConverter.GetBytes((int)bt.Lenth); //这里用4个字符来表示这个可变长度数组内容的长度, ms.Write(lbt,0,lbt.Lenth); //先写入4字节的长度, ms.Write(bt,0,bt.Lenth); //再写入对应长度的内容, } ms.Seek(0,SeekOrigin.Begin); //这里是必须的,为了让ms流回到起点,否则后面的toarray会返回0字节 var result = ms.ToArray(); //这里就得到了要写入到数据库的所有内容,当然,这里注意,result的大小要在8000以内,否则存储不进去的。

再看反序列化过程:(byte数组还原为数组)
var sourcedata = https://www.it610.com/article/new byte[8000]; //从数据库中读出的原始数据 var resultlist = new List(); //将要转换为的不规则数据的类型的数组 var ms = new MemoryStream(sourcedata); do{ var bt = new byte[sizeof(int)]; ms.Read(bt,0,bt.Lenth); var l = BitConverter.ToInt(bt,0); //获取不规则数据的长度 bt = new byte[l]; ms.Read(bt,0,bt.Lenth); resultlist.Add(Encoding.Utf8.GetString(bt)); //这里要使用存入数据库时使用的编码形式去转化,否则得到的结果可能都是乱码 } while(ms.Position < ms.Lenth); var result = resultlist.ToArray(); //得到了之前的不规则数据

此方法的优缺点:
【sql|sql server数据库如何存储数组,int[]float[]double[]数组存储到数据库方法】这种方法可以对批量的字符串数组进行存储,能够很好的将不规则不等长的数组进行存储,补足了方法二中的缺点。但是我写的列子是对string[]进行存储的,没有写对其他类型的比如对象的属性变量等进行存储的,如果想要将某个对象进行存储,其实这个方法进行一定的变化就可以了,利用反射,获取对象的属性和变量,就可以进行存储了。代码就不贴了,如果有人想要就直接联系我。一般没几个人想要的。

    推荐阅读