Building a dynamic msg.topic to create a mysql insert statement

Good Morning
I wondered if i could get a point in the right direction please.

Kind Regards
Andrew

I created a flow (pic attached) the inject node sends a mysql statement and inserts data as expected.

msg.topic = "INSERT INTO Sensors (Device_ID, Event_Type, Event_Time_Date) VALUES ('151', 'R' , Current_Timestamp())";

return msg;

When I try to create the msg.topic in a function I need to replace the value 151 with a variable called Test1 and replace the value R with Test2

This is my attempt but it throws a syntax error

msg.topic = "INSERT INTO Sensors (Device_ID, Event_Type, Event_Time_Date) " + "VALUES (" + Test1 + ", " + Test2 + " ," CURRENT_TIMESTAMP );";

As a first step try this in a function node.

// Example values
let Test1 = "Sensor001";  // Device_ID
let Test2 = "ButtonPress";  // Event_Type

// Make sure to quote string values in SQL
msg.topic = "INSERT INTO Sensors (Device_ID, Event_Type, Event_Time_Date) " +
            "VALUES ('" + Test1 + "', '" + Test2 + "', CURRENT_TIMESTAMP);";

return msg;

A better method is to use a parameterised query:

Some versions of Node-RED’s MySQL node support using msg.topic with placeholders and msg.payload:

// safer approach with placeholders
msg.topic = "INSERT INTO Sensors (Device_ID, Event_Type, Event_Time_Date) VALUES (?, ?, CURRENT_TIMESTAMP)";
msg.payload = ["Sensor001", "ButtonPress"];
return msg;

This prevents SQL injection and handles escaping for you.

If deviceID is numeric (rather than text) you could do something like this...

// Extract values from the incoming payload
let deviceID = msg.payload.device_id;       // numeric
let eventType = msg.payload.event_type;     // string

// Build parameterized SQL query
msg.topic = "INSERT INTO Sensors (Device_ID, Event_Type, Event_Time_Date) VALUES (?, ?, CURRENT_TIMESTAMP)";

// Assign to msg.payload as an array for the query
msg.payload = [deviceID, eventType];

return msg;
1 Like

If you are using node-red-node-mysql, a rather better (IMHO) approach to a parameterised query is for the payload to be an object rather than an array.
Using named object keys removes the possibly ambiguous link between the nth ? and payload[n-1]

msg.topic = "INSERT INTO Sensors (Device_ID, Event_Type, Event_Time_Date) VALUES (:device, :event, CURRENT_TIMESTAMP)";
msg.payload = { 
    "event": "ButtonPress",
    "device": "Sensor001" 
}
return msg;
2 Likes

Good Morning Dave, I guess having dynamic in the name is a clue, Thank you for such concise and plentiful options to allow me to try more than one option.
Kind regards
Andrew

Hi there
Many thanks for the informative and prompt response.
I will give that a try when i get home.

Thank you again

Regards
Andrew

Have you tried using named placeholders in a MySQL node query? I'm not sure if it's supported.
I'll have to try it out later when I have some spare time.

Strictly speaking it's Mariadb, but yes, that's how I do it with node-red-node-mysql.

1 Like

If attached is what you mean, I can confirm it works!
Thanks for making my life much simpler. I have several queries which need simplification.

// safer approach with placeholders
msg.topic = "INSERT INTO cc (rdate, totalcases, totalcl, caseperman) VALUES (?, ?, ?, ? )";
msg.payload = ["2025-07-08", 1234, 25, 49];
return msg;

1 Like

I couldn't wait or resist - had to try named-bindings - works just fine.
Thanks, I'll be using that method in my future flows.

EDIT: Will also explain it to my IoT students for their projects.

2 Likes

This works as well,
I couldn't grasp much about what you said about removing ambiguity though.

// safer approach with placeholders
// msg.topic = "INSERT INTO cc (rdate, totalcases, totalcl, caseperman) VALUES (?, ?, ?, ? )";
// msg.payload = ["2025-07-08", 1234, 25, 49];
// return msg;

msg.topic = "INSERT INTO cc (rdate, totalcases, totalcl, caseperman) VALUES (:rdate, :totalcases, :totalcl, :caseperman)";
msg.payload = {
    "rdate": "2025-07-06",
    "totalcases": 1234,
    "totalcl": 25,
    "caseperman": 49
}
return msg;

With named-bindings it doesn't matter what the sequence is of the data-items in the payload.

For example, you could have...

msg.payload = {
    "caseperman": 49,
    "totalcl": 25,
    "totalcases": 1234,
    "rdate": "2025-07-06"
}

And it will still work correctly. So it's a more flexible approach.

1 Like

Got it! Thanks.

Yes, understood, really useful. I even got it to work without naming the field names before VALUES clause, as i am updating ALL the fields.

msg.topic = "INSERT INTO cc VALUES (:rdate, :totalcases, :totalcl, :caseperman, :costpercase, :cpm, :cpc)";
msg.payload = {
    "totalcases": 1234,
    "rdate": "2025-07-03",
    "caseperman": 49,
    "totalcl": 25,
    "costpercase": 39,
    "cpc": 25,
    "cpm": 52,
}

EDIT: Extremely Sorry @fogmajor for hijacking your thread. I will stop.

1 Like

Just to note that in javascript you don't need the quotes round property names, provided they do not contain any special characters. So this will also work

msg.payload = {
    totalcases: 1234,
    rdate: "2025-07-03",
    caseperman: 49,
    totalcl: 25,
    costpercase: 39,
    cpc: 25,
    cpm: 52,
}
1 Like

It hardly matters if you are only inserting a couple of fields, but assuming the table has more fields...
You could do this (which I have not tested)

msg.topic = "INSERT INTO climatesensor"
msg.topic += " (sensorid, timestamp, temperature, pressure, humidity, airquality)"
msg.topic += " VALUES (?, CURRENT_TIMESTAMP(), ?, ?, ?, ?)"
msg.payload = [42, 97.4, 981, 75, 106]
return msg;

And 6 months later you discover that your BME680 can also report sea level pressure so you decide to include that.
Now your code has to change

msg.topic = "INSERT INTO climatesensor"
msg.topic += " (sensorid, timestamp, temperature,  sealevelpressure, pressure, humidity, airquality)"
msg.topic += " VALUES (?, CURRENT_TIMESTAMP(), ?, ?, ?, ?, ?)"
msg.payload = [42, 97.4, 981, 1012.1, 75, 106]

Or is it

msg.topic = "INSERT INTO climatesensor"
msg.topic += " (sensorid, timestamp, temperature, sealevelpressure, pressure, humidity, airquality)"
msg.topic += " VALUES (?, CURRENT_TIMESTAMP(), ?, ?, ?, ?, ?)"
msg.payload = [42, 97.4, 1012.1, 981, 75, 106]

Using named parameters you add in the new fieldname and payload key, no need to ensure they are in the same position in msg.payload (object properties don't really have a position)

msg.topic = "INSERT INTO climatesensor"
msg.topic += " (sensorid, timestamp, temperature, sealevelpressure, pressure, humidity, airquality)"
msg.topic += " VALUES (:sensorid, CURRENT_TIMESTAMP(), :temperature, :sealevelpressure, :pressure, :humidity, :airquality)"
msg.payload = { 
    "sensorid": 42, 
    "temperature": 97.4, 
    "sealevelpressure": 1012.1, 
    "pressure": 981, 
    "humidity": 75, 
    "airquality": 106
}

Note that a BME680 sends it's data already in json form, so you may not actually need to rebuild msg.payload at all.
Here is an example from my flows:

if (msg.payload.Temperature > -20 && msg.payload.Temperature < 80 ) { // sanity check
    msg.topic = "insert into sensordata "
    + "(location, timestamp, sensortype, temperature, humidity, dewpoint, pressure, seapressure, gas) "
// the payload properties are exactly as sent by the sensor
    + "VALUES (:location, :timestamp, :sensortype, :Temperature, :Humidity, :DewPoint, :Pressure, :SeaPressure, :Gas)" 

    return msg
}

Indeed the quotes are not needed here. But if I were assembling msg.payload in a template node they would be needed, so I generally include them.

3 Likes

Just tried using named-bindings with an On Duplicate Key Update query - works just fine.

msg.topic  = "INSERT INTO energy_latest_readings ";
msg.topic += "(node_ref, voltage, current, power) ";
msg.topic += "VALUES (:node_ref, :voltage, :current, :power) ";
msg.topic += "ON DUPLICATE KEY UPDATE ";
msg.topic += "node_ref = :node_ref, ";
msg.topic += "voltage = :voltage, ";
msg.topic += "current = :current, ";
msg.topic += "power = :power;";

msg.payload = {
    node_ref: 'ev_charging_pod',
    voltage: msg.payload.ENERGY.Voltage,
    current: msg.payload.ENERGY.Current,
    power: msg.payload.ENERGY.ApparentPower
};

return msg;

I have a simple DB table where 'node_ref' is a unique key.


This is what the DB contents looks like.

Here's a screenshot of the NR flow.

Wow. there is no ambiguity now. very well explained. Although Dave's explanation also was clear and concise. thank you. learnt something today.

This topic was automatically closed 90 days after the last reply. New replies are no longer allowed.