Kiểu dữ liệu trong postgresSQL (Data Types)
Sau khi đã hiểu postgresSQL là gì và cài đặt nó, thì ở bài viết này mình sẽ giới thiệu đến anh em các kiểu dữ liệu(Data types) trong postgresSQL nhé.
Numberic Types (Kiểu số)
Kiểu dữ liệu kiểu số trong postgresSQL có những kiểu sau, anh em xem bảng dưới này nhé:
Loại | Kích cỡ | Khoảng |
---|---|---|
smallint | 2 bytes | -32768 đến +32768 |
integer | 4 bytes | -2147483648 đến +2147483648 |
bigint | 8 bytes | -9223372036854775808 đến +9223372036854775808 |
decimal | variable | đây là loại dữ liệu động nên có thể có đến 131072 chữ số trước dấu thập phân và 16383 chữ số sau dấu thập phân |
numeric | variable | đây là loại dữ liệu động nên có thể có đến 131072 chữ số trước dấu thập phân và 16383 chữ số sau dấu thập phân |
real | 4 bytes | kiểu dữ liệu số thực, có độ chính xác lên đến 6 chữ số thập phân |
double precission | 8 bytes | kiểu dữ liệu số thực, có độ chính xác lên đến 15 chữ số thập phân |
smallserial | 2 bytes | 1 đến 32767 |
serial | 4 bytes | 1 đến 2147483647 |
bigserial | 8 bytes | 1 đến 9223372036854775807 |
Umm, ở đây có gì lưu ý không nhỉ, anh em không xa lạ gì với những kiểu dữ liệu trên đúng không nào. Ở đây mình sẽ có lưu ý một chút giữa 1 số loại trên.
Kiểu numeric, với thằng này khi sử dụng anh em sẽ có 3 cách sử dụng như sau:
NUMERIC(percision, scale)
NUMERIC(percision)
NUMERIC
Ở cách khai báo trên chúng ta sẽ có 2 tham số là percision(độ chính xác tối đa) và scale(tỉ lệ tối đa), hiểu đơn giản như này. Ví dụ chúng ta có số 374.2341 vậy theo con số này thì percision=7 và scale=4 và trong số nguyên thì dĩ nhiên scale sẽ bằng 0.
Tiếp theo là sự khác biệt giữa 2 thằng numeric và double precission:
Ở ví dụ này mình sẽ sử dụng câu lệnh sau nhé:
SELECT x, round(x::numeric) AS num_round, round(x::double precision) AS dbl_round FROM generate_series(-3.5, 3.5, 1) as x;
Mình giải thích một chút câu query trên cho bạn nào chưa rõ nhé: chúng ta sẽ sử dụng hàm generate_series hàm này sẽ truyền vào 3 tham số lần lượt là: số bắt đầu, số kết thúc, và đơn vị. Câu lệnh trên nó sẽ gen ra các số từ -3.5 đến 3.5 với mỗi số cách nhau 1 đơn vị, hàm round mình sẽ dùng để làm tròn, trước khi làm tròn thì mình sẽ dùng cú pháp :: như trên để ép kiểu về 2 dạng trên, từ đó chúng ta có thể so sánh sự khác nhau giữa 2 kiểu dữ liệu trên.
Như kết quả ở trên anh em thấy, 2 loại dữ liệu numeric và double percission có sự khác biệt khi làm tròn đúng không nào. Theo như kết quả trên thì thằng numeric sẽ làm tròn theo nguyên tắc nếu số thập phân tại vị trí cần làm tròn >=0.5 thì sẽ được làm tròn lên và <0.5 thì sẽ được làm tròn xuống, còn đối với thằng double percission thì nó sẽ theo nguyên tắc làm tròn đến số chẵn gần nhất. Ví dụ 3.5 thì thằng double percission sẽ làm tròn lên 4 bởi vì 4 sẽ gần 3.5 hơn là 2 và với 2.5 thì nó sẽ làm tròn xuống 2 vì 2 sẽ gần 2.5 hơn là 4. Với ví dụ trên thì anh em nên lưu ý khi sử dụng các kiểu dữ liệu trên nhé, để tránh sai sót về mặt dữ liệu.
Monetary Types
Đây là loại dữ liệu dữ liệu lưu trữ một lượng tiền tệ với độ chính xác được cài đặt:
Loại | Kích cỡ | Khoảng |
---|---|---|
money | 8 bytes | -92233720368547758.08 đến +92233720368547758.07 |
Ở loại dữ liệu này anh em có thể thử query vài ba cái xem như nào nhé.
SELECT '12.34'::money;
Character Types
Đây là loại dữ liệu không thể thiếu trong sql, trong postgresSQL thì nó sẽ có các loại sau:
Loại | Khoảng |
---|---|
character varying(n), varchar(n) | Chiều dài thay đổi có giới hạn |
character(n), char(n) | Chiều dài cố định |
text | Chiều dài không giới hạn |
Bây giờ chúng ta sẽ làm một số ví dụ nhỏ để so sánh sự khác nhau giữa các loại dữ liệu trên nhé:
Đầu tiên anh em sẽ tạo ra 1 table với 3 kiểu dữ liệu như trên:
CREATE TABLE users(
id character varying(10),
name character(10),
info text
)
Xong chúng ta đã có table users với 3 kiểu dữ liệu trên, giờ sẽ INSERT 1 dòng dữ liệu bất kì nhé
INSERT INTO users(id, name, info) VALUES('8392d2-3ed', 'My name is ', 'this is my info')
Với câu lệnh trên id chúng ta sẽ để đúng 10 kí tự và với name chúng ta sẽ để khoảng trắng ở cuối, những khoảng trắng này sẽ không có nghĩa về mặt văn bản, nên nó sẽ tự động được loại bỏ khi chúng ta thêm mới vào. Vậy điều này có ảnh hưởng gì tới dữ liệu khi chúng ta thêm vào hay không ? 🤔
Bây giờ ta sẽ làm một ví dụ như sau:
SELECT 'a '::CHAR(2) collate "C" < E'a\n'::CHAR(2)
Câu query trên sẽ trả ra kết quả là true.
Mình sẽ giải thích 1 chút câu query trên nhé: câu query trên sẽ so sánh 2 chuỗi là 'a ' và 'a\n' theo chuẩn collation "C" anh em có thể xem thêm các chuẩn khác trong postgresSQL với câu query:
SELECT * FROM pg_collation
hiểu đơn giản chuẩn collation là 1 nguyên tắc sắp xếp các kí tự và phương thức chuyển mã kí tự, ví dụ như A=65 trong máy tính. Anh em có thể tìm hiểu thêm nhé.
Theo như chuẩn "C" thì kí tự space sẽ lớn hơn kí tự \n nhưng tại sao kết quả trả về lại là true 🤨, chính là do việc tự động xóa kí tự khoảng trắng ở cuối trong postgresSQL, nên anh em lưu ý vấn đề này trong việc sử dụng nhé.
Binary Data Types
Loại | Kích cỡ | Thông tin |
---|---|---|
bytea | 1 hoặc 4 byte | chuỗi nhị phân có độ dài thay đổi |
Kiểu dữ liệu nhị phân thì chắc chắn là dùng để lưu dữ liệu dạng nhị phân rồi đúng không anh em.
Giá trị đầu vào của kiểu dữ liệu này có thể được lấy theo định dạng 'hex' hoặc 'Escape' và đầu ra cũng tương tự, anh em có thể cấu hình loại định dạng đầu ra theo câu query: SET bytea_output = 'escape';
mặc định giá trị bytea_output là 'hex'.
Vậy thì loại dữ liệu này có thể được ứng dụng như nào nhỉ ?.
Bây giờ ta sẽ tạo ra một table với 1 trường dữ liệu có kiểu bytea xem sao:
CREATE TABLE img(
id text,
img_name character varying,
img_data bytea
)
Giờ mình sẽ viết 1 demo nhỏ để đẩy 1 hình ảnh lên postgresSQL và lấy về xem như nào nhé
package main
import (
"database/sql"
"fmt"
"log"
"net/http"
"os"
_ "github.com/lib/pq"
)
const (
host = "localhost"
port = 5432
user = "postgres"
password = "postgres"
dbname = "postgres"
)
type Img struct {
ID string `json:"id" db:"id"`
ImgName string `json:"img_name" db:"img_name"`
ImgData []byte `json:"img_data" db:"img_data"`
}
func main() {
// start connect database
psqlInfo := fmt.Sprintf("host=%s port=%d user=%s "+
"password=%s dbname=%s sslmode=disable",
host, port, user, password, dbname)
db, err := sql.Open("postgres", psqlInfo)
if err != nil {
panic(err)
}
defer db.Close()
err = db.Ping()
if err != nil {
panic(err)
}
fmt.Println("Successfully connected!")
// end connect database
// read file image
imgData, err := os.ReadFile("nature.png")
//insert data to database
_, err = db.Exec("INSERT INTO img(id, img_name, img_data) VALUES($1, $2, $3)", "1", "nature", imgData)
if err != nil {
fmt.Println(err)
}
// read file from database
http.HandleFunc("/", func(writer http.ResponseWriter, request *http.Request) {
img := []byte{}
err = db.QueryRow("SELECT img_data FROM img LIMIT 1").Scan(&img)
if err != nil {
fmt.Println(err)
}
writer.Write(img)
})
err = http.ListenAndServe(":8080", nil)
if err != nil {
log.Fatalf("Error creating server %s\n", err.Error())
}
}
Mình giải thích một chút đoạn code trên nhé: mình sẽ dùng golang để connect tới database, sau khi connect thành công mình sẽ insert 1 hình ảnh vào database dưới dạng bytea và lấy ra show trên trình duyệt.
Và đây là hình ảnh mình lấy từ database:
Ngoài ra anh em có thể insert data hình ảnh trực tiếp bằng query, postgresSQL cũng đã hỗ trợ sẵn hàm để đọc file với câu query như sau:
insert into img values ('2', 'nature', pg_read_binary_file('/Users/huy/dev/nature.png')::bytea);
Câu query trên sẽ dùng hàm pg_read_binary_file để đọc file từ đường dẫn và insert vào data, khi lấy ra và show trên trình duyệt thì kết quả sẽ tương tự.
Đó là một ứng dụng đại diện cho kiểu dữ liệu bytea, anh em có thể tìm hiểu thêm nhé.
Date/Time Types
Kiểu dữ liệu ngày giờ, tương tự như các hệ quản trị cơ sở dữ liệu khác, postgresSQL cũng cung cấp đủ các kiểu dữ liệu ngày giờ cần thiết theo bảng sau:
Loại | Kích cỡ | Thông tin | Giá trị thấp nhất | Giá trị cao nhất | Resolution |
---|---|---|---|---|---|
timestamp [ (p) ] without time zone | 8 bytes | ngày/tháng/năm và thời gian (không time zone) | 4713 BC | 294276 AD | 1 microsecond |
timestamp [ (p) ] with time zone | 8 bytes | ngày/tháng/năm và thời gian (có time zone) | 4713 BC | 294276 AD | 1 microsecond |
date | 4 bytes | ngày/tháng/năm | 4713 BC | 5874897 AD | 1 day |
time [ (p) ] without time zone | 8 bytes | Thời gian(giờ-phút-giây) (không time zone) | 00:00:00 | 24:00:00 | 1 microsecond |
time [ (p) ] with time zone | 12 bytes | Thời gian(giờ-phút-giây) (có time zone) | 00:00:00+1559 | 24:00:00-1559 | 1 microsecond |
interval [fields] [ (p) ] | 16 bytes | Thời gian(giờ-phút-giây) (có time zone) | -178000000 years | 178000000 years | 1 microsecond |
Với bảng thông tin trên ta có một tham số là p, nó dùng để cấu hình số chữ số phân số trong trường giây, Ví dụ: 2022-10-04 23:22:58.66 trong dữ liệu này thì ta xác định tham số p bằng 2 (sau dấu chấm gồm 2 chữ số), đây là tham số tùy chọn (optional) nên khi không truyền vào thì mặc định sẽ lưu đầy đủ số phân số trong trường giây.
Trong phần giá trị thấp nhất, giá trị cao nhất anh em thấy có đơn vị BC, AD. Thế nó là gì nhỉ ? Mình xin giải thích thêm 1 chút về 2 từ này:
- BC trong tiếng Latin là Ante Christum, trong tiếng Anh là Before Christ, là trước khi chúa Jesus ra đời, nghĩa là trước công nguyên.
- AD trong tiếng Latin là Anmo Domini trong tiếng Anh là Refer to the years after the birth of Jesus Christ , là năm chúa Jesus ra đời, nghĩa là sau công nguyên.
Ngoài code ra thì giờ anh em còn biết thêm kiến thức xã hội nữa đúng không nào. 😁😁
Boolean Type
Boolean type là kiểu dữ liệu dạng đúng sai, nó có thể có các giá trị true, false hoặc là null
Loại | Kích cỡ | Thông tin |
---|---|---|
boolean | 1 byte | true hoặc false |
Ở loại dữ liệu này không còn lạ gì với anh em nữa đúng không nào. Nhưng mình sẽ làm 1 ví dụ nhỏ cho anh em hiểu hơn một chút về nó nhé: Trước tiên mình sẽ tạo một table là test1 như sau
CREATE TABLE test1 (a boolean, b text);
Sau đó mình insert data như sau:
INSERT INTO test1 VALUES (TRUE, 'sic est');
INSERT INTO test1 VALUES (FALSE, 'non est');
Như các bạn thấy data được insert vào không có gì đặc biệt: Vậy có cách nào khác để thêm dữ liệu dạng boolean khác 2 từ khóa TRUE và FALSE không ? Ngoài 2 từ khóa trên thì trong postgresSQL còn hỗ trợ thêm các loại như sau thay thế cho true và false:
- true: yes , 1 , on
- false: no, 0, off
Khi đó query của chúng ta sẽ như sau:
INSERT INTO test1 VALUES ('yes'::boolean, 'sic est');
INSERT INTO test1 VALUES ('no'::boolean, 'non est');
Enumerated Types
Enumerated (kiểu liệt kê) đây là một kiểu dữ liệu gồm tập các giá trị tĩnh, có thứ tự. Nó tương đương với kiểu dữ liệu enum có trong một số ngôn ngữ lập trình.
Cùng mình sử dụng qua kiểu dữ liệu này xem có gì hay không nhé.
Trước tiên mình sẽ tạo ra 1 giá trị có kiểu dữ liệu là enum như sau:
CREATE TYPE user_type AS ENUM ('customer', 'employee', 'admin');
Câu query trên sẽ tạo ra enum là user_type với các giá trị như trên, khi này chúng ta đã có có kiểu user_type để dùng rồi, ví dụ:
CREATE TABLE users(
id text,
name text,
user_type user_type
)
Mình sẽ tạo ra bảng users với các trường dữ liệu như trên, và trường user_type có kiểu dữ liệu là user_type mà anh em mới tạo phía trên.
Giờ mình sẽ thử thêm data vào xem chuyện gì sẽ xảy ra nhé.
INSERT INTO users VALUES ('1', 'Xuân Huy', 'customer');
Như anh em thấy, không có chuyện gì xảy ra đúng không, và chúng ta vẫn có dữ liệu được thêm vào bình thường.
Mình thử thêm data với giá trị của user_type khác với các giá trị của enum nó có xem sao nhé.
INSERT INTO users VALUES ('2', 'Van A', 'president');
Khi đó anh xem sẽ nhận được thông báo lỗi như trên hình.
Rất đơn giản để hiểu tác dụng của kiểu dữ liệu enum đúng không nào 😁.
Network Address Types
Đây là kiểu dữ liệu mà PostgresSQL cung cấp cho anh em lưu các địa chỉ IPv4, IPv6 và MAC. Anh em nên sử dụng kiểu dữ liệu này khi lưu các loại địa chỉ mạng nhé, thay vì dùng kiểu dữ liệu text, khi dùng kiểu dữ liệu này nó sẽ đảm bảo dữ liệu có tính đúng đắt nhất nhé.
Nó bao gồm các loại sau:
Loại | Kích cỡ | Thông tin |
---|---|---|
cidr | 7 hoặc 19 bytes | IPv4 và IPv6 |
inet | 7 hoặc 19 bytes | IPv4 và IPv6 |
macaddr | 6 bytes | Địa chỉ mac |
macaddr8 | 8 bytes | Địa chỉ mac(EUI-64 format) |
Kiểu dữ liệu này không có gì đặc biệt, các bạn có thể tìm hiểu thêm ở link chính thức của PostgresSQL nhé 😁😁:
https://www.postgresql.org/docs/current/datatype-net-types.html
**Bài này dài quá, bên dưới còn 1 số các bạn tự tìm hiểu nhé Bạn nào thắc mắc có thể comment mình sẽ viết thêm nhé ** 😁
Text Search Types
UUID Type
XML Types
JSON Types
Arrays
Composite Types
Range Types
Domain Types
Object Identifier Types
pg_lsn Types
Pseudo-Types
All rights reserved