Skip to content

Commit 5b7eabc

Browse files
committed
Add more syntax support for SQL query generation
1. Add basic 'HAVING' clause (before the 'ORDER BY' clause). 2. Select source tables in 'DATE_RANGE' format. 3. Add 'BETWEEN' clause in conditions. 4. Modify 'ORDER BY' to accept multiple parameters.
1 parent f69fe34 commit 5b7eabc

File tree

1 file changed

+43
-9
lines changed

1 file changed

+43
-9
lines changed

bigquery/query_builder.py

Lines changed: 43 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22

33

44
def render_query(dataset, tables, select=None, conditions=None,
5-
groupings=None, order_by=None):
5+
groupings=None, having=None, order_by=None):
66
"""Render a query that will run over the given tables using the specified
77
parameters.
88
@@ -46,12 +46,13 @@ def render_query(dataset, tables, select=None, conditions=None,
4646
if None in (dataset, tables):
4747
return None
4848

49-
query = "%s %s %s %s %s" % (
49+
query = "%s %s %s %s %s %s" % (
5050
_render_select(select),
5151
_render_sources(dataset, tables),
5252
_render_conditions(conditions),
5353
_render_groupings(groupings),
54-
_render_order(order_by),
54+
_render_having(having),
55+
_render_order(order_by)
5556
)
5657

5758
return query
@@ -85,7 +86,7 @@ def _render_select(selections):
8586
for options_dict in options:
8687
name = original_name
8788
alias = options_dict.get('alias')
88-
alias = "as %s" % alias if alias else ""
89+
alias = "AS %s" % alias if alias else ""
8990

9091
formatter = options_dict.get('format')
9192
if formatter:
@@ -133,8 +134,20 @@ def _render_sources(dataset, tables):
133134
a string that represents the from part of a query.
134135
"""
135136

136-
return "FROM " + ", ".join(
137-
["[%s.%s]" % (dataset, table) for table in tables])
137+
if isinstance(tables, dict):
138+
if tables['date_range']:
139+
try:
140+
dataset_table = '.'.join([dataset, tables['table']])
141+
return "FROM (TABLE_DATE_RANGE([{}], TIMESTAMP('{}'),"\
142+
" TIMESTAMP('{}'))) ".format(dataset_table,
143+
tables['from_date'],
144+
tables['to_date'])
145+
except KeyError as exp:
146+
raise Exception('Missing parameter %s' % (exp))
147+
148+
else:
149+
return "FROM " + ", ".join(
150+
["[%s.%s]" % (dataset, table) for table in tables])
138151

139152

140153
def _render_conditions(conditions):
@@ -206,6 +219,12 @@ def _render_condition(field, field_type, comparators):
206219
else:
207220
value = _render_condition_value(value, field_type)
208221
value = "(" + value + ")"
222+
elif condition == "BETWEEN":
223+
if isinstance(value, (tuple, list)):
224+
value = ' AND '.join(
225+
sorted([_render_condition_value(v, field_type)
226+
for v in value])
227+
)
209228
else:
210229
value = _render_condition_value(value, field_type)
211230

@@ -242,25 +261,40 @@ def _render_condition_value(value, field_type):
242261
value = 1 if value else 0
243262
elif field_type in ("STRING", "INTEGER", "FLOAT"):
244263
value = "'%s'" % (value)
264+
elif field_type in ("TIMESTAMP"):
265+
value = "'%s'" % (str(value))
245266
return "%s(%s)" % (field_type, value)
246267

247268

269+
def _render_having(having):
270+
"""Render the having part of a query.
271+
272+
Args:
273+
having: accepts the having query as it is.
274+
275+
Returns:
276+
a string that represents the having part of a query.
277+
"""
278+
279+
return "HAVING %s" % (having) if having else ""
280+
281+
248282
def _render_order(order):
249283
"""Render the order by part of a query.
250284
251285
Args:
252286
order: a dictionary with two keys, field and direction.
253287
Such that the dictionary should be formatted as
254-
{'field':'TimeStamp, 'direction':'desc'}.
288+
{'fields': ['TimeStamp'], 'direction':'desc'}.
255289
256290
Returns:
257291
a string that represents the order by part of a query.
258292
"""
259293

260-
if not order or 'field' not in order or 'direction' not in order:
294+
if not order or 'fields' not in order or 'direction' not in order:
261295
return ''
262296

263-
return "ORDER BY %s %s" % (order['field'], order['direction'])
297+
return "ORDER BY %s %s" % (", ".join(order['fields']), order['direction'])
264298

265299

266300
def _render_groupings(fields):

0 commit comments

Comments
 (0)