Many-to-Many Relationship
A Many-to-Many Relationship in Salesforce links two objects so that each record on one side can connect to many records on the other side, and the reverse holds too.
Definition
A Many-to-Many Relationship in Salesforce links two objects so that each record on one side can connect to many records on the other side, and the reverse holds too. Salesforce has no native many-to-many field type. You model the relationship with a junction object, which is a custom object that holds two master-detail relationships, one to each parent. Every row on the junction represents a single pairing between one record on the left and one record on the right.
This pattern shows up constantly in real data models. A student takes many courses while a course enrolls many students. A property is shown to many candidates while a candidate views many properties. Standard objects use it too: CampaignMember links contacts and leads to campaigns, and OpportunityContactRole links contacts to opportunities. Building one well means creating the junction, configuring both master-detail fields, adding any attribute fields the pairing needs, and relabeling the related lists on each parent.
How the junction object pattern works
Why Salesforce needs a junction object
Relational databases handle many-to-many through a bridge table, and Salesforce follows the same idea with a junction object. A junction object is a plain custom object whose only special trait is that it carries two master-detail relationships, one pointing at each parent. There is no checkbox labeled many-to-many anywhere in Setup. You create the relationship by building the junction and wiring up its two parents. Each junction record stores exactly one pairing, so an account linked to fifty contacts produces fifty junction rows. Because both links are master-detail, a junction record cannot exist without both parents present. Delete either parent and the junction rows cascade away with it, which keeps orphaned pairings out of the data. This design also means the junction is a real object with its own tab option, page layouts, fields, validation rules, triggers, and reports. Treating it as a first-class object rather than an invisible link is what makes the many-to-many model flexible. You can report on it, automate it, and add business meaning to each pairing.
Primary and secondary master-detail
The order in which you create the two master-detail fields matters. The first master-detail relationship you add becomes the primary relationship, and the second becomes the secondary. The primary parent drives the junction record name format in the related list, its ownership, and its position in the colour and division settings. If you later delete the primary master-detail field, or convert it to a lookup, the secondary master automatically becomes the primary. So pick the primary parent deliberately, because reversing the choice after data exists is awkward. A common guideline is to make the parent that users think of as the main owner the primary side. For a job application junction between Position and Candidate, many teams set Position as primary because recruiters work from the position record. Both parents still see the junction rows in a related list, and both can roll up summary fields from the junction. The primary just holds the senior role in the chain. Plan this early in the design rather than after the object is live.
Adding meaning with custom fields
Because the junction is a full custom object, you can add fields that describe the pairing itself, not either parent. This is where a many-to-many model becomes more than a raw link. On an Account to Contact junction you might store a Role field (Decision Maker, Influencer, Buyer), a Status field, or a date range showing when the relationship was active. On a Product to Contract junction you might store quantity, a discount percentage, or an allocation share. These attributes belong to the combination of the two records, which is why they sit on the junction rather than on either parent. You can add validation rules to keep the data clean, build formula fields, and create roll-up summary fields on a parent that aggregate junction values. For example, a roll-up on the position record could count related applications or sum a score field. Lightning page layouts and the related list on each parent control which junction fields users see. Treating the junction as a place to capture relationship context turns a simple link into a richer, reportable record that supports automation and analytics.
Standard junctions Salesforce ships
You do not always have to build a junction by hand. Salesforce ships several standard objects that already act as junctions, and they often carry behavior a custom junction cannot reproduce. CampaignMember links a contact or lead to a campaign and tracks member status, so a contact can belong to many campaigns and a campaign can hold many members. OpportunityContactRole connects contacts to opportunities along with the role each contact plays in the deal. AccountContactRelation, enabled through the Contacts to Multiple Accounts feature, lets one contact relate to several accounts beyond its primary account. Pricebook Entry sits between a product and a price book so a product can appear in many price books at different prices. Each of these has platform support that a hand-built junction would have to mimic with code or configuration. Before building a custom junction, check whether a standard one already covers the use case. Using the standard object means you inherit its reports, its API behavior, and its integration with features like Campaigns or Pricing. Reach for a custom junction when no standard object fits the two things you need to relate.
Reporting and SOQL across the bridge
A many-to-many relationship gives you two standard report types automatically once the junction is in place. One joins the primary master with the junction and the secondary master, and the other joins the secondary master with the junction and the primary master. Choosing the right one decides which parent leads the report and which appears as related detail. For example, a Positions with Job Applications and Candidates report lists positions and the candidates tied to each. Querying in SOQL means traversing the junction. A parent-to-junction subquery uses the junction relationship name, as in SELECT Name, (SELECT Candidate__r.Name FROM Job_Applications__r) FROM Position__c, which returns each position with its related candidates. From the junction itself you walk up to either parent with dot notation, such as SELECT Position__r.Name, Candidate__r.Name FROM Job_Application__c. Getting the relationship names right is the main hurdle, so check the child relationship name on each master-detail field in Setup. Because reporting and querying both pass through the junction, the fields you place there directly shape what your reports and integrations can return.
Sharing, limits, and data volume
Junction records do not have their own owner. Like any detail record in a master-detail relationship, they inherit sharing and ownership from the primary master, so a user who can see the primary parent can see its junction rows. You cannot set sharing rules, manual sharing, or queues directly on a detail object. Plan access around the primary parent for that reason. There are hard limits to respect. Each custom object can hold at most two master-detail relationships, which is exactly what a junction needs, and up to forty relationships in total. Salesforce also recommends staying under about ten thousand child records per parent for a single master-detail relationship to avoid data skew and locking issues. High-volume many-to-many models can grow large quickly. A contact tied to thousands of campaigns over several years produces a matching pile of junction rows, and those count against data storage. For very large junctions, plan an archival or rollup strategy so the table stays manageable. The master-detail field is required on the junction page layout, and the parent on a relationship cannot be changed after the record is saved.
Build a many-to-many relationship with a junction object
There is no single many-to-many setting. You create a junction object and add two master-detail relationships, one to each parent, then relabel the related lists. These steps follow Salesforce Help for creating a many-to-many object relationship.
- Create the junction custom object
In Setup, open Object Manager and create a new custom object to act as the junction. Give it a label that names the pairing, such as Job Application. For the Record Name, choose the Auto Number data type so each pairing gets a unique generated name instead of forcing users to type one.
- Add the first master-detail relationship
On the junction object, create a custom field of type Master-Detail Relationship pointing to the first parent. This first relationship becomes the primary. Pick the parent that users treat as the main owner, since the primary drives sharing and ownership for every junction record.
- Add the second master-detail relationship
Create a second Master-Detail Relationship field on the junction pointing to the other parent. This one becomes the secondary relationship. With both links in place the object is now a working junction, and each record pairs one primary parent with one secondary parent.
- Relabel the related lists on each parent
On each parent page layout, edit the junction related list and rename it after the other parent. On the Position layout, label the list Candidates, and on the Candidate layout, label it Positions, so users read meaningful names instead of the raw junction object name.
- Add attribute fields and confirm reports
Add any fields that describe the pairing, such as Role, Status, or a date range, to the junction. Then confirm the two standard report types appear so you can report from either parent through the junction.
Set this to Auto Number on the junction so each pairing gets a system-generated name like JA-{0000}.
The first master-detail you create. Controls junction ownership and sharing and becomes the lead in the standard report types.
The second master-detail you create. Becomes primary automatically if the primary relationship is deleted or converted to a lookup.
The name shown on each parent layout for the junction list. Rename it to the opposite parent so users see Candidates and Positions, not the junction name.
- Create both master-detail relationships before the junction object holds data; master-detail fields are hard to add to a populated object.
- The first master-detail you add is the primary; if you need a different primary parent, create that relationship first.
- Junction records inherit sharing from the primary master and have no separate owner, so plan access around that parent.
- A custom object allows at most two master-detail relationships, which is exactly what a junction uses, leaving no room for a third.
Prefer this walkthrough as its own page? How to Many-to-Many Relationship in Salesforce, step by step
Trust & references
Cross-checked against the following references.
- Create a Many-to-Many Object RelationshipSalesforce
- Considerations for Object RelationshipsSalesforce
Straight from the source - Salesforce's reference material on Many-to-Many Relationship.
- Create a Many-to-Many Object RelationshipSalesforce
- Object Relationships OverviewSalesforce
Hands-on resources to go deeper on Many-to-Many Relationship.
About the Author
Dipojjal Chakrabarti is a B2C Solution Architect with 29 Salesforce certifications and over 13 years in the Salesforce ecosystem. He runs salesforcedictionary.com to help admins, developers, architects, and cert/interview candidates sharpen their fundamentals. More about Dipojjal.
Test your knowledge
Q1. What is a Many-to-Many Relationship between two Salesforce objects?
Q2. How is a Many-to-Many Relationship implemented in the Salesforce data model?
Q3. Beyond the two master-detail fields, what can a junction object in a Many-to-Many setup store on each record?
Discussion
Loading discussion…