PostgreSQL
 sql >> Teknologi Basis Data >  >> RDS >> PostgreSQL

Oracle ke PostgreSQL:MULAI DENGAN/HUBUNGKAN OLEH

Dan sekarang kita sampai pada artikel kedua dalam migrasi kita dari seri Oracle ke PostgreSQL. Kali ini kita akan melihat START WITH/CONNECT BY konstruksi.

Di Oracle, START WITH/CONNECT BY digunakan untuk membuat struktur daftar tertaut tunggal mulai dari baris sentinel tertentu. Daftar tertaut dapat berbentuk pohon, dan tidak memiliki persyaratan penyeimbangan.

Sebagai ilustrasi, mari kita mulai dengan kueri, dan anggap tabel memiliki 5 baris di dalamnya.

SELECT * FROM person;
 last_name  | first_name | id | parent_id
------------+------------+----+-----------
 Dunstan    | Andrew     |  1 |    (null)
 Roybal     | Kirk       |  2 |         1
 Riggs      | Simon      |  3 |         1
 Eisentraut | Peter      |  4 |         1
 Thomas     | Shaun      |  5 |         3
(5 rows)

Berikut adalah kueri hierarki tabel menggunakan sintaks Oracle.

select id, parent_id
from person
start with parent_id IS NULL
connect by prior id = parent_id;
 id | parent_id
----+-----------
  1 |    (null)
  4 |         1
  3 |         1
  2 |         1
  5 |         3

Dan ini dia lagi menggunakan PostgreSQL.

WITH RECURSIVE a AS (
SELECT id, parent_id
FROM person
WHERE parent_id IS NULL
UNION ALL
SELECT d.id, d.parent_id
FROM person d
JOIN a ON a.id = d.parent_id )
SELECT id, parent_id FROM a;
 id | parent_id
----+-----------
  1 |    (null)
  4 |         1
  3 |         1
  2 |         1
  5 |         3
(5 rows)

Kueri ini menggunakan banyak fitur PostgreSQL, jadi mari kita bahas secara perlahan.

WITH RECURSIVE

Ini adalah "Ekspresi Tabel Umum" (CTE). Ini mendefinisikan satu set kueri yang akan dieksekusi dalam pernyataan yang sama, tidak hanya dalam transaksi yang sama. Anda mungkin memiliki sejumlah ekspresi tanda kurung, dan pernyataan akhir. Untuk penggunaan ini, kita hanya membutuhkan satu. Dengan mendeklarasikan pernyataan tersebut sebagai RECURSIVE , itu akan dijalankan secara iteratif sampai tidak ada lagi baris yang dikembalikan.

SELECT
UNION ALL
SELECT

Ini adalah frasa yang ditentukan untuk kueri rekursif. Ini didefinisikan dalam dokumentasi sebagai metode untuk membedakan titik awal dan algoritma rekursi. Dalam istilah Oracle, Anda dapat menganggapnya sebagai klausa START WITH yang digabungkan dengan klausa CONNECT BY.

JOIN a ON a.id = d.parent_id

Ini adalah self-join ke pernyataan CTE yang menyediakan data baris sebelumnya ke iterasi berikutnya.

Untuk mengilustrasikan cara kerjanya, mari tambahkan indikator iterasi ke kueri.

WITH RECURSIVE a AS (
SELECT id, parent_id, 1::integer recursion_level
FROM person
WHERE parent_id IS NULL
UNION ALL
SELECT d.id, d.parent_id, a.recursion_level +1
FROM person d
JOIN a ON a.id = d.parent_id )
SELECT * FROM a;

 id | parent_id | recursion_level
----+-----------+-----------------
  1 |    (null) |               1
  4 |         1 |               2
  3 |         1 |               2
  2 |         1 |               2
  5 |         3 |               3
(5 rows)

Kami menginisialisasi indikator level rekursi dengan sebuah nilai. Perhatikan bahwa di baris yang dikembalikan, tingkat rekursi pertama hanya terjadi sekali. Itu karena klausa pertama hanya dieksekusi sekali.

Klausa kedua adalah tempat keajaiban berulang terjadi. Di sini, kami memiliki visibilitas data baris sebelumnya, bersama dengan data baris saat ini. Itu memungkinkan kita untuk melakukan perhitungan rekursif.

Simon Riggs memiliki video yang sangat bagus tentang cara menggunakan fitur ini untuk desain basis data grafik. Ini sangat informatif, dan Anda harus melihatnya.

Anda mungkin telah memperhatikan bahwa kueri ini dapat menyebabkan kondisi melingkar. Itu betul. Terserah pengembang untuk menambahkan klausa pembatas ke kueri kedua untuk mencegah rekursi tanpa akhir ini. Misalnya, hanya berulang 4 level sebelum menyerah begitu saja.

WITH RECURSIVE a AS (
SELECT id, parent_id, 1::integer recursion_level  --<-- initialize it here
FROM person
WHERE parent_id IS NULL
UNION ALL
SELECT d.id, d.parent_id, a.recursion_level +1    --<-- iteration increment
FROM person d
JOIN a ON a.id = d.parent_id
WHERE d.recursion_level <= 4  --<-- bail out here
) SELECT * FROM a;

Nama kolom dan tipe data ditentukan oleh klausa pertama. Perhatikan bahwa contoh menggunakan operator casting untuk tingkat rekursi. Dalam grafik yang sangat dalam, jenis data ini juga dapat didefinisikan sebagai 1::bigint recursion_level .

Grafik ini sangat mudah divisualisasikan dengan skrip shell kecil dan utilitas graphviz.

#!/bin/bash -
#===============================================================================
#
#          FILE: pggraph
#
#         USAGE: ./pggraph
#
#   DESCRIPTION:
#
#       OPTIONS: ---
#  REQUIREMENTS: ---
#          BUGS: ---
#         NOTES: ---
#        AUTHOR: Kirk Roybal (), [email protected]
#  ORGANIZATION:
#       CREATED: 04/21/2020 14:09
#      REVISION:  ---
#===============================================================================

set -o nounset                              # Treat unset variables as an error

dbhost=localhost
dbport=5432
dbuser=$USER
dbname=$USER
ScriptVersion="1.0"
output=$(basename $0).dot

#===  FUNCTION  ================================================================
#         NAME:  usage
#  DESCRIPTION:  Display usage information.
#===============================================================================
function usage ()
{
cat <<- EOT

  Usage :  ${0##/*/} [options] [--]

  Options:
  -h|host     name Database Host Name default:localhost
  -n|name     name Database Name      default:$USER
  -o|output   file Output file        default:$output.dot
  -p|port   number TCP/IP port        default:5432
  -u|user     name User name          default:$USER
  -v|version    Display script version

EOT
}    # ----------  end of function usage  ----------

#-----------------------------------------------------------------------
#  Handle command line arguments
#-----------------------------------------------------------------------

while getopts ":dh:n:o:p:u:v" opt
do
  case $opt in

    d|debug    )  set -x ;;

    h|host     )  dbhost="$OPTARG" ;;

    n|name     )  dbname="$OPTARG" ;;

    o|output   )  output="$OPTARG" ;;

    p|port     )  dbport=$OPTARG ;;

    u|user     )  dbuser=$OPTARG ;;

    v|version  )  echo "$0 -- Version $ScriptVersion"; exit 0   ;;

    \? )  echo -e "\n  Option does not exist : $OPTARG\n"
          usage; exit 1   ;;

  esac    # --- end of case ---
done
shift $(($OPTIND-1))

[[ -f "$output" ]] && rm "$output"

tee "$output" <<eof< span="">
digraph g {
    node [shape=rectangle]
    rankdir=LR
EOF

psql -h $dbhost -U $dbuser -d $dbname -p $dbport -qtAf cte.sql |
    sed -e 's/^/node/' -e 's/.*(null)|/node/' -e 's/^/\t/' -e 's/|[[:digit:]]*$//' |
    sed -e 's/|/ -> node/' | tee -a "$output"

tee -a "$output" <<eof< span="">
}
EOF

dot -Tpng "$output" > "${output/dot/png}"

[[ -f "$output" ]] && rm "$output"

open "${output/dot/png}"</eof<></eof<>

Skrip ini memerlukan pernyataan SQL ini dalam file bernama cte.sql

WITH RECURSIVE a AS (
SELECT id, parent_id, 1::integer recursion_level
FROM person
WHERE parent_id IS NULL
UNION ALL
SELECT d.id, d.parent_id, a.recursion_level +1
FROM person d
JOIN a ON a.id = d.parent_id )
SELECT parent_id, id, recursion_level FROM a;

Kemudian Anda memanggilnya seperti ini:

chmod +x pggraph
./pggraph

Dan Anda akan melihat grafik yang dihasilkan.

INSERT INTO person (id, parent_id) VALUES (6,2);

Jalankan utilitas lagi, dan lihat perubahan langsung pada grafik yang Anda arahkan:

Sekarang, itu tidak terlalu sulit, bukan?


  1. Database
  2.   
  3. Mysql
  4.   
  5. Oracle
  6.   
  7. Sqlserver
  8.   
  9. PostgreSQL
  10.   
  11. Access
  12.   
  13. SQLite
  14.   
  15. MariaDB
  1. Dengan sqlalchemy cara mengikat secara dinamis ke mesin database berdasarkan permintaan

  2. Panduan Mempartisi Data Di PostgreSQL

  3. GALAT:tempat hubungan tidak ada impor Heroku db

  4. Cara Mengamankan Database PostgreSQL Anda - 10 Tips

  5. Simpan keluaran PL/pgSQL dari PostgreSQL ke file CSV