PostgreSQL資料型別

前言

因為最近有碰PostgreSQL的需要,因此來整理儲存型別。
此外,發現PostgreSQL的欄位型態具有Alias別名,所以有些型別找不到就要參考文件的Alias。
在以下只列常見的型別,更多請自己參閱參考資料。

常見資料型別

字串

名稱 描述 備註
character varying(n), varchar(n) 可變長度,但有限制 其中 n 是正整數。可以長度最多為 n 個字元(不是位元組)的字串。
character(n), char(n) 固定長度,空白填充 其中 n 是正整數。可以長度最多為 n 個字元(不是位元組)的字串。
text 可變且無限長度

數字

名稱 描述 備註
smallint 2 bytes ,-32768 to +32767
integer 4 bytes,-2147483648 to +2147483647
bigint 8 bytes,-9223372036854775808 to +9223372036854775807
numeric up to 131072 digits before the decimal point; up to 16383 digits after the decimal point。可調式精確度數值型別 Alias decimal
real 4Bytes,6 decimal digits precision 非精度浮點數型別
double precision 8Bytes,15 decimal digits precision 非精度浮點數型別

布林

名稱 描述 備註
boolean 1Bytes

日期時間

名稱 描述 備註
timestamp [ (p) ] [ without time zone ] 8 bytes, both date and time (no time zone), 4713 BC ~ 294276 AD
timestamp [ (p) ] with time zone 8 bytes, both date and time, with time zone, 4713 BC ~ 294276 AD

總結

在這篇中以一個熟悉SQL Server的角度,列出了平常可能會見到的PostgreSQL資料型態。
因為後者的資料型別彈性高(能夠自己建立物件存進、支援儲存JSON等等),所以等後面實際碰到再額外補充。
而完整的型別請自行參考網址的參考資料吧!

可能會有人有疑問怎沒SQL Server的NVARCHAR?
Generally you just use a UTF-8 encoded DB, and everything works with no hassle.

參考資料

SQL Server NVARCHAR與VARCHAR差別比較

前言

在開資料時在存字串常會有選擇上的問題,到底該存哪種型別?
而在此就稍微介紹NVARCHAR、VARCHAR的差異。
以SQL Server 2019資料庫為準。

NVARCHAR [ ( n | max ) ]

n表示可變大小字串資料。 n 會定義字串大小 (單位為位元組配對),且可以是 1 到 4,000 之間的值。max 表示儲存大小上限是 2^30-1 個字元 (2 GB)。

VARCHAR [ ( n | max ) ]

使用 n 以位元組為單位來定義字串大小,該值可以介於 1 到 8,000,或使用 max 來表示資料行大小限制最多為 2^31-1 個位元組 (2 GB) 的儲存體。

NVARCHAR VARCHAR
Unicode支援
每個字元儲存容量 英文字元2 Byte;非英文字元2 Byte 英文字元1 Byte;非英文字元2 Byte
儲存長度 不定長,依照資料內容 不定長,依照資料內容
n的長度限制 1~4000(表示可以用寫4000個中、英文字元) 1~8000(表示可以用寫8000個英文字元)
max 2GB 2GB
結論 要儲存英文以外的用NVARCHAR,可以避免亂碼問題,但儲存空間相較VARCHAR會多一些。 只存英文VARCHAR沒問題,存其他可能會有亂碼問題。

總結

使用NVARCHAR來儲存字串會比較好,因為可避免字串因為中英混雜亂碼。

參考資料

https://docs.microsoft.com/zh-tw/sql/t-sql/data-types/char-and-varchar-transact-sql?view=sql-server-ver15
https://docs.microsoft.com/zh-tw/sql/t-sql/data-types/nchar-and-nvarchar-transact-sql?view=sql-server-ver15
https://ithelp.ithome.com.tw/articles/10213922

C# 產生PDF使用IText7

在這篇文章中介紹如何用C#產生PDF檔案。
使用套件為IText7。

IText7:https://itextpdf.com/en/products/itext-7/itext-7-core
程式碼範例:https://github.com/yuhsiang237/csharp-notes/tree/master/IText7Sample

先用Nuget安裝:

1
Install-Package itext7

如下,以下為產生一個PDF檔案,並加入標題。
其中標題為中英文混雜。

Program.cs

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 iText.Kernel.Font;
using iText.Kernel.Pdf;
using iText.Layout;
using iText.Layout.Element;
using iText.Layout.Properties;
using System;

namespace IText7Sample
{
public class Program
{
public static void Main()
{
PdfFont sysFont = PdfFontFactory.CreateFont(Environment.CurrentDirectory + "/src/fonts/NotoSansCJKtc-Regular.otf", iText.IO.Font.PdfEncodings.IDENTITY_H);

// Must have write permissions to the path folder
PdfWriter writer = new PdfWriter(@"demo.pdf");
PdfDocument pdf = new PdfDocument(writer);
Document document = new Document(pdf);
Paragraph header = new Paragraph("標題 HEADER")
.SetFont(sysFont)
.SetTextAlignment(TextAlignment.CENTER)
.SetFontSize(20);

document.Add(header);
document.Close();
}
}
}

總結

可以使用此套件來快速產生PDF,如果有中文需求可以像我上面那樣自己去補中文字型即可。

參考資料

C#擷取中英混雜字串

在這篇文中紀錄如何擷取中英混雜的子字串。

以下是我想到的作法,就是利用big5中文兩字元、英文一字元的特性去做。
20220217,使用Linq重構部分程式碼。

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
using System;
using System.Text;
using System.Linq;

namespace SubEnglishChineseString
{
public static class Program
{
/// <summary>
/// C#中擷取某長度中英混雜字串範例
/// </summary>
/// <param name="args"></param>
static void Main(string[] args)
{
string str = "TESTWORD測試文字TEST";
Console.WriteLine(SubBig5String(str, 1, 15));
}
/// <summary>
/// C#中擷取Big5子字串
/// </summary>
/// <param name="str">字串</param>
/// <param name="start">擷取起始索引</param>
/// <param name="length">擷取長度</param>
/// <returns>string</returns>
public static string SubBig5String(string str, int start, int length)
{
Encoding.RegisterProvider(CodePagesEncodingProvider.Instance);
byte[] subByteArray = Encoding.GetEncoding("big5").GetBytes(str).Skip(start).Take(length).ToArray();
return Encoding.GetEncoding("big5").GetString(subByteArray);
}
}
}

專案存庫:https://github.com/yuhsiang237/csharp-notes/tree/master/SubEnglishChineseString

撰寫C#單元測試(Unit Test)

前言

在這回中主要介紹C#的單元測試。
將會以AspNetCore中的xUnit、Moq套件進行範例測試。

至於為什麼要單元測試?
因為能夠快速的驗證結果、在撰寫時也只要執行片段程式碼。
對於有些程式碼容易有改A壞B的問題,能夠透過測試直接幫我們一次測完。
讓我們只需要針對錯誤案例進行程式碼錯誤區塊修改,加快整體專案除錯的時間。

完整測試專案連結:
https://github.com/yuhsiang237/csharp-unit-test

範例

使用Moq套件的Mock建立假的物件,並且設定函式、屬性、返回的值,再使用xUnit提供的驗證方法進行測試。

首先建立我們要測試的介面與類別,一個是IFoo.cs、Bar.cs。

csharp-unit-test/UnitTestProject/Bar.cs

1
2
3
4
5
6
7
8
9
10
11
12
13
using System;
using System.Collections.Generic;
using System.Text;

namespace UnitTestProject
{

public class Bar
{
public virtual bool Submit() { return false; }
}

}

csharp-unit-test/UnitTestProject/IFoo.cs

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
using System;
using System.Collections.Generic;
using System.Text;

namespace UnitTestProject
{
public interface IFoo
{
Bar Bar { get; set; }
string Name { get; set; }
int Value { get; set; }
bool DoSomething(string value);
}

}

再來建立測試,使用Moq、Xunit。
我們可以對函式返回值、假物件、屬性進行測試。
以下為測試範例:
/csharp-unit-test/UnitTestProjectTests/UnitTest.cs

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
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
using System;

using Xunit;
using Moq;

using UnitTestProject;

namespace UnitTestProjectTests
{
/// <summary>
/// xUnit單元測試,並使用Moq的Mock
/// 參考資料:
/// https://github.com/Moq/moq4/wiki/Quickstart#verification
/// https://vimsky.com/examples/detail/csharp-ex---Mock-VerifySet-method.html
/// https://codertw.com/%E5%89%8D%E7%AB%AF%E9%96%8B%E7%99%BC/206352/
/// </summary>
public class UnitTest
{
/// <summary>
/// 方法單元測試
/// </summary>
[Fact]
public void MethodsTest()
{
// 準備 Mock IFoo 介面
var mock = new Mock<IFoo>();
// 配置準備模擬的方法,當呼叫介面中的 DoSomething 方法,並傳遞引數 "bing" 的時候,返回 true
mock.Setup(foo => foo.DoSomething("ping")).Returns(true);
// 現在,你可以:
IFoo foo = mock.Object;
// 檢查使用時返回值為True
Assert.True(foo.DoSomething("ping"));
}
/// <summary>
/// 屬性單元測試
/// </summary>
[Fact]
public void PropertiesTest()
{
// 準備 Mock IFoo 介面
var mock = new Mock<IFoo>();
// 開始 "tracking" 屬性的 sets/gets
mock.SetupProperty(f => f.Name);
// 提供一個預設的值
mock.SetupProperty(f => f.Name, "foo");
// 現在,你可以:
IFoo foo = mock.Object;
// 儲存的值
Assert.Equal("foo", foo.Name);
// 重新設定一個值
foo.Name = "bar";
Assert.Equal("bar", foo.Name);
}
}
}

再來開啟VisualStudio的測試視窗運行,可以得到以下結果:

表示測試成功。

總結

撰寫測試能夠改善平常除錯的速度,特別當專案大起來時。
此外有些平台有測試的功能(如Azure DevOps Server的TFS),可以在CI自動整合時跑測試。

參考資料

C# nameof用法

如果在C#中遇到一些重複的String字串變數,是由變數的命名而來,就可以使用nameof直接提取出自串,避免再打一次造成打錯的問題。

nameof

nameof運算式會產生變數、類型或成員的名稱做為字串常數

1
2
3
4
5
6
7
8
9
Console.WriteLine(nameof(System.Collections.Generic));  // output: Generic
Console.WriteLine(nameof(List<int>)); // output: List
Console.WriteLine(nameof(List<int>.Count)); // output: Count
Console.WriteLine(nameof(List<int>.Add)); // output: Add

var numbers = new List<int> { 1, 2, 3 };
Console.WriteLine(nameof(numbers)); // output: numbers
Console.WriteLine(nameof(numbers.Count)); // output: Count
Console.WriteLine(nameof(numbers.Add)); // output: Add

參考資料:

https://docs.microsoft.com/zh-tw/dotnet/csharp/language-reference/operators/nameof#code-try-0

C# LINQ 延遲執行 (Deferred Execution)

前言

在這篇中主要筆記LINQ 延遲執行的特性。
一般我們如果使用純SQL語言去與資料庫查詢通常一下指令就會被馬上執行,而LINQ卻不一樣,是直到某個觸發點他才會開始運作。
即寫Select時並不會馬上運作。

常見的觸發點

1.foreach(var item in items)
2.轉為ToList(), ToArray(), ToDictionary(),因為需巡覽才能轉型,所以會直接執行。
3.取得彙總結果的聚合函数 Count(), Max(), Sum(), First(), Last()。

主要原因

延遲執行的基礎 - Iterator、yield

  1. 您只要提供 Iterator,它會只往返於類別中的資料結構。當編譯器偵測到您的 Iterator 時,它會自動產生 IEnumerable 或 IEnumerable 介面的 Current、MoveNext 和 Dispose 方法。
  2. Iterator 程式碼會使用 yield return 陳述式輪流傳回各元素。yield break 則會結束反覆運算。

關鍵:使用 yield 關鍵字,C# 會自動將該 block 實作為 iterator 方式執行。

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;

namespace DeferredExec
{
class Program
{
public class Data : IEnumerable
{
public IEnumerator GetEnumerator()
{
Console.WriteLine("yield 1");
yield return 1;
Console.WriteLine("yield 2");
yield return 2;
Console.WriteLine("yield 3");
yield return 3;
}
}
static void Main(string[] args)
{
Data data = new Data();
foreach(var item in data)
{
Console.WriteLine(item);
}
}
}
}

實作IEnumerable介面,並且撰寫方法GetEnumerator,yield會把每個區塊視為iterator(迭代器)。
而foreach中取得每個item在逆向工程可以見到其實是呼叫GetEnumerator取得一個IEnumerator(即iterator)。

有這觀念再拉回來看LINQ的方法,可以發現是IEnumerable。
Where:

1
public static IEnumerable<TSource> Where<TSource>(this IEnumerable<TSource> source, Func<TSource, bool> predicate)

Select:

1
public static IEnumerable<TResult> Select<TSource, TResult>(this IEnumerable<TSource> source, Func<TSource, TResult> selector)

想知道是不是延遲執行可以看返回值是否為以下3種物件:

1
IEnumerable<TSource>
1
IEnumerable<IGrouping<TKey, TSource>>
1
IOrderedEnumerable<TSource>

範例

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
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
using System;
using System.Collections;
using System.Collections.Generic;
using System.Linq;

namespace DeferredExec
{
class Program
{
private static void Main(string[] args)
{
var people = GetPeople();
var names = people
.Where(person => person.Age > 18)
.Select(person => person.Name);

foreach (var name in names)
{
Console.WriteLine(name);
}
}

private static IEnumerable<Person> GetPeople()
{
yield return new Person { Id = 1, Name = "John", Age = 36 };
yield return new Person { Id = 2, Name = "Bill", Age = 6 };
yield return new Person { Id = 3, Name = "Steve", Age = 23 };
}

public class Person
{

private int _id;
private string _name;
private int _age;
public int Id {
get {
return _id;
}
set
{
_id = value;
}
}

public string Name
{
get
{
return _name;
}
set
{
_name = value;
}
}
public int Age
{
get
{
return _age;
}
set
{
_age = value;
}
}
}
}
}

在這範例中使用泛型(Generic)的IEnumerable

1
IEnumerable<T>

因為Select、Where方法是IEnumerable,所以會在foreach觸發GetEnumerator時執行,進而達到延遲執行的效果。

延遲執行好處

1.可以再添加額外的表達式,如主要的查詢表達式後面可以添加子表達式過濾作為最終結果。
2.取得資料是最新的,因為當下才執行

總結

可以得知C#的LINQ為什麼能夠延遲執行是因為採用IEnumerable介面,裡面會有yield區塊形成迭代器,所以直到foreach執行時才會開始觸發。

參考資料

https://dotblogs.com.tw/hatelove/2013/09/10/csharp-linq-deferred-execution-introduction
https://dotblogs.com.tw/hatelove/2012/05/10/introducing-foreach-ienumerable-ienumerator-yield-iterator
https://dotblogs.com.tw/regionbbs/2012/01/12/linq_deferred_execution

C# Properity(屬性) 與 Field(欄位)

在這篇主要介紹Properity(屬性)、Field(欄位)在物件導向class中的差別。

Properity(屬性)

1.以public為主,能夠讓外部能存取。
2.包含get、set訪問器(能要求欄位只能讀或只能寫)。
3.可以增加邏輯處理,在改變一個欄位的值的時候希望改變物件的其它一些狀態,舉例:更改某外部公開屬性值x為1時,另一個內部私有欄位y需要連動更改數值為2。
4.不佔有實際內存,主要為get;set;存取私有欄位資料。

Field(欄位)

1.以private為主(有時會以protected),主要以class內部使用為主。
2.命名會加_在前面。
3.可能為靜態值所以設定為readonly
4.佔有實際內存、位址。

範例

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
public class User
{
private string _name;// _name為欄位
public string Name // Name為屬性,它含有程式碼塊
{
get
{
// 可以在此添加額外會影響的私有變數
return _name;// 讀取(返回_name值)
}
set
{
_name = value;// 為_name賦值
// 可以在此添加額外會影響的私有變數
}
}
}

總結

為什麼要使用屬性為什麼不用欄位就好?
因為能夠確保內部私有資料的安全性,並且能夠透過get;set;方式訪問時能夠增加邏輯處理、方便限制可讀、可寫。

參考資料

https://kevintsengtw.blogspot.com/2011/09/property-field.html?m=1#:~:text=Field%EF%BC%9A_FullDescription%E7%82%BA%E7%89%A9%E4%BB%B6%E7%A7%81%E6%9C%89,%E4%BE%86%E5%AD%98%E6%94%BE%E5%B1%AC%E6%80%A7%E7%9A%84%E8%B3%87%E6%96%99%E3%80%82&text=%E7%AF%80%E9%8C%84%E6%95%B4%E7%90%86%EF%BC%9A,%E9%9C%80%E8%A6%81%E5%AE%8C%E5%85%A8%E7%9B%B8%E5%90%8C%E7%9A%84%E8%AA%9E%E6%B3%95%E3%80%82
https://www.itread01.com/content/1548110544.html