Funktionen zum Dokumentieren von PostgreSQL-Datenbanken. Teil zwei

Dies ist der zweite Teil des Artikels, der benutzerdefinierte Funktionen für die Arbeit mit Systemverzeichnissen beschreibt: pg_class, pg_attribute, pg_constraints usw.

In diesem Teil des Artikels werden Funktionen erläutert, die die Eigenschaften von Einschränkungen und Indizes zurückgeben .

Die erste Hälfte des Artikels enthält Kommentare zur Implementierung von Funktionen. Der zweite ist der Quellcode der Funktionen. Für diejenigen Leser, die nur an den Ausgangstexten interessiert sind, empfehlen wir, sofort mit dem Anhang fortzufahren .

Siehe auch
Funktionen zum Dokumentieren von PostgreSQL-Datenbanken. Teil eins ;
Funktionen zum Dokumentieren von PostgreSQL-Datenbanken. Teil drei .
Funktionen zum Dokumentieren von PostgreSQL-Datenbanken. Das Ende (Teil vier) .

admtf_Table_Constraintes Liste der Einschränkungen der Datenbanktabelle und ihrer Eigenschaften



Die Funktion admtf_Table_Constraintes gibt eine Liste der Einschränkungen (CONSTRAINT) der Datenbanktabelle und ihrer Merkmale zurück. Der Quellcode kann hier angezeigt und heruntergeladen werden . Hier ist eine Version einer Funktion, die keinen Cursor verwendet .



Als Parameter verwendet die Funktion den Namen der Quelltabelle ( a_TableName ) und den Namen des Schemas, in dem die Tabelle erstellt wird ( a_SchemaName ).

Die Beschreibung einer bestimmten Einschränkung ist eine Kombination aus dem Datensatz in pg_class , der sie als physikalische Beziehung beschreibt, und dem Datensatz in pg_constraint , der Daten zu den spezifischen Merkmalen der Einschränkung enthält.




der Quellcode des Operators in der Abbildung
SELECT tbl.OID,con.conname,con.contype,con.conkey,reftbl.OID, reftbl.relname,con.confkey,con.consrc FROM pg_constraint con INNER JOIN pg_namespace nsp ON con.connamespace=nsp.oid LEFT OUTER JOIN pg_class tbl ON con.conrelid=tbl.oid LEFT OUTER JOIN pg_class reftbl ON con.confrelid=reftbl.oid WHERE LOWER(nsp.nspname)=LOWER(a_SchemaName) AND LOWER(tbl.relname)=LOWER(a_TableOID) ORDER BY con.contype DESC,con.conname; 


Die Hauptdaten (Name und Art der Einschränkung) werden aus dem Verzeichniseintrag pg_constraint abgerufen. Die Merkmale jeder Einschränkung werden aus demselben Katalog extrahiert, der in Form von OID-Tabellen ( conrelid , confrelid ) oder Arrays von Attribut-Seriennummern ( conkey , confkey ) dargestellt wird, die an der Einschränkung teilnehmen.



Die Funktion gibt Einschränkungsmerkmale in Form von Tabellennamen und Attributen zurück. In diesem Fall werden die Tabellennamen aus dem Katalogeintrag pg_class nach Kennung (OID) und die Attributnamen aus den Katalogeinträgen pg_attribute nach Tabellenkennung und Attributseriennummer extrahiert . Weil Da Seriennummern in Form eines Arrays (einer Liste) im Hauptverzeichnis gespeichert sind, werden Listen von Attributnamen innerhalb der Funktion mithilfe einer Schleife erstellt.


Die Funktion gibt ein spezielles Merkmal zurück - die Regel zum Überprüfen der Werte von Feldern in Tabelleneinträgen (Einschränkung CHECK). Dieses Merkmal wird als Textwert im Feld consrc des Verzeichnisses pg_constraint gespeichert .


Tabelle 7. Das Ergebnis der Ausführung der Funktion admtf_Table_Constraintes ('public', 'Street').

Die Textversion der Tabelle in der Abbildung
TitelTypQuellentabellenattributeDer Name der externen TabelleAttribute einer externen TabelleValidierungsregel
xpkstreetpwcrccode, localityid, streetid
fk_street_localityfwcrccode, localityidLokalitätwcrccode, localityid
fk_street_streettypefStreettypeacrmStreettypStreettypeacrm
ck_street_streetnamecStraßenname((Straßenname) :: text! ~ * '[az]' :: text)
ck_street_streettypeacrmcStreettypeacrm((streettypeacrm) :: bpchar! ~ * '[az]' :: text)



Version ohne Cursor


Ich sehe Fragen und Kommentare zur Verwendung des Cursors in der Hauptversion der Funktion voraus.


Ich werde nicht antworten - es gibt keine Kameraden für den Geschmack und die Farbe. Aber ich werde eine Version der Funktion ohne Cursor geben. Die Version der Funktionsimplementierung ohne Verwendung des Cursors kann hier angezeigt und heruntergeladen werden .


Die Hauptschwierigkeit besteht darin, den Join (JOIN) der Tabellen gemäß den Werten zu organisieren, die sich im Attributtyp des Arrays eines dieser Tabellen befinden. Solche Arrays sind in diesem Fall conkey und confkey .



 SELECT c.conname,c.contype,c.conkey::SMALLINT[], GENERATE_SUBSCRIPTS(c.conkey, 1) as No FROM pg_constraint c WHERE c.conname='fk_street_locality' ORDER BY No; 

Um ein solches Problem zu lösen, enthält PostgrSQL Funktionen, die eine Wertetabelle von Zeigern auf Array-Elemente zurückgeben. In unserem Fall wird die Funktion generate_subscripts verwendet. Es werden nicht nur viele Zeiger auf die Position des Arrays generiert, das als Parameter an das Array übergeben wird, sondern es wird auch ein Datensatz, der das Array enthält, durch die Anzahl der Elemente im Array in mehrere umgewandelt. Jeder Datensatz einer solchen Tabelle enthält einen eindeutigen Wert - die Position des Arrays.



Tabelle 8. Weitergabe der ursprünglichen Zeichenfolge mithilfe von generate_subscripts .

RestriktionsnameTypArray von AttributnummernZeiger auf eine Array-Position
fk_street_localityf{1,2}1
fk_street_localityf{1,2}2



der Quellcode des Operators in der Abbildung
 SELECT con.conname AS ConstraintName,con.contype::VARCHAR(2) AS ConstraintType, STRING_AGG(attr.attname, ', 'ORDER BY con.No) AS r_ConstraintKeyNames, reftbl.relname AS RefTableName, STRING_AGG(rattr.attname,', 'ORDER BY con.No) AS r_RefTableKeyNames, con.consrc AS ConstraintSource FROM (SELECT c.oid, c.conrelid,c.confrelid,c.conname,c.contype, c.conkey::SMALLINT[],c.consrc, c.confkey::SMALLINT[], generate_subscripts(c.conkey, 1) as No FROM pg_constraint c) con INNER JOIN pg_class tbl ON con.conrelid=tbl.oid INNER JOIN pg_attribute attr ON attr.attrelid=tbl.oid AND attr.attnum=con.conkey[con.No] INNER JOIN pg_namespace nsp ON tbl.relnamespace=nsp.oid LEFT OUTER JOIN pg_class reftbl ON con.confrelid=reftbl.oid LEFT OUTER JOIN pg_attribute rattr ON rattr.attrelid=reftbl.oid AND rattr.attnum=con.confkey[con.No] WHERE LOWER(nsp.nspname)=LOWER(a_SchemaName) AND LOWER(tbl.relname)=LOWER(a_TableName) GROUP BY con.conname,con.contype,reftbl.relname,con.consrc ORDER BY con.contype DESC,con.conname; 


Eine solche Tabelle kann mit dem Attributkatalog pg_attribute verbunden werden, indem Attributnamen unter der Bedingung attr.attrelid = tbl.oid AND attr.attnum = con.conkey [con.No] daraus extrahiert werden .
Jetzt müssen Sie nicht mehr unnötige Datensätze entfernen, indem Sie Datensätze gruppieren und aus den Namen der Attribute eine Zeichenfolge erstellen.


Das Erstellen einer Zeile erfolgt mit der Aggregationsfunktion STRING_AGG , in der Sie die Sortieroption (ORDER BY) angeben müssen. Andernfalls stimmt die Reihenfolge der Attribute möglicherweise nicht mit der Reihenfolge der Deklaration der Attribute im Index überein.



Die Ausführungszeit beider Versionen der Funktionen stimmte überein. Die Ausgabe der Daten in der Ergebnistabelle dauerte 20 ms.


Admtf_Table_Indexes Funktionsliste der Datenbanktabellenindizes und ihrer Eigenschaften



Die Funktion admtf_Table_Indexes gibt eine Liste der Indizes (INDEX) der Datenbanktabelle und ihrer Merkmale zurück. Der Quellcode kann hier angezeigt und heruntergeladen werden . Hier ist eine Version einer Funktion, die keinen Cursor verwendet .


Als Parameter verwendet die Funktion den Namen der Quelltabelle ( a_TableName ) und den Namen des Schemas, in dem die Tabelle erstellt wird ( a_SchemaName ).

der Quellcode des Operators in der Abbildung
 SELECT tbl.oid,inxcls.relname,inxam.amname,inx.indisunique,inx.indisprimary, inx.indkey::SMALLINT[],inx.indoption::SMALLINT[],inxam.amcanorder FROM pg_index inx INNER JOIN pg_class inxcls ON inx.indexrelid=inxcls.oid INNER JOIN pg_namespace inxnsp ON inxcls.relnamespace=inxnsp.oid INNER JOIN pg_am inxam ON inxcls.relam=inxam.oid INNER JOIN pg_class tbl ON inx.indrelid=tbl.oid INNER JOIN pg_namespace nsp ON tbl.relnamespace=nsp.oid WHERE LOWER(nsp.nspname)=LOWER(a_SchemaName) AND LOWER(tbl.relname)=LOWER(a_TableOID) ORDER BY inxam.amname, inxcls.relname; 


Die Beschreibung eines einzelnen Index ist eine Kombination aus einem Datensatz in pg_class , der ihn als physikalische Beziehung beschreibt, und einem Datensatz in pg_index , der Daten zu den spezifischen Merkmalen des Index enthält. Darüber hinaus werden Informationen zu Indexzugriffsmethoden im Systemverzeichnis pg_am gespeichert.


 CASE inxam.amcanorder WHEN true THEN CASE inx.indoption[inx.No] & 1 WHEN 1 THEN ' DESC' ELSE ' ASC' END ELSE '' END; 

Ein Attribut der Index-Eindeutigkeit ( indisunique ), ein Zeichen dafür, dass der Index gemäß der Beschreibung des Primärschlüssels ( indisprimary ) erstellt wurde, sowie Arrays von Seriennummern der Tabellenattribute, basierend auf den Werten, deren Index ( indkey ) und Zeichen der Reihenfolge der Sortierattributwerte aus dem Katalogeintrag pg_index extrahiert werden , werden extrahiert im Index ( indoption ).


Aus dem Katalogeintrag, der die Zugriffsmethode des pg_am- Index beschreibt, werden das Attribut der Eignung der im Index enthaltenen Daten für die Sortierung ( amcanorder ) und der Name oder Typ der Zugriffsmethode des Index ( amname ) extrahiert .

Mit anderen Worten, das amcanorder-Attribut gibt an, ob es möglich ist, eine Sortierreihenfolge für die Werte der im Index enthaltenen Attribute festzulegen. Wenn amcanorder = true ist , kann die Sortierreihenfolge angegeben werden, andernfalls nicht. Aus derselben Abbildung ist die Bedeutung der Werte des Indoptionsarrays ersichtlich. Wenn das rechte Bit der Binärform des Werts 1B enthält, wird der Wert des entsprechenden Attributs in absteigender Reihenfolge sortiert, andernfalls in aufsteigender Reihenfolge.


Innerhalb der Funktion werden mithilfe eines Zyklus Listen mit Namen von Attributen erstellt, die im Index enthalten sind, sowie Zeichen für die Reihenfolge der Attributwerte.



Tabelle 9. Das Ergebnis der Ausführung der Funktion admtf_Table_Indexes ('public', 'Street').


Die Textversion der Tabelle in der Abbildung
IndexnameMethode? Einzigartig? PrimärschlüsselAttribute im Index
xie1streetbtreeffwcrccode ASC, localityid ASC, streettypeacrm ASC, Straßenname ASC
xie2streebtreeffwcrccode ASC, localityid ASC, Straßenname ASC
xie3streetbtreeffStraßenname ASC
xie9streetbtreeffwcrccode ASC, localityid ASC, Straßenname DESC
xpkstreetbtreettwcrccode ASC, localityid ASC, streetid ASC
xts1streetGinffstreettsvector
xts2streetGinffstreettsvector


Version ohne Cursor


Der Ansatz zum Erstellen einer Version einer Funktion ohne Cursor ist genau der gleiche wie im vorherigen Abschnitt beschrieben:


  • Reproduktion von Datensätzen mit generate_subscripts;
  • nachfolgende Gruppierung von Datensätzen;
  • Erstellen einer Liste von Indexattributen mit der Funktion STRING_AGG mit der Option ORDER BY.


der Quellcode des Operators in der Abbildung
 SELECT inxcls.relname AS r_IndexName ,inxam.amname AS r_IndexType, inx.indisunique AS r_isUnique,inx.indisprimary AS r_isPrimary, STRING_AGG(attr.attname|| CASE inxam.amcanorder WHEN true THEN CASE inx.indoption[inx.No] & 1 WHEN 1 THEN ' DESC' ELSE ' ASC' END ELSE '' END, c_Delimiter ORDER BY inx.No) FROM (SELECT i.indrelid, i.indexrelid,i.indisunique,i.indisprimary, i.indkey::SMALLINT[],i.indoption::SMALLINT[], generate_subscripts(i.indkey, 1) as No FROM pg_index i) inx INNER JOIN pg_class inxcls ON inx.indexrelid=inxcls.oid INNER JOIN pg_am inxam ON inxcls.relam=inxam.oid INNER JOIN pg_class tbl ON inx.indrelid=tbl.oid INNER JOIN pg_namespace nsp ON tbl.relnamespace=nsp.oid INNER JOIN pg_attribute attr ON attr.attrelid=tbl.OID AND attr.attnum=inx.indkey[inx.No] WHERE LOWER(nsp.nspname)=LOWER(a_SchemaName) AND LOWER(tbl.relname)=LOWER(a_TableName) GROUP BY inxcls.relname,inxam.amname,inx.indisunique,inx.indisprimary ORDER BY inxcls.relname; 



Die Ausführungszeit beider Versionen der Funktionen stimmte überein. Ich brauchte 20 ms, um die Daten in der Ergebnistabelle auszugeben.


Daher werde ich keine Funktionsversionen mehr produzieren, wie Wer möchte, kann sie nach seinen Wünschen neu gestalten oder mich kontaktieren. Ich werde eine modifizierte Version kostenlos senden .

Siehe auch den ersten , dritten und vierten Teil des Artikels.



ANHANG 1. Skripte



Erstellen der Funktion admtf_Table_Constraintes


Kommentare zum Funktionsquellcode finden Sie hier.
Funktionsquellcode
 BEGIN TRANSACTION; DROP FUNCTION IF EXISTS admtf_Table_Constraintes (a_SchemaName NAME, a_TableName NAME); /********************************************************************************************************/ /*      */ /********************************************************************************************************/ CREATE OR REPLACE FUNCTION admtf_Table_Constraintes (a_SchemaName name default 'public', /*     */ a_TableName name default NULL /*   */ ) RETURNS TABLE (r_ConstraintName NAME,r_ConstraintType NAME,r_ConstraintKeyNames Text,r_RefTableName NAME,r_RefTableKeyNames Text,r_ConstraintSource Text) AS $BODY$ DECLARE v_Scale INTEGER; /*   */ v_ConstraintRec RECORD; /*   */ v_TableOID INTEGER; /* OID */ v_ConstraintOID INTEGER; /* OID */ v_ConstraintKeyNos SMALLINT[]; /* */ v_ConstraintName name; /*   */ v_ConstraintType name; /*     */ v_isUnique BOOLEAN; /*   */ v_isPrimary BOOLEAN; /*      Primary KEY */ v_AttributeNum INTEGER; /*   */ v_AttributeName name; /*  */ v_ConstraintKeyNames TEXT; /*     */ v_RefTableOID INTEGER; /* OID ,     */ v_RefTableName name;/*  ,     */ v_RefTableKeyNos SMALLINT[]; /* */ v_RefTableKeyNames TEXT; /*     ,    */ v_ConstraintSource TEXT; /*     CHECK*/ c_Delimiter CONSTANT VARCHAR(2):=','; --****************************************************************************************************** BEGIN FOR v_ConstraintRec IN SELECT con.oid AS ConstraintOID ,tbl.OID AS TableOID, con.conname AS ConstraintName, con.contype AS ConstraintType, con.conkey AS ConstraintKeyNos, reftbl.OID AS RefTableOID, reftbl.relname AS RefTableName, con.confkey AS RefTableKeyNos, con.consrc AS ConstraintSource FROM pg_constraint con INNER JOIN pg_class tbl ON con.conrelid=tbl.oid INNER JOIN pg_namespace nsp ON tbl.relnamespace=nsp.oid LEFT OUTER JOIN pg_class reftbl ON con.confrelid=reftbl.oid WHERE LOWER(nsp.nspname)=LOWER(a_SchemaName) AND LOWER(tbl.relname)=LOWER(a_TableName) ORDER BY con.contype DESC,con.conname LOOP v_ConstraintOID:=v_ConstraintRec.ConstraintOID; v_TableOID:=v_ConstraintRec.TableOID; v_ConstraintName:=v_ConstraintRec.ConstraintName; v_ConstraintType:=v_ConstraintRec.ConstraintType; v_ConstraintKeyNos:=v_ConstraintRec.ConstraintKeyNos; v_RefTableOID:=v_ConstraintRec.RefTableOID; v_RefTableName:=v_ConstraintRec.RefTableName; v_RefTableKeyNos:=v_ConstraintRec.RefTableKeyNos; v_ConstraintSource:=v_ConstraintRec.ConstraintSource; v_ConstraintKeyNames:=''; FOREACH v_AttributeNum IN ARRAY v_ConstraintKeyNos LOOP SELECT INTO v_AttributeName attr.attname::VARCHAR(100) AS r_AttributeName FROM pg_attribute attr WHERE attr.attrelid=v_TableOID AND attr.attnum=v_AttributeNum; v_ConstraintKeyNames:=v_ConstraintKeyNames|| CASE WHEN v_ConstraintKeyNames='' THEN '' ELSE c_Delimiter END ||v_AttributeName; END LOOP; v_RefTableKeyNames:=''; IF v_RefTableKeyNos IS NOT NULL THEN FOREACH v_AttributeNum IN ARRAY v_RefTableKeyNos LOOP SELECT INTO v_AttributeName attr.attname::VARCHAR(100) AS r_AttributeName FROM pg_attribute attr WHERE attr.attrelid=v_RefTableOID AND attr.attnum=v_AttributeNum; v_RefTableKeyNames:=v_RefTableKeyNames|| CASE WHEN v_RefTableKeyNames='' THEN '' ELSE c_Delimiter END ||v_AttributeName; END LOOP; END IF; RETURN QUERY SELECT v_ConstraintName,v_ConstraintType,v_ConstraintKeyNames, v_RefTableName,v_RefTableKeyNames, v_ConstraintSource; END LOOP; RETURN; END $BODY$ LANGUAGE plpgsql; COMMENT ON FUNCTION admtf_Table_Constraintes(a_SchemaName NAME, a_TableName NAME) IS '    '; --ROLLBACK TRANSACTION; COMMIT TRANSACTION; BEGIN TRANSACTION; DROP FUNCTION IF EXISTS admtf_Table_Constraintes (a_SchemaName VARCHAR(256), a_TableName VARCHAR(256)); /********************************************************************************************************/ /*      */ /********************************************************************************************************/ CREATE OR REPLACE FUNCTION admtf_Table_Constraintes (a_SchemaName VARCHAR(256) default 'public', /*     */ a_TableName VARCHAR(256) default NULL /*   */ ) RETURNS TABLE (r_ConstraintName VARCHAR(256),r_ConstraintType VARCHAR(256),r_ConstraintKeyNames Text,r_RefTableName VARCHAR(256),r_RefTableKeyNames Text,r_ConstraintSource Text) AS $BODY$ DECLARE --****************************************************************************************************** BEGIN RETURN QUERY SELECT tc.r_ConstraintName::VARCHAR(256), tc.r_ConstraintType::VARCHAR(256),tc.r_ConstraintKeyNames::TEXT, tc.r_RefTableName::VARCHAR(256),tc.r_RefTableKeyNames::TEXT, tc.r_ConstraintSource::TEXT FROM admtf_Table_Constraintes(a_SchemaName::NAME,a_TableName::NAME) tc; END $BODY$ LANGUAGE plpgsql; COMMENT ON FUNCTION admtf_Table_Constraintes(a_SchemaName VARCHAR(256), a_TableName VARCHAR(256)) IS '    '; --ROLLBACK TRANSACTION; COMMIT TRANSACTION; SELECT * FROM admtf_Table_Constraintes('public'::VARCHAR(256),'Street'::VARCHAR(256)); SELECT * FROM admtf_Table_Constraintes('public'::name,'Street'::name); 



Erstellen einer Version von admtf_Table_Constraintes ohne Cursor


Kommentare zum Funktionsquellcode finden Sie hier.
Funktionsquellcode
 BEGIN TRANSACTION; DROP FUNCTION IF EXISTS admtf_Table_Constraintes (a_SchemaName NAME, a_TableName NAME); /********************************************************************************************************/ /*      */ /********************************************************************************************************/ CREATE OR REPLACE FUNCTION admtf_Table_Constraintes (a_SchemaName name default 'public', /*     */ a_TableName name default NULL /*   */ ) RETURNS TABLE (r_ConstraintName NAME,r_ConstraintType NAME,r_ConstraintKeyNames Text,r_RefTableName NAME,r_RefTableKeyNames Text,r_ConstraintSource Text) AS $BODY$ DECLARE v_Scale INTEGER; /*   */ v_ConstraintRec RECORD; /*   */ v_TableOID INTEGER; /* OID */ v_ConstraintOID INTEGER; /* OID */ v_ConstraintKeyNos SMALLINT[]; /* */ v_ConstraintName name; /*   */ v_ConstraintType name; /*     */ v_isUnique BOOLEAN; /*   */ v_isPrimary BOOLEAN;/*      Primary KEY */ v_AttributeNum INTEGER; /*   */ v_AttributeName name; /*  */ v_ConstraintKeyNames TEXT; /*     */ v_RefTableOID INTEGER; /* OID ,     */ v_RefTableName name;/*  ,     */ v_RefTableKeyNos SMALLINT[]; /* */ v_RefTableKeyNames TEXT;/*     ,    */ v_ConstraintSource TEXT; /*     CHECK*/ c_Delimiter CONSTANT VARCHAR(2):=','; --****************************************************************************************************** BEGIN FOR v_ConstraintRec IN SELECT con.oid AS ConstraintOID , tbl.OID AS TableOID, con.conname AS ConstraintName, con.contype AS ConstraintType, con.conkey AS ConstraintKeyNos, reftbl.OID AS RefTableOID, reftbl.relname AS RefTableName, con.confkey AS RefTableKeyNos, con.consrc AS ConstraintSource FROM pg_constraint con INNER JOIN pg_class tbl ON con.conrelid=tbl.oid INNER JOIN pg_namespace nsp ON tbl.relnamespace=nsp.oid LEFT OUTER JOIN pg_class reftbl ON con.confrelid=reftbl.oid WHERE LOWER(nsp.nspname)=LOWER(a_SchemaName) AND LOWER(tbl.relname)=LOWER(a_TableName) ORDER BY con.contype DESC,con.conname LOOP v_ConstraintOID:=v_ConstraintRec.ConstraintOID; v_TableOID:=v_ConstraintRec.TableOID; v_ConstraintName:=v_ConstraintRec.ConstraintName; v_ConstraintType:=v_ConstraintRec.ConstraintType; v_ConstraintKeyNos:=v_ConstraintRec.ConstraintKeyNos; v_RefTableOID:=v_ConstraintRec.RefTableOID; v_RefTableName:=v_ConstraintRec.RefTableName; v_RefTableKeyNos:=v_ConstraintRec.RefTableKeyNos; v_ConstraintSource:=v_ConstraintRec.ConstraintSource; v_ConstraintKeyNames:=''; FOREACH v_AttributeNum IN ARRAY v_ConstraintKeyNos LOOP SELECT INTO v_AttributeName attr.attname::VARCHAR(100) AS r_AttributeName FROM pg_attribute attr WHERE attr.attrelid=v_TableOID AND attr.attnum=v_AttributeNum; v_ConstraintKeyNames:=v_ConstraintKeyNames|| CASE WHEN v_ConstraintKeyNames='' THEN '' ELSE c_Delimiter END ||v_AttributeName; END LOOP; v_RefTableKeyNames:=''; IF v_RefTableKeyNos IS NOT NULL THEN FOREACH v_AttributeNum IN ARRAY v_RefTableKeyNos LOOP SELECT INTO v_AttributeName attr.attname::VARCHAR(100) AS r_AttributeName FROM pg_attribute attr WHERE attr.attrelid=v_RefTableOID AND attr.attnum=v_AttributeNum; v_RefTableKeyNames:=v_RefTableKeyNames|| CASE WHEN v_RefTableKeyNames='' THEN '' ELSE c_Delimiter END ||v_AttributeName; END LOOP; END IF; RETURN QUERY SELECT v_ConstraintName,v_ConstraintType,v_ConstraintKeyNames, v_RefTableName,v_RefTableKeyNames, v_ConstraintSource; END LOOP; RETURN; END $BODY$ LANGUAGE plpgsql; COMMENT ON FUNCTION admtf_Table_Constraintes(a_SchemaName NAME, a_TableName NAME) IS '    '; --ROLLBACK TRANSACTION; COMMIT TRANSACTION; BEGIN TRANSACTION; DROP FUNCTION IF EXISTS admtf_Table_Constraintes (a_SchemaName VARCHAR(256), a_TableName VARCHAR(256)); /********************************************************************************************************/ /*      */ /********************************************************************************************************/ CREATE OR REPLACE FUNCTION admtf_Table_Constraintes (a_SchemaName VARCHAR(256) default 'public', /*     */ a_TableName VARCHAR(256) default NULL /*   */ ) RETURNS TABLE (r_ConstraintName VARCHAR(256),r_ConstraintType VARCHAR(256),r_ConstraintKeyNames Text,r_RefTableName VARCHAR(256),r_RefTableKeyNames Text,r_ConstraintSource Text) AS $BODY$ DECLARE --****************************************************************************************************** BEGIN RETURN QUERY SELECT tc.r_ConstraintName::VARCHAR(256), tc.r_ConstraintType::VARCHAR(256), tc.r_ConstraintKeyNames::TEXT, tc.r_RefTableName::VARCHAR(256), tc.r_RefTableKeyNames::TEXT, tc.r_ConstraintSource::TEXT FROM admtf_Table_Constraintes(a_SchemaName::NAME,a_TableName::NAME) tc; END $BODY$ LANGUAGE plpgsql; COMMENT ON FUNCTION admtf_Table_Constraintes(a_SchemaName VARCHAR(256), a_TableName VARCHAR(256)) IS '    '; --ROLLBACK TRANSACTION; COMMIT TRANSACTION; SELECT * FROM admtf_Table_Constraintes('public'::VARCHAR(256),'Street'::VARCHAR(256)); SELECT * FROM admtf_Table_Constraintes('public'::name,'Street'::name); 



Erstellen der Funktion admtf_Table_Indexes


Kommentare zum Funktionsquellcode finden Sie hier.
Funktionsquellcode
 BEGIN TRANSACTION; DROP FUNCTION IF EXISTS admtf_Table_Indexes (a_SchemaName NAME, a_TableName NAME); /********************************************************************************************************/ /*      */ /********************************************************************************************************/ CREATE OR REPLACE FUNCTION admtf_Table_Indexes (a_SchemaName NAME default 'public', /*     */ a_TableName NAME default NULL /*   */ ) RETURNS TABLE (r_IndexName NAME,r_IndexType NAME,r_isUnique BOOLEAN,r_isPrimary BOOLEAN, r_IndexKeyNames Text) AS $BODY$ DECLARE c_IndexKind CONSTANT CHAR:='i'; v_IndexRec RECORD; /*   */ v_Scale INTEGER; /*   */ v_TableOID INTEGER; /* OID */ v_IndexOID INTEGER; /* OID */ v_IndexKeyNos SMALLINT[]; /* */ v_IndexName NAME; /*   */ v_IndexAMName NAME; /*    ( ) */ v_isUnique BOOLEAN; /*   */ v_isPrimary BOOLEAN; /*      Primary KEY */ v_AttributeNum INTEGER; /*   */ v_AttributeName NAME; /*  */ v_IndexKeyNames TEXT; /*     */ c_Delimiter CONSTANT VARCHAR(2):=','; --****************************************************************************************************** BEGIN FOR v_IndexRec IN SELECT inxcls.oid AS IndexOID,tbl.oid AS TableOID, inxcls.relname AS IndexName,inxam.amname AS IndexAMName, inx.indisunique AS isUnique,inx.indisprimary isPrimary, inx.indkey::SMALLINT[] AS IndexKeyNos FROM pg_index inx INNER JOIN pg_class inxcls ON inx.indexrelid=inxcls.oid INNER JOIN pg_am inxam ON inxcls.relam=inxam.oid INNER JOIN pg_class tbl ON inx.indrelid=tbl.oid INNER JOIN pg_namespace nsp ON tbl.relnamespace=nsp.oid WHERE LOWER(nsp.nspname)=LOWER(a_SchemaName) AND inxcls.relkind=c_IndexKind AND tbl.relname=LOWER(a_TableName) ORDER BY inxam.amname, inxcls.relname LOOP v_IndexOID:=v_IndexRec.IndexOID; v_TableOID:=v_IndexRec.TableOID; v_IndexName:=v_IndexRec.IndexName; v_IndexAMName:=v_IndexRec.IndexAMName; v_isUnique:=v_IndexRec.isUnique; v_isPrimary:=v_IndexRec.isPrimary; v_IndexKeyNos:=v_IndexRec.IndexKeyNos; v_IndexKeyNames:=''; FOREACH v_AttributeNum IN ARRAY v_IndexKeyNos LOOP SELECT INTO v_AttributeName attr.attname::VARCHAR(100) AS r_AttributeName FROM pg_attribute attr WHERE attr.attrelid=v_TableOID AND attr.attnum=v_AttributeNum; v_IndexKeyNames:=v_IndexKeyNames|| CASE WHEN v_IndexKeyNames='' THEN '' ELSE c_Delimiter||' ' END || v_AttributeName; END LOOP; RETURN QUERY SELECT v_IndexName,v_IndexAMName,v_isUnique, v_isPrimary,v_IndexKeyNames; END LOOP; RETURN; END $BODY$ LANGUAGE plpgsql; COMMENT ON FUNCTION admtf_Table_Indexes(a_SchemaName NAME, a_TableName NAME) IS '    '; --ROLLBACK TRANSACTION; COMMIT TRANSACTION; BEGIN TRANSACTION; DROP FUNCTION IF EXISTS admtf_Table_Indexes (a_SchemaName VARCHAR(256), a_TableName VARCHAR(256)); /********************************************************************************************************/ /*      */ /********************************************************************************************************/ CREATE OR REPLACE FUNCTION admtf_Table_Indexes (a_SchemaName VARCHAR(256) default 'public', /*     */ a_TableName VARCHAR(256) default NULL /*   */ ) RETURNS TABLE (r_IndexName VARCHAR(256),r_IndexType VARCHAR(256),r_isUnique BOOLEAN,r_isPrimary BOOLEAN, r_IndexKeyNames TEXT) AS $BODY$ DECLARE --****************************************************************************************************** BEGIN RETURN QUERY SELECT ti.r_IndexName::VARCHAR(256), ti.r_IndexType::VARCHAR(256), ti.r_isUnique::BOOLEAN, ti.r_isPrimary::BOOLEAN, ti.r_IndexKeyNames::TEXT FROM admtf_Table_Indexes(a_SchemaName::NAME,a_TableName::NAME) ti; END $BODY$ LANGUAGE plpgsql; COMMENT ON FUNCTION admtf_Table_Indexes(a_SchemaName VARCHAR(256), a_TableName VARCHAR(256)) IS '    '; --ROLLBACK TRANSACTION; COMMIT TRANSACTION; SELECT * FROM admtf_Table_Indexes('public'::NAME,'Street'::NAME); SELECT * FROM admtf_Table_Indexes('public'::VARCHAR(256),'Street'::VARCHAR(256)); 



Erstellen einer Version von admtf_Table_Indexes ohne Cursor


.
 BEGIN TRANSACTION; DROP FUNCTION IF EXISTS admtf_Table_Indexes (a_SchemaName NAME, a_TableName NAME); /********************************************************************************************************/ /*      */ /********************************************************************************************************/ CREATE OR REPLACE FUNCTION admtf_Table_Indexes (a_SchemaName NAME default 'public', /*     */ a_TableName NAME default NULL /*   */ ) RETURNS TABLE (r_IndexName NAME,r_IndexType NAME,r_isUnique BOOLEAN,r_isPrimary BOOLEAN, r_IndexKeyNames Text) AS $BODY$ DECLARE c_IndexKind CONSTANT CHAR:='i'; c_Delimiter CONSTANT VARCHAR(2):=', '; --****************************************************************************************************** BEGIN RETURN QUERY SELECT inxcls.relname AS r_IndexName, inxam.amname AS r_IndexType, inx.indisunique AS r_isUnique, inx.indisprimary r_isPrimary, STRING_AGG(attr.attname||CASE inxam.amcanorder WHEN true THEN CASE inx.indoption[inx.No] & 1 WHEN 1 THEN ' DESC' ELSE ' ASC' END ELSE '' END, c_Delimiter ORDER BY inx.No) FROM (SELECT i.indrelid, i.indexrelid,i.indisunique, i.indisprimary,i.indkey::SMALLINT[], i.indoption::SMALLINT[], generate_subscripts(i.indkey, 1) as No FROM pg_index i) inx INNER JOIN pg_class inxcls ON inx.indexrelid=inxcls.oid INNER JOIN pg_am inxam ON inxcls.relam=inxam.oid INNER JOIN pg_class tbl ON inx.indrelid=tbl.oid INNER JOIN pg_namespace nsp ON tbl.relnamespace=nsp.oid INNER JOIN pg_attribute attr ON attr.attrelid=tbl.OID AND attr.attnum=inx.indkey[inx.No] WHERE LOWER(nsp.nspname)=LOWER(a_SchemaName) AND inxcls.relkind=c_IndexKind AND tbl.relname=LOWER(a_TableName) GROUP BY inxcls.relname,inxam.amname,inx.indisunique,inx.indisprimary ORDER BY inxcls.relname; RETURN; END $BODY$ LANGUAGE plpgsql; COMMENT ON FUNCTION admtf_Table_Indexes(a_SchemaName NAME, a_TableName NAME) IS '    '; --ROLLBACK TRANSACTION; COMMIT TRANSACTION; BEGIN TRANSACTION; DROP FUNCTION IF EXISTS admtf_Table_Indexes (a_SchemaName VARCHAR(256), a_TableName VARCHAR(256)); /********************************************************************************************************/ /*      */ /********************************************************************************************************/ CREATE OR REPLACE FUNCTION admtf_Table_Indexes (a_SchemaName VARCHAR(256) default 'public', /*    */ a_TableName VARCHAR(256) default NULL /*   */ ) RETURNS TABLE (r_IndexName VARCHAR(256),r_IndexType VARCHAR(256),r_isUnique BOOLEAN,r_isPrimary BOOLEAN, r_IndexKeyNames TEXT) AS $BODY$ DECLARE --****************************************************************************************************** BEGIN RETURN QUERY SELECT ti.r_IndexName::VARCHAR(256), ti.r_IndexType::VARCHAR(256), ti.r_isUnique::BOOLEAN, ti.r_isPrimary::BOOLEAN, ti.r_IndexKeyNames::TEXT FROM admtf_Table_Indexes(a_SchemaName::NAME,a_TableName::NAME) ti; END $BODY$ LANGUAGE plpgsql; COMMENT ON FUNCTION admtf_Table_Indexes(a_SchemaName VARCHAR(256), a_TableName VARCHAR(256)) IS '    '; --ROLLBACK TRANSACTION; COMMIT TRANSACTION; SELECT * FROM admtf_Table_Indexes('public'::NAME,'Street'::NAME); SELECT * FROM admtf_Table_Indexes('public'::VARCHAR(256),'Street'::VARCHAR(256)); 


Siehe auch


PostgreSQL. ;
PostgreSQL. .
PostgreSQL. ( ) .

Source: https://habr.com/ru/post/de415897/


All Articles