Halaman ini menjelaskan detail tentang operator yang digunakan dalam Rencana eksekusi kueri Spanner. Untuk mempelajari cara mengambil rencana eksekusi untuk kueri tertentu menggunakan konsol Google Cloud , lihat Memahami cara Spanner menjalankan kueri.
Kueri dan rencana eksekusi di halaman ini didasarkan pada skema database berikut:
CREATE TABLE Singers (
SingerId INT64 NOT NULL,
FirstName STRING(1024),
LastName STRING(1024),
SingerInfo BYTES(MAX),
BirthDate DATE
) PRIMARY KEY(SingerId);
CREATE INDEX SingersByFirstLastName ON Singers(FirstName, LastName);
CREATE TABLE Albums (
SingerId INT64 NOT NULL,
AlbumId INT64 NOT NULL,
AlbumTitle STRING(MAX),
MarketingBudget INT64
) PRIMARY KEY(SingerId, AlbumId),
INTERLEAVE IN PARENT Singers ON DELETE CASCADE;
CREATE INDEX AlbumsByAlbumTitle ON Albums(AlbumTitle);
CREATE INDEX AlbumsByAlbumTitle2 ON Albums(AlbumTitle) STORING (MarketingBudget);
CREATE TABLE Songs (
SingerId INT64 NOT NULL,
AlbumId INT64 NOT NULL,
TrackId INT64 NOT NULL,
SongName STRING(MAX),
Duration INT64,
SongGenre STRING(25)
) PRIMARY KEY(SingerId, AlbumId, TrackId),
INTERLEAVE IN PARENT Albums ON DELETE CASCADE;
CREATE INDEX SongsBySingerAlbumSongNameDesc ON Songs(SingerId, AlbumId, SongName DESC), INTERLEAVE IN Albums;
CREATE INDEX SongsBySongName ON Songs(SongName);
CREATE TABLE Concerts (
VenueId INT64 NOT NULL,
SingerId INT64 NOT NULL,
ConcertDate DATE NOT NULL,
BeginTime TIMESTAMP,
EndTime TIMESTAMP,
TicketPrices ARRAY<INT64>
) PRIMARY KEY(VenueId, SingerId, ConcertDate);
Anda dapat menggunakan pernyataan Bahasa Manipulasi Data (DML) berikut untuk menambahkan data ke tabel ini:
INSERT INTO Singers (SingerId, FirstName, LastName, BirthDate)
VALUES (1, "Marc", "Richards", "1970-09-03"),
(2, "Catalina", "Smith", "1990-08-17"),
(3, "Alice", "Trentor", "1991-10-02"),
(4, "Lea", "Martin", "1991-11-09"),
(5, "David", "Lomond", "1977-01-29");
INSERT INTO Albums (SingerId, AlbumId, AlbumTitle)
VALUES (1, 1, "Total Junk"),
(1, 2, "Go, Go, Go"),
(2, 1, "Green"),
(2, 2, "Forever Hold Your Peace"),
(2, 3, "Terrified"),
(3, 1, "Nothing To Do With Me"),
(4, 1, "Play");
INSERT INTO Songs (SingerId, AlbumId, TrackId, SongName, Duration, SongGenre)
VALUES (2, 1, 1, "Let's Get Back Together", 182, "COUNTRY"),
(2, 1, 2, "Starting Again", 156, "ROCK"),
(2, 1, 3, "I Knew You Were Magic", 294, "BLUES"),
(2, 1, 4, "42", 185, "CLASSICAL"),
(2, 1, 5, "Blue", 238, "BLUES"),
(2, 1, 6, "Nothing Is The Same", 303, "BLUES"),
(2, 1, 7, "The Second Time", 255, "ROCK"),
(2, 3, 1, "Fight Story", 194, "ROCK"),
(3, 1, 1, "Not About The Guitar", 278, "BLUES");
Operator daun
Operator leaf adalah operator yang tidak memiliki turunan. Jenis operator daun adalah:
Unnest array
Operator membuka array meratakan array input menjadi baris elemen. Setiap baris yang dihasilkan berisi hingga dua kolom: nilai aktual dari array, dan secara opsional posisi berbasis nol dalam array.
Misalnya, menggunakan kueri ini:
SELECT a, b FROM UNNEST([1,2,3]) a WITH OFFSET b;
Kueri meratakan array [1,2,3] di kolom a dan menampilkan posisi array di kolom b.
Berikut hasilnya:
| a | b |
|---|---|
| 1 | 0 |
| 2 | 1 |
| 3 | 2 |
Berikut adalah rencana eksekusi:
Buat relasi
Operator generate relation menampilkan nol baris atau lebih.
Relasi unit
unit relation menampilkan satu baris. Operator ini adalah kasus khusus dari operator generate relation.
Misalnya, menggunakan kueri ini:
SELECT 1 + 2 AS Result;
Hasilnya adalah:
| Hasil |
|---|
| 3 |
Berikut adalah rencana eksekusi:
Relasi kosong
Relasi kosong tidak menampilkan baris. Operator ini adalah kasus khusus dari operator generate relation.
Misalnya, menggunakan kueri ini:
SELECT *
FROM albums
LIMIT 0
Hasilnya adalah:
Tidak ada hasil
Berikut adalah rencana eksekusi:
Pindai
Operator scan menampilkan baris dengan memindai sumber baris. Berikut adalah jenis operator pemindaian:
- Pemindaian tabel: Pemindaian terjadi pada tabel.
- Pemindaian indeks: Pemindaian terjadi pada indeks.
- Pemindaian batch: Pemindaian terjadi pada tabel perantara yang dibuat oleh operator relasional lainnya (misalnya, tabel yang dibuat oleh penerapan silang terdistribusi).
Jika memungkinkan, Spanner menerapkan predikat pada kunci sebagai bagian dari
pemindaian. Pemindaian dijalankan lebih efisien saat predikat diterapkan karena
pemindaian tidak perlu membaca seluruh tabel atau indeks. Predikat muncul dalam
rencana eksekusi dalam bentuk KeyPredicate: column=value.
Dalam kasus terburuk, kueri mungkin perlu mencari semua baris dalam tabel. Situasi
ini menyebabkan pemindaian penuh, dan muncul dalam rencana eksekusi sebagai full scan:
true.
Misalnya, menggunakan kueri ini:
SELECT s.lastname
FROM singers@{FORCE_INDEX=SingersByFirstLastName} as s
WHERE s.firstname = 'Catalina';
Berikut hasilnya:
| LastName |
|---|
| Smith |
Berikut adalah rencana eksekusi:
Dalam rencana eksekusi, operator gabungan terdistribusi
tingkat teratas mengirimkan sub-rencana ke server jarak jauh. Setiap sub-rencana memiliki operator serialize
result dan operator pemindaian indeks. Predikat
Key Predicate: FirstName = 'Catalina' membatasi pemindaian ke baris dalam indeks
SingersByFirstLastname yang memiliki FirstName sama dengan Catalina. Output
pemindaian indeks ditampilkan ke operator hasil serialisasi.
Operator unary
Operator unary adalah operator yang memiliki satu turunan relasional.
Operator berikut adalah operator unary:
- Agregat
- Menerapkan mutasi
- Buat batch
- Komputasi
- Compute struct
- DataBlockToRowAdapter
- Filter
- Filter pemindaian
- Batas
- Gabungan pemisahan lokal
- Random Id Assign
- RowToDataBlockAdapter
- Hasil serialisasi
- Urutkan
- TVF
- Input gabungan
Agregat
Operator agregat menerapkan pernyataan SQL GROUP BY dan fungsi agregat (seperti COUNT). Input untuk operator agregat secara logis dipartisi ke dalam grup yang disusun pada kolom kunci (atau ke dalam satu grup jika GROUP BY tidak ada). Untuk setiap grup, nol atau lebih agregat dihitung.
Misalnya, menggunakan kueri ini:
SELECT s.singerid,
Avg(s.duration) AS average,
Count(*) AS count
FROM songs AS s
GROUP BY singerid;
Kueri dikelompokkan menurut SingerId dan melakukan agregasi AVG dan agregasi COUNT.
Berikut hasilnya:
| SingerId | rata-rata | jumlah |
|---|---|---|
| 3 | 278 | 1 |
| 2 | 225.875 | 8 |
Berikut adalah rencana eksekusi:
Operator agregat dapat berupa berbasis streaming atau berbasis hash. Rencana eksekusi
sebelumnya menunjukkan agregat berbasis streaming. Agregat berbasis streaming dibaca dari
input yang sudah diurutkan sebelumnya (jika GROUP BY ada) dan menghitung grup
tanpa pemblokiran. Agregat berbasis hash membuat tabel hash untuk mempertahankan agregat inkremental dari beberapa baris input secara bersamaan. Agregat berbasis streaming lebih cepat dan menggunakan lebih sedikit memori daripada agregat berbasis hash, tetapi memerlukan input yang diurutkan (baik berdasarkan kolom kunci atau indeks sekunder).
Untuk skenario terdistribusi, operator gabungan dapat dipisahkan menjadi pasangan lokal-global. Setiap server jarak jauh melakukan agregasi lokal pada baris input-nya, lalu menampilkan hasilnya ke server root. Server root melakukan agregasi global.
Menerapkan mutasi
Operator apply mutations menerapkan mutasi dari Pernyataan Manipulasi Data (DML) ke tabel. Operator ini adalah operator teratas dalam paket kueri untuk pernyataan DML.
Misalnya, menggunakan kueri ini:
DELETE FROM singers
WHERE firstname = 'Alice';
Berikut hasilnya:
4 rows deleted This statement deleted 4 rows and did not return any rows.
Berikut adalah rencana eksekusi:
Buat batch
Operator buat batch mengelompokkan baris inputnya ke dalam urutan. Operasi batch pembuatan biasanya terjadi sebagai bagian dari operasi penerapan silang terdistribusi. Baris input dapat diurutkan ulang selama pengelompokan. Jumlah baris input yang dikelompokkan dalam setiap eksekusi operator batch bervariasi.
Lihat operator distributed cross apply untuk mengetahui contoh operator batch buat dalam rencana eksekusi.
Compute
Operator compute menghasilkan output dengan membaca baris inputnya dan menambahkan satu atau beberapa kolom tambahan yang dihitung menggunakan ekspresi skalar. Lihat operator union all untuk contoh operator komputasi dalam paket eksekusi.
Struktur komputasi
Operator compute struct membuat variabel untuk struktur yang berisi kolom untuk setiap kolom input.
Misalnya, menggunakan kueri ini:
SELECT FirstName,
ARRAY(SELECT AS STRUCT song.SongName, song.SongGenre
FROM Songs AS song
WHERE song.SingerId = singer.SingerId)
FROM singers AS singer
WHERE singer.SingerId = 3;
Berikut hasilnya:
| FirstName | Tidak ditentukan |
|---|---|
| Alice | [["Not About The Guitar","BLUES"]] |
Berikut adalah rencana eksekusi:
Dalam rencana eksekusi, operator subkueri array menerima input dari operator
gabungan terdistribusi, yang menerima input dari operator
struktur komputasi. Operator struct compute membuat struktur dari kolom SongName dan SongGenre dalam tabel Songs.
DataBlockToRowAdapter
Operator DataBlockToRowAdapter otomatis disisipkan oleh
pengoptimal kueri Spanner di antara sepasang operator yang beroperasi
menggunakan metode eksekusi yang berbeda. Inputnya adalah operator yang menggunakan metode eksekusi berorientasi batch dan outputnya dimasukkan ke operator yang dieksekusi dalam metode eksekusi berorientasi baris. Untuk mengetahui informasi selengkapnya, lihat Mengoptimalkan eksekusi
kueri.
Filter
Operator filter membaca semua baris dari inputnya, menerapkan predikat skalar pada setiap baris, lalu hanya menampilkan baris yang memenuhi predikat.
Misalnya, menggunakan kueri ini:
SELECT s.lastname
FROM (SELECT s.lastname
FROM singers AS s
LIMIT 3) s
WHERE s.lastname LIKE 'Rich%';
Berikut hasilnya:
| LastName |
|---|
| Richards |
Berikut adalah rencana eksekusi:
Predikat untuk penyanyi yang nama belakangnya diawali dengan Rich diimplementasikan sebagai
filter. Input filter adalah output dari scan indeks, dan
output filter adalah baris tempat LastName dimulai dengan Rich.
Untuk performa, setiap kali filter diposisikan langsung di atas pemindaian,
filter akan memengaruhi cara data dibaca. Misalnya, pertimbangkan tabel dengan kunci k.
Filter dengan predikat k = 5 langsung di atas pemindaian tabel mencari baris yang cocok dengan k = 5, tanpa membaca seluruh input. Hal ini menghasilkan eksekusi kueri yang lebih efisien. Pada contoh sebelumnya, operator filter
hanya membaca baris yang memenuhi predikat WHERE s.LastName LIKE 'Rich%'.
Memfilter pemindaian
Operator pemindaian filter selalu berada di atas pemindaian tabel atau indeks. Filter ini berfungsi dengan pemindaian untuk mengurangi jumlah baris yang dibaca dari database, dan pemindaian yang dihasilkan biasanya lebih cepat daripada dengan filter. Spanner menerapkan pemindaian filter dalam kondisi tertentu:
- Kondisi dapat dicari: Kondisi dapat dicari berlaku jika Spanner dapat menentukan baris tertentu yang akan diakses dalam tabel. Secara umum, hal ini terjadi saat filter berada di awalan kunci utama. Misalnya, jika
kunci utama terdiri dari
Col1danCol2, maka klausaWHEREyang mencakup nilai eksplisit untukCol1, atauCol1danCol2dapat dicari. Dalam hal ini, Spanner hanya membaca data dalam rentang kunci. - Kondisi residual: Kondisi lain saat Spanner dapat mengevaluasi pemindaian untuk membatasi jumlah data yang dibaca.
Misalnya, menggunakan kueri ini:
SELECT lastname
FROM singers
WHERE singerid = 1
Berikut hasilnya:
| LastName |
|---|
| Richards |
Berikut adalah rencana eksekusi:
Batas
Operator limit membatasi jumlah baris yang ditampilkan. Parameter OFFSET
opsional menentukan baris awal yang akan ditampilkan. Untuk skenario terdistribusi, operator batas dapat dipisahkan menjadi pasangan lokal-global. Setiap server jarak jauh
menerapkan batas lokal untuk baris output-nya, lalu menampilkan hasilnya ke
server root. Server root menggabungkan baris yang dikirim oleh server jarak jauh, lalu menerapkan batas global.
Misalnya, menggunakan kueri ini:
SELECT s.songname
FROM songs AS s
LIMIT 3;
Berikut hasilnya:
| SongName |
|---|
| Bukan Tentang Gitar |
| Kedua Kalinya |
| Mulai Lagi |
Berikut adalah rencana eksekusi:
Batas lokal adalah batas untuk setiap server jarak jauh. Server root menggabungkan baris dari server jarak jauh, lalu menerapkan batas global.
Penetapan ID Acak
Operator penugasan ID acak menghasilkan output dengan membaca baris inputnya dan
menambahkan angka acak ke setiap baris. Fungsi ini kompatibel dengan operator Filter atau Sort
untuk mencapai metode pengambilan sampel. Metode pengambilan sampel yang didukung adalah
Bernoulli
dan Reservoir.
Misalnya, kueri berikut menggunakan pengambilan sampel Bernoulli dengan rasio pengambilan sampel sebesar 10 persen.
SELECT s.songname
FROM songs AS s TABLESAMPLE bernoulli (10 PERCENT);
Berikut hasilnya:
| SongName |
|---|
| Mulai Lagi |
| Tidak Ada yang Sama |
Perhatikan bahwa karena hasilnya adalah sampel, hasil dapat bervariasi setiap kali kueri dijalankan meskipun kuerinya sama.
Berikut adalah rencana eksekusi:
Dalam rencana eksekusi ini, operator Random Id Assign menerima inputnya dari
operator union terdistribusi, yang menerima inputnya
dari pemindaian indeks. Operator menampilkan baris dengan ID acak dan
operator Filter kemudian menerapkan predikat skalar pada ID acak dan
menampilkan sekitar 10 persen baris.
Contoh berikut menggunakan Reservoir
sampling dengan frekuensi pengambilan sampel 2 baris.
SELECT s.songname
FROM songs AS s TABLESAMPLE reservoir (2 rows);
Berikut hasilnya:
| SongName |
|---|
| I Knew You Were Magic |
| Kedua Kalinya |
Perhatikan bahwa karena hasilnya adalah sampel, hasil dapat bervariasi setiap kali kueri dijalankan meskipun kuerinya sama.
Berikut adalah rencana eksekusi:
Dalam rencana eksekusi ini, operator Random Id Assign menerima inputnya dari
operator union terdistribusi, yang menerima inputnya
dari pemindaian indeks. Operator menampilkan baris dengan ID acak dan
operator Sort kemudian menerapkan urutan pengurutan pada ID acak dan menerapkan
LIMIT dengan 2 baris.
Gabungan pemisahan lokal
Operator gabungan pemisahan lokal menemukan pemisahan tabel yang disimpan di server lokal, menjalankan subkueri pada setiap pemisahan, lalu membuat gabungan yang menggabungkan semua hasil.
Local split union muncul dalam rencana eksekusi yang memindai tabel placement. Penempatan dapat meningkatkan jumlah pemisahan dalam tabel, sehingga lebih efisien untuk memindai pemisahan dalam batch berdasarkan lokasi penyimpanan fisiknya.
Misalnya, tabel Singers menggunakan kunci penempatan untuk mempartisi
data penyanyi:
CREATE TABLE Singers (
SingerId INT64 NOT NULL,
SingerName STRING(MAX) NOT NULL,
...
Location STRING(MAX) NOT NULL PLACEMENT KEY
) PRIMARY KEY (SingerId);
Sekarang, pertimbangkan kueri ini:
SELECT BirthDate FROM Singers;
Berikut adalah rencana eksekusi:
Gabungan terdistribusi mengirimkan subkueri ke setiap batch
pemisahan yang disimpan secara fisik bersama di server yang sama. Di setiap server, local
split union menemukan bagian yang menyimpan data Singers, menjalankan subkueri di setiap
bagian, dan menampilkan hasil gabungan. Dengan cara ini, gabungan terdistribusi dan gabungan pemisahan lokal bekerja sama untuk memindai tabel Singers secara efisien. Tanpa
penggabungan pemisahan lokal, penggabungan terdistribusi akan mengirim satu RPC per pemisahan, bukan per batch pemisahan, sehingga menghasilkan perjalanan pulang pergi RPC yang berlebihan jika ada lebih dari satu pemisahan per batch.
RowToDataBlockAdapter
Operator RowToDataBlockAdapter otomatis disisipkan oleh
pengoptimal kueri Spanner di antara sepasang operator yang beroperasi
menggunakan metode eksekusi yang berbeda. Inputnya adalah operator yang menggunakan metode eksekusi berorientasi baris dan outputnya dimasukkan ke operator yang dieksekusi dalam metode eksekusi berorientasi batch. Untuk mengetahui informasi selengkapnya, lihat Mengoptimalkan eksekusi kueri.
Hasil serialisasi
Operator serialize result adalah kasus khusus dari operator compute struct yang melakukan serialisasi setiap baris hasil akhir kueri, untuk ditampilkan ke klien.
Misalnya, menggunakan kueri ini:
SELECT array
(
select as struct so.songname,
so.songgenre
FROM songs AS so
WHERE so.singerid = s.singerid)
FROM singers AS s;
Kueri meminta array SongName dan SongGenre berdasarkan SingerId.
Berikut hasilnya:
| Tidak ditentukan |
|---|
| [] |
| [[Let's Get Back Together, NEGARA], [Starting Again, ROCK]] |
| [[Not About The Guitar, BLUES]] |
| [] |
| [] |
Berikut adalah rencana eksekusi:
Operator hasil serialisasi membuat hasil yang berisi, untuk setiap baris tabel Singers, array pasangan SongName dan SongGenre untuk lagu-lagu dari penyanyi tersebut.
Urutkan
Operator sort membaca baris input, mengurutkannya berdasarkan kolom, lalu menampilkan hasil yang telah diurutkan.
Misalnya, menggunakan kueri ini:
SELECT s.songgenre
FROM songs AS s
ORDER BY songgenre;
Berikut hasilnya:
| SongGenre |
|---|
| BLUES |
| BLUES |
| BLUES |
| BLUES |
| KLASIK |
| NEGARA |
| ROCK |
| ROCK |
| ROCK |
Berikut adalah rencana eksekusi:
Dalam rencana eksekusi ini, operator pengurutan menerima baris inputnya dari operator gabungan terdistribusi, mengurutkan baris input, dan menampilkan baris yang diurutkan ke operator hasil serialisasi.
Untuk membatasi jumlah baris yang ditampilkan, operator pengurutan dapat secara opsional memiliki parameter
LIMIT dan OFFSET. Untuk skenario terdistribusi, operator pengurutan dengan
operator LIMIT atau OFFSET dipisahkan menjadi pasangan lokal-global. Setiap
server jarak jauh menerapkan urutan pengurutan dan batas atau offset lokal untuk baris inputnya, lalu menampilkan hasilnya ke server root. Server root
menggabungkan baris yang dikirim oleh server jarak jauh, mengurutkannya, lalu menerapkan
batas/offset global.
Misalnya, menggunakan kueri ini:
SELECT s.songgenre
FROM songs AS s
ORDER BY songgenre
LIMIT 3;
Berikut hasilnya:
| SongGenre |
|---|
| BLUES |
| BLUES |
| BLUES |
Berikut adalah rencana eksekusi:
Rencana eksekusi menampilkan batas lokal untuk server jarak jauh dan batas global untuk server root.
TVF
Operator fungsi bernilai tabel menghasilkan output dengan membaca baris inputnya dan menerapkan fungsi yang ditentukan. Fungsi ini dapat menerapkan pemetaan dan menampilkan jumlah baris yang sama dengan input. Hal ini juga dapat berupa generator yang menampilkan lebih banyak baris atau filter yang menampilkan lebih sedikit baris.
Misalnya, menggunakan kueri ini:
SELECT genre,
songname
FROM ml.predict(model genreclassifier, TABLE songs)
Berikut hasilnya:
| Genre | SongName |
|---|---|
| Negara | Bukan Tentang Gitar |
| Rock | Kedua Kalinya |
| Pop | Mulai Lagi |
| Pop | Tidak Ada yang Sama |
| Negara | Mari Kita Kembali Bersama |
| Pop | I Knew You Were Magic |
| Elektronik | Biru |
| Rock | 42 |
| Rock | Fight Story |
Berikut adalah rencana eksekusi:
Input gabungan
Operator input gabungan menampilkan hasil ke operator gabungan semua. Lihat operator gabungan semua untuk contoh operator input gabungan dalam rencana eksekusi.
Operator biner
Operator binary adalah operator yang memiliki dua turunan relasional. Operator berikut adalah operator biner:
- Penerapan silang
- Hash join
- Gabungkan penggabungan
- Gabungan hash siaran push
- Penerapan luar
- Gabungan rekursif
Menerapkan silang
Operator cross apply menjalankan kueri tabel pada setiap baris yang diambil dengan kueri tabel lain, dan menampilkan gabungan dari semua kueri tabel yang berjalan. Operator penerapan silang dan penerapan luar menjalankan pemrosesan berorientasi baris, tidak seperti operator yang menjalankan pemrosesan berbasis set seperti gabungan hash . Operator cross apply memiliki dua input, input dan map. Operator penerapan silang menerapkan setiap baris di sisi input ke sisi peta. Hasil penerapan silang memiliki kolom dari sisi input dan peta.
Misalnya, menggunakan kueri ini:
SELECT si.firstname,
(SELECT so.songname
FROM songs AS so
WHERE so.singerid = si.singerid
LIMIT 1)
FROM singers AS si;
Kueri meminta nama depan setiap penyanyi, beserta nama hanya salah satu lagu penyanyi tersebut.
Berikut hasilnya:
| FirstName | Tidak ditentukan |
|---|---|
| Alice | Bukan Tentang Gitar |
| Catalina | Mari Kita Kembali Bersama |
| David | NULL |
| Lea | NULL |
| Marc | NULL |
Kolom pertama diisi dari tabel Singers, dan kolom kedua diisi dari tabel Songs. Jika SingerId ada dalam tabel
Singers, tetapi tidak ada SingerId yang cocok dalam tabel Songs, kolom
kedua berisi NULL.
Berikut adalah rencana eksekusi:
Node tingkat teratas adalah operator gabungan terdistribusi. Operator gabungan terdistribusi mendistribusikan sub-rencana ke server jarak jauh. Subplan berisi operator serialize result yang menghitung nama depan penyanyi dan nama salah satu lagu penyanyi tersebut, lalu men-serialize setiap baris output.
Operator hasil serialisasi menerima inputnya dari operator lintas penerapan.
Sisi input untuk operator penerapan silang adalah pemindaian tabel pada tabel
Singers.
Sisi peta untuk operasi lintas penerapan berisi hal berikut (dari atas ke bawah):
- Operator aggregate yang menampilkan
Songs.SongName. - Operator limit yang membatasi jumlah lagu yang ditampilkan menjadi satu per penyanyi.
- Pemindaian indeks pada indeks
SongsBySingerAlbumSongNameDesc.
Operator penerapan silang memetakan setiap baris dari sisi input ke baris di sisi peta yang memiliki SingerId yang sama. Output operator cross apply adalah
nilai FirstName dari baris input, dan nilai SongName dari baris peta.
(Nilai SongName adalah NULL jika tidak ada baris peta yang cocok dengan
SingerId.) Operator gabungan terdistribusi di bagian atas rencana eksekusi
kemudian menggabungkan semua baris output dari server jarak jauh dan menampilkannya sebagai
hasil kueri.
Gabungan hash
Operator hash join adalah implementasi berbasis hash dari gabungan SQL. Gabungan hash mengeksekusi pemrosesan berbasis set. Operator hash join membaca baris dari input yang ditandai sebagai build dan memasukkannya ke dalam tabel hash berdasarkan kondisi join. Operator hash join kemudian membaca baris dari input yang ditandai sebagai probe. Untuk setiap baris yang dibaca dari input probe, operator hash join mencari baris yang cocok dalam tabel hash. Operator hash join menampilkan baris yang cocok sebagai hasilnya.
Misalnya, menggunakan kueri ini:
SELECT a.albumtitle,
s.songname
FROM albums AS a join@{join_method=hash_join} songs AS s
ON a.singerid = s.singerid
AND a.albumid = s.albumid;
Berikut hasilnya:
| AlbumTitle | SongName |
|---|---|
| Tidak Ada Hubungannya Dengan Saya | Bukan Tentang Gitar |
| Hijau | Kedua Kalinya |
| Hijau | Mulai Lagi |
| Hijau | Tidak Ada yang Sama |
| Hijau | Mari Kita Kembali Bersama |
| Hijau | I Knew You Were Magic |
| Hijau | Biru |
| Hijau | 42 |
| Ketakutan | Kisah Pertarungan |
Berikut adalah rencana eksekusi:
Dalam rencana eksekusi, build adalah gabungan terdistribusi yang
mendistribusikan pemindaian pada tabel Albums. Probe adalah operator gabungan terdistribusi
yang mendistribusikan pemindaian pada indeks SongsBySingerAlbumSongNameDesc.
Operator hash join membaca semua baris dari sisi build. Setiap baris build ditempatkan dalam tabel hash berdasarkan kolom dalam kondisi a.SingerId =
s.SingerId AND a.AlbumId = s.AlbumId. Selanjutnya, operator hash join membaca semua
baris dari sisi probe. Untuk setiap baris probe, operator hash join mencari kecocokan dalam tabel hash. Hasil kecocokan ditampilkan oleh operator gabungan hash.
Kecocokan yang dihasilkan dalam tabel hash juga dapat difilter berdasarkan kondisi residual sebelum ditampilkan. (Contoh kondisi residual muncul dalam gabungan non-persamaan). Rencana eksekusi gabungan hash bisa jadi rumit karena pengelolaan memori dan varian gabungan. Algoritma hash join utama diadaptasi untuk menangani varian gabungan dalam, semi, anti, dan luar.
Gabungkan gabungan
Operator merge join adalah implementasi gabungan SQL berbasis penggabungan. Kedua sisi
join menghasilkan baris yang diurutkan berdasarkan kolom yang digunakan dalam kondisi join. Penggabungan gabung menggunakan kedua aliran input secara bersamaan dan menghasilkan baris saat kondisi gabung terpenuhi. Jika input awalnya tidak diurutkan sesuai kebutuhan, pengoptimal akan menambahkan operator Sort eksplisit ke rencana.
Gabungan penggabungan tidak dipilih secara otomatis oleh pengoptimal. Untuk menggunakan
operator ini, tetapkan metode gabungan ke MERGE_JOIN pada petunjuk kueri, seperti yang ditunjukkan
dalam contoh berikut:
SELECT a.albumtitle,
s.songname
FROM albums AS a join@{join_method=merge_join} songs AS s
ON a.singerid = s.singerid
AND a.albumid = s.albumid;
Berikut hasilnya:
| AlbumTitle | SongName |
|---|---|
| Hijau | Kedua Kalinya |
| Hijau | Mulai Lagi |
| Hijau | Tidak Ada yang Sama |
| Hijau | Mari Kita Kembali Bersama |
| Hijau | I Knew You Were Magic |
| Hijau | Biru |
| Hijau | 42 |
| Ketakutan | Fight Story |
| Tidak Ada Hubungannya Dengan Saya | Bukan Tentang Gitar |
Berikut adalah rencana eksekusi:
Dalam rencana eksekusi ini, gabungan penggabungan didistribusikan sehingga gabungan dieksekusi di tempat data berada. Hal ini juga memungkinkan penggabungan gabungan dalam contoh ini beroperasi tanpa pengenalan operator pengurutan tambahan, karena pemindaian kedua tabel sudah diurutkan berdasarkan SingerId, AlbumId, yang merupakan kondisi gabungan. Dalam rencana ini, pemindaian sisi kiri tabel Albums akan maju
setiap kali SingerId, AlbumId-nya relatif lebih kecil daripada pemindaian indeks SongsBySingerAlbumSongNameDesc sisi kanan SingerId_1, AlbumId_1.
Demikian pula, sisi kanan akan maju setiap kali kurang dari sisi kiri. Penggabungan ini terus menelusuri kesamaan sehingga hasil yang cocok dapat ditampilkan.
Pertimbangkan contoh penggabungan gabungan lain menggunakan kueri berikut:
SELECT a.albumtitle,
s.songname
FROM albums AS a join@{join_method=merge_join} songs AS s
ON a.albumid = s.albumid;
Hasilnya adalah sebagai berikut:
| AlbumTitle | SongName |
|---|---|
| Sampah Total | Kedua Kalinya |
| Sampah Total | Mulai Lagi |
| Sampah Total | Tidak Ada yang Sama |
| Sampah Total | Mari Kita Kembali Bersama |
| Sampah Total | I Knew You Were Magic |
| Sampah Total | Biru |
| Sampah Total | 42 |
| Sampah Total | Bukan Tentang Gitar |
| Hijau | Kedua Kalinya |
| Hijau | Mulai Lagi |
| Hijau | Tidak Ada yang Sama |
| Hijau | Mari Kita Kembali Bersama |
| Hijau | I Knew You Were Magic |
| Hijau | Biru |
| Hijau | 42 |
| Hijau | Bukan Tentang Gitar |
| Tidak Ada Hubungannya Dengan Saya | Kedua Kalinya |
| Tidak Ada Hubungannya Dengan Saya | Mulai Lagi |
| Tidak Ada Hubungannya Dengan Saya | Tidak Ada yang Sama |
| Tidak Ada Hubungannya Dengan Saya | Mari Kita Kembali Bersama |
| Tidak Ada Hubungannya Dengan Saya | I Knew You Were Magic |
| Tidak Ada Hubungannya Dengan Saya | Biru |
| Tidak Ada Hubungannya Dengan Saya | 42 |
| Tidak Ada Hubungannya Dengan Saya | Bukan Tentang Gitar |
| Putar | Kedua Kalinya |
| Putar | Mulai Lagi |
| Putar | Tidak Ada yang Sama |
| Putar | Mari Kita Kembali Bersama |
| Putar | I Knew You Were Magic |
| Putar | Biru |
| Putar | 42 |
| Putar | Bukan Tentang Gitar |
| Ketakutan | Kisah Pertarungan |
Berikut adalah rencana eksekusi:
Dalam rencana eksekusi sebelumnya, operator Sort tambahan telah
diperkenalkan oleh pengoptimal kueri untuk mencapai properti yang diperlukan agar
penggabungan gabungan dapat dieksekusi. Kondisi JOIN dalam kueri contoh ini hanya ada di
AlbumId, yang bukan cara data disimpan, sehingga pengurutan harus ditambahkan. Mesin kueri mendukung algoritma Penggabungan Terdistribusi, sehingga pengurutan dapat terjadi secara lokal, bukan global, yang mendistribusikan dan memparalelkan biaya CPU.
Hasil kecocokan juga dapat difilter berdasarkan kondisi residual sebelum ditampilkan. (Contoh tempat kondisi residual muncul adalah dalam gabungan non-persamaan). Rencana eksekusi gabungan penggabungan dapat menjadi rumit karena persyaratan pengurutan tambahan. Algoritma gabungan penggabungan utama disesuaikan untuk menangani varian gabungan dalam, semi, anti, dan luar.
Hash join siaran push
Operator push broadcast hash join adalah implementasi gabungan SQL berbasis hash-join terdistribusi. Operator gabungan hash siaran push membaca baris dari sisi input untuk membuat batch data. Batch tersebut kemudian disiarkan ke semua server yang berisi data sisi peta. Di server tujuan tempat batch data diterima, gabungan hash dibuat menggunakan batch sebagai data sisi pembuatan dan data lokal kemudian dipindai sebagai sisi pemeriksaan gabungan hash.
Push broadcast hash join tidak dipilih secara otomatis oleh pengoptimal. Untuk menggunakan
operator ini, tetapkan metode gabungan ke PUSH_BROADCAST_HASH_JOIN pada
petunjuk kueri, seperti yang ditunjukkan dalam contoh berikut:
SELECT a.albumtitle,
s.songname
FROM albums AS a join@{join_method=push_broadcast_hash_join} songs AS s
ON a.singerid = s.singerid
AND a.albumid = s.albumid;
Berikut hasilnya:
| AlbumTitle | SongName |
|---|---|
| Hijau | Kedua Kalinya |
| Hijau | Mulai Lagi |
| Hijau | Tidak Ada yang Sama |
| Hijau | Mari Kita Kembali Bersama |
| Hijau | I Knew You Were Magic |
| Hijau | Biru |
| Hijau | 42 |
| Ketakutan | Fight Story |
| Tidak Ada Hubungannya Dengan Saya | Bukan Tentang Gitar |
Berikut adalah rencana eksekusi:
Input untuk gabungan hash siaran Push adalah indeks AlbumsByAlbumTitle.
Input tersebut diserialisasi menjadi batch data. Batch tersebut kemudian dikirim ke semua
pemisahan lokal indeks SongsBySingerAlbumSongNameDesc, tempat batch
kemudian dideserialisasi dan dibangun menjadi tabel hash. Tabel hash kemudian menggunakan data indeks lokal sebagai probe yang menampilkan kecocokan yang dihasilkan.
Hasil kecocokan juga dapat difilter berdasarkan kondisi residual sebelum ditampilkan. (Contoh tempat kondisi residual muncul adalah dalam gabungan non-persamaan).
Penerapan luar
Operator outer apply mirip dengan operator cross apply, kecuali operator outer apply memastikan bahwa setiap eksekusi di sisi peta menampilkan setidaknya satu baris dengan menghasilkan baris NULL-padded jika diperlukan. (Dengan kata lain, ini memberikan semantik left outer join.)
Gabungan rekursif
Operator recursive union melakukan gabungan dua input, satu yang merepresentasikan kasus base, dan yang lainnya merepresentasikan kasus recursive. Fungsi ini digunakan dalam
kueri grafik dengan traversal jalur yang dikuantifikasi. Input dasar diproses terlebih dahulu
dan tepat satu kali. Input rekursif diproses hingga rekursi
berakhir. Rekursi berakhir saat batas atas, jika ditentukan, tercapai, atau saat rekursi tidak menghasilkan hasil baru. Dalam contoh
berikut, tabel Collaborations ditambahkan ke skema, dan grafik properti
bernama MusicGraph dibuat.
CREATE TABLE Collaborations (
SingerId INT64 NOT NULL,
FeaturingSingerId INT64 NOT NULL,
AlbumTitle STRING(MAX) NOT NULL,
) PRIMARY KEY(SingerId, FeaturingSingerId, AlbumTitle);
CREATE OR REPLACE PROPERTY GRAPH MusicGraph
NODE TABLES(
Singers
KEY(SingerId)
LABEL Singers PROPERTIES(
BirthDate,
FirstName,
LastName,
SingerId,
SingerInfo)
)
EDGE TABLES(
Collaborations AS CollabWith
KEY(SingerId, FeaturingSingerId, AlbumTitle)
SOURCE KEY(SingerId) REFERENCES Singers(SingerId)
DESTINATION KEY(FeaturingSingerId) REFERENCES Singers(SingerId)
LABEL CollabWith PROPERTIES(
AlbumTitle,
FeaturingSingerId,
SingerId),
);
Kueri grafik berikut menemukan penyanyi yang telah berkolaborasi dengan penyanyi tertentu atau berkolaborasi dengan kolaborator tersebut.
GRAPH MusicGraph
MATCH (singer:Singers {singerId:42})-[c:CollabWith]->{1,2}(featured:Singers)
RETURN singer.SingerId AS singer, featured.SingerId AS featured
Operator recursive union memfilter tabel Singers untuk menemukan penyanyi
dengan SingerId yang diberikan. Ini adalah input dasar untuk gabungan rekursif. Input rekursif ke gabungan rekursif terdiri dari penerapan silang terdistribusi atau operator gabungan lainnya untuk kueri lain yang berulang kali menggabungkan tabel Collaborations dengan hasil iterasi gabungan sebelumnya. Baris dari input dasar membentuk iterasi nol.
Pada setiap iterasi, output iterasi disimpan oleh pemindaian
spul rekursif. Baris dari recursive spool scan digabungkan dengan tabel Collaborations
pada spoolscan.featuredSingerId = Collaborations.SingerId. Rekursi
berakhir saat dua iterasi selesai, karena itulah batas
atas yang ditentukan dalam kueri.
Operator N-ary
Operator N-ary adalah operator yang memiliki lebih dari dua turunan relasional. Operator berikut adalah operator N-ary:
Gabungkan semua
Operator gabungan semua menggabungkan semua set baris turunannya tanpa menghapus duplikat. Operator gabungan menerima input dari operator input gabungan yang didistribusikan di beberapa server. Operator gabungan semua mensyaratkan bahwa inputnya memiliki skema yang sama, yaitu kumpulan jenis data yang sama untuk setiap kolom.
Misalnya, menggunakan kueri ini:
SELECT 1 a,
2 b
UNION ALL
SELECT 3 a,
4 b
UNION ALL
SELECT 5 a,
6 b;
Jenis baris untuk turunan terdiri dari dua bilangan bulat.
Berikut hasilnya:
| a | b |
|---|---|
| 1 | 2 |
| 3 | 4 |
| 5 | 6 |
Berikut adalah rencana eksekusi:
Operator gabungan semua menggabungkan baris inputnya, dan dalam contoh ini, operator tersebut mengirimkan hasil ke operator serialize result.
Kueri seperti berikut akan berhasil, karena set jenis data yang sama digunakan untuk setiap kolom, meskipun turunan menggunakan variabel yang berbeda untuk nama kolom:
SELECT 1 a,
2 b
UNION ALL
SELECT 3 c,
4 e;
Kueri seperti berikut tidak akan berhasil, karena kolom anak menggunakan jenis data yang berbeda:
SELECT 1 a,
2 b
UNION ALL
SELECT 3 a,
'This is a string' b;
Subkueri skalar
Subkueri skalar adalah sub-ekspresi SQL yang merupakan bagian dari ekspresi skalar. Spanner akan berusaha menghapus subkueri skalar jika memungkinkan. Namun, dalam skenario tertentu, rencana dapat secara eksplisit berisi subkueri skalar.
Misalnya, menggunakan kueri ini:
SELECT firstname,
IF(firstname = 'Alice', (SELECT Count(*)
FROM songs
WHERE duration > 300), 0)
FROM singers;
Berikut adalah sub-ekspresi SQL:
SELECT Count(*)
FROM songs
WHERE duration > 300;
Berikut hasilnya (dari kueri lengkap):
| FirstName | |
|---|---|
| Alice | 1 |
| Catalina | 0 |
| David | 0 |
| Lea | 0 |
| Marc | 0 |
Berikut adalah rencana eksekusi:
Rencana eksekusi berisi subkueri skalar, yang ditampilkan sebagai Scalar Subquery, melalui operator aggregate.
Spanner terkadang mengonversi subkueri skalar menjadi operator lain seperti gabungan atau penerapan silang, untuk meningkatkan performa.
Misalnya, menggunakan kueri ini:
SELECT *
FROM songs
WHERE duration = (SELECT Max(duration)
FROM songs);
Berikut adalah sub-ekspresi SQL:
SELECT MAX(Duration)
FROM Songs;
Berikut hasilnya (dari kueri lengkap):
| SingerId | AlbumId | TrackId | SongName | Durasi | SongGenre |
|---|---|---|---|---|---|
| 2 | 1 | 6 | Tidak Ada yang Sama | 303 | BLUES |
Berikut adalah rencana eksekusi:
Rencana eksekusi tidak berisi subkueri skalar karena Spanner mengonversi subkueri skalar menjadi cross apply.
Subkueri array
Subkueri array mirip dengan subkueri skalar, kecuali subkueri diizinkan untuk menggunakan lebih dari satu baris input. Baris yang digunakan dikonversi menjadi array output skalar tunggal yang berisi satu elemen per baris input yang digunakan.
Misalnya, menggunakan kueri ini:
SELECT a.albumid,
array
(
select concertdate
FROM concerts
WHERE concerts.singerid = a.singerid)
FROM albums AS a;
Ini adalah subkueri:
SELECT concertdate
FROM concerts
WHERE concerts.singerid = a.singerid;
Hasil subkueri untuk setiap AlbumId dikonversi menjadi array baris ConcertDate terhadap AlbumId tersebut. Rencana eksekusi berisi subkueri array, yang ditampilkan sebagai Array Subquery, di atas operator gabungan terdistribusi:
Operator terdistribusi
Operator yang dijelaskan sebelumnya di halaman ini dieksekusi dalam batas satu mesin. Operator terdistribusi dijalankan di beberapa server.
Operator berikut adalah operator terdistribusi:
- Gabungan terdistribusi
- Gabungan penggabungan terdistribusi
- Penerapan silang terdistribusi
- Penerapan luar terdistribusi
- Menerapkan mutasi
Operator gabungan terdistribusi adalah operator primitif yang darinya cross apply terdistribusi dan outer apply terdistribusi berasal.
Operator terdistribusi muncul dalam rencana eksekusi dengan varian union terdistribusi di atas satu atau beberapa varian union terdistribusi lokal. Varian gabungan terdistribusi melakukan distribusi jarak jauh sub-paket. Varian gabungan terdistribusi lokal berada di atas setiap pemindaian yang dilakukan untuk kueri, seperti yang ditunjukkan dalam rencana eksekusi ini:
Varian penggabungan terdistribusi lokal memastikan eksekusi kueri yang stabil saat terjadi mulai ulang untuk batas pemisahan yang berubah secara dinamis.
Jika memungkinkan, varian gabungan terdistribusi memiliki predikat pemisahan yang menghasilkan pemangkasan pemisahan, yang berarti server jarak jauh hanya menjalankan subrencana pada pemisahan yang memenuhi predikat. Hal ini meningkatkan latensi dan performa kueri secara keseluruhan.
Gabungan terdistribusi
Operator gabungan terdistribusi secara konseptual membagi satu atau beberapa tabel menjadi beberapa pemisahan, mengevaluasi subkueri secara terpisah pada setiap pemisahan, lalu menggabungkan semua hasil.
Misalnya, menggunakan kueri ini:
SELECT s.songname,
s.songgenre
FROM songs AS s
WHERE s.singerid = 2
AND s.songgenre = 'ROCK';
Berikut hasilnya:
| SongName | SongGenre |
|---|---|
| Mulai Lagi | ROCK |
| Kedua Kalinya | ROCK |
| Kisah Pertarungan | ROCK |
Berikut adalah rencana eksekusi:
Operator gabungan terdistribusi mengirimkan subrencana ke server jarak jauh, yang melakukan
pemindaian tabel di seluruh pemisahan yang memenuhi predikat kueri WHERE
s.SingerId = 2 AND s.SongGenre = 'ROCK'. Operator serialize
result menghitung nilai SongName dan SongGenre
dari baris yang ditampilkan oleh pemindaian tabel. Operator gabungan terdistribusi
kemudian menampilkan hasil gabungan dari server jarak jauh sebagai hasil
kueri SQL.
Gabungan penggabungan terdistribusi
Operator gabungan penggabungan terdistribusi mendistribusikan kueri di beberapa server jarak jauh. Kemudian, hasil kueri digabungkan untuk menghasilkan hasil yang diurutkan, yang dikenal sebagai penggabungan pengurutan terdistribusi.
Gabungan penggabungan terdistribusi menjalankan langkah-langkah berikut:
Server root mengirimkan subkueri ke setiap server jarak jauh yang menghosting split data yang dikueri. Subkueri menyertakan petunjuk bahwa hasil diurutkan dalam urutan tertentu.
Setiap server jarak jauh menjalankan subkueri pada pemisahannya, lalu mengirimkan kembali hasilnya dalam urutan yang diminta.
Server root menggabungkan subkueri yang diurutkan untuk menghasilkan hasil yang diurutkan sepenuhnya.
Gabungan penggabungan terdistribusi diaktifkan secara default untuk Spanner Versi 3 dan yang lebih baru.
Penerapan silang terdistribusi
Operator cross apply terdistribusi (DCA) memperluas operator cross apply dengan mengeksekusi di beberapa server. Sisi input DCA mengelompokkan batch baris (tidak seperti operator penerapan silang biasa, yang hanya bertindak pada satu baris input dalam satu waktu). Sisi peta DCA adalah sekumpulan operator penerapan silang yang dijalankan di server jarak jauh.
Misalnya, menggunakan kueri ini:
SELECT albumtitle
FROM songs
JOIN albums
ON albums.albumid = songs.albumid;
Hasilnya dalam format:
| AlbumTitle |
|---|
| Hijau |
| Tidak Ada Hubungannya Dengan Saya |
| Putar |
| Sampah Total |
| Hijau |
Berikut adalah rencana eksekusi:
Input DCA berisi pemindaian indeks pada indeks
SongsBySingerAlbumSongNameDesc yang mengelompokkan baris AlbumId. Sisi peta untuk operator penerapan silang ini adalah pemindaian indeks pada indeks AlbumsByAlbumTitle, yang tunduk pada predikat AlbumId dalam baris input yang cocok dengan kunci AlbumId dalam indeks AlbumsByAlbumTitle. Pemetaan
menampilkan SongName untuk nilai SingerId dalam baris input yang dikelompokkan.
Untuk meringkas proses DCA dalam contoh ini, input DCA adalah baris yang dikelompokkan dari tabel Albums, dan output DCA adalah penerapan baris ini ke peta pemindaian indeks.
Penerapan luar terdistribusi
Operator distributed outer apply memperluas operator outer apply dengan menjalankan beberapa server, mirip dengan cara operator distributed cross apply memperluas operator cross apply.
Misalnya, menggunakan kueri ini:
SELECT lastname,
concertdate
FROM singers LEFT OUTER join@{JOIN_TYPE=APPLY_JOIN} concerts
ON singers.singerid=concerts.singerid;
Hasilnya dalam format:
| LastName | ConcertDate |
|---|---|
| Trentor | 2014-02-18 |
| Smith | 2011-09-03 |
| Smith | 2010-06-06 |
| Lomond | 2005-04-30 |
| Martin | 2015-11-04 |
| Richards |
Berikut adalah rencana eksekusi:
Menerapkan mutasi
Operator apply mutations menerapkan mutasi dari Pernyataan Manipulasi Data (DML) ke tabel. Operator ini adalah operator teratas dalam paket kueri untuk pernyataan DML.
Misalnya, menggunakan kueri ini:
DELETE FROM singers
WHERE firstname = 'Alice';
Berikut hasilnya:
4 rows deleted This statement deleted 4 rows and did not return any rows.
Berikut adalah rencana eksekusi:
Informasi tambahan
Bagian ini menjelaskan item yang bukan operator mandiri, tetapi menjalankan tugas untuk mendukung satu atau beberapa operator yang tercantum sebelumnya. Item yang dijelaskan di sini secara teknis adalah operator, tetapi bukan operator terpisah dalam rencana kueri Anda.
Konstruktor struct
Konstruktor struct membuat struct, atau kumpulan kolom. Biasanya membuat struct untuk baris yang dihasilkan dari operasi komputasi. Konstruktor struct bukan operator mandiri. Sebagai gantinya, operator ini muncul di operator compute struct atau operator serialize result.
Untuk operasi struct komputasi, konstruktor struct membuat struct sehingga kolom untuk baris yang dikomputasi dapat menggunakan satu referensi variabel ke struct.
Untuk operasi hasil serialisasi, konstruktor struct membuat struct untuk menserialisasi hasilnya.
Misalnya, menggunakan kueri ini:
SELECT IF(TRUE, struct(1 AS A, 1 AS B), struct(2 AS A , 2 AS B)).A;
Berikut hasilnya:
| A |
|---|
| 1 |
Berikut adalah rencana eksekusi:
Dalam rencana eksekusi, konstruktor struct muncul di dalam operator hasil serialisasi.