Concate string path in psql command line - sql

I have to merge path directory and actual time in psql command line.
This absolute path works well:
\copy (select row_to_json(row(column1,column5)) from testdatabase.test) TO 'C:\Users\path\file.json';
But when I want merge two string like this:
\copy (select row_to_json(row(column1,column5)) from testdatabase.test) TO 'C:\Users\path\' || to_char(now(), 'YYYY_MM_DD');
I get Permission denied. This is permission issue i think because first request works well on same path. I tried also CONCATE statement but it fails.

If using COPY directly is an option, you could dynamically build the command, including your custom path logic and EXECUTE it. For example in an anonymous DO block:
DO
$$
BEGIN
EXECUTE 'COPY (SELECT row_to_json(...) ...) TO ''C:\...\' || to_char(now(), 'YYYY_MM_DD') || '.json''';
END;
$$
LANGUAGE plpgsql;

Related

using psql environment in a function declaration

I am failing to use a parameter in function declaration.
a SQL script like :
CREATE OR REPLACE FUNCTION test_functon() RETURNS trigger AS
$BODY$
DECLARE
test int:=:SRID;
BEGIN
RETURN NEW;
END;
$BODY$
LANGUAGE plpgsql;
in a file.
And running psql -v SRID=2056 -f my_file.sql
leads to the error
ERROR: syntax error at or near ":"
This looks like the SQL is run without the var being properly replaced by its value.
What is the solution for this?
It seems like psql does not interpolate variables in the body of functions.
The following SQL verifies that.
SELECT :SRID;
CREATE OR REPLACE FUNCTION test_functon() RETURNS :TYPE AS
$BODY$
BEGIN
RETURN :SRID;
END;
$BODY$
LANGUAGE plpgsql;
Running that file results in this:
$ psql -v SRID=2056 -v TYPE=int -f query.sql
Expanded display is used automatically.
Null display is "ยค".
?column?
----------
2056
(1 row)
psql:query.sql:9: ERROR: syntax error at or near ":"
LINE 4: RETURN :SRID;
^
Notice how the return type defined by the variable TYPE is still interpolated, but everything inside the body is off limits.
You'll have to resort to a different mechanism to get your variable in there. You could leverage the fact that psql accepts a query through STDIN:
$ sed 's/:SRID/2056/' query.sql | psql

Export table to csv file by using procedure (csv name with timestamp)

I want to export one table to each csv file and name the csv file with timestamp. For example, if I have a table t1, after export, a csv file name t1.YYYYMMDDHHMISS.csv is generated. Here is my code:
create or replace procedure
T_to_CSV(Tname varchar2,Directory varchar2)
BEGIN
set colsep , -- separate columns with a comma
set pagesize 0 -- No header rows
set trimspool on -- remove trailing blanks
set headsep off -- this may or may not be useful...depends on your
spool timestamp.csv --don't know how to write in directory
select *
from Tname
end
Here are my questions:
(1) I don't know how to output the csv file into the requried directory, how to fix the spool code please?
Should I use spool D:\path\filename.csv please?
(2) In the spool line, how to change the name of the csv file using the timestamp now() please?
There are a few steps:
create a directory using `CREATE DIRECTORY my_dir as 'C:\dir';
make sure that Oracle has read,write on the folder on the computer (best accomplished by creating it in the Oracle install folder)
grant the user executing the procedure GRANT read,write on DIRECTORY my_dir to the_user;
download and compile the handy procedure here
I have used this and it works really nicely.
Usage
data_dump ( 'Select emp_name from emp',
CURRENT_TIMESTAMP||'filename.csv',
my_dir);
(vastly simplified sample!)
After creating the directory verify your work by running this:
Select * from ALL_DIRECTORIES;
you should see your directory
logon to the machine where the database is located and verify the folder path exists and the oracle user has permissions on it. Networked drives are only possible if the user running the oracle service has permissions on that folder
Thanks kevin for sharing the procedure and it was very useful for me. I have customized the code:
To Add the column names in the output csv file which was not working earlier.
When i was passing the delimiter as parameter then it was adding the comma in the starting of every row(,1,2,3) which has been corrected.
I am also sharing the customized code which might help others. Customized code can be downloaded here.
Customized Code to add column names
FOR i IN t_describe.FIRST .. t_describe.LAST LOOP
IF i <> t_describe.LAST THEN put('UTL_FILE.PUT(v_fh,'''||t_describe(i).col_name||'''||'''||v_delimiter||''');');
ELSE
put(' UTL_FILE.PUT(v_fh,'''||t_describe(i).col_name||''');');
END IF;
END LOOP;
put(' UTL_FILE.NEW_LINE(v_fh);');
Customized Code for delimiter
IF i <> t_describe.LAST THEN
put(' UTL_FILE.PUT(v_fh,"'||t_describe(i).col_name||'"(i) ||'''||v_delimiter||''');');
ELSE
put(' UTL_FILE.PUT(v_fh,"'||t_describe(i).col_name||'"(i));');
END IF;
And the correct way to call the procedure is to bind the variable with values
data_dump(query_in => 'Select 1 from dual',file_in => 'file.csv',directory_in => 'MY_DIR', delimiter_in => '|' );
Thanks
Naveen
Save and copy file in a directory {file need not be executable}
Export ORACLE_HOME, PATH and SID of database instance
Navigate to that directory and execute sqlplus
Spool file will be created in the same directory as the .sql file
SET COLSEP ,
SET HEADSEP OFF -- character used when defining a two-line column heading
SET TRIMSPOOL ON -- trim trailing spaces from each line
SET LINESIZE 32000 -- number of characters to be printed on one line
SET WRAP OFF -- truncates lines longer than LINESIZE
SET NUMWIDTH 5 -- default width while displaying numbers
COLUMN tm new_value iso8601 noprint
SELECT to_char(sysdate, 'YYYY-MM-DD') tm FROM dual;
spool output_file_&iso8601..csv
-- Append new data to spool file: "spool output_file_&iso8601..csv append"
SELECT
*
FROM
table_name
spool OFF

What's the equivalent of clrscr(); in Oracle SQL?

I want to write a simple script file to display the employee name in a given department name the department name given is case insensitive ,after executing the script, the commands are not displayed.
I use SQL *PLUS and what I did so far is
EDIT script // script is the file name the default extension is .SQL
and inside the script file I wrote the following
SET VERIFY OFF
SELECT Ename, dname
FROM emp, dept
WHERE emp.deptno = dept.deptno
AND UPPER(Dname) = UPPER('&dname');
SET VERIFY ON
then on SQL *Plus
START script
The query works fine but I don't know how to do this part "after executing the script, the commands are not displayed.
"
Maybe are you looking for:
SET ECHO OFF
An other option would be to start SQL*Plus with the -S (silent) option on the command line. From the documentation:
-S[ILENT]
Suppresses all SQLPlus information and prompt messages, including the command prompt, the echoing of commands, and the banner normally displayed when you start SQLPlus.
As about the question as titled:
What's the equivalent of clrscr()
If you are using an ANSI terminal, using the ANSI escape sequence esc[2J should clear your screen:
SET ECHO OFF
SET SERVEROUTPUT ON
VAR ANSI_TERM_CLEAR VARCHAR2 (10)
BEGIN SELECT CHR(27)||'[2J' INTO :ANSI_TERM_CLEAR FROM DUAL; END;
/
PRINT :ANSI_TERM_CLEAR

How to embed substituion variable into string in "psql"

I'm calling a bash script which prepares some directories etc.
At some point it also calls a psql script something like this:
psql $PSQL_LOGIN -v SERVER=$SERVER
So far so good.
The sql that I want to execute is something like this:
select 'wget :SERVER.xxxx.com?geoX=id1&geoY=id2'
from table1 where id1 > zzz;
What I expect out of this is a string that looks like
wget test.xxx.com?geoX=yyy&geoY=qqq
In oracle I could do something like
select 'wget' || :SERVER '.xxx.com?geoX='|| id1 || etc etc
But I cannot make it work in postgres when it should be embedded in a string
I've tried to escape the ' and withoud but so far no luck.
Anybody got any ideas?
I've found the answer to this question.
When calling from the shell script, its in the shell script itself that
the escaping must be done. So instead of the above i should do the calling like this :
psql $PSQL_LOGIN -V SERVER="'$SERVER'"
that is double quote and single quote.
Then the sql file can be done the same way as in Oracle :
select 'wget' || :SERVER '.example.com?geoX='|| id1 || ' etc

Generate a Properties File using Shell Script and Results from a SQL Query

I am trying to create a properties file like this...
firstname=Jon
lastname=Snow
occupation=Nights_Watch
family=Stark
...from a query like this...
SELECT
a.fname as firstname,
a.lname as lastname,
b.occ as occupation...
FROM
names a,
occupation b,
family c...
WHERE...
How can I do this? As I am aware of only using spool to a CSV file which won't work here?
These property files will be picked up by shell scripts to run automated tasks. I am using Oracle DB
Perhaps something like this?
psql -c 'select id, name from test where id = 1' -x -t -A -F = dbname -U dbuser
Output would be like:
id=1
name=test1
(For the full list of options: man psql.)
Since you mentionned spool I will assume you are running on Oracle. This should produce a result in the desired format, that you can spool straight away.
SELECT
'firstname=' || firstname || CHR(10) ||
'lastname=' || lastname || CHR(10) -- and so on for all fields
FROM your_tables;
The same approach should be possible with all database engines, if you know the correct incantation for a litteral new line and the syntax for string concatenation.
It is possible to to this from your command line SQL client but as STTLCU notes it might be better to get the query to output in something "standard" (like CSV) and then transform the results with a shell script. Otherwise, because a lot of the features you would use are not part of any SQL standard, they would depend on the database server and client application. Think of this step as sort of the obverse of ETL where you clean up the data you "unload" so that it is useful for some other application.
For sure there's ways to build this into your query application: e.g. if you use something like perl DBI::Shell as your client (which allows you to connect to many different servers using the DBI module) you can jazz up your output in various ways. But here you'd probably be best off if could send the query output to a text file and run it through awk.
Having said that ... here's how the Postgresql client could do what you want. Notice how the commands to set up the formatting are not SQL but specific to the client.
~/% psql -h 192.168.2.69 -d cropdusting -u stubblejumper
psql (9.2.4, server 8.4.14)
WARNING: psql version 9.2, server version 8.4.
Some psql features might not work.
You are now connected to database "cropdusting" as user "stubblejumper".
cropdusting=# \pset border 0 \pset format unaligned \pset t \pset fieldsep =
Border style is 0.
Output format is unaligned.
Showing only tuples.
Field separator is "=".
cropdusting=# select year,wmean_yld from bckwht where year=1997 AND freq > 13 ;
1997=19.9761904762
1997=14.5533333333
1997=17.9942857143
cropdusting=#
With the psql client the \pset command sets options affecting the output of query results tables. You can probably figure out which option is doing what. If you want to do this using your SQL client tell us which one it is or read through the manual page for tips on how to format the output of your queries.
My answer is very similar to the two already posted for this question, but I try to explain the options, and try to provide a precise answer.
When using Postgres, you can use psql command-line utility to get the intended output
psql -F = -A -x -X <other options> -c 'select a.fname as firstname, a.lname as lastname from names as a ... ;'
The options are:
-F : Use '=' sign as the field separator, instead of the default pipe '|'
-A : Do not align the output; so there is no space between the column header, separator and the column value.
-x : Use expanded output, so column headers are on left (instead of top) and row values are on right.
-X : Do not read $HOME/.psqlrc, as it may contain commands/options that can affect your output.
-c : The SQL command to execute
<other options> : Any other options, such as connection details, database name, etc.
You have to choose if you want to maintain such a file from shell or from PL/SQL. Both solutions are possible and both are correct.
Because Oracle has to read and write from the file I would do it from database side.
You can write data to file using UTL_FILE package.
DECLARE
fileHandler UTL_FILE.FILE_TYPE;
BEGIN
fileHandler := UTL_FILE.FOPEN('test_dir', 'test_file.txt', 'W');
UTL_FILE.PUTF(fileHandler, 'firstname=Jon\n');
UTL_FILE.PUTF(fileHandler, 'lastname=Snow\n');
UTL_FILE.PUTF(fileHandler, 'occupation=Nights_Watch\n');
UTL_FILE.PUTF(fileHandler, 'family=Stark\n');
UTL_FILE.FCLOSE(fileHandler);
EXCEPTION
WHEN utl_file.invalid_path THEN
raise_application_error(-20000, 'ERROR: Invalid PATH FOR file.');
END;
Example's source: http://psoug.org/snippet/Oracle-PL-SQL-UTL_FILE-file-write-to-file-example_538.htm
At the same time you read from the file using Oracle external table.
CREATE TABLE parameters_table
(
parameters_coupled VARCHAR2(4000)
)
ORGANIZATION EXTERNAL
(
TYPE ORACLE_LOADER
DEFAULT DIRECTORY test_dir
ACCESS PARAMETERS
(
RECORDS DELIMITED BY NEWLINE
FIELDS
(
parameters_coupled VARCHAR2(4000)
)
)
LOCATION ('test_file.txt')
);
At this point you can write data to your table which has one column with coupled parameter and value, i.e.: 'firstname=Jon'
You can read it by Oracle
You can read it by any shell script because it is a plain text.
Then it is just a matter of a query, i.e.:
SELECT MAX(CASE WHEN INSTR(parameters_coupled, 'firstname=') = 1 THEN REPLACE(parameters_coupled, 'firstname=') ELSE NULL END) AS firstname
, MAX(CASE WHEN INSTR(parameters_coupled, 'lastname=') = 1 THEN REPLACE(parameters_coupled, 'lastname=') ELSE NULL END) AS lastname
, MAX(CASE WHEN INSTR(parameters_coupled, 'occupation=') = 1 THEN REPLACE(parameters_coupled, 'occupation=') ELSE NULL END) AS occupation
FROM parameters_table;