J'essaie de créer une collection de scripts pour modifier certaines tables dans une base de données Postgres 9.6. Ces scripts sont stockés dans une variété de répertoires et sont à leur tour appelés à partir d'un script de coordinateur principal. J'exécute les scripts dans psql.
Dans le script maître, j'utilise \ir
pour spécifier des chemins d'accès relatifs aux autres scripts. En conséquence, je peux exécuter psql de n'importe où, et le script maître trouvera les autres scripts. Cela fonctionne comme prévu.
Dans certains scripts, cependant, j'utilise le \copy
commande pour charger les données à partir des fichiers CSV. Malheureusement, il semble que le nom de fichier d'entrée pour \copy
est toujours interprété comme par rapport au répertoire de travail actuel de psql, plutôt que par rapport au répertoire de son script, même si j'appelle le script contenant le \copy
commande utilisant \ir
.
Par exemple, disons que je démarre psql
dans C:\
, et prévoyez d'utiliser les fichiers suivants:
C:\
dir1\
script1.sql
dir2\
script2.sql
input.csv
Je peux appeler le premier script en utilisant \ir dir1/script1.sql
. Ça marche bien.
En outre, ce script peut appeler le deuxième script à l'aide de \ir dir2/script2.sql
. Cela fonctionne aussi très bien.
Le problème est le \copy
commande dans le deuxième script:
\COPY foo.bar (col1) FROM 'input.csv' WITH (FORMAT CSV);
Cela échoue à moins que input.csv
existe dans le répertoire courant de la session psql
. Par conséquent, ma question est:
\COPY ... FROM
commande depuis l'intérieur d'un script sans obliger l'utilisateur à exécuter la session psql
à partir d'un répertoire spécifique par rapport au fichier d'entrée?Je comprends que je pourrais utiliser un nom de chemin absolu, mais nous allons exécuter ce script à partir d'une variété de machines/plates-formes différentes lors de la mise à niveau de nos différents déploiements, donc il n'y a pas de chemin absolu commun qui fonctionnera dans ce cas. Je cherche une sorte de solution de chemin d'accès relatif ou configurable.
Merci d'avance.
Je pense qu'il n'y a aucun moyen d'indiquer à \copy
que le fichier est relatif à l'emplacement du script, mais vous pouvez utiliser \cd
pour changer le répertoire courant avant \copy
.
\cd
interpole les variables pour que le répertoire puisse être passé sur la ligne de commande avec -v
:
psql -vscriptdir="c:\path\to\script" -f c:\path\to\script\script1.sql
Avant d'inclure quoi que ce soit, disons qu'il y a cette commande dans le script de niveau supérieur:
\cd :scriptdir
(sauf si le -f
l'argument est simplement un fichier sans chemin: dans ce cas, il n'est pas nécessaire car le répertoire courant est déjà défini là où se trouve le script)
Ensuite, chaque inclusion d'un script dans un sous-répertoire devrait être:
\cd dir2
\i script2.sql
\cd ..
(par opposition à \ir dir2/script2.sql
)
Quand script2.sql
invoquera \copy
avec un fichier relatif, le fichier de données sera trouvé car il se trouve dans le répertoire courant.
Cela devrait fonctionner de manière récursive, en supposant que les scripts dans les sous-répertoires respectent également cette convention, et qu'ils ne font pas de _ \cd
qui interférerait avec le \cd ..
en attente de se produire sur le chemin du retour vers le haut.
Si vous souhaitez stocker des données/(root) obtenir des données de votre machine locale et communiquer avec un serveur PostgreSQL sur une autre machine distante, vous ne pouvez pas simplement utiliser COPY
.
Essayez la méta-commande \copy
en psql . C'est un wrapper pour la commande SQL COPY
et utilise des fichiers locaux.
Votre nom de fichier devrait fonctionner tel quel sur une machine Windows, mais PostgreSQL l'interprète comme un local filename sur le serveur, qui est probablement un dérivé Unix et là le nom de fichier devrait commencer par '/'.
Si ce qui suit est vrai,
psql
en cours d'exécution à partir de C:\
C:\dir1\dir2\input.csv
est le fichier.Ensuite, vous devriez pouvoir le charger comme ceci,
\COPY foo.bar (col1) FROM 'dir1/dir2/input.csv' WITH (FORMAT CSV);
J'ai rencontré exactement le même cas d'utilisation, et je n'étais pas amoureux du \cd
solution pour ma situation particulière.
La solution que j'ai élaborée est assez simple, mais elle n'a de sens que si le seul but des fichiers CSV est d'alimenter les tables. Dans ma configuration, les fichiers CSV étaient stockés dans le contrôle de version juste à côté des fichiers SQL qui les utilisaient, donc la modification était facile à effectuer.
Au lieu de:
\COPY some_table FROM 'input.csv' WITH (FORMAT CSV)
J'ai utilisé:
\COPY some_table FROM STDIN WITH (FORMAT CSV)
some value,some other value
second row,yet another value
and so on,and so forth
\.
En d'autres termes, déplacez simplement les données CSV directement en ligne dans votre fichier SQL.
Certainement pas adapté à tous les cas d'utilisation, mais j'ai pensé qu'il devrait être mentionné ici comme solution alternative. :)
Pour de plus amples informations, voir la documentation officielle du \copy
fonctionnalité .