Skip to content

Commit 567df82

Browse files
committed
add count distinct
1 parent 36c03c3 commit 567df82

File tree

3 files changed

+89
-6
lines changed

3 files changed

+89
-6
lines changed

src/backend/query_builder.rs

Lines changed: 20 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -310,7 +310,12 @@ pub trait QueryBuilder:
310310
}
311311
SimpleExpr::FunctionCall(func) => {
312312
self.prepare_function(&func.func, sql);
313-
self.prepare_tuple(&func.args, sql);
313+
write!(sql, "(").unwrap();
314+
if func.distinct {
315+
write!(sql, "DISTINCT ").unwrap();
316+
}
317+
self.prepare_comma_seperated_sequence(&func.args, sql);
318+
write!(sql, ")").unwrap();
314319
}
315320
SimpleExpr::Binary(left, op, right) => match (op, right.as_ref()) {
316321
(BinOper::In, SimpleExpr::Tuple(t)) if t.is_empty() => {
@@ -510,8 +515,12 @@ pub trait QueryBuilder:
510515
}
511516
TableRef::FunctionCall(func, alias) => {
512517
self.prepare_function(&func.func, sql);
513-
self.prepare_tuple(&func.args, sql);
514-
write!(sql, " AS ").unwrap();
518+
write!(sql, "(").unwrap();
519+
if func.distinct {
520+
write!(sql, "DISTINCT ").unwrap();
521+
}
522+
self.prepare_comma_seperated_sequence(&func.args, sql);
523+
write!(sql, ") AS ").unwrap();
515524
alias.prepare(sql.as_writer(), self.quote());
516525
}
517526
_ => self.prepare_table_ref_iden(table_ref, sql),
@@ -946,16 +955,21 @@ pub trait QueryBuilder:
946955
});
947956
}
948957

949-
/// Translate [`SimpleExpr::Tuple`] into SQL statement.
950-
fn prepare_tuple(&self, exprs: &[SimpleExpr], sql: &mut dyn SqlWriter) {
951-
write!(sql, "(").unwrap();
958+
/// Write a comma seperated sequence of [`SimpleExpr`]s.
959+
fn prepare_comma_seperated_sequence(&self, exprs: &[SimpleExpr], sql: &mut dyn SqlWriter) {
952960
exprs.iter().fold(true, |first, expr| {
953961
if !first {
954962
write!(sql, ", ").unwrap();
955963
}
956964
self.prepare_simple_expr(expr, sql);
957965
false
958966
});
967+
}
968+
969+
/// Translate [`SimpleExpr::Tuple`] into SQL statement.
970+
fn prepare_tuple(&self, exprs: &[SimpleExpr], sql: &mut dyn SqlWriter) {
971+
write!(sql, "(").unwrap();
972+
self.prepare_comma_seperated_sequence(exprs, sql);
959973
write!(sql, ")").unwrap();
960974
}
961975

src/expr.rs

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1430,6 +1430,35 @@ impl Expr {
14301430
Func::count(self.left).into()
14311431
}
14321432

1433+
/// Express a `COUNT` function with the DISTINCT modifier.
1434+
///
1435+
/// # Examples
1436+
///
1437+
/// ```
1438+
/// use sea_query::{tests_cfg::*, *};
1439+
///
1440+
/// let query = Query::select()
1441+
/// .expr(Expr::col((Char::Table, Char::SizeW)).count_distinct())
1442+
/// .from(Char::Table)
1443+
/// .to_owned();
1444+
///
1445+
/// assert_eq!(
1446+
/// query.to_string(MysqlQueryBuilder),
1447+
/// r#"SELECT COUNT(DISTINCT `character`.`size_w`) FROM `character`"#
1448+
/// );
1449+
/// assert_eq!(
1450+
/// query.to_string(PostgresQueryBuilder),
1451+
/// r#"SELECT COUNT(DISTINCT "character"."size_w") FROM "character""#
1452+
/// );
1453+
/// assert_eq!(
1454+
/// query.to_string(SqliteQueryBuilder),
1455+
/// r#"SELECT COUNT(DISTINCT "character"."size_w") FROM "character""#
1456+
/// );
1457+
/// ```
1458+
pub fn count_distinct(self) -> SimpleExpr {
1459+
Func::count_distinct(self.left).into()
1460+
}
1461+
14331462
/// Express a `IF NULL` function.
14341463
///
14351464
/// # Examples

src/func.rs

Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -33,13 +33,15 @@ pub enum Function {
3333
#[derive(Debug, Clone, PartialEq)]
3434
pub struct FunctionCall {
3535
pub(crate) func: Function,
36+
pub(crate) distinct: bool,
3637
pub(crate) args: Vec<SimpleExpr>,
3738
}
3839

3940
impl FunctionCall {
4041
pub(crate) fn new(func: Function) -> Self {
4142
Self {
4243
func,
44+
distinct: false,
4345
args: Vec::new(),
4446
}
4547
}
@@ -62,6 +64,12 @@ impl FunctionCall {
6264
self
6365
}
6466

67+
/// Add the `DISTINCT` modifier to the first argument
68+
pub fn distinct(mut self) -> Self {
69+
self.distinct = true;
70+
self
71+
}
72+
6573
pub fn get_func(&self) -> &Function {
6674
&self.func
6775
}
@@ -307,6 +315,38 @@ impl Func {
307315
FunctionCall::new(Function::Count).arg(expr)
308316
}
309317

318+
/// Call `COUNT` function with the `DISTINCT` modifier.
319+
///
320+
/// # Examples
321+
///
322+
/// ```
323+
/// use sea_query::{tests_cfg::*, *};
324+
///
325+
/// let query = Query::select()
326+
/// .expr(Func::count_distinct(Expr::col((Char::Table, Char::Id))))
327+
/// .from(Char::Table)
328+
/// .to_owned();
329+
///
330+
/// assert_eq!(
331+
/// query.to_string(MysqlQueryBuilder),
332+
/// r#"SELECT COUNT(DISTINCT `character`.`id`) FROM `character`"#
333+
/// );
334+
/// assert_eq!(
335+
/// query.to_string(PostgresQueryBuilder),
336+
/// r#"SELECT COUNT(DISTINCT "character"."id") FROM "character""#
337+
/// );
338+
/// assert_eq!(
339+
/// query.to_string(SqliteQueryBuilder),
340+
/// r#"SELECT COUNT(DISTINCT "character"."id") FROM "character""#
341+
/// );
342+
/// ```
343+
pub fn count_distinct<T>(expr: T) -> FunctionCall
344+
where
345+
T: Into<SimpleExpr>,
346+
{
347+
FunctionCall::new(Function::Count).arg(expr).distinct()
348+
}
349+
310350
/// Call `CHAR_LENGTH` function.
311351
///
312352
/// # Examples

0 commit comments

Comments
 (0)