Many-to-Many Relationships

AppSheet be default supports One-to-many relationships:

  • you have one record (say an Order) that has many related records (order details).

But there is another relationship connection type that allows you to connect many records to many records

  • you have a table of records (say Products) where each record can be related to many records in another table (say orders).
    • this means when you look at a product, you can see a bunch of related orders - and when you look at an order, you will see a bunch of related products.

Shared with CloudApp

AppSheet doesn’t support this type of relationship by default, though it is possible to implement this functionality.

It just takes a little formula magic

partyparrot (Appsheet)


I’ve put together a simple sample app that demonstrates how the formulas work to produce this interaction:
https://www.appsheet.com/samples/A-Sample-App-To-Demonstrate-How-To-Do-ManyToMany-Relationships?appGuidString=9fc05730-3794-4445-9e08-12fbacbfbf21

Here’s a run through of how it works:


The Secret

select(Orders[Order_Key], 
  in([_thisrow].[Product_Key], [Order_Products])
)

The column [Order_Products] = an enumlist of Product keys on the Orders table

  • This SELECT() formula checks to see if the Product Key (for the row that’s being evaluated) is inside the list for the row that’s being calculated.
24 Likes

Awesome,

Thank you :smiley:

1 Like

Yup yup. Been using this method forever.

1 Like

Love this thanks so much! Helped me finally figure out how to accomplish this with my app!

Quick question/observation: this currently only allows adding new orders but doesn’t allow adding new products since the Products table is set to read-only. My app has a requirement to support adding new Products and then to associate the product with one or more Projects (similar to your Orders table). Similarly, by creating/adding new Projects, I need to associate one or more Products to one or more Projects. With this requirement, I believe this would still require creating an intermediary table that connects the Products and Projects tables together - storing Product IDs and Project IDs in the table.

1 Like

This would be a good thing to store in it’s own table, yes.

Thanks @MultiTech_Visions… Excellent content as always… Cheers… :slight_smile:

2 Likes

So I’m a little stuck right now trying to figure out how to accomplish what I’m trying to do. Here’s my needs:

  1. I need to be able to pick/select multiple Projects for a single Product. From the Products edit screen, how can I include an Enumlist to provide all active Projects, and then based on which ones are checked, store those in the intermediary table that connects the Products and Projects (let’s call it Project Details)? I’ve tried adding a “Projects” column to the Products table as an Enumlist with a base type of Text, and the Valid If = SORTED(Projects[Project Name]). This gives me the list of all projects in the Projects table to pick from, and when saved, it does save the project names in this field. However I’m not clear how to show all of these selected Projects when viewing from the Projects table to display a single project and which selected products.

  2. From the Projects table, when viewing/editing a project, I need to be able to A) view which products are associated with this project, and B) to edit/update the selected products to check or uncheck products.

So basically I need to be able to add/edit bi-directionally - either when viewing/editing products to pick which projects it’s tied to, or when viewing/editing a project to pick which products are associated to it.

Hi @MultiTech_Visions I had done virtually the same kind of query to maintain a Product has many Controls and a Control can be for many Products relationship. It worked for a short while and then developed a time-out syncing error.

Unable to fetch app definition. Error:Request cancelled: took too long to process: Computing a formula/n

  • Product table has a maximum record count of 4, 645, which has a sever-side security filter to restrict that number to only the products the user has created to: 50 records.

  • Control table has a maximum record count of 56 records, security filter down to 20 based on AddedBy.

So not huge numbers by database standards (MySql), yet the solution resulted in the sync error, which only went away after deleting the query listed below.

Product table had a virtual column Inherited controls that used the following query, like the one suggested:

Select(
Control[ControlUUID],
IN( [_THISROW].[ProductUUID], [Products] ),
FALSE )

On the Control table side, it has an enum-list of type Ref called Products of ProductUUIDs that identifies Products the control should apply to. See screenshot below. Note: this is not a virtual column.

So now I am at a bit of a loss as to how to represent this relationship. I have considered an intermediary table: Product_Has_Many_Controls but how do you update such a table, when there are no Loop constructs (While, For) in AppSheet?

I.e: how do you create / update a record in an intermediary table from a list of ProductUUIDs identified in the Control table?

Any suggestions or recommendations greatly appreciated.

I’m surprised this timed out.

It should be noted, creating many-to-many relationships like this is not natively supported by the platform

  • As such, it’s prone to poor performance effects - which become magnified with larger data sets and increased complexity.

I wouldn’t do things this way, I would create many controls for products - then select the control when selecting which specifics should be applied - sticking with a one-to-many relationship.


PS:

2 Likes

Unfortunately manually selecting controls for a product is not the requirement. Consider a Compliance Team, who want to the specify controls for products with certain characteristics. For example: Credit Cards offered to Retail clients. This approach ensures conformity across ‘like’ credit card offerings and new credit card products with ‘similar’ characteristics inherit the controls automatically.

This ensures consistency, which would be difficult to achieve if each control had to be manually assigned to a product every time a new product was created. Easy for operational-risk to creep in and for a control to be missed. Furthermore the Designers creating the products should not be allowed to amend the controls set-by the Compliance team. This is segregation of duty requirement.

I fully understand why the solution currently outlined does not scale. Thank you for the looping actions tips. I’ll take a look and hopefully will find a solution.

1 Like

@MultiTech_Visions

I implemented the looping with action approach based on the examples sent, with an intermediary table to maintain the many-to-many relationship, between controls and products. I can see how the same could have achieved by manipulating arrays. Avery good video with the sample app. :+1: Kudos to @Steve :clap:

Just one last question, how do you hide or change the app owner email to be a general email for the company, rather than an individual’s email?

1 Like

Which part of App you see App owners email address and you are refering to?

Hi @tsuji_koichi

On start-up the app mentions the app creator. It used to give out my work email and say this person@1stidea.com may receive data on your locale app usage etc.

Has that changed? See screenshot below.

The text displayed there was recently localized: you can change it in the app editor in UX >> Localize.

Many thanks @Steve , brilliant news. Although I do most of the development work, I didn’t really want to broadcast my email address. Fame has its limitations :wink:

Happy Easter to all the Appsheet community. :rabbit2:

1 Like