Skip to main content

Overview

This tutorial follows the ClickHouse tutorial but runs all its queries via pg_clickhouse.

Start ClickHouse

First, create a ClickHouse database if you don’t already have one. A quick way to start is with the Docker image:

Create a table

Let’s borrow from the ClickHouse tutorial to create a simple database with The New York City taxi dataset:

Add the data set

And then import the data:
Make sure we can query it then quit the client:

Install pg_clickhouse

Build and install pg_clickhouse from PGXN or GitHub. Or spin up a Docker container using the pg_clickhouse image, which simply adds pg_clickhouse to the Docker Postgres image:

Connect pg_clickhouse

Now connect to Postgres:
And create pg_clickhouse:
Create a foreign server using the host name, port, and database for your ClickHouse database.
Here we’ve elected to use the binary driver, which uses the ClickHouse binary protocol. You can also use the “http” driver, which uses the HTTP interface. Next, map a PostgreSQL user to a ClickHouse user. The simplest way to do so is just to map the current PostgreSQL user to a remote user for the foreign server:
You can also specify a password option. Now, add the taxi table, just import it all of the tables from the remote ClickHouse database into a Postgres schema:
And now the table should be imported: In psql, use \det+ to see it:
Success! Use \d to show all the columns:
Now query the table:
Note how quickly the query executed. pg_clickhouse pushes down the entire query, including the COUNT() aggregate, so it runs on ClickHouse and only returns the single row to Postgres. Use EXPLAIN to see it:
Note that “Foreign Scan” appears at the root of the plan, meaning that the entire query was pushed down to ClickHouse.

Analyze the data

Run some queries to analyze the data. Explore the following examples or try your own SQL query.
  • Calculate the average tip amount:
  • Calculate the average cost based on the number of passengers:
  • Calculate the daily number of pickups per neighborhood:
  • Calculate the length of each trip in minutes, then group the results by trip length:
  • Show the number of pickups in each neighborhood broken down by hour of the day:
  • Set display time zone for New York and retrieve rides to LaGuardia or JFK airports:

Create a dictionary

Create a dictionary associated with a table in your ClickHouse service. The table and dictionary are based on a CSV file that contains a row for each neighborhood in New York City. The neighborhoods are mapped to the names of the five New York City boroughs (Bronx, Brooklyn, Manhattan, Queens and Staten Island), as well as Newark Airport (EWR). Here’s an excerpt from the CSV file you’re using in table format. The LocationID column in the file maps to the pickup_nyct2010_gid and dropoff_nyct2010_gid columns in your trips table:
  1. Still in Postgres, use the clickhouse_raw_query function to create a ClickHouse dictionary named taxi_zone_dictionary and populate the dictionary from the CSV file in S3:
Setting LIFETIME to 0 disables automatic updates to avoid unnecessary traffic to our S3 bucket. In other cases, you might configure it differently. For details, see Refreshing dictionary data using LIFETIME.
  1. Now import it:
  1. Confirm we can query it:
  1. Excellent. Now use the dictGet function unction to retrieve a borough’s name in a query. For this query sums up the number of taxi rides per borough that end at either the LaGuardia or JFK airport:
This query sums up the number of taxi rides per borough that end at either the LaGuardia or JFK airport. Notice there are quite a few trips where the pickup neighborhood is unknown.

Perform a join

Write some queries that join the taxi_zone_dictionary with your trips table.
  1. Start with a simple JOIN that acts similarly to the previous airport query above:
Notice the output of the above JOIN query is the same as the dictGet query above, (except that the Unknown values aren’t included). Behind the scenes, ClickHouse is actually calling the dictGet function for the taxi_zone_dictionary dictionary, but the JOIN syntax is more familiar for SQL developers.
  1. This query returns rows for the 1000 trips with the highest tip amount, then performs an inner join of each row with the dictionary:
Generally, we avoid using SELECT * in PostgreSQL and ClickHouse. You should only retrieve the columns you actually need.
Last modified on June 19, 2026