原文地址:https://www.zhaimaojun.top/Note/5475296
將數組存儲到數據庫的方法
方法一:
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= 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 = 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 = new byte[8000];//從數據庫中讀出的原始數據 var resultlist = new List<string>();//將要轉換為的不規則數據的類型的數組 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();//得到了之前的不規則數據
此方法的優缺點:
這種方法可以對批量的字符串數組進行存儲,能夠很好的將不規則不等長的數組進行存儲,補足了方法二中的缺點。但是我寫的列子是對string[]進行存儲的,沒有寫對其他類型的比如對象的屬性變量等進行存儲的,如果想要將某個對象進行存儲,其實這個方法進行一定的變化就可以了,利用反射,獲取對象的屬性和變量,就可以進行存儲了。代碼就不貼了,如果有人想要就直接聯系我。一般沒幾個人想要的。