All Products
Search
Document Center

MaxCompute:Common table expression (CTE)

Last Updated:Aug 25, 2026

COMMON TABLE EXPRESSION (CTE) adalah set hasil bernama sementara yang menyederhanakan SQL. MaxCompute mendukung CTE SQL standar untuk meningkatkan keterbacaan dan efisiensi eksekusi pernyataan SQL. Topik ini menjelaskan fitur, sintaksis, dan contoh CTE.

Ikhtisar

  • CTE dapat dianggap sebagai set hasil sementara yang didefinisikan dalam cakupan eksekusi satu pernyataan DML. Mirip dengan tabel turunan, CTE tidak disimpan sebagai objek dan hanya bertahan selama durasi kueri. Penggunaan CTE selama pengembangan meningkatkan keterbacaan SQL dan menyederhanakan pemeliharaan kueri kompleks.

  • CTE merupakan ekspresi klausa tingkat pernyataan yang dimulai dengan WITH, diikuti oleh nama ekspresi. MaxCompute mendukung dua bentuk CTE:

    • NON RECURSIVE CTE: CTE yang tidak mereferensi dirinya sendiri dan tidak melakukan iterasi. Gunakan ini untuk menyederhanakan kueri yang menggunakan kembali logika subkueri yang sama.

    • RECURSIVE CTE: CTE yang dapat mereferensi dirinya sendiri secara iteratif, memungkinkan kemampuan kueri rekursif dalam SQL. Biasanya digunakan untuk menelusuri data hierarkis seperti struktur organisasi.

  • Materialized CTE: Saat mendefinisikan CTE, gunakan HINT MATERIALIZE dalam pernyataan SELECT untuk menyimpan cache hasil CTE ke dalam tabel temporary. Referensi berikutnya membaca dari cache, menghindari masalah batas memori pada skenario CTE bersarang dalam serta meningkatkan performa.

NON RECURSIVE CTE

Sintaksis

WITH
  <cte_name> [(col_name [, col_name] ...)] AS (
    <cte_query>
  )
  [, <cte_name> [(col_name [, col_name] ...)] AS (
    <cte_query2>
  )
  , ...]

Parameter

Parameter

Wajib

Deskripsi

cte_name

Ya

Nama CTE. Harus unik dalam klausa WITH. Setiap referensi ke cte_name di bagian selanjutnya dalam pernyataan akan membaca dari CTE ini.

col_name

Tidak

Nama kolom output untuk CTE. Jika dihilangkan, nama kolom diwariskan dari daftar SELECT dalam cte_query.

cte_query

Ya

Pernyataan SELECT yang set hasilnya mendefinisikan CTE.

Contoh

Kueri berikut menggunakan UNION ALL untuk menggabungkan dua operasi JOIN. Kedua JOIN tersebut menggunakan subkueri sebelah kiri yang sama, sehingga harus diduplikasi tanpa CTE:

INSERT OVERWRITE TABLE srcp PARTITION (p='abc')
SELECT * FROM (
    SELECT a.key, b.value
    FROM (
        SELECT * FROM src WHERE key IS NOT NULL) a
    JOIN (
        SELECT * FROM src2 WHERE value > 0) b
    ON a.key = b.key
) c
UNION ALL
SELECT * FROM (
    SELECT a.key, b.value
    FROM (
        SELECT * FROM src WHERE key IS NOT NULL) a
    LEFT OUTER JOIN (
        SELECT * FROM src3 WHERE value > 0) b
    ON a.key = b.key AND b.key IS NOT NULL
) d;

Dengan menulis ulang menggunakan CTE, duplikasi dihilangkan. Subkueri a didefinisikan sekali dan digunakan kembali oleh kedua JOIN:

WITH
  a AS (SELECT * FROM src WHERE key IS NOT NULL),
  b AS (SELECT * FROM src2 WHERE value > 0),
  c AS (SELECT * FROM src3 WHERE value > 0),
  d AS (SELECT a.key, b.value FROM a JOIN b ON a.key = b.key),
  e AS (SELECT a.key, c.value FROM a LEFT OUTER JOIN c ON a.key = c.key AND c.key IS NOT NULL)
INSERT OVERWRITE TABLE srcp PARTITION (p='abc')
SELECT * FROM d UNION ALL SELECT * FROM e;

RECURSIVE CTE

Sintaksis

WITH RECURSIVE <cte_name> [(col_name [, col_name] ...)] AS (
  <initial_part> UNION ALL <recursive_part>
)
SELECT ... FROM ...;

Parameter

Parameter

Wajib

Deskripsi

RECURSIVE

Ya

Klausa CTE rekursif harus dimulai dengan WITH RECURSIVE.

cte_name

Ya

Nama CTE. Harus unik dalam klausa WITH saat ini.

col_name

Tidak

Nama kolom output. Jika dihilangkan, nama kolom diinferensi dari initial_part.

initial_part

Ya

Pernyataan SELECT yang menghasilkan set data seed (iterasi 0). Tidak boleh mereferensi cte_name.

recursive_part

Ya

Pernyataan SELECT yang mereferensi cte_name untuk menghitung iterasi berikutnya dari iterasi sebelumnya.

UNION ALL

Ya

Menghubungkan initial_part dan recursive_part. UNION (tanpa ALL) tidak didukung.

Batasan

  • CTE rekursif tidak dapat muncul dalam subkueri IN, EXISTS, atau subkueri skalar.

  • Jumlah maksimum iterasi default adalah 10. Tingkatkan batas ini dengan mengatur odps.sql.rcte.max.iterate.num (nilai maksimum: 100).

  • Hasil antara tidak disimpan antar iterasi. Jika task gagal, eksekusi dimulai ulang dari awal. Untuk komputasi rekursif berdurasi panjang, batasi jumlah iterasi atau simpan hasil antara dalam tabel temporary.

  • CTE rekursif tidak didukung dalam mode Query Acceleration (MaxQA/MCQA).

    • Dalam mode MCQA, jika interactive_auto_rerun=true diatur, task akan fallback ke mode normal. Jika tidak, task gagal.

    • Dalam mode MaxQA, auto-fallback tidak didukung. Pekerjaan gagal langsung dan harus dikirimkan secara manual ke kelompok kuota Pemrosesan batch untuk dicoba ulang.

Contoh

  • Contoh 1: Mendefinisikan CTE rekursif bernama cte_name.

    -- Metode 1: Menentukan eksplisit nama kolom output
    WITH RECURSIVE cte_name(a, b) AS (
      SELECT 1L, 1L                                   -- initial_part: iterasi 0
      UNION ALL
      SELECT a+1, b+1 FROM cte_name WHERE a+1 <= 5    -- recursive_part: mereferensi iterasi sebelumnya
    )
    SELECT * FROM cte_name ORDER BY a LIMIT 100;
    
    -- Metode 2: Menginferensi nama kolom dari initial_part
    WITH RECURSIVE cte_name AS (
      SELECT 1L AS a, 1L AS b
      UNION ALL
      SELECT a+1, b+1 FROM cte_name WHERE a+1 <= 5
    )
    SELECT * FROM cte_name ORDER BY a LIMIT 100;
    Catatan
    • Dalam recursive_part, tetapkan kondisi terminasi untuk menghindari loop tak terbatas. Dalam contoh ini, WHERE a + 1 <= 5 berfungsi sebagai kriteria terminasi. Jika kondisi WHERE tidak terpenuhi, dataset yang dihasilkan pada iterasi saat ini kosong, dan iterasi berhenti.

    • Jika nama kolom output tidak ditentukan secara eksplisit, sistem mendukung inferensi otomatis. Misalnya, pada Metode 2, nama kolom output dari initial_part digunakan sebagai nama kolom output CTE rekursif.

    Hasil:

    +------------+------------+
    | a          | b          |
    +------------+------------+
    | 1          | 1          |
    | 2          | 2          |
    | 3          | 3          |
    | 4          | 4          |
    | 5          | 5          |
    +------------+------------+

  • Contoh 2: CTE rekursif dalam subkueri (error kompilasi)

    CTE rekursif tidak diperbolehkan di dalam subkueri IN, EXISTS, atau subkueri skalar. Kueri berikut gagal dikompilasi:

    WITH RECURSIVE cte_name(a, b) AS (
      SELECT 1L, 1L
      UNION ALL
      SELECT a+1, b+1 FROM cte_name WHERE a+1 <= 5)
    SELECT x, x IN (SELECT a FROM cte_name) FROM VALUES (1L), (2L) AS t(x);

    Error:

    FAILED: ODPS-0130071:[5,31] Semantic analysis exception - using Recursive-CTE cte_name in scalar/in/exists sub-query is not allowed, please check your query, the query text location is from [line 5, column 13] to [line 5, column 40]

  • Contoh 3: Menelusuri hierarki organisasi

    Buat tabel employees dan masukkan data:

    CREATE TABLE employees(name STRING, boss_name STRING);
    INSERT INTO TABLE employees VALUES
      ('zhang_3', null),
      ('li_4',    'zhang_3'),
      ('wang_5',  'zhang_3'),
      ('zhao_6',  'li_4'),
      ('qian_7',  'wang_5');

    Definisikan CTE rekursif bernama company_hierarchy dengan tiga kolom output: nama karyawan, nama manajernya, dan levelnya dalam hierarki:

    WITH RECURSIVE company_hierarchy(name, boss_name, level) AS (
        SELECT name, boss_name, 0L FROM employees WHERE boss_name IS NULL
        UNION ALL
        SELECT e.name, e.boss_name, h.level + 1
          FROM employees e
          JOIN company_hierarchy h ON e.boss_name = h.name
    )
    SELECT * FROM company_hierarchy ORDER BY level, boss_name, name LIMIT 1000;

    Eksekusi berlangsung sebagai berikut:

    • Iterasi 0 (initial_part): Memilih karyawan yang boss_name IS NULL, memberikan level = 0. Hasil: ('zhang_3', NULL, 0).

    • Iterasi 1 (recursive_part): Melakukan JOIN antara employees dengan tabel kerja (iterasi 0). Kondisi e.boss_name = h.name menemukan karyawan yang dikelola oleh zhang_3. Hasil: li_4 dan wang_5 pada level = 1.

    • Iterasi 2: Menemukan karyawan yang dikelola oleh li_4 atau wang_5. Hasil: zhao_6 dan qian_7 pada level = 2.

    • Iterasi 3: Tidak ada karyawan yang memiliki zhao_6 atau qian_7 sebagai manajer. Tabel kerja kosong, dan iterasi berhenti.

    Hasil:

    +---------+-----------+------------+
    | name    | boss_name | level      |
    +---------+-----------+------------+
    | zhang_3 | NULL      | 0          |
    | li_4    | zhang_3   | 1          |
    | wang_5  | zhang_3   | 1          |
    | zhao_6  | li_4      | 2          |
    | qian_7  | wang_5    | 2          |
    +---------+-----------+------------+

  • Contoh 4: Data siklik menyebabkan loop tak terbatas

    Masukkan catatan ke dalam tabel employees dari Contoh 3 di mana seorang karyawan menjadi manajer dirinya sendiri.

    INSERT INTO TABLE employees VALUES('qian_7', 'qian_7');

    Catatan ini menyatakan bahwa manajer dari qian_7 adalah qian_7 sendiri. Menjalankan CTE rekursif yang didefinisikan sebelumnya menghasilkan loop tak terbatas. Sistem membatasi jumlah maksimum iterasi. Kueri akhirnya gagal.

    Error:

    FAILED: ODPS-0010000:System internal error - recursive-cte: company_hierarchy exceed max iterate number 10

Materialized CTE

Untuk CTE non-rekursif, MaxCompute melakukan ekspansi semua CTE secara inline saat menghasilkan rencana eksekusi. Contohnya:

WITH v1 AS (SELECT SIN(1.0) AS a)
SELECT a FROM v1 UNION ALL SELECT a FROM v1;

Hasil:

+------------+
| a          |
+------------+
| 0.8414709848078965 |
| 0.8414709848078965 |
+------------+

Ini setara dengan menjalankan SIN(1.0) dua kali:

SELECT a FROM (SELECT SIN(1.0) AS a)
UNION ALL
SELECT a FROM (SELECT SIN(1.0) AS a);

Dalam skenario kompleks dengan CTE bersarang dalam, jika semua CTE diekspansi menjadi node daun paling dasar, hasilnya adalah pohon sintaksis yang sangat besar. Hal ini dapat menyebabkan kegagalan akibat jumlah node pohon sintaksis yang berlebihan selama pembuatan rencana eksekusi dan dapat menimbulkan masalah batas memori. Contohnya:

WITH
  v1 AS (SELECT 1L AS a, 2L AS b, 3L AS c),
  v2 AS (SELECT * FROM v1 UNION ALL SELECT * FROM v1 UNION ALL SELECT * FROM v1),
  v3 AS (SELECT * FROM v2 UNION ALL SELECT * FROM v2 UNION ALL SELECT * FROM v2),
  v4 AS (SELECT * FROM v3 UNION ALL SELECT * FROM v3 UNION ALL SELECT * FROM v3),
  v5 AS (SELECT * FROM v4 UNION ALL SELECT * FROM v4 UNION ALL SELECT * FROM v4),
  v6 AS (SELECT * FROM v5 UNION ALL SELECT * FROM v5 UNION ALL SELECT * FROM v5),
  v7 AS (SELECT * FROM v6 UNION ALL SELECT * FROM v6 UNION ALL SELECT * FROM v6),
  v8 AS (SELECT * FROM v7 UNION ALL SELECT * FROM v7 UNION ALL SELECT * FROM v7),
  v9 AS (SELECT * FROM v8 UNION ALL SELECT * FROM v8 UNION ALL SELECT * FROM v8)
SELECT * FROM v9;

Untuk mengatasi masalah ini, MaxCompute menyediakan kemampuan Materialized CTE, yang menyimpan cache hasil komputasi CTE untuk direferensikan oleh SQL di luar klausa WITH tanpa ekspansi penuh. Mekanisme ini secara efektif menghindari masalah batas memori akibat ekspansi CTE bersarang dan meningkatkan performa pernyataan CTE.

Contoh penggunaan

Tambahkan hint /*+ MATERIALIZE */ ke pernyataan SELECT tingkat atas dari CTE non-rekursif untuk menyimpan cache hasilnya ke dalam tabel temporary. Referensi berikutnya membaca dari cache alih-alih menjalankan kueri ulang:

WITH v1 AS (SELECT /*+ MATERIALIZE */ SIN(1.0) AS a)
SELECT a FROM v1 UNION ALL SELECT a FROM v1;

-- Hasil:
+------------+
| a          |
+------------+
| 0.8414709848078965 |
| 0.8414709848078965 |
+------------+

Saat hint berlaku, tab Job Details di LogView menampilkan beberapa Fuxi Jobs, yang mengonfirmasi bahwa hasil antara telah disimpan.

Batasan

  • HINT MATERIALIZE harus diterapkan pada pernyataan SELECT tingkat atas dari CTE non-rekursif. CTE rekursif tidak memerlukan hint MATERIALIZE.

    • Contoh salah: Pada CTE berikut, pernyataan tingkat atas adalah UNION, bukan SELECT, sehingga hint MATERIALIZE tidak berlaku.

      WITH v1 AS (
        SELECT /*+ MATERIALIZE */ SIN(1.0) AS a
        UNION ALL
        SELECT /*+ MATERIALIZE */ SIN(1.0) AS a)
      SELECT a FROM v1 UNION ALL SELECT a FROM v1;
    • Contoh benar: Tulis ulang contoh salah di atas dengan membungkusnya dalam subkueri.

      WITH 
        v1 AS (SELECT /*+ MATERIALIZE */ * FROM 
                 (SELECT SIN(1.0) AS a
                  UNION ALL 
                  SELECT SIN(1.0) AS a)
              ) 
      SELECT a FROM v1 UNION ALL SELECT a FROM v1;
  • Jika CTE menggunakan fungsi non-deterministik seperti RAND atau UDF Java/Python non-deterministik, materialisasi CTE menyimpan cache satu evaluasi fungsi tersebut. Referensi berikutnya mengembalikan nilai cache, yang mengubah perilaku semantik kueri yang mengharapkan nilai acak independen per pemanggilan.

  • CTE termaterialisasi tidak didukung di MCQA(Query Acceleration 1.0) -deprecated. Jika task dijalankan dalam mode MCQA dengan interactive_auto_rerun=true, task akan fallback ke mode normal. Jika tidak, task gagal.