Eventually Consistent

Dispatches from the grey area between correct and working

Node MySQL

Quick and dirty MySQL hacks: Check one tables entries against a join table, user a subquery in a where, check if an entry exists without actually checking, make a quick array with group_concat

Disclaimer: I am not a database developer. That being said I really enjoy my sqls. So you might find the tips catalogued here as hackish. But that’s what I know how to do. Also note that because this is an article about node, I’ll use squel to make SQL strings

Check one tables entries against a join table

I have a table of articles that users of this sample website can subscribe to. When a user subscribes their id and the id of the article go in a join table. When a user wants a list articles of the articles this is how I see if the user is subscribed.

var query = squel.select()
    .field("a.*")
    .field("s.subscribed")
    .from("articles a")
    .left_join( squel.select()
        .field("1 as subscribed")
        .from("subscriptions")
        .where("user_id = ?" user.id), "s", "s.article_id = a.id")
    .toString()

This creates a table of all articles with a “1” where the user is subscribed.

Use a subquery in a WHERE

Although squel is awesome in letting you use squels in joins, it doesn’t like them in WHEREs. Try stringing it first and wrapping it in paranthesis.

var subquery = squel.select()
    .field("user_id")
    .from("user")
    .where("user_name = ?", user.name)

var mainquery = squel.select()
    .from("article")
    .where("author_id = (" +  subquery.toString() + ")")

Check if an entry exists on update without actually checking

Often when a user updates an entry in any DB, a developers first instinct would be to check if it exists before making the update resulting in two queries. With node-mysql just check rows affected.

var query = squel.update()
    .table("article")
    .set("title", req.body.title)
    .where("article_id = ?", req.params.id)

mysql.query( query.toString(), function ( err, result ){
    if (result.rowsAffected == 0){
        res.send(404, "Article does not exists")
    } else {
        res.send(200, "Article Updated... probably") 
}

Make a quick array with GROUP_CONCAT

Have a one to many relationship somewhere where the manys represent an array of strings? Use group_concat instead of bothering with a second query.

var query = squel.select()
    .from("articles a")
    .field("a.*")
    .field("t.tagGroup")
    .left_join( squel.select()
        .field("*, group_concat( tag_name separator ,) as tagGroup")
        .from("tags")
        .group("article_id"), "t", "t.article_id = a.id" )

mysql.query( query.toString(), function (err, result){
    result.forEach( function (row) {
        row.tagGroup = row.tagGroup.split(",")
    }
    res.send(200, result)
}

Now each article entry returned has an array of tags that really live as their own entries in another table.

Use GROUP_CONCAT to search across join tables

Sort of a tangent of the previous hack, you can use group_concat to search for an entry in one table based on its relationships in a join table.

var query = squel.select()
    .from("articles a")
    .field("a.*")
    .field("t.tagGroup")
    .field("GROUP_CONCAT( a.title, ' ', a.desc, ' ', t.tagGroup ) as searchColumn")
    .left_join( squel.select()
        .field("*, group_concat( tag_name separator ,) as tagGroup")
        .from("tags")
        .group("article_id"), "t", "t.article_id = a.id")
    .where("searchColumn LIKE '%"+ req.query.searchTerm +"%'")

This query will search a term across the article’s title, description, and tags (which live in another table). NOTE: use IFNULL() on each item in the GROUP_CONCAT to keep one null value from nullifying that row’s searchColumn field. For example, if an article had no tags it would have a searchColumn value of null despite having a title and description.

Thanks for reading! If you found this helpful, consider sharing it.