sql server資料庫如何儲存陣列,int[]float[]double[]陣列儲存到資料庫方法

宅貓君發表於2022-03-15

原文地址: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= 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 &lt; ms.Lenth);
var result = resultlist.ToArray();//得到了之前的不規則資料

此方法的優缺點:

這種方法可以對批量的字串陣列進行儲存,能夠很好的將不規則不等長的陣列進行儲存,補足了方法二中的缺點。但是我寫的列子是對string[]進行儲存的,沒有寫對其他型別的比如物件的屬性變數等進行儲存的,如果想要將某個物件進行儲存,其實這個方法進行一定的變化就可以了,利用反射,獲取物件的屬性和變數,就可以進行儲存了。程式碼就不貼了,如果有人想要就直接聯絡我。一般沒幾個人想要的。

相關文章