---
layout: docu
redirect_from:
- /docs/guides/sql_features/friendly_sql
- /docs/guides/sql_features/friendly_sql/
- /docs/sql/dialect/friendly_sql
title: Friendly SQL
---

DuckDB offers several advanced SQL features and syntactic sugar to make SQL queries more concise. We refer to these colloquially as “friendly SQL”.

> Several of these features are also supported in other systems while some are (currently) exclusive to DuckDB.

## Clauses

* Creating tables and inserting data:
    * [`CREATE OR REPLACE TABLE`]({% link docs/stable/sql/statements/create_table.md %}#create-or-replace): avoid `DROP TABLE IF EXISTS` statements in scripts.
    * [`CREATE TABLE ... AS SELECT` (CTAS)]({% link docs/stable/sql/statements/create_table.md %}#create-table--as-select-ctas): create a new table from the output of a table without manually defining a schema.
    * [`INSERT INTO ... BY NAME`]({% link docs/stable/sql/statements/insert.md %}#insert-into--by-name): this variant of the `INSERT` statement allows using column names instead of positions.
    * [`INSERT OR IGNORE INTO ...`]({% link docs/stable/sql/statements/insert.md %}#insert-or-ignore-into): insert the rows that do not result in a conflict due to `UNIQUE` or `PRIMARY KEY` constraints.
    * [`INSERT OR REPLACE INTO ...`]({% link docs/stable/sql/statements/insert.md %}#insert-or-replace-into): insert the rows that do not result in a conflict due to `UNIQUE` or `PRIMARY KEY` constraints. For those that result in a conflict, replace the columns of the existing row to the new values of the to-be-inserted row.
* Describing tables and computing statistics:
    * [`DESCRIBE`]({% link docs/stable/guides/meta/describe.md %}): provides a succinct summary of the schema of a table or query.
    * [`SUMMARIZE`]({% link docs/stable/guides/meta/summarize.md %}): returns summary statistics for a table or query.
* Making SQL clauses more compact and readable:
    * [`FROM`-first syntax with an optional `SELECT` clause]({% link docs/stable/sql/query_syntax/from.md %}#from-first-syntax): DuckDB allows queries in the form of `FROM tbl` which selects all columns (performing a `SELECT *` statement).
    * [`GROUP BY ALL`]({% link docs/stable/sql/query_syntax/groupby.md %}#group-by-all): omit the group-by columns by inferring them from the list of attributes in the `SELECT` clause.
    * [`ORDER BY ALL`]({% link docs/stable/sql/query_syntax/orderby.md %}#order-by-all): shorthand to order on all columns (e.g., to ensure deterministic results).
    * [`SELECT * EXCLUDE`]({% link docs/stable/sql/expressions/star.md %}#exclude-clause): the `EXCLUDE` option allows excluding specific columns from the `*` expression.
    * [`SELECT * REPLACE`]({% link docs/stable/sql/expressions/star.md %}#replace-clause): the `REPLACE` option allows replacing specific columns with different expressions in a `*` expression.
    * [`UNION BY NAME`]({% link docs/stable/sql/query_syntax/setops.md %}#union-all-by-name): perform the `UNION` operation along the names of columns (instead of relying on positions).
    * [Prefix aliases in the `SELECT` and `FROM` clauses]({% link docs/stable/sql/query_syntax/select.md %}): write `x: 42` instead of `42 AS x` for improved readability.
* Transforming tables:
    * [`PIVOT`]({% link docs/stable/sql/statements/pivot.md %}) to turn long tables to wide tables.
    * [`UNPIVOT`]({% link docs/stable/sql/statements/unpivot.md %}) to turn wide tables to long tables.
* Defining SQL-level variables:
    * [`SET VARIABLE`]({% link docs/stable/sql/statements/set.md %}#set-variable)
    * [`RESET VARIABLE`]({% link docs/stable/sql/statements/set.md %}#reset-variable)

## Query Features

* [Column aliases in `WHERE`, `GROUP BY`, and `HAVING`]({% post_url 2022-05-04-friendlier-sql %}#column-aliases-in-where--group-by--having). (Note that column aliases cannot be used in the `ON` clause of [`JOIN` clauses]({% link docs/stable/sql/query_syntax/from.md %}#joins).)
* [`COLUMNS()` expression]({% link docs/stable/sql/expressions/star.md %}#columns-expression) can be used to execute the same expression on multiple columns:
    * [with regular expressions]({% post_url 2023-08-23-even-friendlier-sql %}#columns-with-regular-expressions)
    * [with `EXCLUDE` and `REPLACE`]({% post_url 2023-08-23-even-friendlier-sql %}#columns-with-exclude-and-replace)
    * [with lambda functions]({% post_url 2023-08-23-even-friendlier-sql %}#columns-with-lambda-functions)
* Reusable column aliases, e.g.: `SELECT i + 1 AS j, j + 2 AS k FROM range(0, 3) t(i)`
* Advanced aggregation features for analytical (OLAP) queries:
    * [`FILTER` clause]({% link docs/stable/sql/query_syntax/filter.md %})
    * [`GROUPING SETS`, `GROUP BY CUBE`, `GROUP BY ROLLUP` clauses]({% link docs/stable/sql/query_syntax/grouping_sets.md %})
* [`count()` shorthand]({% link docs/stable/sql/functions/aggregates.md %}) for `count(*)`

## Literals and Identifiers

* [Case-insensitivity while maintaining case of entities in the catalog]({% link docs/stable/sql/dialect/keywords_and_identifiers.md %}#case-sensitivity-of-identifiers)
* [Deduplicating identifiers]({% link docs/stable/sql/dialect/keywords_and_identifiers.md %}#deduplicating-identifiers)
* [Underscores as digit separators in numeric literals]({% link docs/stable/sql/dialect/keywords_and_identifiers.md %}#numeric-literals)

## Data Types

* [`MAP` data type]({% link docs/stable/sql/data_types/map.md %})
* [`UNION` data type]({% link docs/stable/sql/data_types/union.md %})

## Data Import

* [Auto-detecting the headers and schema of CSV files]({% link docs/stable/data/csv/auto_detection.md %})
* Directly querying [CSV files]({% link docs/stable/data/csv/overview.md %}) and [Parquet files]({% link docs/stable/data/parquet/overview.md %})
* Loading from files using the syntax `FROM 'my.csv'`, `FROM 'my.csv.gz'`, `FROM 'my.parquet'`, etc.
* [Filename expansion (globbing)]({% link docs/stable/sql/functions/pattern_matching.md %}#globbing), e.g.: `FROM 'my-data/part-*.parquet'`

## Functions and Expressions

* [Dot operator for function chaining]({% link docs/stable/sql/functions/overview.md %}#function-chaining-via-the-dot-operator): `SELECT ('hello').upper()`
* String formatters:
    the [`format()` function with the `fmt` syntax]({% link docs/stable/sql/functions/char.md %}#fmt-syntax) and
    the [`printf() function`]({% link docs/stable/sql/functions/char.md %}#printf-syntax)
* [List comprehensions]({% post_url 2023-08-23-even-friendlier-sql %}#list-comprehensions)
* [List slicing]({% post_url 2022-05-04-friendlier-sql %}#string-slicing)
* [String slicing]({% post_url 2022-05-04-friendlier-sql %}#string-slicing)
* [`STRUCT.*` notation]({% post_url 2022-05-04-friendlier-sql %}#struct-dot-notation)
* [Simple `LIST` and `STRUCT` creation]({% post_url 2022-05-04-friendlier-sql %}#simple-list-and-struct-creation)

## Join Types

* [`ASOF` joins]({% link docs/stable/sql/query_syntax/from.md %}#as-of-joins)
* [`LATERAL` joins]({% link docs/stable/sql/query_syntax/from.md %}#lateral-joins)
* [`POSITIONAL` joins]({% link docs/stable/sql/query_syntax/from.md %}#positional-joins)

## Trailing Commas

DuckDB allows [trailing commas](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Trailing_commas),
both when listing entities (e.g., column and table names) and when constructing [`LIST` items]({% link docs/stable/sql/data_types/list.md %}#creating-lists).
For example, the following query works:

```sql
SELECT
    42 AS x,
    ['a', 'b', 'c',] AS y,
    'hello world' AS z,
;
```

## "Top-N in Group" Queries

Computing the "top-N rows in a group" ordered by some criteria is a common task in SQL that unfortunately often requires a complex query involving window functions and/or subqueries.

To aid in this, DuckDB provides the aggregate functions [`max(arg, n)`]({% link docs/stable/sql/functions/aggregates.md %}#maxarg-n), [`min(arg, n)`]({% link docs/stable/sql/functions/aggregates.md %}#minarg-n), [`arg_max(arg, val, n)`]({% link docs/stable/sql/functions/aggregates.md %}#arg_maxarg-val-n), [`arg_min(arg, val, n)`]({% link docs/stable/sql/functions/aggregates.md %}#arg_minarg-val-n), [`max_by(arg, val, n)`]({% link docs/stable/sql/functions/aggregates.md %}#max_byarg-val-n) and [`min_by(arg, val, n)`]({% link docs/stable/sql/functions/aggregates.md %}#min_byarg-val-n) to efficiently return the "top" `n` rows in a group based on a specific column in either ascending or descending order.

For example, let's use the following table:

```sql
SELECT * FROM t1;
```

```text
┌─────────┬───────┐
│   grp   │  val  │
│ varchar │ int32 │
├─────────┼───────┤
│ a       │     2 │
│ a       │     1 │
│ b       │     5 │
│ b       │     4 │
│ a       │     3 │
│ b       │     6 │
└─────────┴───────┘
```

We want to get a list of the top-3 `val` values in each group `grp`. The conventional way to do this is to use a window function in a subquery:

```sql
SELECT array_agg(rs.val), rs.grp
FROM
    (SELECT val, grp, row_number() OVER (PARTITION BY grp ORDER BY val DESC) AS rid
    FROM t1 ORDER BY val DESC) AS rs
WHERE rid < 4
GROUP BY rs.grp;
```

```text
┌───────────────────┬─────────┐
│ array_agg(rs.val) │   grp   │
│      int32[]      │ varchar │
├───────────────────┼─────────┤
│ [3, 2, 1]         │ a       │
│ [6, 5, 4]         │ b       │
└───────────────────┴─────────┘
```

But in DuckDB, we can do this much more concisely (and efficiently!):

```sql
SELECT max(val, 3) FROM t1 GROUP BY grp;
```

```text
┌─────────────┐
│ max(val, 3) │
│   int32[]   │
├─────────────┤
│ [3, 2, 1]   │
│ [6, 5, 4]   │
└─────────────┘
```

## Related Blog Posts

* [“Friendlier SQL with DuckDB”]({% post_url 2022-05-04-friendlier-sql %}) blog post
* [“Even Friendlier SQL with DuckDB”]({% post_url 2023-08-23-even-friendlier-sql %}) blog post
* [“SQL Gymnastics: Bending SQL into Flexible New Shapes”]({% post_url 2024-03-01-sql-gymnastics %}) blog post