
## LINEAGE_ADJACENCY_LIST table

URL: https://docs.atlan.com/platform/lakehouse/references/gold-layer/lineage-adjacency-list

> Reference documentation for the LINEAGE_ADJACENCY_LIST table containing directed lineage edges between assets and processes

The **LINEAGE_ADJACENCY_LIST** table is a flat list of directed lineage edges between assets and processes. Each row represents a single hop, where `from_guid` is the upstream asset or process and `to_guid` is the downstream asset or process.

Use this table to traverse lineage starting from any asset. Pair it with the [ASSETS](https://docs.atlan.com/llms/governance/lakehouse/assets/llms.txt) table to resolve GUIDs to human-readable names and types.

## Columns

The **LINEAGE_ADJACENCY_LIST** table includes the following columns:

| Column | Description |
|--------|-------------|
| `from_guid` | The GUID of the upstream asset or process in the edge |
| `to_guid` | The GUID of the downstream asset or process in the edge |

## Example queries

The examples below cover Snowflake, Databricks, and BigQuery. Each engine's tab includes a direct multi-hop query and a cycle-detection variant that's safe for graphs that may contain cycles.

Replace the placeholder values for your platform:

- **Snowflake**: examples assume you've set your default database and schema (for example, `USE DATABASE ATLAN_CONTEXT_STORE`). Otherwise, fully qualify each table reference (for example, `ATLAN_CONTEXT_STORE."gold".lineage_adjacency_list`).
- **Databricks**: replace `LAKEHOUSE_CATALOG` with your Lakehouse catalog name in Unity Catalog. Recursive CTEs require Databricks Runtime 14.0+ or a Databricks SQL warehouse on a recent channel.
- **BigQuery**: replace `PROJECT_ID.LAKEHOUSE_DATASET` with the project and dataset where your Lakehouse external tables are registered.

For multi-hop queries that return direction, hop level, and asset names in a single result set, see [Set up lineage tables](https://docs.atlan.com/llms/governance/lakehouse/set-up-lineage-tables/llms.txt) for the optional `LINEAGE` helper view.

### Upstream lineage

Find every asset upstream of a target asset, joined to `ASSETS` for names and types.

### Snowflake

**Direct**

```sql
-- Replace '<base_guid>' with the GUID of the asset you're tracing from.
SELECT guid, asset_name, asset_type
FROM (
 WITH RECURSIVE lineage AS (
 SELECT from_guid, 1 AS level
 FROM gold.lineage_adjacency_list
 WHERE to_guid = '<base_guid>'
 UNION ALL
 SELECT a.from_guid, l.level + 1
 FROM lineage l
 JOIN gold.lineage_adjacency_list a
 ON l.from_guid = a.to_guid
 WHERE l.level < 50
 )
 SELECT DISTINCT from_guid FROM lineage
) t
LEFT JOIN gold.assets ON from_guid = guid;
```

**With cycle detection**

For graphs that may contain cycles, track visited GUIDs and skip any edge that revisits one. The hop limit is raised to 100 since cycles are filtered out.

```sql
SELECT guid, asset_name, asset_type
FROM (
 WITH RECURSIVE lineage AS (
 SELECT from_guid, 1 AS level, ARRAY_CONSTRUCT(from_guid) AS visited
 FROM gold.lineage_adjacency_list
 WHERE to_guid = '<base_guid>'
 UNION ALL
 SELECT a.from_guid, l.level + 1, ARRAY_APPEND(l.visited, a.from_guid)
 FROM lineage l
 JOIN gold.lineage_adjacency_list a
 ON l.from_guid = a.to_guid
 WHERE l.level < 100
 AND NOT ARRAY_CONTAINS(a.from_guid::VARIANT, l.visited)
 )
 SELECT DISTINCT from_guid FROM lineage
) t
LEFT JOIN gold.assets ON from_guid = guid;
```

### Databricks

**Direct**

```sql
-- Replace '<base_guid>' with the GUID of the asset you're tracing from.
SELECT guid, asset_name, asset_type
FROM (
 WITH RECURSIVE lineage AS (
 SELECT from_guid, 1 AS level
 FROM LAKEHOUSE_CATALOG.gold.lineage_adjacency_list
 WHERE to_guid = '<base_guid>'
 UNION ALL
 SELECT a.from_guid, l.level + 1
 FROM lineage l
 JOIN LAKEHOUSE_CATALOG.gold.lineage_adjacency_list a
 ON l.from_guid = a.to_guid
 WHERE l.level < 50
 )
 SELECT DISTINCT from_guid FROM lineage
) t
LEFT JOIN LAKEHOUSE_CATALOG.gold.assets ON from_guid = guid;
```

**With cycle detection**

```sql
SELECT guid, asset_name, asset_type
FROM (
 WITH RECURSIVE lineage AS (
 SELECT from_guid, 1 AS level, array(from_guid) AS visited
 FROM LAKEHOUSE_CATALOG.gold.lineage_adjacency_list
 WHERE to_guid = '<base_guid>'
 UNION ALL
 SELECT a.from_guid, l.level + 1, array_append(l.visited, a.from_guid)
 FROM lineage l
 JOIN LAKEHOUSE_CATALOG.gold.lineage_adjacency_list a
 ON l.from_guid = a.to_guid
 WHERE l.level < 100
 AND NOT array_contains(l.visited, a.from_guid)
 )
 SELECT DISTINCT from_guid FROM lineage
) t
LEFT JOIN LAKEHOUSE_CATALOG.gold.assets ON from_guid = guid;
```

### BigQuery

**Direct**

```sql
-- Replace '<base_guid>' with the GUID of the asset you're tracing from.
SELECT guid, asset_name, asset_type
FROM (
 WITH RECURSIVE lineage AS (
 SELECT from_guid, 1 AS level
 FROM `PROJECT_ID.LAKEHOUSE_DATASET.lineage_adjacency_list`
 WHERE to_guid = '<base_guid>'
 UNION ALL
 SELECT a.from_guid, l.level + 1
 FROM lineage l
 JOIN `PROJECT_ID.LAKEHOUSE_DATASET.lineage_adjacency_list` a
 ON l.from_guid = a.to_guid
 WHERE l.level < 50
 )
 SELECT DISTINCT from_guid FROM lineage
) t
LEFT JOIN `PROJECT_ID.LAKEHOUSE_DATASET.assets` ON from_guid = guid;
```

**With cycle detection**

```sql
SELECT guid, asset_name, asset_type
FROM (
 WITH RECURSIVE lineage AS (
 SELECT from_guid, 1 AS level, [from_guid] AS visited
 FROM `PROJECT_ID.LAKEHOUSE_DATASET.lineage_adjacency_list`
 WHERE to_guid = '<base_guid>'
 UNION ALL
 SELECT a.from_guid, l.level + 1, ARRAY_CONCAT(l.visited, [a.from_guid])
 FROM lineage l
 JOIN `PROJECT_ID.LAKEHOUSE_DATASET.lineage_adjacency_list` a
 ON l.from_guid = a.to_guid
 WHERE l.level < 100
 AND a.from_guid NOT IN UNNEST(l.visited)
 )
 SELECT DISTINCT from_guid FROM lineage
) t
LEFT JOIN `PROJECT_ID.LAKEHOUSE_DATASET.assets` ON from_guid = guid;
```

### Downstream lineage

Find every asset downstream of a source asset, joined to `ASSETS` for names and types.

### Snowflake

**Direct**

```sql
-- Replace '<base_guid>' with the GUID of the asset you're tracing from.
SELECT guid, asset_name, asset_type
FROM (
 WITH RECURSIVE lineage AS (
 SELECT to_guid, 1 AS level
 FROM gold.lineage_adjacency_list
 WHERE from_guid = '<base_guid>'
 UNION ALL
 SELECT a.to_guid, l.level + 1
 FROM lineage l
 JOIN gold.lineage_adjacency_list a
 ON l.to_guid = a.from_guid
 WHERE l.level < 50
 )
 SELECT DISTINCT to_guid FROM lineage
) t
LEFT JOIN gold.assets ON to_guid = guid;
```

**With cycle detection**

```sql
SELECT guid, asset_name, asset_type
FROM (
 WITH RECURSIVE lineage AS (
 SELECT to_guid, 1 AS level, ARRAY_CONSTRUCT(to_guid) AS visited
 FROM gold.lineage_adjacency_list
 WHERE from_guid = '<base_guid>'
 UNION ALL
 SELECT a.to_guid, l.level + 1, ARRAY_APPEND(l.visited, a.to_guid)
 FROM lineage l
 JOIN gold.lineage_adjacency_list a
 ON l.to_guid = a.from_guid
 WHERE l.level < 100
 AND NOT ARRAY_CONTAINS(a.to_guid::VARIANT, l.visited)
 )
 SELECT DISTINCT to_guid FROM lineage
) t
LEFT JOIN gold.assets ON to_guid = guid;
```

### Databricks

**Direct**

```sql
-- Replace '<base_guid>' with the GUID of the asset you're tracing from.
SELECT guid, asset_name, asset_type
FROM (
 WITH RECURSIVE lineage AS (
 SELECT to_guid, 1 AS level
 FROM LAKEHOUSE_CATALOG.gold.lineage_adjacency_list
 WHERE from_guid = '<base_guid>'
 UNION ALL
 SELECT a.to_guid, l.level + 1
 FROM lineage l
 JOIN LAKEHOUSE_CATALOG.gold.lineage_adjacency_list a
 ON l.to_guid = a.from_guid
 WHERE l.level < 50
 )
 SELECT DISTINCT to_guid FROM lineage
) t
LEFT JOIN LAKEHOUSE_CATALOG.gold.assets ON to_guid = guid;
```

**With cycle detection**

```sql
SELECT guid, asset_name, asset_type
FROM (
 WITH RECURSIVE lineage AS (
 SELECT to_guid, 1 AS level, array(to_guid) AS visited
 FROM LAKEHOUSE_CATALOG.gold.lineage_adjacency_list
 WHERE from_guid = '<base_guid>'
 UNION ALL
 SELECT a.to_guid, l.level + 1, array_append(l.visited, a.to_guid)
 FROM lineage l
 JOIN LAKEHOUSE_CATALOG.gold.lineage_adjacency_list a
 ON l.to_guid = a.from_guid
 WHERE l.level < 100
 AND NOT array_contains(l.visited, a.to_guid)
 )
 SELECT DISTINCT to_guid FROM lineage
) t
LEFT JOIN LAKEHOUSE_CATALOG.gold.assets ON to_guid = guid;
```

### BigQuery

**Direct**

```sql
-- Replace '<base_guid>' with the GUID of the asset you're tracing from.
SELECT guid, asset_name, asset_type
FROM (
 WITH RECURSIVE lineage AS (
 SELECT to_guid, 1 AS level
 FROM `PROJECT_ID.LAKEHOUSE_DATASET.lineage_adjacency_list`
 WHERE from_guid = '<base_guid>'
 UNION ALL
 SELECT a.to_guid, l.level + 1
 FROM lineage l
 JOIN `PROJECT_ID.LAKEHOUSE_DATASET.lineage_adjacency_list` a
 ON l.to_guid = a.from_guid
 WHERE l.level < 50
 )
 SELECT DISTINCT to_guid FROM lineage
) t
LEFT JOIN `PROJECT_ID.LAKEHOUSE_DATASET.assets` ON to_guid = guid;
```

**With cycle detection**

```sql
SELECT guid, asset_name, asset_type
FROM (
 WITH RECURSIVE lineage AS (
 SELECT to_guid, 1 AS level, [to_guid] AS visited
 FROM `PROJECT_ID.LAKEHOUSE_DATASET.lineage_adjacency_list`
 WHERE from_guid = '<base_guid>'
 UNION ALL
 SELECT a.to_guid, l.level + 1, ARRAY_CONCAT(l.visited, [a.to_guid])
 FROM lineage l
 JOIN `PROJECT_ID.LAKEHOUSE_DATASET.lineage_adjacency_list` a
 ON l.to_guid = a.from_guid
 WHERE l.level < 100
 AND a.to_guid NOT IN UNNEST(l.visited)
 )
 SELECT DISTINCT to_guid FROM lineage
) t
LEFT JOIN `PROJECT_ID.LAKEHOUSE_DATASET.assets` ON to_guid = guid;
```

## Optional: Full-lineage helper

If you frequently explore lineage from a single starting asset, the helpers below walk both directions in one call. They return edges (not just nodes) annotated with `direction` (`upstream` or `downstream`) and `level` (hop distance), and join both ends to `ASSETS` for names and types.

Each helper walks up to 100 hops in each direction. Cycle suppression isn't built in—use the cycle-detection variants from [Example queries](#example-queries) if your graph contains cycles.

### Snowflake

A Snowflake stored procedure that returns a result set.

```sql
-- Replace:
-- with your context store database
-- with the schema where you want the procedure to live

CREATE OR REPLACE PROCEDURE ..FULL_LINEAGE(BASE_GUID VARCHAR)
RETURNS TABLE (
 direction VARCHAR,
 from_guid VARCHAR,
 from_asset_name VARCHAR,
 from_asset_type VARCHAR,
 to_guid VARCHAR,
 to_asset_name VARCHAR,
 to_asset_type VARCHAR,
 level INT
)
LANGUAGE SQL
EXECUTE AS CALLER
AS
$$
DECLARE
 res RESULTSET DEFAULT (
 WITH RECURSIVE upstream AS (
 SELECT
 from_guid,
 to_guid,
 1 AS level,
 'upstream' AS direction
 FROM .GOLD.LINEAGE_ADJACENCY_LIST
 WHERE to_guid = :BASE_GUID

 UNION ALL

 SELECT
 a.from_guid,
 a.to_guid,
 l.level + 1,
 'upstream'
 FROM upstream l
 JOIN .GOLD.LINEAGE_ADJACENCY_LIST a
 ON l.from_guid = a.to_guid
 WHERE l.level < 100
 ),

 downstream AS (
 SELECT
 from_guid,
 to_guid,
 1 AS level,
 'downstream' AS direction
 FROM .GOLD.LINEAGE_ADJACENCY_LIST
 WHERE from_guid = :BASE_GUID

 UNION ALL

 SELECT
 a.from_guid,
 a.to_guid,
 l.level + 1,
 'downstream'
 FROM downstream l
 JOIN .GOLD.LINEAGE_ADJACENCY_LIST a
 ON l.to_guid = a.from_guid
 WHERE l.level < 100
 ),

 combined AS (
 SELECT
 from_guid,
 to_guid,
 direction,
 MIN(level) AS level
 FROM (
 SELECT * FROM upstream
 UNION ALL
 SELECT * FROM downstream
 )
 GROUP BY from_guid, to_guid, direction
 )

 SELECT
 d.direction,
 d.from_guid,
 src.asset_name AS from_asset_name,
 src.asset_type AS from_asset_type,
 d.to_guid,
 tgt.asset_name AS to_asset_name,
 tgt.asset_type AS to_asset_type,
 d.level
 FROM combined d
 LEFT JOIN .GOLD.ASSETS src ON d.from_guid = src.guid
 LEFT JOIN .GOLD.ASSETS tgt ON d.to_guid = tgt.guid
 ORDER BY d.direction, d.level
 );
BEGIN
 RETURN TABLE(res);
END;
$$;
```

Call it with the GUID of the asset you want to explore:

```sql
CALL ..FULL_LINEAGE('<base_guid>');
```

### Databricks

A Databricks SQL table-valued function. Requires Databricks Runtime 14.0+ (or a Databricks SQL warehouse on a recent channel) for both `WITH RECURSIVE` and table-returning UDFs.

```sql
-- Replace:
-- LAKEHOUSE_CATALOG with your Lakehouse catalog name
-- with the catalog where you want the function to live
-- with the schema where you want the function to live

CREATE OR REPLACE FUNCTION ..full_lineage(BASE_GUID STRING)
RETURNS TABLE (
 direction STRING,
 from_guid STRING,
 from_asset_name STRING,
 from_asset_type STRING,
 to_guid STRING,
 to_asset_name STRING,
 to_asset_type STRING,
 level INT
)
RETURN
WITH RECURSIVE upstream AS (
 SELECT
 from_guid,
 to_guid,
 1 AS level,
 'upstream' AS direction
 FROM LAKEHOUSE_CATALOG.gold.lineage_adjacency_list
 WHERE to_guid = full_lineage.BASE_GUID

 UNION ALL

 SELECT
 a.from_guid,
 a.to_guid,
 l.level + 1,
 'upstream'
 FROM upstream l
 JOIN LAKEHOUSE_CATALOG.gold.lineage_adjacency_list a
 ON l.from_guid = a.to_guid
 WHERE l.level < 100
),

downstream AS (
 SELECT
 from_guid,
 to_guid,
 1 AS level,
 'downstream' AS direction
 FROM LAKEHOUSE_CATALOG.gold.lineage_adjacency_list
 WHERE from_guid = full_lineage.BASE_GUID

 UNION ALL

 SELECT
 a.from_guid,
 a.to_guid,
 l.level + 1,
 'downstream'
 FROM downstream l
 JOIN LAKEHOUSE_CATALOG.gold.lineage_adjacency_list a
 ON l.to_guid = a.from_guid
 WHERE l.level < 100
),

combined AS (
 SELECT
 from_guid,
 to_guid,
 direction,
 MIN(level) AS level
 FROM (
 SELECT * FROM upstream
 UNION ALL
 SELECT * FROM downstream
 )
 GROUP BY from_guid, to_guid, direction
)

SELECT
 d.direction,
 d.from_guid,
 src.asset_name AS from_asset_name,
 src.asset_type AS from_asset_type,
 d.to_guid,
 tgt.asset_name AS to_asset_name,
 tgt.asset_type AS to_asset_type,
 d.level
FROM combined d
LEFT JOIN LAKEHOUSE_CATALOG.gold.assets src ON d.from_guid = src.guid
LEFT JOIN LAKEHOUSE_CATALOG.gold.assets tgt ON d.to_guid = tgt.guid
ORDER BY d.direction, d.level;
```

Call it with the GUID of the asset you want to explore:

```sql
SELECT * FROM ..full_lineage('<base_guid>');
```

### BigQuery

A BigQuery stored procedure. The unbound `SELECT` at the end of the procedure body returns rows to the caller.

```sql
-- Replace:
-- PROJECT_ID, LAKEHOUSE_DATASET with your Lakehouse project and dataset
-- with the dataset where you want the procedure to live

CREATE OR REPLACE PROCEDURE `PROJECT_ID..full_lineage`(BASE_GUID STRING)
BEGIN
 WITH RECURSIVE upstream AS (
 SELECT
 from_guid,
 to_guid,
 1 AS level,
 'upstream' AS direction
 FROM `PROJECT_ID.LAKEHOUSE_DATASET.lineage_adjacency_list`
 WHERE to_guid = BASE_GUID

 UNION ALL

 SELECT
 a.from_guid,
 a.to_guid,
 l.level + 1,
 'upstream'
 FROM upstream l
 JOIN `PROJECT_ID.LAKEHOUSE_DATASET.lineage_adjacency_list` a
 ON l.from_guid = a.to_guid
 WHERE l.level < 100
 ),

 downstream AS (
 SELECT
 from_guid,
 to_guid,
 1 AS level,
 'downstream' AS direction
 FROM `PROJECT_ID.LAKEHOUSE_DATASET.lineage_adjacency_list`
 WHERE from_guid = BASE_GUID

 UNION ALL

 SELECT
 a.from_guid,
 a.to_guid,
 l.level + 1,
 'downstream'
 FROM downstream l
 JOIN `PROJECT_ID.LAKEHOUSE_DATASET.lineage_adjacency_list` a
 ON l.to_guid = a.from_guid
 WHERE l.level < 100
 ),

 combined AS (
 SELECT
 from_guid,
 to_guid,
 direction,
 MIN(level) AS level
 FROM (
 SELECT * FROM upstream
 UNION ALL
 SELECT * FROM downstream
 )
 GROUP BY from_guid, to_guid, direction
 )

 SELECT
 d.direction,
 d.from_guid,
 src.asset_name AS from_asset_name,
 src.asset_type AS from_asset_type,
 d.to_guid,
 tgt.asset_name AS to_asset_name,
 tgt.asset_type AS to_asset_type,
 d.level
 FROM combined d
 LEFT JOIN `PROJECT_ID.LAKEHOUSE_DATASET.assets` src ON d.from_guid = src.guid
 LEFT JOIN `PROJECT_ID.LAKEHOUSE_DATASET.assets` tgt ON d.to_guid = tgt.guid
 ORDER BY d.direction, d.level;
END;
```

Call it with the GUID of the asset you want to explore:

```sql
CALL `PROJECT_ID..full_lineage`('<base_guid>');
```

## See also

- [Gold namespace](https://docs.atlan.com/llms/governance/lakehouse/gold-layer/llms.txt)
- [ASSETS table](https://docs.atlan.com/llms/governance/lakehouse/assets/llms.txt)
- [Set up lineage tables](https://docs.atlan.com/llms/governance/lakehouse/set-up-lineage-tables/llms.txt) for optional helper views with direction and asset names
- [Lineage use cases](https://docs.atlan.com/llms/governance/lakehouse/overview/llms.txt)
- [Atlan metamodel reference](https://docs.atlan.com/llms/platform/types/llms.txt)

---
