Kolom yang dihasilkan adalah kolom yang selalu dihitung dari kolom lain dalam baris. Kolom ini dapat menyederhanakan kueri, menghemat biaya evaluasi ekspresi pada waktu kueri, dan dapat diindeks atau digunakan sebagai kunci asing. Halaman ini menjelaskan cara mengelola jenis kolom ini di database untuk database dialek GoogleSQL dan database dialek PostgreSQL.
Menambahkan kolom yang dibuat ke tabel baru
Dalam cuplikan CREATE TABLE berikut, kita membuat tabel untuk menyimpan informasi tentang pengguna. Kita memiliki kolom untuk FirstName dan LastName serta menentukan kolom yang dihasilkan untuk FullName, yang merupakan gabungan dari FirstName dan LastName. SQL dalam tanda kurung disebut ekspresi pembuatan.
Kolom yang dihasilkan dapat ditandai sebagai STORED untuk menghemat biaya evaluasi ekspresi pada waktu kueri. Akibatnya, nilai FullName hanya dihitung
saat baris baru disisipkan atau saat FirstName atau LastName diperbarui untuk
baris yang ada. Nilai yang dihitung disimpan bersama dengan kolom lain dalam tabel.
GoogleSQL
CREATE TABLE Users (
Id STRING(20) NOT NULL,
FirstName STRING(50),
LastName STRING(50),
Age INT64 NOT NULL,
FullName STRING(100) AS (FirstName || ' ' || LastName) STORED
) PRIMARY KEY (Id);
PostgreSQL
CREATE TABLE users (
id VARCHAR(20) NOT NULL,
firstname VARCHAR(50),
lastname VARCHAR(50),
age BIGINT NOT NULL,
fullname VARCHAR(100) GENERATED ALWAYS AS (firstname || ' ' || lastname) STORED,
PRIMARY KEY(id)
);
Anda dapat membuat kolom yang dihasilkan dan tidak disimpan dengan menghilangkan atribut STORED
dalam DDL. Jenis kolom yang dibuat ini dievaluasi pada waktu kueri
dan dapat menyederhanakan kueri. Di PostgreSQL, Anda dapat membuat kolom yang dihasilkan
tidak tersimpan menggunakan atribut VIRTUAL.
GoogleSQL
FullName STRING(MAX) AS (CONCAT(FirstName, " ", LastName))
PostgreSQL
fullname text GENERATED ALWAYS AS (firstname || ' ' || lastname) VIRTUAL
expressiondapat berupa ekspresi SQL valid yang dapat ditetapkan ke jenis data kolom dengan batasan berikut.Ekspresi hanya dapat mereferensikan kolom dalam tabel yang sama.
Ekspresi tidak boleh berisi subkueri.
Ekspresi dengan fungsi non-deterministik seperti
PENDING_COMMIT_TIMESTAMP(),CURRENT_DATE(), danCURRENT_TIMESTAMP()tidak dapat dijadikan kolom yang dihasilkanSTOREDatau kolom yang dihasilkan yang diindeks.Anda tidak dapat mengubah ekspresi kolom yang dihasilkan dan diindeks atau
STORED.
Untuk database dialek GoogleSQL, kolom yang dihasilkan dan tidak disimpan dengan jenis
STRINGatauBYTESharus memiliki panjangMAX.Untuk database dialek PostgreSQL, kolom virtual atau tidak disimpan yang dihasilkan dengan jenis
VARCHARharus memiliki panjangMAX.Atribut
STOREDyang mengikuti ekspresi menyimpan hasil ekspresi bersama dengan kolom lain dalam tabel. Pembaruan berikutnya pada kolom yang dirujuk menyebabkan Spanner mengevaluasi ulang dan menyimpan ekspresi.Kolom turunan yang bukan
STOREDtidak dapat ditandai sebagaiNOT NULL.Penulisan langsung ke kolom yang dihasilkan tidak diizinkan.
Opsi kolom
allow_commit_timestamptidak diizinkan di kolom yang dibuat atau kolom apa pun yang direferensikan oleh kolom yang dibuat.Untuk kolom
STOREDatau kolom yang dibuat yang diindeks, Anda tidak dapat mengubah jenis data kolom, atau kolom apa pun yang dirujuk oleh kolom yang dibuat.Anda tidak dapat melepaskan kolom yang dirujuk oleh kolom yang dihasilkan.
Anda dapat menggunakan kolom yang dihasilkan sebagai kunci primer dengan batasan tambahan berikut:
Kunci utama yang dibuat tidak dapat merujuk ke kolom lain yang dibuat.
Kunci utama yang dihasilkan dapat mereferensikan paling banyak satu kolom non-kunci.
Kunci utama yang dihasilkan tidak dapat bergantung pada kolom non-kunci dengan klausa
DEFAULT.
Aturan berikut berlaku saat menggunakan kolom kunci yang dibuat:
- Read API: Anda harus menentukan kolom kunci sepenuhnya, termasuk kolom kunci yang dihasilkan.
- Mutation API: Untuk
INSERT,INSERT_OR_UPDATE, danREPLACE, Spanner tidak mengizinkan Anda menentukan kolom kunci yang dihasilkan. UntukUPDATE, Anda dapat secara opsional menentukan kolom kunci yang dihasilkan. UntukDELETE, Anda harus menentukan kolom kunci sepenuhnya, termasuk kunci yang dihasilkan. - DML: Anda tidak dapat menulis secara eksplisit ke kunci yang dibuat dalam pernyataan
INSERTatauUPDATE. - Kueri: Secara umum, sebaiknya gunakan kolom kunci yang dihasilkan sebagai
filter dalam kueri Anda. Secara opsional, jika ekspresi untuk kolom kunci yang dihasilkan hanya menggunakan satu kolom sebagai referensi, kueri dapat menerapkan kondisi kesetaraan (
=) atauINke kolom yang direferensikan. Untuk mengetahui informasi dan contoh selengkapnya, lihat Membuat kunci unik yang berasal dari kolom nilai.
Kolom yang dihasilkan dapat dikueri seperti kolom lainnya, seperti yang ditunjukkan dalam contoh berikut.
GoogleSQL
SELECT Id, FullName
FROM Users;
PostgreSQL
SELECT id, fullname
FROM users;
Kueri yang menggunakan Fullname setara dengan kueri dengan ekspresi
yang dihasilkan. Oleh karena itu, kolom yang dibuat dapat menyederhanakan kueri.
GoogleSQL
SELECT Id, ARRAY_TO_STRING([FirstName, LastName], " ") as FullName
FROM Users;
PostgreSQL
SELECT id, firstname || ' ' || lastname as fullname
FROM users;
Membuat indeks pada kolom yang dibuat
Anda juga dapat mengindeks atau menggunakan kolom yang dibuat sebagai kunci asing.
Untuk membantu pencarian di kolom yang dihasilkan FullName, kita dapat membuat indeks sekunder seperti yang ditunjukkan dalam cuplikan berikut.
GoogleSQL
CREATE INDEX UsersByFullName ON Users (FullName);
PostgreSQL
CREATE INDEX UserByFullName ON users (fullname);
Menambahkan kolom yang dihasilkan ke tabel yang ada
Dengan menggunakan pernyataan ALTER TABLE berikut, kita dapat menambahkan kolom yang dibuat ke
tabel Users untuk membuat dan menyimpan inisial pengguna.
GoogleSQL
ALTER TABLE Users ADD COLUMN Initials STRING(2)
AS (ARRAY_TO_STRING([SUBSTR(FirstName, 0, 1), SUBSTR(LastName, 0, 1)], "")) STORED;
PostgreSQL
ALTER TABLE users ADD COLUMN initials VARCHAR(2)
GENERATED ALWAYS AS (SUBSTR(firstname, 0, 1) || SUBSTR(lastname, 0, 1)) STORED;
Jika Anda menambahkan kolom yang disimpan dan dibuat ke tabel yang ada, operasi berjalan lama untuk mengisi ulang nilai kolom akan dimulai. Selama pengisian ulang, kolom yang dihasilkan dan disimpan tidak dapat dibaca atau dikueri. Status pengisian ulang dicerminkan dalam tabel INFORMATION_SCHEMA.
Membuat indeks parsial menggunakan kolom yang dihasilkan
Bagaimana jika kita hanya ingin membuat kueri pengguna yang berusia di atas 18 tahun? Pemindaian tabel secara penuh akan tidak efisien, jadi kita menggunakan indeks parsial.
Gunakan pernyataan berikut untuk menambahkan kolom yang dihasilkan lainnya yang menampilkan usia pengguna jika mereka berusia di atas 18 tahun, dan menampilkan
NULLjika tidak.GoogleSQL
ALTER TABLE Users ADD COLUMN AgeAbove18 INT64 AS (IF(Age > 18, Age, NULL));PostgreSQL
ALTER TABLE Users ADD COLUMN AgeAbove18 BIGINT GENERATED ALWAYS AS (nullif( Age , least( 18, Age) )) VIRTUAL;Buat indeks pada kolom baru ini, dan nonaktifkan pengindeksan nilai
NULLdengan kata kunciNULL_FILTEREDdi GoogleSQL atau predikatIS NOT NULLdi PostgreSQL. Indeks parsial ini lebih kecil dan lebih efisien daripada indeks normal karena mengecualikan semua orang yang berusia 18 tahun atau lebih muda.GoogleSQL
CREATE NULL_FILTERED INDEX UsersAbove18ByAge ON Users (AgeAbove18);PostgreSQL
CREATE INDEX UsersAbove18ByAge ON users (AgeAbove18) WHERE AgeAbove18 IS NOT NULL;Untuk mengambil
IddanAgesemua pengguna yang berusia di atas 18 tahun, jalankan kueri berikut.GoogleSQL
SELECT Id, Age FROM Users@{FORCE_INDEX=UsersAbove18ByAge} WHERE AgeAbove18 IS NOT NULL;PostgreSQL
SELECT Id, Age FROM users /*@ FORCE_INDEX = UsersAbove18ByAge */ WHERE AgeAbove18 IS NOT NULL;Untuk memfilter berdasarkan usia yang berbeda, misalnya, untuk mengambil semua pengguna yang berusia di atas 21 tahun, gunakan indeks yang sama dan filter pada kolom yang dihasilkan sebagai berikut:
GoogleSQL
SELECT Id, Age FROM Users@{FORCE_INDEX=UsersAbove18ByAge} WHERE AgeAbove18 > 21;PostgreSQL
SELECT Id, Age FROM users /*@ FORCE_INDEX = UsersAbove18ByAge */ WHERE AgeAbove18 > 21;Kolom yang dihasilkan dan diindeks dapat menghemat biaya evaluasi ekspresi pada waktu kueri dan menghindari penyimpanan nilai dua kali (dalam tabel dasar dan indeks) dibandingkan dengan kolom yang dihasilkan
STORED.
Menghapus kolom yang dihasilkan
Pernyataan DDL berikut menghapus kolom yang dihasilkan dari tabel Users:
GoogleSQL
ALTER TABLE Users DROP COLUMN Initials;
PostgreSQL
ALTER TABLE users DROP COLUMN initials;
Mengubah ekspresi kolom yang dibuat
GoogleSQL
ALTER TABLE Users ALTER COLUMN FullName STRING(100)
AS (ARRAY_TO_STRING(ARRAY_TO_STRING([LastName, FirstName ], " ")));
PostgreSQL
ALTER TABLE users ADD COLUMN Initials VARCHAR(2)
GENERATED ALWAYS AS (lastname || ' ' || firstname) VIRTUAL;
Ekspresi kolom yang dihasilkan STORED atau kolom yang dihasilkan non-tersimpan dan diindeks tidak boleh diperbarui.
Membuat kunci utama pada kolom yang dihasilkan
Di Spanner, Anda dapat menggunakan kolom yang dibuat STORED di
kunci utama.
Contoh berikut menunjukkan pernyataan DDL yang membuat tabel UserInfoLog
dengan kolom yang dihasilkan ShardId. Nilai kolom ShardId bergantung pada kolom lain. Kolom ini berasal dari penggunaan fungsi MOD pada
kolom UserId. ShardId dideklarasikan sebagai bagian dari kunci utama.
GoogleSQL
CREATE TABLE UserInfoLog (
ShardId INT64 NOT NULL
AS (MOD(UserId, 2048)) STORED,
UserId INT64 NOT NULL,
FullName STRING(1024) NOT NULL,
) PRIMARY KEY (ShardId, UserId);
PostgreSQL
CREATE TABLE UserInfoLog (
ShardId BIGINT GENERATED ALWAYS
AS (MOD(UserId, '2048'::BIGINT)) STORED NOT NULL,
UserId BIGINT NOT NULL,
FullName VARCHAR(1024) NOT NULL,
PRIMARY KEY(ShardId, UserId));
Biasanya, untuk mengakses baris tertentu secara efisien, Anda harus menentukan semua kolom kunci. Dalam contoh sebelumnya, ini berarti memberikan ShardId dan
UserId. Namun, Spanner terkadang dapat menyimpulkan nilai kolom kunci utama yang dihasilkan jika bergantung pada satu kolom lain dan jika nilai kolom yang bergantung padanya ditentukan sepenuhnya. Hal ini benar jika
kolom yang dirujuk oleh kolom kunci utama yang dibuat memenuhi salah satu
kondisi berikut:
- Sama dengan nilai konstanta atau parameter terikat dalam klausa
WHERE, atau - Nilainya ditetapkan oleh operator
INdalam klausaWHERE - Nilai ini diperoleh dari kondisi equi-join
Misalnya, untuk kueri berikut:
GoogleSQL
SELECT * FROM UserInfoLog
AS T WHERE T.UserId=1;
PostgreSQL
SELECT * FROM UserInfoLog
AS T WHERE T.UserId=1;
Spanner dapat menyimpulkan nilai ShardId dari UserId yang diberikan.
Kueri sebelumnya setara dengan kueri berikut setelah pengoptimalan
kueri:
GoogleSQL
SELECT * FROM UserInfoLog
AS T WHERE T.ShardId = MOD(1, 2048)
AND T.UserId=1;
PostgreSQL
SELECT * FROM UserInfoLog
AS T WHERE T.ShardId = MOD(1, 2048)
AND T.UserId=1;
Contoh berikutnya menunjukkan cara membuat tabel Students dan menggunakan
ekspresi yang mengambil kolom id dari kolom JSON StudentInfo dan
menggunakannya sebagai kunci utama:
GoogleSQL
CREATE TABLE Students (
StudentId INT64 NOT NULL
AS (INT64(StudentInfo.id)) STORED,
StudentInfo JSON NOT NULL,
) PRIMARY KEY (StudentId);
PostgreSQL
CREATE TABLE Students (
StudentId BIGINT GENERATED ALWAYS
AS ((StudentInfo ->> 'id')::BIGINT) STORED NOT NULL,
StudentInfo JSONB NOT NULL,
PRIMARY KEY(StudentId));
Melihat properti kolom yang dihasilkan
INFORMATION_SCHEMA Spanner berisi informasi tentang kolom yang dihasilkan di database Anda. Berikut adalah beberapa contoh pertanyaan yang dapat Anda jawab saat membuat kueri skema informasi.
Kolom turunan apa yang ditentukan dalam database saya?
GoogleSQL
SELECT c.TABLE_NAME, c.COLUMN_NAME, C.IS_STORED
FROM INFORMATION_SCHEMA.COLUMNS as c
WHERE c.GENERATION_EXPRESSION IS NOT NULL;
PostgreSQL
SELECT c.TABLE_NAME, c.COLUMN_NAME, C.IS_STORED
FROM INFORMATION_SCHEMA.COLUMNS as c
WHERE c.GENERATION_EXPRESSION IS NOT NULL;
IS_STORED adalah YES untuk kolom virtual tersimpan, NO untuk kolom virtual tidak tersimpan, atau NULL untuk kolom non-virtual.
Apa status kolom yang dihasilkan saat ini dalam tabel Users?
Jika Anda telah menambahkan kolom yang dibuat ke tabel yang ada, Anda mungkin ingin meneruskan SPANNER_STATE dalam kueri untuk mengetahui status kolom saat ini.
SPANNER_STATE menampilkan nilai berikut:
COMMITTED: Kolom dapat digunakan sepenuhnya.WRITE_ONLY: Kolom sedang diisi ulang. Tidak ada pembacaan yang diizinkan.
Gunakan kueri berikut untuk menemukan status kolom:
GoogleSQL
SELECT c.TABLE_NAME, c.COLUMN_NAME, c.SPANNER_STATE
FROM INFORMATION_SCHEMA.COLUMNS AS c
WHERE c.TABLE_NAME="Users" AND c.GENERATION_EXPRESSION IS NOT NULL;
PostgreSQL
SELECT c.TABLE_NAME, c.COLUMN_NAME, c.SPANNER_STATE
FROM INFORMATION_SCHEMA.COLUMNS AS c
WHERE c.TABLE_NAME='users' AND c.GENERATION_EXPRESSION IS NOT NULL;
Catatan: Kolom yang dibuat dan tidak disimpan hanya dapat diakses menggunakan kueri SQL. Namun, jika diindeks, Anda dapat menggunakan API baca untuk mengakses nilai dari indeks.
Performa
Kolom yang dihasilkan STORED tidak memengaruhi performa operasi baca atau kueri. Namun, kolom virtual yang tidak disimpan yang digunakan dalam kueri dapat memengaruhi
performa kueri karena overhead evaluasi ekspresi kolom virtual.
Performa operasi tulis (pernyataan DML dan mutasi) terpengaruh saat menggunakan kolom yang dihasilkan STORED atau kolom yang dihasilkan yang diindeks. Overhead terjadi karena evaluasi ekspresi kolom yang dihasilkan saat operasi tulis menyisipkan atau mengubah salah satu kolom yang dirujuk dalam ekspresi kolom yang dihasilkan. Karena overhead bervariasi bergantung pada beban kerja penulisan untuk aplikasi, desain skema, dan karakteristik set data, sebaiknya Anda melakukan tolok ukur aplikasi sebelum menggunakan kolom yang dihasilkan.
Langkah berikutnya
Pelajari lebih lanjut Skema informasi untuk database dialek GoogleSQL dan Skema informasi untuk database dialek PostgreSQL di Spanner.
Lihat detail selengkapnya tentang kolom yang dihasilkan dalam detail parameter CREATE TABLE.