Pour alimenter un outil de reporting, j'ai besoin de réaliser des extractions régulières depuis une base Oracle, en prenant les objets qui répondent à certains critères (entre autres : qui ont été modifiés au cours des X derniers jours, X étant généralement égal à 3). L'instance Oracle se trouve sur un serveur en production sur lequel je ne suis même pas censé installer un script crontabé, donc mon but est de minimiser les traitements à ce niveau.
J'ai donc un .sh qui balance ma requête à Oracle (via sqlplus, le client en ligne de commande par défaut), récupère le résultat brut dans un fichier et l'envoie par SCP vers un serveur sur lequel je peux faire ce que je veux. Je cherche à obtenir un fichier dont chaque ligne est au format suivant :
champ1=valeur1;champ2=valeur2;champ3=valeur3;[...];
Du coup ma requête ressemble à ça :
CREATE OR REPLACE FUNCTION escape(val IN VARCHAR2) RETURN VARCHAR2 AS BEGIN RETURN REPLACE(REPLACE(REPLACE(REPLACE(REPLACE(val, '#', '#25'), '=', '#3D'), ';', '#3B'), CHR(13), '#0D'), CHR(10), '#0A'); END; / CREATE OR REPLACE FUNCTION getField(key IN VARCHAR2, val IN VARCHAR2) RETURN VARCHAR2 AS BEGIN IF val IS NULL THEN RETURN ''; ELSE RETURN escape (key) || '=' || escape (val) || ';'; END IF; END; / SELECT getField ('champ1', champ1) || getField ('champ2', champ2) || getField ('champ3', champ3) || [...] FROM matable WHERE mescritères;
La fonction "escape" me permet d'échapper les caractères ";", "=" et les retours à la ligne puisqu'ils ont une signification spéciale dans mon format de sortie. Ça marche plutôt pas mal, tant que la requête ne remonte pas trop de résultats. Mais de temps en temps j'ai besoin de faire une extraction totale de la base, et ça coince au niveau de la mémoire ("ORA-01652: unable to extend temp segment by 256 in tablespace TEMP").
Je pense que mes fonctions getField et escape, en plus du fait de concaténer toutes les chaines du résultat, provoquent une consommation de mémoire non négligeable. En plus certains champs sont des textes potentiellement très longs, donc j'ai du faire des versions de getField et escape qui utilisent le type "CLOB" au lieu de "VARCHAR2", et ça doit être encore pire au niveau mémoire.
Ma seule solution pour l'instant serait de découper la requête en plusieurs, en sélectionnant plusieurs fenêtres de temps, mais c'est un contournement pas très élégant. Y aurait-il une autre façon de m'y prendre qui bouffe moins de ressources ?