---
title: "Row and column security"
description: "Control what data different groups can see by filtering rows and columns based on user attributes. Useful for multi-tenant analytics."
url: "https://www.metabase.com/docs/latest/permissions/row-and-column-security"
version: "65"
---

# Row and column security

> Row and column security is only available on [Pro](https://www.metabase.com/product/pro) and [Enterprise](https://www.metabase.com/product/enterprise) plans (both self-hosted and on Metabase Cloud).

Row and column security lets you give granular permissions for different groups of people. You can change what data a group [can view](https://www.metabase.com/docs/latest/permissions/data.md#can-view-data-permission), as well as what data a group [can query](https://www.metabase.com/docs/latest/permissions/data.md#create-queries-permissions) with the query builder.

You can use row and column security to set up [self-service analytics](https://www.metabase.com/docs/latest/permissions/embedding.md), so that each of your customers can only view the rows that match their customer ID. For example, if you have an Accounts table with information about your customers, you can add permissions to the table so that each customer only sees the data relevant to them.

![Row and column security](https://www.metabase.com/docs/latest/permissions/images/row-and-column-level-security.png)

> Row and column security was formerly called data sandboxing. It's the same feature, it just now has a more descriptive name.

## Row and column security examples

You can skip the theory and go [straight to examples of row and column security](https://www.metabase.com/docs/latest/permissions/row-and-column-security-examples.md).

## How row and column security works

You can think of row and column security as a bundle of permissions that includes:

- The filtered version of a table that will replace your original table, everywhere that the original table is used in Metabase.
- The [group](https://www.metabase.com/docs/latest/people-and-groups/managing.md#groups) of people who should see the filtered version of the table.

You can define up to one row and column security policy for each table/group combo in your Metabase. This means you can display different versions of a table for different groups, such as "Accounts for Sales" to your salespeople, and "Accounts for Managers" for sales managers.

## Types of row and column security

Row and column security shows specific data to each person based on their [user attributes](https://www.metabase.com/docs/latest/people-and-groups/managing.md#adding-a-user-attribute). You can:

- [Restrict **rows**](#row-level-security-filter-by-a-column-in-the-table)
- [Restrict **columns** and rows](#custom-row-and-column-security-use-a-sql-question-to-create-a-custom-view-of-a-table) for specific people.

| Goal                                           | Row (filter by a column in the table) | Custom (use a saved SQL question) |
| ---------------------------------------------- | ------------------------------------- | --------------------------------- |
| Restrict rows by filtering on a single column  | ✅                                    | ✅                                |
| Restrict rows by filtering on multiple columns | ✅                                    | ✅                                |
| Restrict columns                               | ❌                                    | ✅                                |
| Edit columns                                   | ❌                                    | ✅                                |

### Row-level security: filter by a column in the table

You can **restrict rows** by filtering a column using a [user attribute value](#choosing-user-attributes-for-row-and-column-security).

For example, you can filter the Accounts table for a group so that the user attribute filters the table:

- "Basic" will see rows where `Plan = "Basic"` (rows where the Plan column matches the value "Basic").
- "Premium" will see the rows where `Plan = "Premium"` (rows where the Plan column matches the value "Premium").

To filter on more than one column, click **Add a filter** and map another column to a user attribute. Each filter must use a different user attribute. People only see rows that match all of the filters.

### Custom row and column security: use a SQL question to create a custom "view" of a table

To **restrict rows _and_ columns**, you can use a SQL question to filter the table. When someone views that table, they'll instead see the question's results, not the raw table.

For example, say your original Accounts table includes the columns: `ID`, `Email`, `Plan`, and `Created At`. If you want to hide the Email column, you can create a "Restricted Accounts" SQL question with the columns: `ID`, `Plan`, and `Created At`.

You can use a question to:

- [Display an edited column instead of hiding the column](#displaying-edited-columns).
- [Pass a user attribute to a SQL parameter](#restricting-rows-with-user-attributes-using-a-sql-variable).

## Prerequisites for row security

- A [group](https://www.metabase.com/docs/latest/people-and-groups/managing.md#groups) of people to add row security.
- [User attributes](https://www.metabase.com/docs/latest/people-and-groups/managing.md#adding-a-user-attribute) for each person in the group.

Row security displays a filtered table, in place of an original table, to a specific group. How Metabase filters that table depends on the value in each person's user attribute.

For example, you can set up a row-level security so that the user attribute `plan` shows different rows for different values:

- "Basic" will see a version of the Accounts table with a filter for `Plan = "Basic"` (that is, only the rows where the Plan column matches the value "Basic").
- "Premium" will see a different version of the Accounts table with the filter `Plan = "Premium"` applied.

## Choosing user attributes for row and column security

**User attributes are required for row security, and optional for column security**. When [adding a new user attribute](https://www.metabase.com/docs/latest/people-and-groups/managing.md#adding-a-user-attribute), you'll set up a key-value pair for each person.

Metabase uses the user attribute key to look up the user attribute value for a specific person. User attribute keys can be mapped to parameters in Metabase.

The **user attribute value** must match the filter value. Whether the match is case-sensitive depends on your database (for example, PostgreSQL compares text case-sensitively, while MySQL and SQL Server usually don't), so it's safest to match the case too. For example, if you add row security to the Accounts table with the filter `Plan = "Basic"`, make sure that you enter "Basic" as the user attribute value. If you set the user attribute value to lowercase "basic" (a value which doesn't exist in the Plan column of the Accounts table), the person will get an empty result instead of the table.

Examples of user attributes in play:

- [Row security](https://www.metabase.com/docs/latest/permissions/row-and-column-security-examples.md#filtering-rows-based-on-user-attributes)
- [Restricting rows and columns](https://www.metabase.com/docs/latest/permissions/row-and-column-security-examples.md#custom-example-2-filtering-rows-and-columns)

## Adding row-level security

1. Make sure to do the [prerequisites for row security](#prerequisites-for-row-security) first.
2. Go to **Admin** > **Permissions**.
3. Select the database and table that you want to secure.
4. Find the group that you want to secure.
5. Click on the dropdown under **View data** for that group.
6. Select "Row and column security".
7. Click the dropdown under **Column** and enter the column to filter the table on, such as "Plan".
8. Click the dropdown under **User attribute** and enter the user attribute **key**, such as "Plan".

> If you have SQL questions that query data with row-level security, make sure to move all of those questions to admin-only collections. For more info, see [You cannot secure the rows or columns of SQL results](#you-cannot-secure-the-rows-or-columns-of-sql-results).

Check out [row and column security examples](https://www.metabase.com/docs/latest/permissions/row-and-column-security-examples.md).

## Prerequisites for column-level security

- A [group](https://www.metabase.com/docs/latest/people-and-groups/managing.md#groups) of people.
- An admin-only [collection](https://www.metabase.com/docs/latest/exploration-and-organization/collections.md), with [collection permissions](https://www.metabase.com/docs/latest/permissions/collections.md) set to **No access** for all groups except Administrators.
- A [SQL question](https://www.metabase.com/docs/latest/questions/native-editor/writing-sql.md) with the rows and columns to be displayed to the people in the group, stored in the admin-only collection.
- Optional: if you want to restrict **rows** as well, set up [user attributes](#choosing-user-attributes-for-row-and-column-security) for each of the people in the group.

### Creating a SQL question for Metabase to display instead of a table

Metabase will display the results of the question in place of an original table to a particular group.

**Use a SQL question** to define the exact rows and columns to be included in the custom view. Avoid using a query builder (GUI) question, as you might accidentally expose extra data, since GUI questions can include data from other questions or models.

> Make sure to save the SQL question in an admin-only collection ([collection permissions](https://www.metabase.com/docs/latest/permissions/collections.md) set to **No access** for all groups except Administrators). For more info, see [You cannot secure the rows or columns of SQL results](#you-cannot-secure-the-rows-or-columns-of-sql-results).

### Displaying edited columns

Aside from excluding rows and columns, you can also **display edited columns** (without changing the columns in your database).

For example, you can create an "Edited Accounts" SQL question that truncates the Email column to display usernames instead of complete email addresses.

If you edit a column, keep the column's name and data type the same as in the original table. Metabase matches the question's columns to the table's columns by name, and it shows an error if a column's data type doesn't match. For example, the "Edited Accounts" question can return a shortened Email column, as long as the column is still called Email and still returns text.

The question can leave out columns from the original table (that's how you hide them), but it can't add new ones. Metabase drops any columns that aren't in the original table.

## Setting up column security

1. Make sure to do the [prerequisites](#prerequisites-for-column-level-security) first.
2. Go to **Admin** > **Permissions**.
3. Select the database and table that you want to secure.
4. Find the group to restrict.
5. Click on the dropdown under **View data** for that group.
6. Select "Row and column security".
7. Select "Use a saved question to create a custom view for this table".
8. Select your saved question. Metabase lets you pick any saved question or model, but a SQL question is the safest choice (see [Creating a SQL question](#creating-a-sql-question-for-metabase-to-display-instead-of-a-table)). If the question contains parameters, those parameters must be required (they can't be optional).
9. Optional: [restrict rows based on people's user attributes](#restricting-rows-with-user-attributes-using-a-sql-variable).

You can find sample setups in the [Row and column security examples](https://www.metabase.com/docs/latest/permissions/row-and-column-security-examples.md).

### Restricting rows with user attributes using a SQL variable

If you set up column security, you can also restrict different rows for each person depending on their [user attributes](https://www.metabase.com/docs/latest/people-and-groups/managing.md#adding-a-user-attribute). For example, you can display the "Accounts" question with the filter `Plan = "Basic"` for one group, and the filter `Plan = "Premium"` for another group.

1. Make sure you've done all the [prerequisites for column-level security](#prerequisites-for-column-level-security).
2. Go to the SQL question that will be displayed to the people in place of the table.
3. Add a [parameterized](https://www.metabase.com/docs/latest/questions/native-editor/sql-parameters.md) `WHERE` clause to your SQL query, such as `WHERE plan = {{ plan_variable }} `.
4. Save the SQL question.
5. Go to **Admin** > **Permissions**.
6. Find the group and table you want to secure.
7. Open the dropdown under **View data**.
8. Click **Edit row and column security**.
9. Scroll down and set **Parameter or variable** to the name of the parameter in your SQL question (such as "Plan Variable").
10. Set the **User attribute** to a [user attribute key](#choosing-user-attributes-for-row-and-column-security) (such as the key "User's Plan", _not_ the value "Basic").
11. Click **Save**.

For a sample SQL variable and user attribute setup, see the [Row and column security examples](https://www.metabase.com/docs/latest/permissions/row-and-column-security-examples.md).

### How row restriction works with column security

A standard `WHERE` clause filters a table by setting a column to a fixed value:

```sql
WHERE column_name = column_value
```

In step 2 of the [row restriction setup](#restricting-rows-with-user-attributes-using-a-sql-variable) above, you'll add a SQL variable so that the `WHERE` clause will accept a dynamic value. The [SQL variable type](https://www.metabase.com/docs/latest/questions/native-editor/sql-parameters.md#sql-variable-types) must be text, number, or date:

```sql
WHERE plan = {{ plan_variable }}
```

In steps 9-10 of the [row restriction setup](#restricting-rows-with-user-attributes-using-a-sql-variable) above, you're telling Metabase to map the SQL variable `plan_variable` to a **user attribute key** (such as "User's Plan"). Metabase will use the key to look up the specific **user attribute value** (such as "Basic") associated with a person's Metabase account. When that person logs into Metabase and uses the secured table, they'll see the query result that is filtered on:

```sql
WHERE plan = "Basic"
```

Note that the parameters must be required for SQL questions used to create custom views. For example, you can't use an optional parameter; the following won't work:

```sql

[[WHERE plan = {{ plan_variable }}]]

```

Learn more about [SQL parameters](https://www.metabase.com/docs/latest/questions/native-editor/sql-parameters.md)

### Advanced row-level security: filtering tables for people that have multiple IDs

For example, say you have a table like this:

| User_ID | Value |
| ------- | ----- |
| 1       | 10    |
| 1       | 50    |
| 2       | 5     |
| 2       | 50    |
| 3       | 5     |
| 3       | 5     |

If you want to give someone access to multiple user IDs (e.g., the person should see rows for both `User_ID` 1 and 2.), you can set up a user attribute, like `user_id` that can handle comma-separated values like "1,2".

1. Create a SQL question that parses the comma-separated string and filters the table:

   ```sql
   
   SELECT *
   FROM users_with_values
   WHERE user_id = ANY(STRING_TO_ARRAY(REGEXP_REPLACE(TRIM({{user_id}}), '\\s*,\\s*', ','), ','))
   
   ```

   This query:

   - Trims whitespace from the user attribute value
   - Replaces any spaces around commas with just commas
   - Converts the comma-separated string to an array
   - Filters rows where the user_id matches any value in the array

   The `STRING_TO_ARRAY()` and `REGEXP_REPLACE()` functions are PostgreSQL-specific. To see which functions your database supports, see your database's documentation.

2. Set up the row and column security using this SQL question. See [setting up column security](#setting-up-column-security).

## Preventing row and column security permissions conflicts

Some Metabase permissions can conflict with row and column security to give more permissive or more restrictive data access than you intended.

Say you've set up [column security](#custom-row-and-column-security-use-a-sql-question-to-create-a-custom-view-of-a-table) that hides the Email column from the Accounts table (for a particular group).

The Email column may get exposed to someone if:

- The person is also in another group with **Can view** access to the Accounts table. Metabase gives people the most permissive access across all of their groups, so it won't apply the row and column security policy to that person.
- The person belongs to [multiple row and column security policies](#multiple-row-and-column-security-permissions).
- Someone else in a non-secured group shares the Email column from:
  - A [SQL question](https://www.metabase.com/docs/latest/questions/native-editor/writing-sql.md)
  - A [public link](#public-sharing)
  - An [alert, or dashboard subscription](https://www.metabase.com/docs/latest/permissions/notifications.md)

### Multiple row and column security permissions

Multiple row and column security policies on the same table can create a permissions conflict. You can add a person to a maximum of one row and column security policy per table (via the person's group).

For example, if you have:

- Set up security for the group "Basic Accounts" that filters the Accounts table on `Plan = "Basic"`.
- Another setup for the group "Converted Accounts" that filters the Accounts table on `Trial Converted = true`.

If you put Vincent Accountman in both groups, he'll have conflicting permissions for the Accounts table, and get an error message whenever he tries to use Accounts in Metabase.

To resolve row and column security permissions conflicts:

- Remove the person from all but one of the groups.
- Set all but one of the group's [View data](https://www.metabase.com/docs/latest/permissions/data.md#view-data-permissions) access to the database to "Blocked".

### You cannot secure the rows or columns of SQL results

Row and column security permissions don't apply to the results of SQL questions. That is, SQL questions will always display results from the original table rather than the secured table.

Say that you've set up column security on the Accounts table to hide the Email column. If someone creates a SQL question that includes the Email column, **anyone with collection permissions to view that SQL question** will be able to:

- See the Email column in the SQL question results.
- Use the SQL question to start a new question that includes the Email column (if they have query permissions).

To prevent the Email column from being exposed via a SQL question:

- Put any SQL questions that include the Email column in a separate collection.
- Set the [collection permissions](https://www.metabase.com/docs/latest/permissions/collections.md) to **No access** for groups with row and column security set up to hide the Email column.

[Collection permissions](https://www.metabase.com/docs/latest/permissions/collections.md) must be used to prevent secured groups from viewing SQL questions that reference secured tables.

### Public sharing

Row and column security permissions don't apply to public questions or public dashboards. If someone in an unsecured group creates a public link using an original table, the original table will be displayed to anyone who has the public link URL.

To prevent this from happening, you'll have to [disable public sharing](https://www.metabase.com/docs/latest/embedding/public-links.md) for your Metabase.

Metabase can only create row and column security using the group membership or user attributes of people who are logged in. Since public links don't require logins, Metabase won't have enough info to apply permissions.

## Limitations of row and column security

Some things to keep in mind when using row and column security.

### People with row and column security can't use the SQL editor on that database

Row and column security is limited to the [query builder](https://www.metabase.com/docs/latest/questions/query-builder/editor.md). Native (SQL) queries would get around it, so Metabase keeps people with row and column security out of the SQL editor:

- You can't give [native query permissions](https://www.metabase.com/docs/latest/permissions/data.md#create-queries-permissions) to a group with row and column security on that database. When you save a row and column security policy, Metabase changes the group's **Create queries** permission for the database from **Query builder and native** to **Query builder only**.
- If row and column security applies to a person on any table in a database, that person can't run native queries on that database. But row and column security doesn't apply to a person if another of their groups has **Can view** access to the table, so that person can use the SQL editor if any of their groups has native query access.
- People with row and column security can't view the SQL that Metabase generates for their questions, or convert their questions to SQL.
- Groups with row and column security can't get [Transforms](https://www.metabase.com/docs/latest/permissions/data.md#transform-permissions) permissions, since transforms require native query access.

To enforce row-level permissions with the native query editor, check out [impersonation](https://www.metabase.com/docs/latest/permissions/impersonation.md).

### You can't apply row and column security to SQL questions

Since Metabase can't parse SQL queries, the results of SQL questions will always use original tables.

[Use collection permissions](#you-cannot-secure-the-rows-or-columns-of-sql-results) to prevent groups from viewing saved SQL questions with restricted data.

### MongoDB only supports row-level security

MongoDB only supports [row-level security](#row-level-security-filter-by-a-column-in-the-table). It doesn't support column-level security.

### Advanced data types require a workaround

If you're trying to set up row security on a column with an advanced data type (like enums or arrays), you'll need to convert that data to a basic type first. Options include:

#### Option 1: Use SQL to cast the advanced data type to a basic SQL type

Create a SQL question that casts the advanced data type column to a basic data type, then use that question for your row and column security setup.

#### Option 2: Create a database view

If you can't use SQL casting in Metabase, create a view in your database that converts the advanced data type to a basic type, then set up row and column security on that view instead of the original table. You'll also need to block the original table.

#### Option 3: Use transforms

Use a [transform](https://www.metabase.com/docs/latest/data-modeling/transforms/transforms-overview.md) to create a table that casts the advanced data type to a basic type. Then set up row and column security on the transformed table instead. You'll also need to block the original table.

### People with row and column security can't create Slack subscriptions or alerts

People in groups with row and column security can't create Slack [alerts](https://www.metabase.com/docs/latest/questions/alerts.md) or [dashboard subscriptions](https://www.metabase.com/docs/latest/dashboards/subscriptions.md). Email alerts and subscriptions are still available. See [Notification permissions](https://www.metabase.com/docs/latest/permissions/notifications.md).

### People with row and column security can't run actions

If row and column security applies to a person on any table in a database, that person can't run [actions](https://www.metabase.com/docs/latest/data-modeling/data-actions.md) on that database. Actions write to tables directly, so they would get around the row and column security.

[Public action forms](https://www.metabase.com/docs/latest/data-modeling/data-actions.md#share-a-data-action-as-a-public-form) don't check row and column security. An admin can make any action public, even on a database where row and column security applies to some groups, and anyone with the link can then run the action. Before you make an action public, make sure that it's OK for anyone to make that change to your data.

### Other features aren't available to people with row and column security

If row and column security applies to a person on any table, that person can't:

- [Upload CSVs](https://www.metabase.com/docs/latest/databases/uploads.md).
- See or use [SQL snippets](https://www.metabase.com/docs/latest/questions/native-editor/snippets.md).
- Create Slack [alerts](https://www.metabase.com/docs/latest/questions/alerts.md) or [dashboard subscriptions](https://www.metabase.com/docs/latest/dashboards/subscriptions.md), or see other people in the list of recipients. Email alerts and subscriptions to themselves still work. See [Notification permissions](https://www.metabase.com/docs/latest/permissions/notifications.md).
- Find individual records from [models with indexed values](https://www.metabase.com/docs/latest/data-modeling/models/models.md#surface-individual-records-in-search-by-matching-against-this-column) in search results.

If your Metabase can't confirm that your plan includes row and column security (for example, if your license token is no longer valid), people with row and column security get an error instead of unfiltered data.

### Only admins can change questions that row and column security uses

If a row and column security policy uses a saved question, only admins can change that question's query, archive it, or delete it. The same goes for anything that the question uses, like other questions, snippets, segments, and measures, even if other people have [Curate access](https://www.metabase.com/docs/latest/permissions/collections.md#curate-access) to the collection the item is in.

## Further reading

- [Row and column security examples](https://www.metabase.com/docs/latest/permissions/row-and-column-security-examples.md)
- [Permissions strategies](https://www.metabase.com/learn/metabase-basics/administration/permissions/strategy)
- [Configuring permissions for embedding](https://www.metabase.com/docs/latest/permissions/embedding.md)
- [Securing embedded Metabase](https://www.metabase.com/docs/latest/embedding/securing-embeds.md)
