MBS FileMaker Advent calendar

Welcome to door 21. We have already seen in other doors that we can output the results of SQL queries not only as text but also as a matrix or JSON. Today, I would like to introduce you to another return format: XML.

Like JSON, XML is format that is often used for exchange between applications. Its form is reminiscent of HTML, because tags are also used here to structure the data. In XML, the data is structured, hierarchical, and clearly described.

If we want to output an SQL result as XML, we use the FM.SQL.XMLRecords function. First, we use FM.SQL.Execute to determine the data set that we want to output as XML. Only then we can output the data set with FM.SQL.XMLRecords. Here, we first specify the SQL reference in the function. This is followed by the root node name and the name of the tag for each individual record. Then we list the individual field names as a list. This completes the parameters that we must specify in any case.

Set Variable [ $SQLRes ; Value: MBS("FM.SQL.Execute" ; Get(FileName);
   "SELECT Name,Launch FROM Movie" ) ]
Set Variable [ $xml ; Value: MBS("FM.SQL.XMLRecords"; $SQLRes; "Root"; 
   "Record"; "Name¶Launch") ]

The result can look like this:

<?xml version="1.0" encoding="UTF-8"?>
<Root>
  <Record>
    <Name>The Nightmare Before Christmas</Name>
    <Launch>1993</Launch>
  </Record>
  <Record>
    <Name>It's a Wonderful Life</Name>
    <Launch>1946</Launch>
  </Record>
  <Record>
    <Name>Die Hard</Name>
    <Launch>1988</Launch>
  </Record>
  <Record>
    <Name>Gremlins</Name>
    <Launch>1984</Launch>
  </Record>
…
 <Record>
    <Name>A Christmas Story</Name>
    <Launch>1983</Launch>
  </Record>
</Root>

In addition to the required parameters, there are also optional parameters. This allows us to set flags. The number 1 means that we can return all values as text values, and the number 2 means that we get all dates, times, and timestamps in SQL format. In addition, we can also define a range of rows to be output in the parameters. By default, we return all rows from our SQL result. In addition to the FM.SQL.XMLRecords function, there is also the FM.SQL.XMLRecord function. This returns a single data record as XML. In the parameters, we first specify the reference. Then we specify the node name for the XML node, the number of the row of the result we want, starting with 0, and again a list with the names of the fields. Optionally, we can again specify our flags 1 for text values and 2 for date, time, and timestamp.

Set Variable [ $SQLRes ; Value: MBS("FM.SQL.Execute" ; Get(FileName); 
  "SELECT Name,Launch FROM Movie" ) ]
Set Variable [ $xml ; Value: MBS("FM.SQL.XMLRecord"; $SQLRes; "Record"; 0; "Name¶Launch") ]

That brings us to the end of our Advent calendar, and I wish you a wonderful Advent Sunday.

Welcome to door 22. Today's door will be another short one, but no less importantly. Because today I would like to talk to you about the FM.TableStatistics function. This function provides you with table statistics for your tables.

In this function, we first specify the file name and then the table name. Our call then looks like this:

Set Variable [ $Statistics ; Value: MBS( "FM.TableStatistics"; Get(FileName); "Movie" ) ]
Show Custom Dialog [ "Table statistics from the tabl…" ; $Statistics ]

And this is the result:

We can extract some information, such as the number of records, how many fields we have with text and numbers, and much more. The plugin will walk over all records and count fields with various data types and how many empty fields there are. It skips global and formula fields. We sum up the length of all text in text fields as well as the text content of all fields. This function can take a long time for huge tables with millions of records.

I hope this function will be useful to you in analyzing your data.

Welcome to door 23. We have already seen that we can turn an SQL result into JSON, XML, a matrix, CSV, or text. Now I would like to introduce you to another data structure: dictionaries.

These dictionaries are key-value pairs. Instead of addressing values via positions or sequences, you access the desired data directly via a meaningful key. This allows related information to be stored in a clear and structured manner. In addition, dictionaries are very fast at querying data, making them ideal for intermediate results. Today, we want to look at how we can fill such a dictionary with data using SQL. To do this, we have the functions Dictionary.AddSQLRecord and Dictionary.AddSQLRecords. Both functions are very similar in structure. First, both are passed the references to the dictionary. This is followed by a list of the names for the individual keys to be entered into the dictionary. This is followed by the SQL query we use to select the data for our dictionary. Next, we specify the file on which this SQL query is to be executed. If necessary, you can also pass any number of parameters to the function. We do not need these in our example. If both functions have the same parameter input and a similar description, what is the difference between the two? The Dictionary.AddSQLRecord function takes only the first entry from the SQL result set and stores it in the dictionary. Dictionary.AddSQLRecords, on the other hand, takes all records from the SQL result and enters the data into the dictionary.

But before we can use the two functions, we first have to create a reference to a new dictionary. The Dictionary.Create function helps us with this. We can then use the reference provided by this function in the corresponding function. Let's start with the Dictionary.AddSQLRecord function.

Set Variable [ $Dict ; Value: MBS("Dictionary.Create") ]
Set Variable [ $r ; Value: MBS( "Dictionary.AddSQLRecord"; $Dict; "Name¶Director¶Launch"; 
   "SELECT Name, Director, Launch FROM Movie WHERE Launch > 2000 AND Launch < 2005"; Get(FileName)) ]
Set Variable [ $text ; Value: MBS( "Dictionary.ToText"; $Dict  ) ]
Show Custom Dialog [ "Dictionary" ; $text ]

To output the dictionary as text, we then use the Dictionary.ToText function and pass on the reference. The result looks like this:

Now let's try this with the Dictionary.AddSQLRecords function.

Set Variable [ $Dict ; Value: MBS("Dictionary.Create") ]
Set Variable [ $r ; Value: MBS( "Dictionary.AddSQLRecords"; $Dict; "Name¶Director¶Launch";
   "SELECT Name, Director, Launch FROM Movie WHERE Launch > 2000 AND Launch < 2005"; Get(FileName)) ]
Set Variable [ $text ; Value: MBS( "Dictionary.ToText"; $Dict  ) ]
Show Custom Dialog [ "Dictionary" ; $text ]

We can see that this looks much more confusing for us, so what can we do? Instead of text, we can display the content as JSON, for example. We use the Dictionary.ToJSON function for this and pass the reference again.

Set Variable [ $Dict ; Value: MBS("Dictionary.Create") ]
Set Variable [ $r ; Value: MBS( "Dictionary.AddSQLRecords"; $Dict; "Name¶Director¶Launch";
   "SELECT Name, Director, Launch FROM Movie WHERE Launch > 2000 AND Launch < 2005"; Get(FileName)) ]
Set Variable [ $text ; Value: MBS( "Dictionary.ToJSON"; $Dict ) ]
Show Custom Dialog [ "Dictionary" ; $text ]

We see that the values belonging to a key are listed. But how do we now query the values for a single key? For this, we have the Dictionary.ValueForKey function. We again specify the reference as a parameter and then specify the name of the key.

…
Set Variable [ $text ; Value: MBS( "Dictionary.ValueForKey";$Dict; "Director" ) ]
…

We want to get the directors back. This function provides us with a list of all directors in the dictionary.

I hope you enjoyed this door as well, and we will see again tomorrow to open the last door together.

1 Like

Welcome to the last door of the Advent calendar. Today, we would like to take a look at a function in MBS that is particularly popular with users: FM.InsertOrUpdateRecord. This function means that we don't always have to decide whether a data record needs to be inserted if it doesn't already exist, or whether it already exists and just needs to be updated. The function makes this decision for you. It checks whether the entry already exists in the database. If so, it updates the data record; otherwise, it creates a new data record.

This is what the structure of the function looks like:

MBS( "FM.InsertOrUpdateRecord"; FileName; TableName; IDField; IDValue; FieldName...; FieldValue... )  

When we use this function, we first enter the file name and the table name of the table into which we want to insert data or make updates. Then we enter the ID field. This must contain a unique value, but unlike the functions we learned about earlier, it does not have to be defined as unique in the database. This means that it is sufficient for the movie name to appear only once in the database, but this does not have to be based on the database definition. We then specify the value for the IDField. This field combination then determines which data record is to be changed. We can then specify the values we want to set in the data record that is to be changed or newly created. To do this, we specify the field names alternating with the values and separate them with a semicolon.

Let's try this out with an example. We want the movie Snow Bride, released in 2013 and directed by Bert Kish, to be added to the database, or if the movie title already exists, the fields should be updated accordingly. Such an entry might look like this:

Set Variable [ $r ; Value: MBS( "FM.InsertOrUpdateRecord"; Get(FileName); "Movie"; "Name"; "Snow Bride"; 
   "Name"; "Snow Bride";"Director";"Bert Kish";"Launch";2023 ) ]
Show Custom Dialog [ $r ]

We can now see that the ID field can also be set in the same function, which is why we have entered the name and value Snow Bride twice here.

Since the data record is not yet contained in our database, it will be added to the database this time. We can now see that we have made an “unintentional” ;-) typo. It should say 2013 and not 2023. We can now simply change this in the script step and execute it again.

As you can see, the value has been changed without any further effort.

We don't just have one FM.InsertOrUpdateRecord function, but several functions that sound very similar: FM.InsertOrUpdateRecord2, FM.InsertOrUpdateRecord3, FM.InsertOrUpdateRecord4, FM.InsertOrUpdateRecord5. All of these functions work very similarly, but again, the difference is the number of ID fields. FM.InsertOrUpdateRecord has one ID field, but if you want to describe the record in more detail and need to identify it uniquely, this is often not enough. For this reason, you have functions that have more ID fields and that you can choose according to your needs.

Now I would like to introduce you to a very special function: FM.InsertOrUpdateRecordQuery. This function allows you to update and insert records into a table using another table. We use an SQL query to retrieve records from a table and insert them if the corresponding value does not yet exist in the ID field. If the value already exists, we change the specified fields.

Let's look at an example. We have created a table called Door24 and filled it with data covering all films released between 2000 and 2009. We did this with this command.

Set Variable [ $r ; Value: MBS( "FM.InsertOrUpdateRecordQuery"; Get(FileName); "Door24"; 
   "Name¶Launch¶Generated_Sales"; "Name=0"; Get(FileName); 
   "SELECT Name, Launch, Generated_Sales FROM Movie WHERE Launch>=2000 AND Launch <=2009") ]

Now let's delete a few of these entries, change others, and add a movie so we can see the changes right away. Next, we call FM.InsertOrUpdateRecordQuery in a script. In the parameters, we first specify the name of the file and the table in which we want to update and insert the data, followed by a list of fields that we want to fill in during the update or insert. Now comes an important parameter that is often a little difficult to understand. Here, we have to specify which field or fields are used for the comparison. This determines whether the record already exists in the table and is updated (reference fields are identical) or whether it is added (there is no reference field with the same value). In our table, the reference field is the movie name. But now we don't specify the names of the two fields that should match. Instead, we specify the field name from the table into which the data is to be inserted and then specify the index of the field that is now in the SELECT part of the SQL query we are writing in a moment. In our example, we want to compare the movie names, so we enter the name of the field from the Door 24 table, and when we define the Select table, we name the field Name first in the Select part, giving it the index 0. So we can write Name=0. The next parameter is the file name of the file containing the table to which we are about to send our data request. As already mentioned, the SQL query finally comes next. In the WHERE part of the query, we again want all movies between 2000 and 2009.

Set Variable [ $r ; Value: MBS( "FM.InsertOrUpdateRecordQuery"; Get(FileName); "Door24";
   "Name¶Director¶Launch¶Generated_Sales"; "Name=0"; Get(FileName); 
   "SELECT Name, Director, Launch, Generated_Sales FROM Movie WHERE Launch>=2000 AND Launch <=2009") ]

We see that the previously deleted movies are reinserted into the table because they are imported again from the Movie table. The previously changed records are also changed again. The movie Harry Potter and the Philosopher's Stone does not exist in the Movie table. When the data is synchronized, it is not deleted, but simply left as it is.

But what happens now if we make a change to the reference field and the data is no longer the same?

We tried this with the movie ”The Family Stone” and we see that it is no longer recognized as a movie to be synchronized and is therefore newly inserted.

This brings us to the end of this year’s calendar. I hope you enjoyed it and that you were able to take away a thing or two for your everyday life. All that remains is for me to wish you a wonderful and peaceful Christmas.

Merry Christmas!

Download the example file: Christmas2025.fmp12.zip and all articles as PDF: MBS_Advent_25_complete.pdf.

1 Like