Virtual Tables

Virtual Tables

Virtual Tables in Synapse allow you to create queryable views based on existing Synapse tables. Unlike Materialized Views, Virtual Tables don’t store the results but instead provide a view that is evaluated at query time. They’re useful for providing different perspectives of your data without duplicating the storage.

Tutorial purpose

In this tutorial you will:

  1. Log in, get your project, and create tables with data
  2. Create and query a basic Virtual Table
  3. Create a Virtual Table with column selection
  4. Create a Virtual Table with filtering
  5. Create a Virtual Table with ordering
  6. Create a Virtual Table with aggregation

Prerequisites

This tutorial assumes that you have a Synapse project.

1. Log in, get your project, and create tables with data

Before creating Virtual Tables, we need to log in to Synapse, retrieve your project, and create the tables with data that will be used.

You will want to replace "My uniquely named project about Alzheimer's Disease" with the name of your project.

library(synapser)
synLogin()

# Get the project where we want to create the virtual table
project <- Project(name = "My uniquely named project about Alzheimer's Disease") |> synGet()
project_id <- project$id
cat("Got project with ID:", project_id, "\n")

# Create the first table with some columns and rows
table1_columns <- list(
  Column(name = "sample_id", column_type = "STRING"),
  Column(name = "patient_id", column_type = "STRING"),
  Column(name = "age", column_type = "INTEGER"),
  Column(name = "diagnosis", column_type = "STRING")
)

table1 <- Table(
  name = "Patient Demographics",
  parent_id = project_id,
  columns = table1_columns
) |> synStore()
cat("Created table 1 with ID:", table1$id, "\n")

# Add rows to the first table
data1 <- data.frame(
  sample_id = c("S1", "S2", "S3", "S4", "S5", "S6"),
  patient_id = c("P1", "P2", "P3", "P4", "P5", "P6"),
  age = c(70, 65, 72, 68, 75, 80),
  diagnosis = c("Alzheimer's", "Healthy", "Alzheimer's", "Healthy", "Alzheimer's", "Healthy")
)
table1 |> synUpsertRows(values = data1, primary_keys = list("sample_id"))

# Create the second table with some columns and rows
table2_columns <- list(
  Column(name = "sample_id", column_type = "STRING"),
  Column(name = "gene", column_type = "STRING"),
  Column(name = "expression_level", column_type = "DOUBLE")
)

table2 <- Table(
  name = "Gene Expression Data",
  parent_id = project_id,
  columns = table2_columns
) |> synStore()
cat("Created table 2 with ID:", table2$id, "\n")

# Add rows to the second table
data2 <- data.frame(
  sample_id = c("S1", "S2", "S3", "S4", "S5", "S7"),
  gene = c("APOE", "APP", "PSEN1", "MAPT", "APP", "PSEN2"),
  expression_level = c(2.5, 1.8, 3.2, 2.1, 3.5, 1.9)
)
table2 |> synUpsertRows(values = data2, primary_keys = list("sample_id"))

Note: Virtual Tables do not support JOIN or UNION operations in the defining SQL query. If you need to combine data from multiple tables, consider using a Materialized View instead.

2. Create and query a basic Virtual Table

First, we will create a simple Virtual Table that selects all rows from a table and then query it to retrieve the results.

virtual_table <- VirtualTable(
  name = "Patient Data View",
  description = "A virtual table showing patient demographics",
  parent_id = project_id,
  defining_sql = sprintf("SELECT * FROM %s", table1$id)
) |> synStore()
cat("Created Virtual Table with ID:", virtual_table$id, "\n")

query_result <- synQuery(
  sprintf("SELECT * FROM %s", virtual_table$id),
  include_row_id_and_row_version = FALSE
)
cat("Results from the basic virtual table:\n")
query_result
The result of querying your Virtual Table should look like:
Results from the basic virtual table:
  sample_id patient_id  age    diagnosis
0        S1         P1   70  Alzheimer's
1        S2         P2   65      Healthy
2        S3         P3   72  Alzheimer's
3        S4         P4   68      Healthy
4        S5         P5   75  Alzheimer's
5        S6         P6   80      Healthy

3. Create a Virtual Table with column selection

Next, we’ll create a Virtual Table that selects only specific columns from the source table.

virtual_table <- VirtualTable(
  name = "Patient Age View",
  description = "A virtual table showing only patient IDs and ages",
  parent_id = project_id,
  defining_sql = sprintf("SELECT patient_id, age FROM %s", table1$id)
) |> synStore()
cat("Created Virtual Table with ID:", virtual_table$id, "\n")

query_result <- synQuery(
  sprintf("SELECT * FROM %s", virtual_table$id),
  include_row_id_and_row_version = FALSE
)
cat("Results from the virtual table with column selection:\n")
query_result
The result of querying your Virtual Table with column selection should look like:
Results from the virtual table with column selection:
  patient_id  age
0         P1   70
1         P2   65
2         P3   72
3         P4   68
4         P5   75
5         P6   80

4. Create a Virtual Table with filtering

We can create a Virtual Table that filters rows from the source table using a WHERE clause.

virtual_table <- VirtualTable(
  name = "Alzheimer's Patients",
  description = "A virtual table showing only patients with Alzheimer's",
  parent_id = project_id,
  defining_sql = sprintf("SELECT * FROM %s WHERE diagnosis = 'Alzheimer''s'", table1$id)
) |> synStore()
cat("Created Virtual Table with ID:", virtual_table$id, "\n")

query_result <- synQuery(
  sprintf("SELECT * FROM %s", virtual_table$id),
  include_row_id_and_row_version = FALSE
)
cat("Results from the virtual table with filtering:\n")
query_result
The result of querying your Virtual Table with filtering should look like:
Results from the virtual table with filtering:
  sample_id patient_id  age    diagnosis
0        S1         P1   70  Alzheimer's
1        S3         P3   72  Alzheimer's
2        S5         P5   75  Alzheimer's

5. Create a Virtual Table with ordering

You can also create a Virtual Table that orders the rows from the source table using an ORDER BY clause.

virtual_table <- VirtualTable(
  name = "Patients by Age",
  description = "A virtual table showing patients ordered by age",
  parent_id = project_id,
  defining_sql = sprintf("SELECT * FROM %s ORDER BY age DESC", table1$id)
) |> synStore()
cat("Created Virtual Table with ID:", virtual_table$id, "\n")

query_result <- synQuery(
  sprintf("SELECT * FROM %s", virtual_table$id),
  include_row_id_and_row_version = FALSE
)
cat("Results from the virtual table with ordering:\n")
query_result
The result of querying your Virtual Table with ordering should look like:
Results from the virtual table with ordering:
  sample_id patient_id  age    diagnosis
0        S6         P6   80      Healthy
1        S5         P5   75  Alzheimer's
2        S3         P3   72  Alzheimer's
3        S1         P1   70  Alzheimer's
4        S4         P4   68      Healthy
5        S2         P2   65      Healthy

6. Create a Virtual Table with aggregation

Finally, we can create a Virtual Table that aggregates data using functions like COUNT, along with GROUP BY.

virtual_table <- VirtualTable(
  name = "Diagnosis Count",
  description = "A virtual table showing the count of patients by diagnosis",
  parent_id = project_id,
  defining_sql = sprintf("SELECT diagnosis, COUNT(*) AS patient_count FROM %s GROUP BY diagnosis", table1$id)
) |> synStore()
cat("Created Virtual Table with ID:", virtual_table$id, "\n")

query_result <- synQuery(
  sprintf("SELECT * FROM %s", virtual_table$id),
  include_row_id_and_row_version = FALSE
)
cat("Results from the virtual table with aggregation:\n")
query_result
The result of querying your Virtual Table with aggregation should look like:
Results from the virtual table with aggregation:
     diagnosis  patient_count
0  Alzheimer's              3
1      Healthy              3

Clean Up

Delete the project and all its contents when done:

synDelete(project)