LogoDTreeLabs

Format custom SQL queries in Rails

Ranjeet JagtapBy Ranjeet Jagtap in RailsRubySQLActiveRecord on December 2, 2019

While Rails comes with ActiveRecords that saves us from writing some scary (at least to me :)) SQL logic but still there are times when we need to write raw SQL queries. We have well defined coding conventions as far as Ruby is concerned. But we usually pay little attention to formatting the SQL queries. This makes it more difficult to read them.

Many developers tend use a readability-wise bad code. Here is one example:

@start_date = Time.parse(start_date).to_date.beginning_of_day
@end_date = Time.parse(end_date).to_date.end_of_day

sql_query = "select u1.name,im.lat,
im.long from  users as u1 JOIN  images as
im ON u1.id= im.imageable_id where  u1.created_at
BETWEEN \'#{@from_date}\' AND \'#{@till_date}\' ;"

query_result = ActiveRecord::Base.connection.execute(sql_query)

The alternative to this is using heredoc. The heredoc nicely explains about how to write multiple strings involving HTML or SQL snippets. The heredoc syntax is very useful for writing multi-line strings and avoiding quoting issues. Hence they can also be used for writing clean SQL queries.

Before

Let’s take a look at following snippet involving raw SQL, where heredoc syntax is used:

def query source_id, tags
      <<-SQL
        SELECT articles.id FROM articles
          INNER JOIN taggings
          ON taggings.taggable_id = articles.id
            AND taggings.context = \'tags\'
            AND taggings.taggable_type = \'Article\'
          INNER JOIN tags
            ON tags.id = taggings.tag_id
          INNER JOIN source_articles
            ON articles.id = source_articles.article_id
            GROUP BY source_articles.source_id, articles.id
            HAVING source_articles.source_id = #{source_id}
              AND articles.state = 0
              AND (
              (array#{tags}) && (ARRAY_AGG(tags.id))
              )
      SQL
end

From the above snippet one can understand what’s going on here but that takes considerable efforts for sure.

After

As they say, there is always a room for improvement, we can still improve the readability of above code snippet. While there are multiple different ways to format the SQL queries and one can argue about its simplicity, we found this one as most simple.

def query source_id, tags
  <<-SQL
        SELECT articles.id
          FROM articles
    INNER JOIN taggings
            ON taggings.taggable_id = articles.id
           AND taggings.context = \'tags\'
           AND taggings.taggable_type = \'Article\'
    INNER JOIN tags
            ON tags.id = taggings.tag_id
    INNER JOIN source_articles
            ON articles.id = source_articles.article_id
      GROUP BY source_articles.source_id, articles.id
        HAVING source_articles.source_id = #{source_id}
           AND articles.state = 0
           AND (array(#{tags}) && (ARRAY_AGG(tags.id)))
  SQL
end

A simple formatting can make a piece of code more readable. This can make a developer’s life a lot more easy!