0

I try to get some information from my database backend by performing a raw SQL query. I need to do calculations (multiplying a field with a factor, truncating it) and to convert it in a performant way to JSON. That's the reason why I have chosen to not use the (Django) ORM here.

with connection.cursor() as cursor:
    cursor.execute(
      '''
      SELECT json_build_object(
        'data', array_agg(r)
      )
      FROM (
        SELECT
          time,
          trunc(column1 * 100, 2) as COL1,
          trunc(column2 * 100, 2) as COL2,
          [...]
        FROM measurements
          WHERE device_id=%s
          ORDER BY time DESC
          LIMIT 5
      ) r
      ''',
      [device_id]
  )
  result = cursor.fetchall()

I need to adapt the statement above from the following list:

[
  {'column': 'column1', 'factor': 100, 'description': 'COL1', 'trunc': 2},
  {'column': 'column2', 'factor': 100, 'description': 'COL2', 'trunc': 2},
  [..]
]

Since I am not used to the python syntax yet I am wondering if there is an elegant solution for creating such a statement. I know I could just loop over the list of dicts and append the query but that doesn't feel good/right to me. Any suggestions would be appreciated.

I am thinking about something like this:

['trunc({} * {}, {}) as {}'.format(
  d['column'], d['factor'], d['trunc'], d['description']) for d in l
]

1 Answer 1

1

If you are using psycopg 2.7 you can use the sql module, which will make everything safe

from psycopg2 import sql

query = sql.SQL('''
  SELECT json_build_object(
    'data', array_agg(r)
  )
  FROM (
    SELECT time, {}
    FROM measurements
      WHERE device_id=%s
      ORDER BY time DESC
      LIMIT 5
  ) r
''')

truncated_rows = sql.SQL(', ').join(
    sql.SQL('trunc({} * {}, {}) as {}').format(
        sql.Identifier(d['column']),
        sql.Literal(d['factor']),
        sql.Literal(d['trunc']),
        sql.Identifier(d['description'])
    )
    for d in l
)

with connection.cursor() as cursor:
    cursor.execute(query.format(truncated_rows), [device_id])

With 2.6 and earlier you will have to fall back to python string format

','.join(
    'trunc({column} * {factor}, {trunc}) as {description}'.format(**data)
    for data in
    [
        {'column': 'column1', 'factor': 100, 'description': 'COL1', 'trunc': 2},
        {'column': 'column2', 'factor': 100, 'description': 'COL2', 'trunc': 2},
    ]
)
Sign up to request clarification or add additional context in comments.

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.