# Data Warehouse Exports

## How data warehouse exports work

You can export your data from Statsig to your data warehouse using a data connection. The data connection lets you send exposures and events directly to your warehouse for further analysis. Statsig supports connections to Snowflake, Redshift, S3, BigQuery, and Databricks.

{% callout type="info" %}
Data Warehouse Exports are an Enterprise-only feature. If you're on the Developer or Pro tiers and want to upgrade to Enterprise, reach out to the Statsig team [here](https://www.statsig.com/contact/demo) or through support.
{% /callout %}

## Set up a data warehouse export

1. Go to Statsig Console.
2. Navigate to the Help and Tools Section on the side navigation bar.
3. Go to "Exports List".

{% figure %}
![Statsig Help and Tools menu showing Exports List option](https://docs.statsig.com/images/exports-list.png)
{% /figure %}

On the Exports page, you find an *Export History* tab and a *Schedule Export* tab. The *Export History* tab lists all previous experimentation exports triggered from the *Experiment Results* page. To export one-off experimentation results, refer to [Experiment Result Exports](https://docs.statsig.com/integrations/data-exports/experiment_result_exports).

Under the *Scheduled Export* tab, set up a data warehouse connection. Configure the connection with the necessary credentials and grant Statsig Read, Write, and Delete permissions. The warehouse-specific tabs below show the fields Statsig requires and example SQL you can use to create each export table yourself.

{% figure %}
![Scheduled export setup form for Snowflake connection with credential fields](https://docs.statsig.com/images/exports-schedule.png)
{% /figure %}

## Export table schema

Statsig can export both Exposures and Events to your destination. The exported columns are logically the same across SQL warehouses, but the physical types vary by warehouse. S3 is a file destination rather than a SQL table destination.

{% tabs %}
{% tab title="BigQuery" %}
**Setup summary**

Configure:

* BigQuery Project ID
* BigQuery Dataset ID
* one destination table name per export type

Permissions:

* Grant the Statsig service account `BigQuery User` at the project level.
* Grant the same service account `BigQuery Data Editor` on the target dataset.

Statsig uses this access to create the table if needed and validate the connection by inserting and deleting a test row.

If you prefer to create the export tables yourself, you can use SQL like this:

```sql BigQuery Exposures
CREATE TABLE IF NOT EXISTS `PROJECT_ID.DATASET_ID.EXPOSURES_TABLE` (
  company_id STRING,
  unit_id STRING,
  unit_type STRING,
  exposure_type STRING,
  name STRING,
  rule STRING,
  experiment_group STRING,
  first_exposure_utc TIMESTAMP,
  first_exposure_pst_date DATE,
  as_of_pst_date DATE,
  percent FLOAT64,
  rollout BIGINT,
  user_dimensions STRING,
  inserted_at TIMESTAMP,
  rule_name STRING,
  group_id STRING,
  non_analytics BOOLEAN
);
```

```sql BigQuery Events
CREATE TABLE IF NOT EXISTS `PROJECT_ID.DATASET_ID.EVENTS_TABLE` (
  user_id STRING,
  stable_id STRING,
  custom_ids JSON,
  timestamp TIMESTAMP,
  event_name STRING,
  event_value STRING,
  user_object JSON,
  statsig_metadata JSON,
  company_metadata JSON
);
```

**Exposures**

Statsig doesn't export exposures from rules that are 0%/100%.

| Field name             | Type      | Mode     |
| ---------------------- | --------- | -------- |
| company\_id             | STRING    | NULLABLE |
| unit\_id                | STRING    | NULLABLE |
| unit\_type              | STRING    | NULLABLE |
| exposure\_type          | STRING    | NULLABLE |
| name                   | STRING    | NULLABLE |
| rule                   | STRING    | NULLABLE |
| experiment\_group       | STRING    | NULLABLE |
| first\_exposure\_utc     | TIMESTAMP | NULLABLE |
| first\_exposure\_pst\_date | DATE      | NULLABLE |
| as\_of\_pst\_date         | DATE      | NULLABLE |
| percent                | FLOAT64   | NULLABLE |
| rollout                | BIGINT    | NULLABLE |
| user\_dimensions        | STRING    | NULLABLE |
| inserted\_at            | TIMESTAMP | NULLABLE |
| rule\_name              | STRING    | NULLABLE |
| group\_id               | STRING    | NULLABLE |
| non\_analytics          | BOOLEAN   | NULLABLE |

**Events**

| Field name        | Type      | Mode     |
| ----------------- | --------- | -------- |
| user\_id           | STRING    | NULLABLE |
| stable\_id         | STRING    | NULLABLE |
| custom\_ids        | JSON      | NULLABLE |
| timestamp         | TIMESTAMP | NULLABLE |
| event\_name        | STRING    | NULLABLE |
| event\_value       | STRING    | NULLABLE |
| user\_object       | JSON      | NULLABLE |
| statsig\_metadata  | JSON      | NULLABLE |
| company\_metadata  | JSON      | NULLABLE |
{% /tab %}

{% tab title="Snowflake" %}
**Setup summary**

Configure:

* Account Name
* Database Name
* Schema Name
* either Username and Password, or Private Key authentication
* one destination table name per export type

Permissions:

* The configured user must be able to create tables and insert/delete rows in the target database and schema.

If you prefer to create the export tables yourself, you can use SQL like this:

```sql Snowflake Exposures
CREATE TABLE IF NOT EXISTS DATABASE_NAME.SCHEMA_NAME.EXPOSURES_TABLE (
  company_id STRING,
  unit_id STRING,
  unit_type STRING,
  exposure_type STRING,
  name STRING,
  rule STRING,
  experiment_group STRING,
  first_exposure_utc TIMESTAMP,
  first_exposure_pst_date DATE,
  as_of_pst_date DATE,
  percent DOUBLE,
  rollout BIGINT,
  user_dimensions STRING,
  inserted_at TIMESTAMP,
  rule_name STRING,
  group_id STRING,
  non_analytics BOOLEAN
);
```

```sql Snowflake Events
CREATE TABLE IF NOT EXISTS DATABASE_NAME.SCHEMA_NAME.EVENTS_TABLE (
  user_id STRING,
  stable_id STRING,
  custom_ids STRING,
  timestamp TIMESTAMP,
  event_name STRING,
  event_value STRING,
  user_object STRING,
  statsig_metadata STRING,
  company_metadata STRING
);
```

**Exposures**

| Field name             | Type      | Mode     |
| ---------------------- | --------- | -------- |
| company\_id             | STRING    | NULLABLE |
| unit\_id                | STRING    | NULLABLE |
| unit\_type              | STRING    | NULLABLE |
| exposure\_type          | STRING    | NULLABLE |
| name                   | STRING    | NULLABLE |
| rule                   | STRING    | NULLABLE |
| experiment\_group       | STRING    | NULLABLE |
| first\_exposure\_utc     | TIMESTAMP | NULLABLE |
| first\_exposure\_pst\_date | DATE      | NULLABLE |
| as\_of\_pst\_date         | DATE      | NULLABLE |
| percent                | DOUBLE    | NULLABLE |
| rollout                | BIGINT    | NULLABLE |
| user\_dimensions        | STRING    | NULLABLE |
| inserted\_at            | TIMESTAMP | NULLABLE |
| rule\_name              | STRING    | NULLABLE |
| group\_id               | STRING    | NULLABLE |
| non\_analytics          | BOOLEAN   | NULLABLE |

**Events**

| Field name        | Type      | Mode     |
| ----------------- | --------- | -------- |
| user\_id           | STRING    | NULLABLE |
| stable\_id         | STRING    | NULLABLE |
| custom\_ids        | STRING    | NULLABLE |
| timestamp         | TIMESTAMP | NULLABLE |
| event\_name        | STRING    | NULLABLE |
| event\_value       | STRING    | NULLABLE |
| user\_object       | STRING    | NULLABLE |
| statsig\_metadata  | STRING    | NULLABLE |
| company\_metadata  | STRING    | NULLABLE |
{% /tab %}

{% tab title="Redshift" %}
**Setup summary**

Configure:

* Cluster Endpoint
* Username and Password
* Staging Schema
* one destination table name per export type

Permissions:

* The configured user must be able to create tables and insert/delete rows in the target database and schema.
* If your cluster isn't directly reachable, you can also configure SSH tunneling.

If you prefer to create the export tables yourself, you can use SQL like this:

```sql Redshift Exposures
CREATE TABLE IF NOT EXISTS "DATABASE_NAME"."STAGING_SCHEMA"."EXPOSURES_TABLE" (
  "company_id" VARCHAR,
  "unit_id" VARCHAR,
  "unit_type" VARCHAR,
  "exposure_type" VARCHAR,
  "name" VARCHAR,
  "rule" VARCHAR,
  "experiment_group" VARCHAR,
  "first_exposure_utc" TIMESTAMP,
  "first_exposure_pst_date" DATE,
  "as_of_pst_date" DATE,
  "percent" DOUBLE PRECISION,
  "rollout" BIGINT,
  "user_dimensions" VARCHAR,
  "inserted_at" TIMESTAMP,
  "rule_name" VARCHAR,
  "group_id" VARCHAR,
  "non_analytics" BOOLEAN
);
```

```sql Redshift Events
CREATE TABLE IF NOT EXISTS "DATABASE_NAME"."STAGING_SCHEMA"."EVENTS_TABLE" (
  "user_id" VARCHAR,
  "stable_id" VARCHAR,
  "custom_ids" VARCHAR,
  "timestamp" TIMESTAMP,
  "event_name" VARCHAR,
  "event_value" VARCHAR,
  "user_object" VARCHAR,
  "statsig_metadata" VARCHAR,
  "company_metadata" VARCHAR
);
```

{% callout type="note" %}
The default size for VARCHAR is 256 and Statsig truncates all columns to 256 characters for Redshift. If you anticipate certain columns exceeding these limits, adjust accordingly and contact Statsig to remove these limits. Common longer columns are `user_dimensions` in exposures and `user_object`, `custom_ids`, and `company_metadata` in events.
{% /callout %}

**Exposures**

| Field name             | Type              | Mode     |
| ---------------------- | ----------------- | -------- |
| company\_id             | VARCHAR           | NULLABLE |
| unit\_id                | VARCHAR           | NULLABLE |
| unit\_type              | VARCHAR           | NULLABLE |
| exposure\_type          | VARCHAR           | NULLABLE |
| name                   | VARCHAR           | NULLABLE |
| rule                   | VARCHAR           | NULLABLE |
| experiment\_group       | VARCHAR           | NULLABLE |
| first\_exposure\_utc     | TIMESTAMP         | NULLABLE |
| first\_exposure\_pst\_date | DATE              | NULLABLE |
| as\_of\_pst\_date         | DATE              | NULLABLE |
| percent                | DOUBLE PRECISION  | NULLABLE |
| rollout                | BIGINT            | NULLABLE |
| user\_dimensions        | VARCHAR           | NULLABLE |
| inserted\_at            | TIMESTAMP         | NULLABLE |
| rule\_name              | VARCHAR           | NULLABLE |
| group\_id               | VARCHAR           | NULLABLE |
| non\_analytics          | BOOLEAN           | NULLABLE |

**Events**

| Field name        | Type              | Mode     |
| ----------------- | ----------------- | -------- |
| user\_id           | VARCHAR           | NULLABLE |
| stable\_id         | VARCHAR           | NULLABLE |
| custom\_ids        | VARCHAR           | NULLABLE |
| timestamp         | TIMESTAMP         | NULLABLE |
| event\_name        | VARCHAR           | NULLABLE |
| event\_value       | VARCHAR           | NULLABLE |
| user\_object       | VARCHAR           | NULLABLE |
| statsig\_metadata  | VARCHAR           | NULLABLE |
| company\_metadata  | VARCHAR           | NULLABLE |
{% /tab %}

{% tab title="Databricks" %}
**Setup summary**

Configure:

* API Key
* Server Hostname
* HTTP Path
* Database
* one destination table name per export type

Permissions:

* The configured user or token must be able to create tables and insert/delete rows in the target database.

If you prefer to create the export tables yourself, you can use SQL like this:

```sql Databricks Exposures
CREATE TABLE IF NOT EXISTS DATABASE_NAME.EXPOSURES_TABLE (
  company_id STRING,
  unit_id STRING,
  unit_type STRING,
  exposure_type STRING,
  name STRING,
  rule STRING,
  experiment_group STRING,
  first_exposure_utc TIMESTAMP,
  first_exposure_pst_date DATE,
  as_of_pst_date DATE,
  percent DOUBLE,
  rollout BIGINT,
  user_dimensions STRING,
  inserted_at TIMESTAMP,
  rule_name STRING,
  group_id STRING,
  non_analytics BOOLEAN
);
```

```sql Databricks Events
CREATE TABLE IF NOT EXISTS DATABASE_NAME.EVENTS_TABLE (
  user_id STRING,
  stable_id STRING,
  custom_ids STRING,
  timestamp TIMESTAMP,
  event_name STRING,
  event_value STRING,
  user_object STRING,
  statsig_metadata STRING,
  company_metadata STRING
);
```

**Exposures**

| Field name             | Type      | Mode     |
| ---------------------- | --------- | -------- |
| company\_id             | STRING    | NULLABLE |
| unit\_id                | STRING    | NULLABLE |
| unit\_type              | STRING    | NULLABLE |
| exposure\_type          | STRING    | NULLABLE |
| name                   | STRING    | NULLABLE |
| rule                   | STRING    | NULLABLE |
| experiment\_group       | STRING    | NULLABLE |
| first\_exposure\_utc     | TIMESTAMP | NULLABLE |
| first\_exposure\_pst\_date | DATE      | NULLABLE |
| as\_of\_pst\_date         | DATE      | NULLABLE |
| percent                | DOUBLE    | NULLABLE |
| rollout                | BIGINT    | NULLABLE |
| user\_dimensions        | STRING    | NULLABLE |
| inserted\_at            | TIMESTAMP | NULLABLE |
| rule\_name              | STRING    | NULLABLE |
| group\_id               | STRING    | NULLABLE |
| non\_analytics          | BOOLEAN   | NULLABLE |

**Events**

| Field name        | Type      | Mode     |
| ----------------- | --------- | -------- |
| user\_id           | STRING    | NULLABLE |
| stable\_id         | STRING    | NULLABLE |
| custom\_ids        | STRING    | NULLABLE |
| timestamp         | TIMESTAMP | NULLABLE |
| event\_name        | STRING    | NULLABLE |
| event\_value       | STRING    | NULLABLE |
| user\_object       | STRING    | NULLABLE |
| statsig\_metadata  | STRING    | NULLABLE |
| company\_metadata  | STRING    | NULLABLE |
{% /tab %}

{% tab title="S3" %}
**Setup summary**

Configure:

* Region
* Bucket
* one folder name per export type

Permissions:

* Grant the Statsig IAM user bucket-level access to list the bucket and retrieve its location.
* Grant object-level read, write, and delete permissions, including multipart upload management.

S3 exports are file-based. Statsig writes export files to the configured bucket and folder for each export type. In the export UI, you configure the per-export destination as a folder rather than a table.

**Export outputs**

| Export type | Destination shape |
| ----------- | ----------------- |
| Exposures   | Files written to the configured folder in your bucket |
| Events      | Files written to the configured folder in your bucket |
{% /tab %}
{% /tabs %}

## Troubleshoot exports

Statsig can notify you if your data connection is failing. To enable notifications, go to *Settings > My Account > Email Notifications > Edit* and select **Alerts** to subscribe.

If your exports are failing, confirm your warehouse is connected with up-to-date credentials and the necessary Read, Write, and Delete permissions.
