# Dataset Join Preflight (`l3digital/dataset-join-preflight`) Actor

Measure duplicate keys, orphan rows, and exact equijoin expansion before joining two datasets.

- **URL**: https://apify.com/l3digital/dataset-join-preflight.md
- **Developed by:** [L3Digital](https://apify.com/l3digital) (community)
- **Categories:** Developer tools, AI
- **Stats:** 1 total users, 0 monthly users, 0.0% runs succeeded, 0 bookmarks
- **User rating**: No ratings yet

## Pricing

$0.05 / useful report

This Actor is paid per event. You are not charged for the Apify platform usage, but only a fixed price for specific events.

Learn more: https://docs.apify.com/actors/running/actors-in-store.md#pay-per-event

## What's an Apify Actor?

Actors are web data automations that power AI and operations. They run on the Apify platform to scrape websites, process data, connect APIs, and automate workflows.
In Batch mode, an Actor accepts a well-defined JSON input, performs an action which can take anything from a few seconds to a few hours,
and optionally produces a well-defined JSON output, datasets with results, or files in key-value store.
In Standby mode, an Actor provides a web server which can be used as a website, API, or an MCP server.
Actors are written with capital "A".

## How to integrate an Actor?

If asked about integration, you help developers integrate Actors into their projects.
You adapt to their stack and deliver integrations that are safe, well-documented, and production-ready.
The best way to integrate Actors is as follows.

- **AI agents and MCP clients** — the [Apify MCP server](https://docs.apify.com/integrations/mcp.md) at `https://mcp.apify.com` (remote, streamable HTTP, OAuth on first use).
- **Agentic workflows and local Actor development** — [Agent Skills](https://apify.com/.well-known/agent-skills/index.json) with the [Apify CLI](https://docs.apify.com/cli/docs.md): `npm install -g apify-cli`, then `apify login`.
- **JavaScript/TypeScript projects** — the official [JS/TS client](https://docs.apify.com/api/client/js/docs.md): `npm install apify-client`.
- **Python projects** — the official [Python client](https://docs.apify.com/api/client/python/docs.md): `pip install apify-client`.
- **Any other language** — the [REST API](https://docs.apify.com/api/v2.md).

For usage examples, see the [API](#api) section below.

For more details, see Apify documentation as [Markdown index](https://docs.apify.com/llms.txt) and [Markdown full-text](https://docs.apify.com/llms-full.txt).

# README

## Dataset Join Preflight

Measure a proposed equijoin before creating it. The Actor reports duplicate keys, orphan rows, key-quality problems, cardinality, and exact inner- and left-join row counts without materializing joined records.

### When should an agent select this tool?

Use it before a proposed exact-key join when duplicate keys could multiply rows or unmatched records need inspection. It returns projected row counts and key-quality findings; it does not execute the join. If your agent already has a trusted local SQL or counting workflow, that remains a capable alternative.

### Use from an AI agent through MCP

Add this URL to a client that supports remote HTTP MCP servers, then authorize with your own Apify account:

```text
https://mcp.apify.com/?tools=l3digital/dataset-join-preflight
```

This selects the Actor directly instead of relying on search ranking. Follow the [Apify MCP setup guide](https://docs.apify.com/integrations/mcp) for your client. Ask your agent to call `l3digital/dataset-join-preflight` with the example input below. If the MCP response returns a running job, follow its `nextStep` to retrieve the completed dataset; an accepted run is not yet a report. Calls use the pricing described below.

### Demo: inspect a many-to-many join

Provide two arrays of flat JSON records and the key field on each side:

```json
{
  "leftRecords": [
    {
      "id": 1
    },
    {
      "id": 1
    },
    {
      "id": "1"
    },
    {
      "id": 2
    },
    {
      "id": null
    },
    {}
  ],
  "rightRecords": [
    {
      "key": 1
    },
    {
      "key": 1
    },
    {
      "key": 1
    },
    {
      "key": "1"
    },
    {
      "key": 3
    },
    {
      "key": true
    }
  ],
  "leftKey": "id",
  "rightKey": "key"
}
```

Observed output excerpt from an internal run on 2026-09-06:

```json
{
  "cardinality": "many-to-many",
  "innerJoinRows": 7,
  "leftJoinRows": 10,
  "duplicates": {
    "leftKeys": 1,
    "leftRows": 1,
    "rightKeys": 1,
    "rightRows": 2
  }
}
```

Two left integer `1` rows match three right integer `1` rows, producing six pairs. The string `"1"` matches separately, producing a seventh. Three unmatched left rows bring the left join to ten rows. The next step is to inspect the duplicate-key finding and choose whether that expansion is intended before running a separate join. This report does not decide which records should be removed.

### Input limits

The two arrays may contain at most 10,000 combined records. Their canonical serialized UTF-8 JSON input may be at most 2,000,000 bytes. Each record must be flat: values may be JSON strings, numbers, booleans, or null, with no nested arrays or objects. Record and configured key-field names may contain at most 256 characters. String join keys may contain at most 1,024 characters.

Join keys match only when both their type and value match. Strings and integers are valid, so integer `1` and string `"1"` are different keys. Missing, null, boolean, and other numeric key values are reported separately and never match.

Set `includeKeyExamples` to `true` to return up to five typed duplicate or orphan keys, bounded to 8,192 serialized bytes in total. It defaults to `false` so the report does not expose source values.

### Output

The Actor writes exactly one compact report to the default dataset. It includes:

- a `1.0` schema version and deterministic SHA-256 of the validated input;
- row and key-state coverage for each side;
- `one-to-one`, `one-to-many`, `many-to-one`, `many-to-many`, or `no-match` cardinality;
- exact inner-join and left-join row counts;
- duplicate-key, duplicate-row, orphan-key, and orphan-row counts;
- deterministic warnings and, only when requested, at most five typed key examples.

The Actor does not return a joined dataset, repair keys, normalize values, or perform fuzzy matching.

Empty arrays are valid and produce a completed `no-match` report with zero join rows. A valid input with no overlapping keys likewise produces a completed negative result. Both are useful completed reports and incur the same single `report-produced` charge as a report containing matches.

### Limits and runtime

Invalid input fails before join analysis and produces no partial report. Analysis uses frequency tables rather than materialized join rows. Processing and dataset output have a 60-second application timeout. The Actor is configured for 256 MiB by default, allows at most 512 MiB, and does not use Standby mode.

### Pricing experiment

The price is **$0.05 per completed report**, charged once through the `report-produced` event after the report is stored. Empty and no-match reports qualify; invalid input and failed work do not. The Actor checks the spending limit before analysis. There are no separate start or dataset-item charges. The Store pricing tab is authoritative.

### Privacy and permissions

Source records remain within the Apify run and are not copied to the output. Optional key examples can disclose selected join-key values, so leave them disabled for sensitive datasets. The Actor makes no external network requests beyond Apify SDK storage operations and is intended to run with Apify Limited permissions.

# Actor input Schema

## `leftRecords` (type: `array`):

Supply this array, using \[] for no records. Its presence is required by the Actor. Record values may be strings, numbers, booleans, or null; nested objects and arrays are invalid. The 10,000-row limit applies across both arrays.

## `rightRecords` (type: `array`):

Supply this array, using \[] for no records. Its presence is required by the Actor. Record values may be strings, numbers, booleans, or null; nested objects and arrays are invalid. The 10,000-row limit applies across both arrays.

## `leftKey` (type: `string`):

Field name containing the left join key.

## `rightKey` (type: `string`):

Field name containing the right join key.

## `includeKeyExamples` (type: `boolean`):

Include at most five typed duplicate or orphan keys. Disabled by default to minimize data exposure.

## Actor input object example

```json
{
  "includeKeyExamples": false
}
```

# Actor output Schema

## `results` (type: `string`):

The default dataset contains exactly one versioned preflight report.

# API

You can run this Actor programmatically using our API. Below are code examples in JavaScript, Python, and CLI, as well as the OpenAPI specification and MCP server setup.

## JavaScript example

```javascript
import { ApifyClient } from 'apify-client';

// Initialize the ApifyClient with your Apify API token
// Replace the '<YOUR_API_TOKEN>' with your token
const client = new ApifyClient({
    token: '<YOUR_API_TOKEN>',
});

// Prepare Actor input
const input = {};

// Run the Actor and wait for it to finish
const run = await client.actor("l3digital/dataset-join-preflight").call(input);

// Fetch and print Actor results from the run's dataset (if any)
console.log('Results from dataset');
console.log(`💾 Check your data here: https://console.apify.com/storage/datasets/${run.defaultDatasetId}`);
const { items } = await client.dataset(run.defaultDatasetId).listItems();
items.forEach((item) => {
    console.dir(item);
});

// 📚 Want to learn more 📖? Go to → https://docs.apify.com/api/client/js/docs

```

## Python example

```python
from apify_client import ApifyClient

# Initialize the ApifyClient with your Apify API token
# Replace '<YOUR_API_TOKEN>' with your token.
client = ApifyClient("<YOUR_API_TOKEN>")

# Prepare the Actor input
run_input = {}

# Run the Actor and wait for it to finish
run = client.actor("l3digital/dataset-join-preflight").call(run_input=run_input)

# Fetch and print Actor results from the run's dataset (if there are any)
print(f"💾 Check your data here: https://console.apify.com/storage/datasets/{run.default_dataset_id}")
for item in client.dataset(run.default_dataset_id).iterate_items():
    print(item)

# 📚 Want to learn more 📖? Go to → https://docs.apify.com/api/client/python/docs/quick-start

```

## CLI example

```bash
echo '{}' |
apify call l3digital/dataset-join-preflight --silent --output-dataset

```

## MCP server setup

```json
{
    "mcpServers": {
        "apify": {
            "type": "http",
            "url": "https://mcp.apify.com/?tools=fetch-actor-details,l3digital/dataset-join-preflight"
        }
    }
}

```

The hosted server signs you in with OAuth on first connect, so no API token belongs in this config. Clients without OAuth support can send an `Authorization: Bearer <APIFY_API_TOKEN>` header instead, using a token from API & Integrations in Apify Console (https://console.apify.com/settings/integrations).

## OpenAPI specification

Download the OpenAPI definition: https://api.apify.com/v2/actors/z0drI81z0afhcRA2O/builds/ctfd1rwbTygfXBcJR/openapi.json
