PostgreSQL
 sql >> Base de données >  >> RDS >> PostgreSQL

plpgsql - utilisation du nom de table dynamique dans l'instruction declare

Il est important de comprendre la nature principale de ces cinq différents types de données/symboles :

1. 'my_tbl'

Un littéral de chaîne unknown taper . Lorsqu'il est utilisé en SQL (intégré ou non dans le code plpgsql), il est contraint à un type dérivé du contexte . Si le type ne peut pas être déterminé, un transtypage explicite peut être nécessaire. Comme :'my_tbl'::text .

2. 'my_tbl'::text

Le même littéral de chaîne converti en type text . Il peut contenir le nom d'une table, mais ce n'est en réalité que du texte.

3. 'my_tbl'::regclass

Un identifiant d'objet (OID) pour une classe enregistrée . Il est affiché et peut être saisi sous forme de chaîne représentant un nom d'objet valide ('my_tbl' ). La sortie est automatiquement qualifiée de schéma ('my_schema.my_tbl' ) et/ou entre guillemets ('"mY_TbL"' ) s'il serait autrement ambigu ou illégal. Il peut s'agir d'un tableau normal , séquence , afficher , vue matérialisée , type composite etc. Détails dans cette réponse connexe :

4. my_tbl_var my_tbl (abréviation de my_tbl_var my_tbl%ROWTYPE )

Dans le DECLARE section d'un bloc de code plpgsql qui est une déclaration de variable avec un bien connu type de ligne (alias type composite). Le type doit être enregistré dans la table système pg_class (comme avec un regclass variable). Ce n'est pas l'OID de l'objet référencé, mais son type de ligne réel. my_tbl_var et my_tbl sont tous les deux des identifiants ici et ne peut pas être paramétré. Vous pouvez également transtyper n'importe quelle ligne ou enregistrement directement :(123, 'foo')::my_tbl

5. my_tbl_var record

Dans le DECLARE section d'un bloc de code plpgsql qui est la déclaration d'un anonyme enregistrement . Fondamentalement, un espace réservé pour un type de ligne encore inconnu / avec une structure encore indéfinie. Il peut être utilisé dans la plupart des endroits où un type de ligne peut être utilisé. Mais vous ne pouvez pas accéder aux champs à partir de celui-ci avant que la variable d'enregistrement ne soit affectée.

Vous confondez 1. , 3. et 4. et l'a résolu en utilisant 5. à la place.
Mais il y a plus de problèmes ici :

  • Vous sélectionnez une table entière, mais une variable de ligne (enregistrement) ne peut contenir qu'une seule ligne à la fois. Ainsi, seul le premier est attribué et renvoyé. Tant qu'il n'y a pas de ORDER BY clause, le résultat est arbitraire et peut changer à tout moment. Piège maléfique.

  • Puisque vous utilisez maintenant un record type, vous devez vous assurer qu'il a été attribué avant de pouvoir exécuter des tests sur ses champs, sinon vous obtiendrez des exceptions pour les tables vides. Dans votre cas, la vérification record_var IS NULL fait presque le même travail. Mais il existe un cas particulier pour les lignes avec NULL dans tous les champs :alors record_var IS NULL évalue à vrai. Encore plus délicat pour le test IS NOT NULL . Détails ici :

    J'ai ajouté une démo au violon SQL ci-dessous.

  • La fonction renvoie un seul scalaire (boolean ) évaluer. Utiliser :

    RETURN false;
    

    Au lieu de :

    RETURN QUERY SELECT false;

Fonction

CREATE FUNCTION check_valid(_tbl regclass)
  RETURNS bool AS
$func$
DECLARE
   r record;
   _row_ct int;
BEGIN
   EXECUTE '
   SELECT is_valid, hit_count, hit_limit
   FROM  ' || _tbl || '
   ORDER  <whatever>
   LIMIT  1'            -- replace <whatever> with your sort criteria
   INTO r;              -- only needed columns

   GET DIAGNOSTICS _row_ct = ROW_COUNT;

   IF _row_ct = 0 THEN  -- necessary, because r may not be assigned
      RETURN false;
   ELSIF NOT r.is_valid OR r.hit_count > r.hit_limit THEN
      RETURN false;
   END IF;

   RETURN true;
END
$func$  LANGUAGE plpgsql;

SQL Fiddle (avec deux variantes de la fonction et une démo pour la ligne IS NULL).

Points majeurs

  • Utilisez GET DIAGNOSTICS pour savoir si des lignes ont été trouvées dans une instruction dynamique avec EXECUTE .

  • Le IF l'expression peut être simplifiée.

  • Le paramètre est de type regclass , pas seulement un nom de table. Je n'utiliserais pas le nom trompeur "tablename" pour ce paramètre. Cela ne fait qu'ajouter à votre confusion initiale. Appelez-le _tbl à la place.

Si vous souhaitez également revenir un ensemble de type de ligne variable :