sql >> Databáze >  >> RDS >> PostgreSQL

Oracle na PostgreSQL:ZAČNĚTE S / PŘIPOJTE SE

A nyní se dostáváme k druhému článku naší série o migraci z Oracle na PostgreSQL. Tentokrát se podíváme na START WITH/CONNECT BY konstrukce.

V Oracle START WITH/CONNECT BY se používá k vytvoření jednoduše propojené struktury seznamu začínající na daném řádku kontroly. Propojený seznam může mít podobu stromu a nemá žádné požadavky na vyvážení.

Pro ilustraci začněme dotazem a předpokládejme, že tabulka má 5 řádků.

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)

Zde je hierarchický dotaz na tabulku pomocí syntaxe 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

A tady je to opět pomocí 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)

Tento dotaz využívá mnoho funkcí PostgreSQL, takže si jej projdeme pomalu.

WITH RECURSIVE

Toto je „Common Table Expression“ (CTE). Definuje sadu dotazů, které budou provedeny ve stejném příkazu, nikoli pouze ve stejné transakci. Můžete mít libovolný počet výrazů v závorkách a závěrečné prohlášení. Pro toto použití potřebujeme pouze jeden. Deklarováním tohoto příkazu jako RECURSIVE , bude se opakovat, dokud nebudou vráceny žádné další řádky.

SELECT
UNION ALL
SELECT

Toto je předepsaná fráze pro rekurzivní dotaz. V dokumentaci je definována jako metoda pro rozlišení výchozího bodu a rekurzního algoritmu. V podmínkách Oracle si je můžete představit jako klauzuli START WITH spojenou s klauzulí CONNECT BY.

JOIN a ON a.id = d.parent_id

Toto je vlastní připojení k příkazu CTE, které poskytuje data předchozího řádku do následující iterace.

Abychom ilustrovali, jak to funguje, přidáme k dotazu indikátor iterace.

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)

Indikátor úrovně rekurze inicializujeme hodnotou. Všimněte si, že ve vrácených řádcích se první úroveň rekurze vyskytuje pouze jednou. Je to proto, že první klauzule je provedena pouze jednou.

Druhá věta je místo, kde se děje iterativní magie. Zde máme viditelnost dat předchozího řádku spolu s daty aktuálního řádku. To nám umožňuje provádět rekurzivní výpočty.

Simon Riggs má velmi pěkné video o tom, jak použít tuto funkci pro návrh databáze grafů. Je to velmi informativní a měli byste se na to podívat.

Možná jste si všimli, že tento dotaz může vést k kruhové podmínce. To je správně. Je na vývojáři, aby do druhého dotazu přidal omezující klauzuli, aby zabránil této nekonečné rekurzi. Například stačí opakovat 4 úrovně hluboko, než to prostě vzdát.

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;

Názvy sloupců a datové typy jsou určeny první klauzulí. Všimněte si, že příklad používá operátor přetypování pro úroveň rekurze. Ve velmi hlubokém grafu lze tento datový typ definovat také jako 1::bigint recursion_level .

Tento graf lze velmi snadno vizualizovat pomocí malého skriptu shellu a nástroje 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<>

Tento skript vyžaduje tento příkaz SQL v souboru s názvem 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;

Potom jej vyvoláte takto:

chmod +x pggraph
./pggraph

A uvidíte výsledný graf.

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

Spusťte nástroj znovu a podívejte se na okamžité změny vašeho orientovaného grafu:

Teď to nebylo tak těžké, že?


  1. SQL Server - sniffování parametrů

  2. Spojení výsledků ze dvou samostatných databází

  3. *ALERT* Nelze otevřít žádnou další chybu databází s Microsoft Office build 2201

  4. Oracle 11g na Mac OS X