Skip to content

Commit 95e7a68

Browse files
authored
Merge pull request #11 from mindsdb/distinct-on
Support `distinct on`
2 parents af97f10 + 92ff773 commit 95e7a68

File tree

3 files changed

+40
-2
lines changed

3 files changed

+40
-2
lines changed

mindsdb_sql_parser/ast/select/select.py

Lines changed: 9 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
from typing import List, Union
12
import json
23
from mindsdb_sql_parser.ast.base import ASTNode
34
from mindsdb_sql_parser.utils import indent
@@ -7,7 +8,7 @@ class Select(ASTNode):
78

89
def __init__(self,
910
targets,
10-
distinct=False,
11+
distinct: Union[List, bool] = False,
1112
from_table=None,
1213
where=None,
1314
group_by=None,
@@ -22,6 +23,8 @@ def __init__(self,
2223
*args, **kwargs):
2324
super().__init__(*args, **kwargs)
2425
self.targets = targets
26+
27+
# if it is list: SELECT DISTINCT ON (c1, c2) ...
2528
self.distinct = distinct
2629
self.from_table = from_table
2730
self.where = where
@@ -105,8 +108,12 @@ def get_string(self, *args, **kwargs):
105108

106109
out_str += "SELECT"
107110

108-
if self.distinct:
111+
if self.distinct is True:
109112
out_str += ' DISTINCT'
113+
elif isinstance(self.distinct, list):
114+
distinct_str = ', '.join([c.to_string() for c in self.distinct])
115+
116+
out_str += f' DISTINCT ON ({distinct_str})'
110117

111118
targets_str = ', '.join([out.to_string() for out in self.targets])
112119
out_str += f' {targets_str}'

mindsdb_sql_parser/parser.py

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1300,6 +1300,11 @@ def select(self, p):
13001300
targets = p.result_columns
13011301
return Select(targets=targets, distinct=True)
13021302

1303+
@_('SELECT DISTINCT ON LPAREN expr_list RPAREN result_columns')
1304+
def select(self, p):
1305+
targets = p.result_columns
1306+
return Select(targets=targets, distinct=p.expr_list)
1307+
13031308
@_('SELECT result_columns')
13041309
def select(self, p):
13051310
targets = p.result_columns

tests/test_base_sql/test_select_structure.py

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -121,6 +121,32 @@ def test_select_distinct(self):
121121
assert str(parse_sql(sql)) == sql
122122
assert parse_sql(sql).distinct
123123

124+
def test_select_distinct_on(self):
125+
# single column with parts, star
126+
sql = """SELECT DISTINCT ON (t1.column1) * FROM t1"""
127+
128+
expected_ast = Select(
129+
targets=[Star()],
130+
from_table=Identifier('t1'),
131+
distinct=[Identifier('t1.column1')]
132+
)
133+
ast = parse_sql(sql)
134+
assert str(ast) == str(expected_ast)
135+
assert ast.to_tree() == expected_ast.to_tree()
136+
137+
# many columns without parts, not star
138+
139+
sql = """SELECT DISTINCT ON (column1, column2) column3, column4 FROM t1"""
140+
141+
expected_ast = Select(
142+
targets=[Identifier('column3'), Identifier('column4')],
143+
from_table=Identifier('t1'),
144+
distinct=[Identifier('column1'), Identifier('column2')]
145+
)
146+
ast = parse_sql(sql)
147+
assert str(ast) == str(expected_ast)
148+
assert ast.to_tree() == expected_ast.to_tree()
149+
124150
def test_select_multiple_from_table(self):
125151
sql = f'SELECT column1, column2, 1 AS renamed_constant FROM tab'
126152
ast = parse_sql(sql)

0 commit comments

Comments
 (0)