Two handy examples of the psql \copy meta-command.

In this blog post, I will cover example uses of the psql \copy meta-command, for client-side work with the PostgreSQL database server. This command allows users who do not have Superuser privileges, to perform COPY type SQL commands.

Note: All data, names or naming found within the database presented in this post, are strictly used for practice, learning, instruction, and testing purposes. It by no means depicts actual data belonging to or being used by any party or organization.

I will be using Xubuntu Linux 16.04.2 LTS (Xenial Xerus) and PostgreSQL 10.0 for these exercises.


imgix-391813

Photo by imgix on Unsplash

Importing Data.

Let’s get a description of the staging_assets target table before proceeding:

fab_tracking=> \d staging_assets;
                        Table "public.staging_assets"
       Column        |         Type          | Collation | Nullable | Default
---------------------+-----------------------+-----------+----------+---------
 pipe_id             | character varying(25) |           | not null |
 kind                | character varying(25) |           |          |
 pipe_jt_num         | character varying(25) |           |          |
 pipe_heat           | character varying(25) |           |          |
 pipe_length         | numeric(4,2)          |           |          |
 pipe_wall_thickness | numeric(4,3)          |           |          | 0.000
 degree              | numeric(4,2)          |           |          | 0
Indexes:
    "staging_assets_pkey" PRIMARY KEY, btree (pipe_id)
Check constraints:
    "pipe_len_chk" CHECK (pipe_length > 0::numeric)
    "up_kind_chk" CHECK (upper(kind::text) = kind::text)
Triggers:
    distinguish AFTER INSERT ON staging_assets FOR EACH ROW EXECUTE PROCEDURE move_along()

Next, I have provided an image of the CSV file to be uploaded:

psql_pipe_tallycsv

Target file to upload.

Below is the stripped-down syntax I will use for the \copy command examples presented here:

\copy { table | ( query ) } { from | to } { 'filename' } [ [ with ] ( option [, ...] ) ]

Prior to uploading, I will get a count of records present in the staging_assets table, if any.

fab_tracking=> SELECT COUNT(*) FROM staging_assets;
count
-------
0
(1 row)

The target table is empty. Let me populate it with some data.

fab_tracking=> \copy staging_assets FROM '~/Practice_Data/psql_pipe_tally.csv' WITH DELIMITER ',' CSV HEADER;
COPY 35

The command tag returns a successful COPY 35.

Glancing over the different parts of the command:

    • \copy staging_assets – The target table to perform the operation on.
    • FROM '~/Practice_Data/psql_pipe_tally.csv' – Specifying the file to use.
    • WITH DELIMITER ',' CSV HEADER; – Specifying that this file’s delimiter will be a comma and that it does contain a header.

To verify, I will re-query for another count of the table, along with retrieving 10 rows of data for viewing with a SELECT statement.

SELECT COUNT(*) FROM staging_assets;
count
-------
35
(1 row)

Returning 10 rows for viewing:

fab_tracking=> SELECT * FROM staging_assets LIMIT 10;
pipe_id   |    kind    | pipe_jt_num | pipe_heat | pipe_length | pipe_wall_thickness | degree
-------------+------------+-------------+-----------+-------------+---------------------+--------
Joint-1849A | PIPE_JOINT | 14525       | 11145     |       19.30 |               0.547 |   0.00
Joint-22011 | PIPE_JOINT | 858A        | 6585      |       14.55 |               0.547 |   0.00
Joint-33899 | BEND       | 4546        | 3333CC4   |        6.66 |               0.547 |  15.45
Joint-66525 | BEND       | 369874PP    | 89874     |       10.45 |               0.547 |   8.25
Joint-11996 | BEND       | 15458       | 555522    |        8.25 |               0.547 |  14.75
Joint-33912 | BEND       | 965412300   | 325877    |        9.65 |               0.547 |  33.50
Joint-55122 | PIPE_JOINT | UI-4545     | 985474    |       15.98 |               0.547 |   0.00
Pipe-55122  | FLANGE     | UU-445      | 98574     |        5.98 |               0.547 |   0.00
Joint-3896  | PIPE_JOINT | 17899L      | 14145     |       12.00 |               0.547 |   0.00
Joint-9872  | PIPE_JOINT | 363621      | 54888911  |       17.11 |               0.547 |   0.00
(10 rows)

The \copy command is successful, executing the wholesale upload.


To receive notifications for the latest post from “Digital Owl’s Prose” via email, please subscribe by clicking the ‘Click To Subscribe!’ button in the sidebar!


Writing to local files.

With importing or uploading data to our tables, we can easily reverse this operation to write back to a file on the local system.


Suppose a task has been given to write to a file named ten_footers.csv, all the records from the staging_assets table that have a pipe_length of 10 feet or less. You wonder how can \copy accomplish that?

Let’s find out.

Before anything, I need to establish a query to retrieve only those records matching the requirement.

This one should do it.

fab_tracking=> SELECT *
FROM staging_assets
WHERE pipe_length < = 10.00;

The following \copy command meets the requirements of providing a CSV file format:

fab_tracking=> \copy (SELECT * FROM staging_assets WHERE pipe_length < = 10.00) TO '~/Practice_Data/ten_footers.csv' WITH DELIMITER ',' CSV HEADER;
COPY 15

An image of the ten_footers.csv file.

Emacs CSV File

The newly written ten_footers.csv file, complete with the required data.

Briefly looking at the supplied arguments.

  • (SELECT * FROM staging_assets WHERE pipe_length < = 10.00)– The query used to retrieve the correct records in accordance with the specification.
  • TO '~/Practice_Data/ten_footers.csv'
    – Write the results of the above query to the provided file, which is ten_footers.csv.
  • WITH DELIMITER ',' CSV HEADER;– Designating a comma for the delimiter and using headers.

This is my initial exploration of the \copy command however, I plan to discover multiple applicable uses of it in the near future.


A Call To Action!

Thank you for taking the time to read this post. I truly hope you discovered something interesting and enlightening. Please share your findings here, with someone else you know who would get the same value out of it as well.

Have I mentioned how much I love a cup of coffee?!?!

To receive notifications for the latest post from “Digital Owl’s Prose” via email, please subscribe by clicking the ‘Click To Subscribe!’ button in the sidebar!
Be sure and visit the “Best Of” page for a collection of my best blog posts.


Josh Otwell has a passion to study and grow as a SQL Developer and blogger. Other favorite activities find him with his nose buried in a good book, article, or the Linux command line. Among those, he shares a love of tabletop RPG games, reading fantasy novels, and spending time with his wife and two daughters.


Disclaimer: The examples presented in this post are hypothetical ideas of how to achieve similar types of results. They are not the utmost best solution(s). The majority, if not all, of the examples provided are performed on a personal development/learning workstation-environment and should not be considered production quality or ready. Your particular goals and needs may vary. Use those practices that best benefit your needs and goals. Opinions are my own.

5 thoughts on “Two handy examples of the psql \copy meta-command.

Hey thanks for commenting! Leave a Reply

This site uses Akismet to reduce spam. Learn how your comment data is processed.