[Day3] SQL Server資料型態 - SQL Server資料庫入門

為了能夠讓每種資料有比較好的儲存方式,所以這回來看一下SQL server內建哪些資料型態。
我們平常常見資料可能會有日期、數字、文字等等。
爬了很多文後發現SQL Server的資料型態真的爆多,所以先列舉,未來如果有用到會陸續回來修補這篇文(X)。

整數

資料型別 位元數 資料範圍
tinyint  1 Byte 0 ~ 2^8 -1
( 0 ~ 255 )
smallint  2 Bytes -2^15 ~ 2^15-1
( -32738 ~ 32767 )
int  4 Bytes

-2^31 ~ 2^31 -1
( -2147483648 ~ 2147483467)

bigint  8 Bytes -2^63~2^63 -1
(-9223372036854775808 ~ 9223372036854776807 )
bit  1 Byte  0 , 1 , Null

精確位數與浮點數

資料型別 位元數 資料範圍 備註
numeric 視精確度而定 -10^38 +1 ~ 10^38 -1
decimal 視精確度而定 -10^38 +1 ~ 10^38 -1 如果要存有小數點的錢、金額請使用decimal,不要使用float,避免造成誤差
decimal(p,s) 需要分別指定小數的最大位數(p)和小數位的數量(s)。 p (precision) :指定小數的最大位數,小數點的左側和右側的數字的總數量不能超過p,p的取值範圍是從1到38,默認值爲18。 s (scale):指定在小數點右側的小數位數,p-s是小數點左邊的最大位數。s必須是從0到p的值,只有在指定了精度的情況下才能指定s,s的默認值是0,因此,0 <= s <= p。
float  8 Bytes  -1.79E+308 ~ 1.79E+308
15 位數
不要使用float存小數點的錢,避免造成誤差。他適合儲存科學用的數字。
real  4 Bytes
-3.40E+38 ~ 3.40E+38
  7 位數

字串 - Unicode 字串 - Binary 字串

資料型別 位元數 資料範圍 備註
 char(n) 1字元 1Byte  1 ~ 8000 字元 輸入的數據長度沒有達到指定的長度時將自動以英文空格在其後面填充,讓長度達到相對應的長度。
 varchar(n) 1字元 1Byte  1 ~ 8000 字元
輸入的數據長度沒有達到指定的長度時不會在後面填充空格。
 varchar(max) 變動長度 max=2GB  1 ~ 2^31 -1 字元
輸入的數據長度沒有達到指定的長度時不會在後面填充空格。
 text 變動長度 max=2GB  1 ~ 2^31 -1 字元
 nchar(n) 1字元 2Byte  1 ~ 4000 字元 所有的文字都用2 Byte來儲存
輸入的數據長度沒有達到指定的長度時將自動以英文空格在其後面填充,讓長度達到相對應的長度。
 nvarchar(n) 1字元 2Byte  1 ~ 4000 字元 所有的文字都用2 Byte來儲存
輸入的數據長度沒有達到指定的長度時不會在後面填充空格。
 nvarchar(max) 1字元 2Byte
變動長度 max=2GB
 1 ~ 2^30 -1 字元 所有的文字都用2 Byte來儲存
輸入的數據長度沒有達到指定的長度時不會在後面填充空格。
 ntext 1字元 2Byte
變動長度 max=2GB
 1 ~ 2^30 -1 字元
 binary(n) 固定長度 8000 Bytes,不足自動補上 0x00
 1 ~ 8000 Bytes
 varbinary(n) 變動長度  1 ~ 8000 Bytes
 varbinary(max) 變動長度 max=2GB  1 ~ 2^31 -1 Bytes
 image 變動長度 max=2GB  1 ~ 2^31 -1 Bytes

日期 - 時間 - 貨幣 - Timestamp

資料型別 位元數 資料範圍
datatime  8 Bytes 1753/1/1 ~ 9999/12/31
ex: 2008-11-27 08:08:08.888
datatime2 6 bytes for precision less than 3.
7 bytes for precision 3 or 4.
All other precision require 8 bytes.2
0001-01-01 ~ 9999-12-31
smalldatatime  4 Bytes
1900/1/1 ~ 2079/6/6
ex: 2008-11-24 15:11
money  8 Bytes  -2^63 ~ 2^63 -1 小數4位
( -922337203685477.5808 ~ 922337203685477.5807)
smallmoney  4 Bytes
-2^31 ~ 2^31 -1 小數4位 
( -214748.3648 ~ 214748.3647) 
timestamp  8 Bytes 8 Bytes 的  16 進位值
uniqueidentifier  16 Bytes
16 Bytes 的 16 進位值

結語

這回以SQL Server的資料型別列舉出資料範圍,對於日後再選擇時就能稍微留意空間大小、資料的範圍等規劃問題。
自己在使用上則是字串nvarchar,整數int、科學數字用浮點數float、有小數的金額用decimal避免誤差、日期datetime2,就能解決大多問題。
而SQL Server沒有MySQL的boolean型態,要改用0、1去存,可以考慮tinyint。
而在不同時區的資料則是統一都用一個標準去儲存日期UTC時間,如果有機會未來會在寫一篇跨國時間可能會遇到的問題。

參考資料
https://docs.microsoft.com/zh-tw/sql/t-sql/data-types/data-types-transact-sql?view=sql-server-ver15
https://ithelp.ithome.com.tw/articles/10203456
https://blog.xuite.net/tolarku/blog/20968398-SQL+Data+Type+%E8%B3%87%E6%96%99%E5%9E%8B%E5%88%A5
https://docs.microsoft.com/en-us/sql/t-sql/data-types/datetime2-transact-sql?view=sql-server-ver15
https://ithelp.ithome.com.tw/articles/10213922
https://ithelp.ithome.com.tw/articles/10203456?sc=iThelpR
https://malagege.github.io/blog/2018/11/09/DB%E5%88%B0%E5%BA%95%E6%9C%89%E6%B2%92%E6%9C%89boolean%E5%9E%8B%E6%85%8B%E5%91%A2/

C# Dictionary字典操作

這次練習使用C#的字典資料結構。

Dictionary

字典型別,一個Key對上一個Value,一對一關係。
像是查字典,你查某個單字,會給你他的解釋。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
using System;
using System.Collections.Generic;

public class Program
{

public static void Main()
{
Dictionary<string, string> dictionary = new Dictionary<string, string>( ); // 建立字典key:string,value:string
dictionary.Add("LastName","Lin");
dictionary.Add("FirstName","Yu-Hsiang");

// 從字典中取出對應值
Console.WriteLine("FirstName:" + dictionary["FirstName"]);
Console.WriteLine("LastName:" + dictionary["LastName"]);

// 遍歷字典
foreach ( var item in dictionary )
{
Console.WriteLine( "Key = " + item.Key + ", Value = " + item.Value );
}
}
}

輸出

1
2
3
4
FirstName:Yu-Hsiang
LastName:Lin
Key = LastName, Value = Lin
Key = FirstName, Value = Yu-Hsiang

結語

有一對一關係的資料,可以利用字典去整理,就能不用二維陣列去做,資料也比較清楚。
如果有重複的Key值,在添加時也會跳出例外事件。例:An item with the same key has already been added.

參考資料
http://code2study.blogspot.com/2012/01/c-dictionary.html
https://ithelp.ithome.com.tw/articles/10234391
https://docs.microsoft.com/en-us/dotnet/api/system.collections.generic.dictionary-2?view=net-5.0

C# class宣告及使用

在C++後有很多物件導向(Object-oriented programming,OOP)的語言竄起,如JAVA、VB、C#等等。
未來可能會以C#為主要語言,所以就來練習一下C#的類別宣告吧。

物件導向程式設計

是種具有物件概念的程式設計典範,同時也是一種程式開發的抽象方針。它可能包含資料、特性、程式碼與方法。物件則指的是類別(class)的實例。物件導向程式設計可以看作一種在程式中包含各種獨立而又互相呼叫的物件的思想,這與傳統的思想剛好相反:傳統的程式設計主張將程式看作一系列函式的集合,或者直接就是一系列對電腦下達的指令。

物件導向程式語言包含Common Lisp、Python、C++、Objective-C、Smalltalk、Delphi、Java、Swift、C#、Perl、Ruby、JavaScript(雖然javascript通常都用function在寫,但ES6有class) 與 PHP等。

C# class宣告及使用

透過人(Person)的類別class,宣告一個人的實例。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
using System;

public class Program
{
public class Person{

private string name; // 內部私有的name變數

public string Name {
get{return name;} // getter 取出私有的name變數
set{this.name = value;} // setter 將value設定到私有的name變數
}

public Person(){ // 建構子
this.name = "沒有名子";
}

public Person(string name){ // 多載(Overload)的建構子,可初始化name
this.name = name;
}

}

public static void Main()
{
Person person= new Person();// 宣告一個人的實例
Console.WriteLine(person.Name);// 透過getter取出變數
person.Name = "Yu Hsiang"; // 透過setter設定變數
Console.WriteLine(person.Name); // 透過getter取出變數
}
}

輸出

1
2
沒有名子
Yu Hsiang

結語

為什麼要用getter、setter?
為了避免誤改到變數及維護性的需求。
比如,未來想要加一個*在每個人名前面,這時如果沒有setter、getter,就得改每一行的程式碼。
如果有setter、getter就像有一個緩衝的函式,可以讓我們直接在這裏面修正好值再存入。
當然,也有人不寫getter或setter,也是能運作,見仁見智,符合本身團隊需求就可以了。

C#有點簡化了getter、setter,以前在寫C++時都會寫成setName、getName,再回傳本身指標,就能無限點下去。
https://github.com/yuhsiang237/C-plusplus-homework/blob/master/HomeworkBoss/final/2/2/Place.cpp

這練習想起了大學的C++作業:
https://github.com/yuhsiang237/C-plusplus-homework

參考資料
https://zh.wikipedia.org/wiki/%E9%9D%A2%E5%90%91%E5%AF%B9%E8%B1%A1%E7%A8%8B%E5%BA%8F%E8%AE%BE%E8%AE%A1
https://docs.microsoft.com/zh-tw/dotnet/csharp/fundamentals/types/classes
https://www.w3schools.com/cs/cs_properties.php

[Day2] 主索引鍵與外部索引鍵,及其他關係鍵 - SQL Server資料庫入門

在資料庫的資料表中,我們常藉「主索引鍵(Primary Key,PK,主鍵)、外部索引鍵(Foreign Key,FK,外來鍵)」,對不同的表進行關聯,最後串起整個資料庫。

主索引鍵(Primary Key)

在一張資料表中,
唯一性且不允許重複。

外部索引鍵(Foreign Key)

在一張資料表中,
外部索引鍵並不一定要唯一。
外部索引鍵是另一個資料表的主索引鍵。

範例

以上是資料表主鍵、外來鍵的關係。

在客戶資料表中,id是主鍵。
在產品資料表中,id是主鍵。
在訂單詳情資料表中,id是主鍵;客戶id、產品id分別是外來鍵,他們各自是客戶資料表中的主鍵、產品資料表中的主鍵。

舉例:
如我想找某筆訂單中的客戶是誰,就能夠用訂單裡的客戶id去客戶資料表中找到對應的客戶。

補充

超鍵(superkey)

是指在一個資料表中,選出兩個或兩個以上的欄位組合起來,以作為唯一識別資料的欄位。

候選鍵(candidate key)

這個屬性集合始終能夠確保在關係中能唯一標識元組
在這個屬性集合中找不出真子集能夠滿足條件。

代理鍵(surrogate key)

是在當資料表中的候選鍵都不適合當主鍵時,例如資料太長,或是意義層面太多,就會請一個無意義的但唯一的欄位來代為作主鍵。

次要鍵(Secondary Key)

主要的功能是提供資料索引。資料庫系統中,除了主鍵這個唯一的識別值,可以用做索引外,也可以設定其他欄位用作資料索引,這類的鍵值並不是主鍵,但因為資料內容並不會重複,所以也可作為資料索引,這就是次要鍵。在員工資料表中,員工姓名、到職日期等欄位都沒有重複,就可以做為次要鍵,提供不同於主鍵的資料索引。

複合鍵(Composite Key)

是指資料表中的主鍵,是由兩個或兩個欄位以上所組成,這種主鍵稱為複合鍵(Composite Key)。
當表格中某一欄位的值無法區分資料記錄時可以使用此方法。

結語

在整理過各種鍵後,理解到各種鍵的使用時機。(未來可能在資料庫正規化方面能夠更加順手。)
而以自身經驗來說,實際設計過的資料表中通常只會用到主鍵、外來鍵居多,其餘的特殊鍵則比較少,甚至沒有用到的情況。

參考資料:
https://ithelp.ithome.com.tw/articles/10213896
https://rileylin91.github.io/2020/05/29/MSSQL-9-Primary-Foreign-Key/
https://zh.wikipedia.org/wiki/%E5%85%B3%E7%B3%BB%E9%94%AE
https://www.1keydata.com/tw/sql/sql-foreign-key.html
https://www.ithome.com.tw/node/46156

[Day1] SQL Server 安裝 - SQL Server資料庫入門

前言

因為工作與後端有關連,不免需要操作資料,而這些資料的儲存體就是資料庫。
網路上有不同種的SQL,如MySQL、MongoDB、SQL Server、Oracle、PostgreSQL…等等。
在未來工作上可能以關聯式資料庫(RDBMS)居多,因此以SQL Server當作練習的修練場。

在此之前需要下載:

  1. SQL Server Express:微軟的資料庫,沒有介面。
    https://www.microsoft.com/en-us/sql-server/sql-server-downloads
  2. SQL Server Management Studio:透過介面式管理SQL Server Express資料庫的工具。(約6G空間)
    https://docs.microsoft.com/en-us/sql/ssms/download-sql-server-management-studio-ssms?view=sql-server-ver15

下載安裝後:

結語

這系列預計會從「資料表建立>SQL語法>進階語法」這樣前進。

目標:是練過一輪基礎SQL語法、SQL進階用法、釐清資料表設計的方式。

基本上會跟這篇 Challenge參考資料雷同,但是會加一些自己不同的見解XD。
https://ithelp.ithome.com.tw/articles/10219497

然後,也會藉由這些主題,逐漸擴充實戰經驗累積的資料。

參考資料
https://ithelp.ithome.com.tw/articles/10219497

C#亂數

前言

亂數(Random)是在程式語言中常見的內建類別。
如陣列洗牌、模擬骰子等等都會常用到,所以就來寫一下他的用法。

Random

1.Next用法
32 位元帶正負號的整數大於或等於 0,並且小於 MaxValue。

1
2
3
4
5
6
7
8
9
10
11
using System;

public class Program
{
public static void Main()
{
Random r = new Random(); //宣告亂數種子
int result = r.Next(1,7); // 可能回傳1~6的整數數字,因為Next第二個參數是小於,以此例為小於7
Console.WriteLine(result); // 可能回傳1~6的整數數字
}
}

輸出結果

1
2

2.Random洗亂陣列
以array長度洗牌,隨意骰出符合陣列的兩個index,比較是否相同,不相同就將其交換。
最後得到一個洗牌後陣列。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
using System;

public class Program
{
public static void Main()
{
Random r = new Random(); //宣告亂數種子

int [] array = new int[5]{1,2,3,4,5}; // 宣告陣列並初始化

for(int i=0;i<array.Length;i++){
int index1 = r.Next(0,array.Length);
int index2 = r.Next(0,array.Length);
if(index1 != index2){ // 交換陣列內容
int temp = array[index1];
array[index1] = array[index2];
array[index2] = temp;
}
}
Console.WriteLine("陣列洗牌後結果:");
foreach(int item in array){
Console.WriteLine(item);
}
}
}

輸出結果

1
2
3
4
5
6
陣列洗牌後結果:
2
4
1
3
5

C# Queue與Stack操作

前言

為了熟悉C#處理資料的方式,所以找了兩種比較常見的資料結構來練習。
分別是Queue(佇列)、Stack(堆疊)。

Queue

先進先出

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
using System;
using System.Collections;

public class Program
{
public static void Main()
{
Queue q = new Queue();
q.Enqueue("hello"); // 向 Queue 的末尾新增一個物件。
q.Enqueue(32);
q.Enqueue(5);
q.Enqueue('N');

Console.WriteLine("印出Queue的內容:");
foreach(var item in q){
Console.WriteLine(item);
}

Console.WriteLine("取出一個資料:");
var result = q.Dequeue(); // 從 Queue 的前端取出一個資料
Console.WriteLine(result);

Console.WriteLine("轉成陣列印出:");
var array = q.ToArray();
foreach(var item in array){
Console.WriteLine(item);
}
}
}

輸出:

1
2
3
4
5
6
7
8
9
10
11
印出Queue的內容:
hello
32
5
N
取出一個資料:
hello
轉成陣列印出:
32
5
N

Stack

先進後出

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
using System;
using System.Collections;

public class Program
{
public static void Main()
{
Stack s = new Stack();
s.Push("hello"); // 向 Stack 的頂部新增一個物件。
s.Push(32);
s.Push(5);
s.Push('N');

Console.WriteLine("印出Stack的內容:");
foreach(var item in s){
Console.WriteLine(item);
}

Console.WriteLine("取出一個資料:");
var result = s.Pop(); // 移除並返回在 Stack 的頂部的物件。
Console.WriteLine(result);

Console.WriteLine("Peek觀察頂部物件但不移除:");
result = s.Peek(); // Peek觀察頂部物件但不移除
Console.WriteLine(result);

Console.WriteLine("轉成陣列印出:");
var array = s.ToArray();
foreach(var item in array){
Console.WriteLine(item);
}
}
}

輸出:

1
2
3
4
5
6
7
8
9
10
11
12
13
印出Stack的內容:
N
5
32
hello
取出一個資料:
N
Peek觀察頂部物件但不移除:
5
轉成陣列印出:
5
32
hello

結語

C#除了已經內置了基礎的佇列(Queue)、堆疊(Stack),還內建了一些簡易的語法。
如Contains,可以對整個資料結構去檢查是否存在某個特定元素。亦有toArray()可以快速的把資料結構轉為陣列型態。

參考資料
https://www.itread01.com/content/1550138429.html?fbclid=IwAR3ty4HR0DlmSWRMl19IFBgESeXM2aH5GsJxxcKHMGdzY6WIJm90brUzsvI

javascript event loop機制(未完)

前言

javascript是單一執行序,所以一次只能執行一件事。我們網頁上的操作也都會影響這機制,造成效能不好(時間延誤)。
而這其實很難用文字去解釋,我們可以看這支影片。
https://www.youtube.com/watch?v=8aGhZQkoFbQ

小結

1.盡量不要在 callback 當中執行負擔過重的函數,避免佔據 call stack
2.理解 microtask 與一般 task queue 執行順序不同。
--引用自參考資料

雖然理解底層跟實際面操作其實並不會有什麼大改變,因為還是得寫。
但至少可以透過這些底層觀念,讓我們思考有些程式碼其實有更好的寫法,就能減少這種效能問題。
(雖然使用者本身就只會覺得是網路lag而已)

其實還有microtask未補,待下次補完。

參考資料
https://ithelp.ithome.com.tw/articles/10214017
https://www.youtube.com/watch?v=8aGhZQkoFbQ

javascript控制時間setTimeout、setInterval

javascript在處理網頁上”連續執行”、”只執行一次”必見到的時間控制函式setTimeout、setInterval。

setTimeout

延遲某一段時間後才執行一次,每個setTimeout都可以得到一個timer ID。
單位:毫秒

1
2
3
var timeoutID = setTimeout(function[, delay, arg1, arg2, ...]);
var timeoutID = setTimeout(function[, delay]);
var timeoutID = setTimeout(code[, delay]);

用法:

1
const timeroutID = window.setTimeout((()=>{console.log('Hello world')}),1000);

Q:如果要清除setTimeout呢?
利用clearTimeout

用法:

1
2
const timeroutID = window.setTimeout((()=>{console.log('Hello world')}),1000);
window.clearTimeout(timeroutID);

setInterval

延遲時間後,不斷循環某段程式碼,每個setInterval都可以得到一個interval ID。
單位:毫秒

1
2
3
var intervalID = setInterval(func, [delay, arg1, arg2, ...]);
var intervalID = setInterval(function[, delay]);
var intervalID = setInterval(code, [delay]);

用法:

1
const intervalID = window.setInterval((()=>{console.log('Hello world')}),1000);

Q:如果要清除setInterval呢?
利用clearInterval

用法:

1
2
const intervalID = window.setInterval((()=>{console.log('Hello world')}),1000);
window.clearInterval(intervalID);

小結

「雖然 JavaScript 有著非同步事件的特色,但仍是依循單一執行緒的規則運作。 換句話說,當我們在主要執行緒內工作的時間太久,就勢必會延遲 Queue Callback 的執行。」--引用參考資料內文

所以,要計算非常精準的時間,setTimeout、setInterval,都一定會有誤差,可以用server端的正確時間去修正誤差,讓他至少在一個合理的範圍內。

此外,仍有IIFE(Immediately Invoked Function Expression)、scope問題,待補。

參考資料:
https://kuro.tw/posts/2019/02/23/%E8%AB%87%E8%AB%87-JavaScript-%E7%9A%84-setTimeout-%E8%88%87-setInterval/
https://developer.mozilla.org/en-US/docs/Web/API/WindowOrWorkerGlobalScope/setInterval

C#陣列常見操作

因為每種程式語言都有它特殊的宣告方式,因此就花時間整理一下,往後需要使用時就能快速的熟悉回來。

陣列宣告

1.一維陣列

1
2
int [] array = new int[5];
Console.WriteLine(array[0]); // 輸出:0 陣列的元素會初始化為整數的元素類型預設值 0 。

2.二維陣列

1
2
int [,] array = new int[4,2];
Console.WriteLine(array[1,1]); // // 輸出:0 陣列的元素會初始化為整數的元素類型預設值 0 。

陣列初始化

1.一維陣列

1
2
int [] array = new int[]{1,2,3,4,5};
Console.WriteLine(array[0]); // 輸出:1

2.二維陣列

1
2
int [,] array = new int[4,2]{{1,2},{3,4},{7,5},{4,8}}; // 在宣告時如果有初始值後面就必須要填滿那些範圍的元素
Console.WriteLine(array[2,1]); // 輸出5

3.不規則陣列
感覺很少用到

1
2
3
4
5
int[][] array = new int[3][]; // 宣告時必須int[][] 而不是 int[,]
array[0] = new int[]{1,3};
array[1] = new int[]{5,3,3};
array[2] = new int[]{1,31,5};
Console.Write(array[2][1]);//輸出 31

4.隱含型別陣列
用var開頭,裡面的元素符合同一個型態即可

1
2
var array = new [] {"hi!",null,"world"};
Console.Write(array[2]);//輸出 world

遍歷陣列方式

1.使用for,如果處理多維較為方便,且比較直覺

1
2
3
4
int [] array = new int[]{1,2,3,4,5};
for(int i=0;i<array.Length;i++){
Console.Write(array[i]);
} // 輸出:12345

2.foreach,如果是單維想簡潔些可以用

1
2
3
4
int [] array = new int[]{1,2,3,4,5};
foreach(int num in array){
Console.Write(num);
} // 輸出:12345

把陣列傳入函式

1
2
3
4
5
6
7
8
9
10
11
12
13
14
using System;			
public class Program
{
public static void printArray(int [] arr){
for(int i=0;i<arr.Length;i++){
Console.Write(arr[i]);
}
}
public static void Main()
{
int [] array = new int[]{1,2,3,4,5};
printArray(array); // 輸出12345
}
}

結語

C#強型別的語言在宣告陣列確實比較麻煩。但相對的,這種寫法比較好”除錯”。
如果平時寫弱型態的如javascript,那要多記一下寫法。
而既然使用了”new 運算子”就是傳址了,要稍微小心寫法。

參考資料
https://docs.microsoft.com/zh-tw/dotnet/csharp/programming-guide/arrays/