From 122a0e0f9a00babfef27bd3dc26dbdbfd18f07a8 Mon Sep 17 00:00:00 2001 From: Kat Batuigas <36839689+kbatuigas@users.noreply.github.com> Date: Fri, 1 May 2026 12:25:02 -0700 Subject: [PATCH] Migrate SQL docs (#521) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * Add converted SQL reference files * Duplicate file * Add index files * Add SQL reference to Cloud nav * Move reference pages to reference module * Convert remaining docs * Add placeholder index files * Cleanup * Move SQL section after RPCN * Continue cleanup using plugin audit and review * External links open in new window * Minor edit to trigger deploy preview * Migrate degraded state doc * Minor style edits * Address drift from Oxla changes since mid-Oct 2025 * Include CREATE TABLE and DROP changes * Update existing pages with placeholders for pages to be created * Another pass at style fixes * Missing TODO * Add remaining new reference pages to address doc drift * Remove Oxla storage compression references * Remove INSERT/UPDATE/DELETE from psycopg2 supported features * Remove Oxla-specific reasons from java-jdbc unsupported list * Remove DELETE/UPDATE references from php-pdo rowCount * Remove CREATE TABLE/INSERT example from select.adoc Assume that a student_data table is already created and populated with data * Remove table duplication TIP * Fix clumped "SELECTstatement" * Add missing Venn diagrams to JOIN pages * Inconsistent formatting with function names and parentheses * Incorrect COUNT logic with HAVING * Add NULLS FIRST/LAST, fix interval sorting * Replace CREATE TABLE oxlafunctions example in offset.adoc Redpanda SQL does not support CREATE TABLE without an external catalog Example assumes precreated salaryemp table * Unrelated "employees" text * Move column compatibility note to set-operations index Column type compatibility and flexible ordering applies to all set operations (UNION, INTERSECT, EXCEPT) * Both ROWS and RANGE frame modes are supported * Add GEOMETRY/GEOGRAPHY * Create sql-operators section with full operator list * timestamp-with-time-zone formatting * Remove internal memory representation for date type * Add month and day conversion note * Remove internal memory representation from bool type * System catalogs disambiguation note and 7 new pg_* pages * Fix nav * Move bitwise operators * Double check SQL examples and outputs * Remove unix_macros * Reorganize math and trig function pages * Implement some deferred fixes related to old Oxla examples * Fix anchors in xrefs * Apply QA checklist first pass across SQL reference docs Run 11-point QA checklist against all migrated SQL pages (148 files). Fixes applied: remove "Please note" phrasing, fix "here" link text, replace future tense output descriptions with present tense, simplify "allows/enables you to" constructions, remove minimizing language, fix wordy instructions, add missing admonition punctuation, replace verbose output lead-ins, remove "etc.", normalize positional references, and update stale PostgreSQL doc links to /docs/current/. Co-Authored-By: Claude Opus 4.6 (1M context) * Person 4 edits, including future tense, passive voice * more Person 4 edits * Person 4 edits * Parallelize :description: metadata for SQL language client pages Standardize the Java, PHP, and Python connector pages on the same imperative "Connect to Redpanda SQL from using " template already used by the C# page and the parent index page. Co-Authored-By: Claude Opus 4.7 (1M context) * Apply Feediver1 review suggestions Co-Authored-By: Claude Sonnet 4.6 * Apply pending PR suggestion comments Co-Authored-By: Claude Sonnet 4.6 * Minor edit - statements index * keywords.adoc - move in nav tree, make table more scannable * alter-table minor edit * create-redpanda-catalog: Improve Options section * create-storage minor edit * Improve CREATE TABLE reference page clarity Clarify NOTE about CREATE TABLE behavior, convert multi-value option descriptions to lists for scannability, and rename ambiguous example heading. Co-Authored-By: Claude Opus 4.6 (1M context) * select: review fixes * set-show: review fixes * show-tables minor edit * show-nodes: review fixes * describe: review fixes * sql-statements review pass * from - review pass * Minor edit * set-operations: review fixes * More review fixes for sql-clauses * other-functions review fixes * Normalize QA fixes * Apply edits based on doc team review * Standardize "To do X, run:" * Structural improvements * Sweep for passive voice * Person 2 style review: aggregate, window, JSON functions Style fixes across 31 files: - Future tense → present tense ("will return" → "returns") - Wordy instructions ("lets you" → direct verb) - Awkward phrasing ("Here's how" → cleaner alternatives) - First person ("we're going to" → "This example") - Passive "It" patterns in tables → active voice - Positional references ("the above query" → "Output:") - Typo fix ("ouput" → "output") Co-Authored-By: Claude Opus 4.5 * Pass to standardize QA * Apply suggestions from review * Minor edits --------- Co-authored-by: Claude Opus 4.6 (1M context) Co-authored-by: micheleRP Co-authored-by: Joyce Fee Co-authored-by: JakeSCahill --- modules/ROOT/nav.adoc | 232 +++++++++ modules/reference/images/sql/join-venn.png | Bin 0 -> 134971 bytes .../reference/images/sql/left-join-venn.png | Bin 0 -> 121565 bytes .../reference/images/sql/outer-join-venn.png | Bin 0 -> 144408 bytes .../images/sql/outer-join-where-venn.png | Bin 0 -> 112561 bytes .../reference/images/sql/right-join-venn.png | Bin 0 -> 125085 bytes .../reference/pages/sql/comment-support.adoc | 61 +++ modules/reference/pages/sql/index.adoc | 12 + .../pages/sql/redpanda-catalogs.adoc | 81 +++ modules/reference/pages/sql/schema.adoc | 228 +++++++++ .../pages/sql/sql-clauses/from/from.adoc | 240 +++++++++ .../pages/sql/sql-clauses/from/index.adoc | 3 + .../pages/sql/sql-clauses/from/join.adoc | 155 ++++++ .../pages/sql/sql-clauses/from/left-join.adoc | 171 +++++++ .../sql/sql-clauses/from/outer-join.adoc | 220 ++++++++ .../sql/sql-clauses/from/right-join.adoc | 165 ++++++ .../pages/sql/sql-clauses/group-by.adoc | 282 +++++++++++ .../pages/sql/sql-clauses/having.adoc | 244 +++++++++ .../pages/sql/sql-clauses/index.adoc | 22 + .../pages/sql/sql-clauses/limit.adoc | 206 ++++++++ .../pages/sql/sql-clauses/offset.adoc | 76 +++ .../pages/sql/sql-clauses/order-by.adoc | 279 +++++++++++ .../pages/sql/sql-clauses/over-window.adoc | 163 ++++++ .../sql-clauses/set-operations/except.adoc | 249 +++++++++ .../sql/sql-clauses/set-operations/index.adoc | 14 + .../sql-clauses/set-operations/intersect.adoc | 207 ++++++++ .../sql/sql-clauses/set-operations/union.adoc | 188 +++++++ .../pages/sql/sql-clauses/where.adoc | 239 +++++++++ .../reference/pages/sql/sql-clauses/with.adoc | 91 ++++ .../pages/sql/sql-data-types/array.adoc | 184 +++++++ .../pages/sql/sql-data-types/bool.adoc | 149 ++++++ .../pages/sql/sql-data-types/date.adoc | 69 +++ .../pages/sql/sql-data-types/geography.adoc | 75 +++ .../pages/sql/sql-data-types/geometry.adoc | 77 +++ .../pages/sql/sql-data-types/index.adoc | 38 ++ .../pages/sql/sql-data-types/interval.adoc | 176 +++++++ .../pages/sql/sql-data-types/json.adoc | 80 +++ .../sql-data-types/numeric-type/index.adoc | 3 + .../numeric-data-type-aliases.adoc | 91 ++++ .../sql-data-types/numeric-type/numeric.adoc | 304 +++++++++++ .../pages/sql/sql-data-types/row.adoc | 77 +++ .../pages/sql/sql-data-types/text.adoc | 126 +++++ .../sql/sql-data-types/time-type/index.adoc | 3 + .../time-type/time-operators.adoc | 379 ++++++++++++++ .../sql/sql-data-types/time-type/time.adoc | 68 +++ .../timestamp-with-time-zone.adoc | 151 ++++++ .../timestamp-without-time-zone.adoc | 210 ++++++++ .../aggregate-functions/avg.adoc | 145 ++++++ .../aggregate-functions/bool-and.adoc | 111 ++++ .../aggregate-functions/bool-or.adoc | 111 ++++ .../aggregate-functions/count.adoc | 138 +++++ .../aggregate-functions/distinct.adoc | 210 ++++++++ .../aggregate-functions/for-max.adoc | 116 +++++ .../aggregate-functions/for-min.adoc | 114 +++++ .../aggregate-functions/index.adoc | 29 ++ .../aggregate-functions/max.adoc | 118 +++++ .../aggregate-functions/min.adoc | 118 +++++ .../index.adoc | 3 + .../ordered-set-aggregate-functions/mode.adoc | 71 +++ .../percentile-cont.adoc | 98 ++++ .../percentile-disc.adoc | 98 ++++ .../aggregate-functions/statistics/corr.adoc | 70 +++ .../statistics/covar-pop.adoc | 70 +++ .../statistics/covar-samp.adoc | 71 +++ .../aggregate-functions/statistics/index.adoc | 27 + .../statistics/regr-avgx.adoc | 70 +++ .../statistics/regr-avgy.adoc | 70 +++ .../statistics/regr-count.adoc | 70 +++ .../statistics/regr-intercept.adoc | 70 +++ .../statistics/regr-r2.adoc | 70 +++ .../statistics/regr-slope.adoc | 70 +++ .../statistics/regr-sxx.adoc | 70 +++ .../statistics/regr-sxy.adoc | 70 +++ .../statistics/regr-syy.adoc | 70 +++ .../statistics/stddev-pop.adoc | 69 +++ .../statistics/stddev-samp.adoc | 70 +++ .../statistics/stddev.adoc | 69 +++ .../statistics/var-pop.adoc | 69 +++ .../statistics/var-samp.adoc | 70 +++ .../statistics/variance.adoc | 69 +++ .../aggregate-functions/sum.adoc | 253 ++++++++++ .../boolean-functions/if-function.adoc | 144 ++++++ .../boolean-functions/index.adoc | 3 + .../is-distinct-from-operator.adoc | 149 ++++++ .../is-not-distinct-from-operator.adoc | 151 ++++++ .../pages/sql/sql-functions/index.adoc | 18 + .../sql-functions/json-functions/index.adoc | 24 + .../json-functions/json-array-extract.adoc | 84 ++++ .../json-functions/json-array-length.adoc | 78 +++ .../json-extract-path-text.adoc | 66 +++ .../json-functions/json-extract-path.adoc | 79 +++ .../sql/sql-functions/math-functions/abs.adoc | 107 ++++ .../sql-functions/math-functions/cbrt.adoc | 137 +++++ .../sql-functions/math-functions/ceil.adoc | 106 ++++ .../sql-functions/math-functions/cosh.adoc | 69 +++ .../sql/sql-functions/math-functions/exp.adoc | 80 +++ .../sql-functions/math-functions/floor.adoc | 96 ++++ .../math-functions/greatest.adoc | 159 ++++++ .../sql-functions/math-functions/index.adoc | 61 +++ .../sql-functions/math-functions/least.adoc | 146 ++++++ .../sql/sql-functions/math-functions/ln.adoc | 79 +++ .../sql/sql-functions/math-functions/log.adoc | 173 +++++++ .../sql-functions/math-functions/power.adoc | 93 ++++ .../sql-functions/math-functions/random.adoc | 82 +++ .../sql-functions/math-functions/round.adoc | 63 +++ .../sql-functions/math-functions/sign.adoc | 105 ++++ .../sql/sql-functions/math-functions/sin.adoc | 134 +++++ .../sql-functions/math-functions/sinh.adoc | 82 +++ .../sql-functions/math-functions/sqrt.adoc | 118 +++++ .../math-functions/to-char-from-number.adoc | 136 +++++ .../other-functions/coalesce.adoc | 191 +++++++ .../other-functions/col-description.adoc | 27 + .../other-functions/current-database.adoc | 34 ++ .../other-functions/current-schema.adoc | 45 ++ .../other-functions/generate-series.adoc | 115 +++++ .../other-functions/has-schema-privilege.adoc | 70 +++ .../sql-functions/other-functions/index.adoc | 31 ++ .../sql-functions/other-functions/nullif.adoc | 194 +++++++ .../other-functions/obj-description.adoc | 21 + .../other-functions/pg-backend-pid.adoc | 12 + .../other-functions/pg-encoding-to-char.adoc | 48 ++ .../other-functions/pg-get-constraintdef.adoc | 21 + .../other-functions/pg-get-expr.adoc | 73 +++ .../other-functions/pg-get-indexdef.adoc | 68 +++ .../pg-get-statisticsobjdef-columns.adoc | 16 + .../other-functions/pg-get-userbyid.adoc | 53 ++ .../pg-relation-is-publishable.adoc | 36 ++ .../other-functions/pg-size-pretty.adoc | 36 ++ .../other-functions/pg-table-is-visible.adoc | 41 ++ .../other-functions/pg-table-size.adoc | 16 + .../pg-total-relation-size.adoc | 55 ++ .../other-functions/pg-typeof.adoc | 104 ++++ .../other-functions/shobj-description.adoc | 21 + .../string-functions/concat.adoc | 118 +++++ .../string-functions/ends-with.adoc | 154 ++++++ .../sql-functions/string-functions/index.adoc | 37 ++ .../string-functions/length.adoc | 105 ++++ .../sql-functions/string-functions/lower.adoc | 105 ++++ .../string-functions/position.adoc | 90 ++++ .../string-functions/regex/index.adoc | 3 + .../regex/posix-regular-expressions.adoc | 20 + .../string-functions/regex/regexp-match.adoc | 138 +++++ .../regex/regexp-replace.adoc | 156 ++++++ .../string-functions/replace.adoc | 152 ++++++ .../string-functions/starts-with.adoc | 155 ++++++ .../string-functions/strpos.adoc | 113 +++++ .../string-functions/substr.adoc | 144 ++++++ .../string-functions/substring.adoc | 50 ++ .../sql-functions/string-functions/upper.adoc | 109 ++++ .../current-timestamp.adoc | 63 +++ .../timestamp-functions/date-trunc.adoc | 160 ++++++ .../timestamp-functions/extract.adoc | 94 ++++ .../timestamp-functions/format-timestamp.adoc | 92 ++++ .../timestamp-functions/index.adoc | 22 + .../timestamp-functions/timestamp-micros.adoc | 93 ++++ .../timestamp-functions/timestamp-millis.adoc | 90 ++++ .../timestamp-seconds.adoc | 90 ++++ .../timestamp-functions/timestamp-trunc.adoc | 83 +++ .../timestamp-functions/to-char.adoc | 165 ++++++ .../timestamp-functions/to-timestamp.adoc | 197 ++++++++ .../timestamp-functions/unix-micros.adoc | 90 ++++ .../timestamp-functions/unix-millis.adoc | 88 ++++ .../timestamp-functions/unix-seconds.adoc | 93 ++++ .../sql-functions/window-functions/avg.adoc | 140 ++++++ .../window-functions/bool-and.adoc | 92 ++++ .../window-functions/bool-or.adoc | 92 ++++ .../sql-functions/window-functions/count.adoc | 156 ++++++ .../window-functions/cume-dist.adoc | 90 ++++ .../window-functions/dense-rank.adoc | 122 +++++ .../window-functions/first-value.adoc | 99 ++++ .../sql-functions/window-functions/index.adoc | 67 +++ .../sql-functions/window-functions/lag.adoc | 143 ++++++ .../window-functions/last-value.adoc | 98 ++++ .../sql-functions/window-functions/lead.adoc | 143 ++++++ .../sql-functions/window-functions/max.adoc | 96 ++++ .../sql-functions/window-functions/min.adoc | 96 ++++ .../window-functions/nth-value.adoc | 100 ++++ .../sql-functions/window-functions/ntile.adoc | 95 ++++ .../window-functions/percent-rank.adoc | 128 +++++ .../sql-functions/window-functions/rank.adoc | 117 +++++ .../window-functions/row-number.adoc | 146 ++++++ .../sql-functions/window-functions/sum.adoc | 191 +++++++ .../sql/sql-operators/bitwise-shift-left.adoc | 97 ++++ .../sql-operators/bitwise-shift-right.adoc | 98 ++++ .../pages/sql/sql-operators/index.adoc | 113 +++++ .../alter-redpanda-catalog.adoc | 27 + .../sql/sql-statements/alter-storage.adoc | 27 + .../pages/sql/sql-statements/alter-table.adoc | 28 ++ .../pages/sql/sql-statements/copy-to.adoc | 202 ++++++++ .../create-redpanda-catalog.adoc | 93 ++++ .../sql/sql-statements/create-storage.adoc | 53 ++ .../sql/sql-statements/create-table.adoc | 105 ++++ .../pages/sql/sql-statements/describe.adoc | 128 +++++ .../sql-statements/drop-redpanda-catalog.adoc | 32 ++ .../sql/sql-statements/drop-storage.adoc | 22 + .../pages/sql/sql-statements/drop-table.adoc | 28 ++ .../pages/sql/sql-statements/index.adoc | 30 ++ .../pages/sql/sql-statements/keywords.adoc | 472 ++++++++++++++++++ .../pages/sql/sql-statements/select.adoc | 128 +++++ .../pages/sql/sql-statements/set-show.adoc | 156 ++++++ .../pages/sql/sql-statements/show-execs.adoc | 19 + .../pages/sql/sql-statements/show-nodes.adoc | 46 ++ .../pages/sql/sql-statements/show-tables.adoc | 52 ++ .../system-catalogs/catalogs/pg_attrdef.adoc | 23 + .../catalogs/pg_attribute.adoc | 183 +++++++ .../system-catalogs/catalogs/pg_authid.adoc | 38 ++ .../system-catalogs/catalogs/pg_class.adoc | 88 ++++ .../catalogs/pg_constraint.adoc | 45 ++ .../system-catalogs/catalogs/pg_database.adoc | 38 ++ .../system-catalogs/catalogs/pg_depend.adoc | 26 + .../catalogs/pg_description.adoc | 18 + .../system-catalogs/catalogs/pg_index.adoc | 35 ++ .../system-catalogs/catalogs/pg_language.adoc | 40 ++ .../catalogs/pg_namespace.adoc | 64 +++ .../sql/system-catalogs/catalogs/pg_proc.adoc | 51 ++ .../system-catalogs/catalogs/pg_roles.adoc | 39 ++ .../system-catalogs/catalogs/pg_settings.adoc | 49 ++ .../system-catalogs/catalogs/pg_shadow.adoc | 35 ++ .../catalogs/pg_statio_user_tables.adoc | 56 +++ .../sql/system-catalogs/catalogs/pg_type.adoc | 83 +++ .../sql/system-catalogs/catalogs/pg_user.adoc | 35 ++ .../pages/sql/system-catalogs/index.adoc | 41 ++ .../pages/sql/system-virtual-tables.adoc | 49 ++ modules/reference/pages/sql/transactions.adoc | 113 +++++ modules/sql/pages/connect-to-sql/index.adoc | 3 + .../language-clients/dotnet-dapper.adoc | 71 +++ .../language-clients/java-jdbc.adoc | 84 ++++ .../language-clients/php-pdo.adoc | 58 +++ .../language-clients/psycopg2.adoc | 266 ++++++++++ .../sql/pages/get-started/oltp-vs-olap.adoc | 78 +++ .../redpanda-sql-vs-postgresql.adoc | 216 ++++++++ .../get-started/what-is-redpanda-sql.adoc | 62 +++ modules/sql/pages/index.adoc | 3 + .../troubleshoot/degraded-state-handling.adoc | 55 ++ modules/sql/pages/troubleshoot/index.adoc | 3 + 235 files changed, 22539 insertions(+) create mode 100644 modules/reference/images/sql/join-venn.png create mode 100644 modules/reference/images/sql/left-join-venn.png create mode 100644 modules/reference/images/sql/outer-join-venn.png create mode 100644 modules/reference/images/sql/outer-join-where-venn.png create mode 100644 modules/reference/images/sql/right-join-venn.png create mode 100644 modules/reference/pages/sql/comment-support.adoc create mode 100644 modules/reference/pages/sql/index.adoc create mode 100644 modules/reference/pages/sql/redpanda-catalogs.adoc create mode 100644 modules/reference/pages/sql/schema.adoc create mode 100644 modules/reference/pages/sql/sql-clauses/from/from.adoc create mode 100644 modules/reference/pages/sql/sql-clauses/from/index.adoc create mode 100644 modules/reference/pages/sql/sql-clauses/from/join.adoc create mode 100644 modules/reference/pages/sql/sql-clauses/from/left-join.adoc create mode 100644 modules/reference/pages/sql/sql-clauses/from/outer-join.adoc create mode 100644 modules/reference/pages/sql/sql-clauses/from/right-join.adoc create mode 100644 modules/reference/pages/sql/sql-clauses/group-by.adoc create mode 100644 modules/reference/pages/sql/sql-clauses/having.adoc create mode 100644 modules/reference/pages/sql/sql-clauses/index.adoc create mode 100644 modules/reference/pages/sql/sql-clauses/limit.adoc create mode 100644 modules/reference/pages/sql/sql-clauses/offset.adoc create mode 100644 modules/reference/pages/sql/sql-clauses/order-by.adoc create mode 100644 modules/reference/pages/sql/sql-clauses/over-window.adoc create mode 100644 modules/reference/pages/sql/sql-clauses/set-operations/except.adoc create mode 100644 modules/reference/pages/sql/sql-clauses/set-operations/index.adoc create mode 100644 modules/reference/pages/sql/sql-clauses/set-operations/intersect.adoc create mode 100644 modules/reference/pages/sql/sql-clauses/set-operations/union.adoc create mode 100644 modules/reference/pages/sql/sql-clauses/where.adoc create mode 100644 modules/reference/pages/sql/sql-clauses/with.adoc create mode 100644 modules/reference/pages/sql/sql-data-types/array.adoc create mode 100644 modules/reference/pages/sql/sql-data-types/bool.adoc create mode 100644 modules/reference/pages/sql/sql-data-types/date.adoc create mode 100644 modules/reference/pages/sql/sql-data-types/geography.adoc create mode 100644 modules/reference/pages/sql/sql-data-types/geometry.adoc create mode 100644 modules/reference/pages/sql/sql-data-types/index.adoc create mode 100644 modules/reference/pages/sql/sql-data-types/interval.adoc create mode 100644 modules/reference/pages/sql/sql-data-types/json.adoc create mode 100644 modules/reference/pages/sql/sql-data-types/numeric-type/index.adoc create mode 100644 modules/reference/pages/sql/sql-data-types/numeric-type/numeric-data-type-aliases.adoc create mode 100644 modules/reference/pages/sql/sql-data-types/numeric-type/numeric.adoc create mode 100644 modules/reference/pages/sql/sql-data-types/row.adoc create mode 100644 modules/reference/pages/sql/sql-data-types/text.adoc create mode 100644 modules/reference/pages/sql/sql-data-types/time-type/index.adoc create mode 100644 modules/reference/pages/sql/sql-data-types/time-type/time-operators.adoc create mode 100644 modules/reference/pages/sql/sql-data-types/time-type/time.adoc create mode 100644 modules/reference/pages/sql/sql-data-types/timestamp-with-time-zone.adoc create mode 100644 modules/reference/pages/sql/sql-data-types/timestamp-without-time-zone.adoc create mode 100644 modules/reference/pages/sql/sql-functions/aggregate-functions/avg.adoc create mode 100644 modules/reference/pages/sql/sql-functions/aggregate-functions/bool-and.adoc create mode 100644 modules/reference/pages/sql/sql-functions/aggregate-functions/bool-or.adoc create mode 100644 modules/reference/pages/sql/sql-functions/aggregate-functions/count.adoc create mode 100644 modules/reference/pages/sql/sql-functions/aggregate-functions/distinct.adoc create mode 100644 modules/reference/pages/sql/sql-functions/aggregate-functions/for-max.adoc create mode 100644 modules/reference/pages/sql/sql-functions/aggregate-functions/for-min.adoc create mode 100644 modules/reference/pages/sql/sql-functions/aggregate-functions/index.adoc create mode 100644 modules/reference/pages/sql/sql-functions/aggregate-functions/max.adoc create mode 100644 modules/reference/pages/sql/sql-functions/aggregate-functions/min.adoc create mode 100644 modules/reference/pages/sql/sql-functions/aggregate-functions/ordered-set-aggregate-functions/index.adoc create mode 100644 modules/reference/pages/sql/sql-functions/aggregate-functions/ordered-set-aggregate-functions/mode.adoc create mode 100644 modules/reference/pages/sql/sql-functions/aggregate-functions/ordered-set-aggregate-functions/percentile-cont.adoc create mode 100644 modules/reference/pages/sql/sql-functions/aggregate-functions/ordered-set-aggregate-functions/percentile-disc.adoc create mode 100644 modules/reference/pages/sql/sql-functions/aggregate-functions/statistics/corr.adoc create mode 100644 modules/reference/pages/sql/sql-functions/aggregate-functions/statistics/covar-pop.adoc create mode 100644 modules/reference/pages/sql/sql-functions/aggregate-functions/statistics/covar-samp.adoc create mode 100644 modules/reference/pages/sql/sql-functions/aggregate-functions/statistics/index.adoc create mode 100644 modules/reference/pages/sql/sql-functions/aggregate-functions/statistics/regr-avgx.adoc create mode 100644 modules/reference/pages/sql/sql-functions/aggregate-functions/statistics/regr-avgy.adoc create mode 100644 modules/reference/pages/sql/sql-functions/aggregate-functions/statistics/regr-count.adoc create mode 100644 modules/reference/pages/sql/sql-functions/aggregate-functions/statistics/regr-intercept.adoc create mode 100644 modules/reference/pages/sql/sql-functions/aggregate-functions/statistics/regr-r2.adoc create mode 100644 modules/reference/pages/sql/sql-functions/aggregate-functions/statistics/regr-slope.adoc create mode 100644 modules/reference/pages/sql/sql-functions/aggregate-functions/statistics/regr-sxx.adoc create mode 100644 modules/reference/pages/sql/sql-functions/aggregate-functions/statistics/regr-sxy.adoc create mode 100644 modules/reference/pages/sql/sql-functions/aggregate-functions/statistics/regr-syy.adoc create mode 100644 modules/reference/pages/sql/sql-functions/aggregate-functions/statistics/stddev-pop.adoc create mode 100644 modules/reference/pages/sql/sql-functions/aggregate-functions/statistics/stddev-samp.adoc create mode 100644 modules/reference/pages/sql/sql-functions/aggregate-functions/statistics/stddev.adoc create mode 100644 modules/reference/pages/sql/sql-functions/aggregate-functions/statistics/var-pop.adoc create mode 100644 modules/reference/pages/sql/sql-functions/aggregate-functions/statistics/var-samp.adoc create mode 100644 modules/reference/pages/sql/sql-functions/aggregate-functions/statistics/variance.adoc create mode 100644 modules/reference/pages/sql/sql-functions/aggregate-functions/sum.adoc create mode 100644 modules/reference/pages/sql/sql-functions/boolean-functions/if-function.adoc create mode 100644 modules/reference/pages/sql/sql-functions/boolean-functions/index.adoc create mode 100644 modules/reference/pages/sql/sql-functions/boolean-functions/is-distinct-from-operator.adoc create mode 100644 modules/reference/pages/sql/sql-functions/boolean-functions/is-not-distinct-from-operator.adoc create mode 100644 modules/reference/pages/sql/sql-functions/index.adoc create mode 100644 modules/reference/pages/sql/sql-functions/json-functions/index.adoc create mode 100644 modules/reference/pages/sql/sql-functions/json-functions/json-array-extract.adoc create mode 100644 modules/reference/pages/sql/sql-functions/json-functions/json-array-length.adoc create mode 100644 modules/reference/pages/sql/sql-functions/json-functions/json-extract-path-text.adoc create mode 100644 modules/reference/pages/sql/sql-functions/json-functions/json-extract-path.adoc create mode 100644 modules/reference/pages/sql/sql-functions/math-functions/abs.adoc create mode 100644 modules/reference/pages/sql/sql-functions/math-functions/cbrt.adoc create mode 100644 modules/reference/pages/sql/sql-functions/math-functions/ceil.adoc create mode 100644 modules/reference/pages/sql/sql-functions/math-functions/cosh.adoc create mode 100644 modules/reference/pages/sql/sql-functions/math-functions/exp.adoc create mode 100644 modules/reference/pages/sql/sql-functions/math-functions/floor.adoc create mode 100644 modules/reference/pages/sql/sql-functions/math-functions/greatest.adoc create mode 100644 modules/reference/pages/sql/sql-functions/math-functions/index.adoc create mode 100644 modules/reference/pages/sql/sql-functions/math-functions/least.adoc create mode 100644 modules/reference/pages/sql/sql-functions/math-functions/ln.adoc create mode 100644 modules/reference/pages/sql/sql-functions/math-functions/log.adoc create mode 100644 modules/reference/pages/sql/sql-functions/math-functions/power.adoc create mode 100644 modules/reference/pages/sql/sql-functions/math-functions/random.adoc create mode 100644 modules/reference/pages/sql/sql-functions/math-functions/round.adoc create mode 100644 modules/reference/pages/sql/sql-functions/math-functions/sign.adoc create mode 100644 modules/reference/pages/sql/sql-functions/math-functions/sin.adoc create mode 100644 modules/reference/pages/sql/sql-functions/math-functions/sinh.adoc create mode 100644 modules/reference/pages/sql/sql-functions/math-functions/sqrt.adoc create mode 100644 modules/reference/pages/sql/sql-functions/math-functions/to-char-from-number.adoc create mode 100644 modules/reference/pages/sql/sql-functions/other-functions/coalesce.adoc create mode 100644 modules/reference/pages/sql/sql-functions/other-functions/col-description.adoc create mode 100644 modules/reference/pages/sql/sql-functions/other-functions/current-database.adoc create mode 100644 modules/reference/pages/sql/sql-functions/other-functions/current-schema.adoc create mode 100644 modules/reference/pages/sql/sql-functions/other-functions/generate-series.adoc create mode 100644 modules/reference/pages/sql/sql-functions/other-functions/has-schema-privilege.adoc create mode 100644 modules/reference/pages/sql/sql-functions/other-functions/index.adoc create mode 100644 modules/reference/pages/sql/sql-functions/other-functions/nullif.adoc create mode 100644 modules/reference/pages/sql/sql-functions/other-functions/obj-description.adoc create mode 100644 modules/reference/pages/sql/sql-functions/other-functions/pg-backend-pid.adoc create mode 100644 modules/reference/pages/sql/sql-functions/other-functions/pg-encoding-to-char.adoc create mode 100644 modules/reference/pages/sql/sql-functions/other-functions/pg-get-constraintdef.adoc create mode 100644 modules/reference/pages/sql/sql-functions/other-functions/pg-get-expr.adoc create mode 100644 modules/reference/pages/sql/sql-functions/other-functions/pg-get-indexdef.adoc create mode 100644 modules/reference/pages/sql/sql-functions/other-functions/pg-get-statisticsobjdef-columns.adoc create mode 100644 modules/reference/pages/sql/sql-functions/other-functions/pg-get-userbyid.adoc create mode 100644 modules/reference/pages/sql/sql-functions/other-functions/pg-relation-is-publishable.adoc create mode 100644 modules/reference/pages/sql/sql-functions/other-functions/pg-size-pretty.adoc create mode 100644 modules/reference/pages/sql/sql-functions/other-functions/pg-table-is-visible.adoc create mode 100644 modules/reference/pages/sql/sql-functions/other-functions/pg-table-size.adoc create mode 100644 modules/reference/pages/sql/sql-functions/other-functions/pg-total-relation-size.adoc create mode 100644 modules/reference/pages/sql/sql-functions/other-functions/pg-typeof.adoc create mode 100644 modules/reference/pages/sql/sql-functions/other-functions/shobj-description.adoc create mode 100644 modules/reference/pages/sql/sql-functions/string-functions/concat.adoc create mode 100644 modules/reference/pages/sql/sql-functions/string-functions/ends-with.adoc create mode 100644 modules/reference/pages/sql/sql-functions/string-functions/index.adoc create mode 100644 modules/reference/pages/sql/sql-functions/string-functions/length.adoc create mode 100644 modules/reference/pages/sql/sql-functions/string-functions/lower.adoc create mode 100644 modules/reference/pages/sql/sql-functions/string-functions/position.adoc create mode 100644 modules/reference/pages/sql/sql-functions/string-functions/regex/index.adoc create mode 100644 modules/reference/pages/sql/sql-functions/string-functions/regex/posix-regular-expressions.adoc create mode 100644 modules/reference/pages/sql/sql-functions/string-functions/regex/regexp-match.adoc create mode 100644 modules/reference/pages/sql/sql-functions/string-functions/regex/regexp-replace.adoc create mode 100644 modules/reference/pages/sql/sql-functions/string-functions/replace.adoc create mode 100644 modules/reference/pages/sql/sql-functions/string-functions/starts-with.adoc create mode 100644 modules/reference/pages/sql/sql-functions/string-functions/strpos.adoc create mode 100644 modules/reference/pages/sql/sql-functions/string-functions/substr.adoc create mode 100644 modules/reference/pages/sql/sql-functions/string-functions/substring.adoc create mode 100644 modules/reference/pages/sql/sql-functions/string-functions/upper.adoc create mode 100644 modules/reference/pages/sql/sql-functions/timestamp-functions/current-timestamp.adoc create mode 100644 modules/reference/pages/sql/sql-functions/timestamp-functions/date-trunc.adoc create mode 100644 modules/reference/pages/sql/sql-functions/timestamp-functions/extract.adoc create mode 100644 modules/reference/pages/sql/sql-functions/timestamp-functions/format-timestamp.adoc create mode 100644 modules/reference/pages/sql/sql-functions/timestamp-functions/index.adoc create mode 100644 modules/reference/pages/sql/sql-functions/timestamp-functions/timestamp-micros.adoc create mode 100644 modules/reference/pages/sql/sql-functions/timestamp-functions/timestamp-millis.adoc create mode 100644 modules/reference/pages/sql/sql-functions/timestamp-functions/timestamp-seconds.adoc create mode 100644 modules/reference/pages/sql/sql-functions/timestamp-functions/timestamp-trunc.adoc create mode 100644 modules/reference/pages/sql/sql-functions/timestamp-functions/to-char.adoc create mode 100644 modules/reference/pages/sql/sql-functions/timestamp-functions/to-timestamp.adoc create mode 100644 modules/reference/pages/sql/sql-functions/timestamp-functions/unix-micros.adoc create mode 100644 modules/reference/pages/sql/sql-functions/timestamp-functions/unix-millis.adoc create mode 100644 modules/reference/pages/sql/sql-functions/timestamp-functions/unix-seconds.adoc create mode 100644 modules/reference/pages/sql/sql-functions/window-functions/avg.adoc create mode 100644 modules/reference/pages/sql/sql-functions/window-functions/bool-and.adoc create mode 100644 modules/reference/pages/sql/sql-functions/window-functions/bool-or.adoc create mode 100644 modules/reference/pages/sql/sql-functions/window-functions/count.adoc create mode 100644 modules/reference/pages/sql/sql-functions/window-functions/cume-dist.adoc create mode 100644 modules/reference/pages/sql/sql-functions/window-functions/dense-rank.adoc create mode 100644 modules/reference/pages/sql/sql-functions/window-functions/first-value.adoc create mode 100644 modules/reference/pages/sql/sql-functions/window-functions/index.adoc create mode 100644 modules/reference/pages/sql/sql-functions/window-functions/lag.adoc create mode 100644 modules/reference/pages/sql/sql-functions/window-functions/last-value.adoc create mode 100644 modules/reference/pages/sql/sql-functions/window-functions/lead.adoc create mode 100644 modules/reference/pages/sql/sql-functions/window-functions/max.adoc create mode 100644 modules/reference/pages/sql/sql-functions/window-functions/min.adoc create mode 100644 modules/reference/pages/sql/sql-functions/window-functions/nth-value.adoc create mode 100644 modules/reference/pages/sql/sql-functions/window-functions/ntile.adoc create mode 100644 modules/reference/pages/sql/sql-functions/window-functions/percent-rank.adoc create mode 100644 modules/reference/pages/sql/sql-functions/window-functions/rank.adoc create mode 100644 modules/reference/pages/sql/sql-functions/window-functions/row-number.adoc create mode 100644 modules/reference/pages/sql/sql-functions/window-functions/sum.adoc create mode 100644 modules/reference/pages/sql/sql-operators/bitwise-shift-left.adoc create mode 100644 modules/reference/pages/sql/sql-operators/bitwise-shift-right.adoc create mode 100644 modules/reference/pages/sql/sql-operators/index.adoc create mode 100644 modules/reference/pages/sql/sql-statements/alter-redpanda-catalog.adoc create mode 100644 modules/reference/pages/sql/sql-statements/alter-storage.adoc create mode 100644 modules/reference/pages/sql/sql-statements/alter-table.adoc create mode 100644 modules/reference/pages/sql/sql-statements/copy-to.adoc create mode 100644 modules/reference/pages/sql/sql-statements/create-redpanda-catalog.adoc create mode 100644 modules/reference/pages/sql/sql-statements/create-storage.adoc create mode 100644 modules/reference/pages/sql/sql-statements/create-table.adoc create mode 100644 modules/reference/pages/sql/sql-statements/describe.adoc create mode 100644 modules/reference/pages/sql/sql-statements/drop-redpanda-catalog.adoc create mode 100644 modules/reference/pages/sql/sql-statements/drop-storage.adoc create mode 100644 modules/reference/pages/sql/sql-statements/drop-table.adoc create mode 100644 modules/reference/pages/sql/sql-statements/index.adoc create mode 100644 modules/reference/pages/sql/sql-statements/keywords.adoc create mode 100644 modules/reference/pages/sql/sql-statements/select.adoc create mode 100644 modules/reference/pages/sql/sql-statements/set-show.adoc create mode 100644 modules/reference/pages/sql/sql-statements/show-execs.adoc create mode 100644 modules/reference/pages/sql/sql-statements/show-nodes.adoc create mode 100644 modules/reference/pages/sql/sql-statements/show-tables.adoc create mode 100644 modules/reference/pages/sql/system-catalogs/catalogs/pg_attrdef.adoc create mode 100644 modules/reference/pages/sql/system-catalogs/catalogs/pg_attribute.adoc create mode 100644 modules/reference/pages/sql/system-catalogs/catalogs/pg_authid.adoc create mode 100644 modules/reference/pages/sql/system-catalogs/catalogs/pg_class.adoc create mode 100644 modules/reference/pages/sql/system-catalogs/catalogs/pg_constraint.adoc create mode 100644 modules/reference/pages/sql/system-catalogs/catalogs/pg_database.adoc create mode 100644 modules/reference/pages/sql/system-catalogs/catalogs/pg_depend.adoc create mode 100644 modules/reference/pages/sql/system-catalogs/catalogs/pg_description.adoc create mode 100644 modules/reference/pages/sql/system-catalogs/catalogs/pg_index.adoc create mode 100644 modules/reference/pages/sql/system-catalogs/catalogs/pg_language.adoc create mode 100644 modules/reference/pages/sql/system-catalogs/catalogs/pg_namespace.adoc create mode 100644 modules/reference/pages/sql/system-catalogs/catalogs/pg_proc.adoc create mode 100644 modules/reference/pages/sql/system-catalogs/catalogs/pg_roles.adoc create mode 100644 modules/reference/pages/sql/system-catalogs/catalogs/pg_settings.adoc create mode 100644 modules/reference/pages/sql/system-catalogs/catalogs/pg_shadow.adoc create mode 100644 modules/reference/pages/sql/system-catalogs/catalogs/pg_statio_user_tables.adoc create mode 100644 modules/reference/pages/sql/system-catalogs/catalogs/pg_type.adoc create mode 100644 modules/reference/pages/sql/system-catalogs/catalogs/pg_user.adoc create mode 100644 modules/reference/pages/sql/system-catalogs/index.adoc create mode 100644 modules/reference/pages/sql/system-virtual-tables.adoc create mode 100644 modules/reference/pages/sql/transactions.adoc create mode 100644 modules/sql/pages/connect-to-sql/index.adoc create mode 100644 modules/sql/pages/connect-to-sql/language-clients/dotnet-dapper.adoc create mode 100644 modules/sql/pages/connect-to-sql/language-clients/java-jdbc.adoc create mode 100644 modules/sql/pages/connect-to-sql/language-clients/php-pdo.adoc create mode 100644 modules/sql/pages/connect-to-sql/language-clients/psycopg2.adoc create mode 100644 modules/sql/pages/get-started/oltp-vs-olap.adoc create mode 100644 modules/sql/pages/get-started/redpanda-sql-vs-postgresql.adoc create mode 100644 modules/sql/pages/get-started/what-is-redpanda-sql.adoc create mode 100644 modules/sql/pages/index.adoc create mode 100644 modules/sql/pages/troubleshoot/degraded-state-handling.adoc create mode 100644 modules/sql/pages/troubleshoot/index.adoc diff --git a/modules/ROOT/nav.adoc b/modules/ROOT/nav.adoc index 65d6f4073..fc68a8f5f 100644 --- a/modules/ROOT/nav.adoc +++ b/modules/ROOT/nav.adoc @@ -334,6 +334,19 @@ *** xref:develop:connect/cookbooks/rag.adoc[] *** xref:develop:connect/cookbooks/jira.adoc[] +* xref:sql:index.adoc[Redpanda SQL] +// ** quickstart.adoc +** xref:sql:get-started/what-is-redpanda-sql.adoc[Overview] +*** xref:sql:get-started/oltp-vs-olap.adoc[] +*** xref:sql:get-started/redpanda-sql-vs-postgresql.adoc[] +** xref:sql:connect-to-sql/index.adoc[Connect to Redpanda SQL] +*** xref:sql:connect-to-sql/language-clients/psycopg2.adoc[] +*** xref:sql:connect-to-sql/language-clients/java-jdbc.adoc[] +*** xref:sql:connect-to-sql/language-clients/php-pdo.adoc[] +*** xref:sql:connect-to-sql/language-clients/dotnet-dapper.adoc[] +** xref:sql:troubleshoot/index.adoc[Troubleshoot] +*** xref:sql:troubleshoot/degraded-state-handling.adoc[] + * xref:develop:index.adoc[Develop] ** xref:develop:kafka-clients.adoc[] ** xref:develop:topics/index.adoc[Topics] @@ -501,6 +514,225 @@ * xref:get-started:partner-integration.adoc[] * xref:reference:index.adoc[Reference] +** xref:reference:sql/index.adoc[Redpanda SQL Reference] +*** xref:reference:sql/redpanda-catalogs.adoc[] +*** xref:reference:sql/sql-statements/index.adoc[Statements] +**** xref:reference:sql/sql-statements/keywords.adoc[] +**** xref:reference:sql/sql-statements/alter-redpanda-catalog.adoc[] +**** xref:reference:sql/sql-statements/alter-storage.adoc[] +**** xref:reference:sql/sql-statements/alter-table.adoc[] +**** xref:reference:sql/sql-statements/create-redpanda-catalog.adoc[] +**** xref:reference:sql/sql-statements/create-storage.adoc[] +**** xref:reference:sql/sql-statements/create-table.adoc[] +**** xref:reference:sql/sql-statements/drop-redpanda-catalog.adoc[] +**** xref:reference:sql/sql-statements/drop-storage.adoc[] +**** xref:reference:sql/sql-statements/drop-table.adoc[] +**** xref:reference:sql/sql-statements/select.adoc[] +**** xref:reference:sql/sql-statements/copy-to.adoc[] +**** xref:reference:sql/sql-statements/describe.adoc[] +**** xref:reference:sql/sql-statements/set-show.adoc[] +**** xref:reference:sql/sql-statements/show-tables.adoc[] +**** xref:reference:sql/sql-statements/show-execs.adoc[] +**** xref:reference:sql/sql-statements/show-nodes.adoc[] +*** xref:reference:sql/sql-clauses/index.adoc[Clauses] +**** xref:reference:sql/sql-clauses/from/index.adoc[FROM] +***** xref:reference:sql/sql-clauses/from/from.adoc[] +***** xref:reference:sql/sql-clauses/from/join.adoc[] +***** xref:reference:sql/sql-clauses/from/left-join.adoc[] +***** xref:reference:sql/sql-clauses/from/outer-join.adoc[] +***** xref:reference:sql/sql-clauses/from/right-join.adoc[] +**** xref:reference:sql/sql-clauses/where.adoc[] +**** xref:reference:sql/sql-clauses/group-by.adoc[] +**** xref:reference:sql/sql-clauses/having.adoc[] +**** xref:reference:sql/sql-clauses/order-by.adoc[] +**** xref:reference:sql/sql-clauses/limit.adoc[] +**** xref:reference:sql/sql-clauses/offset.adoc[] +**** xref:reference:sql/sql-clauses/set-operations/index.adoc[Set Operations] +***** xref:reference:sql/sql-clauses/set-operations/except.adoc[] +***** xref:reference:sql/sql-clauses/set-operations/intersect.adoc[] +***** xref:reference:sql/sql-clauses/set-operations/union.adoc[] +**** xref:reference:sql/sql-clauses/with.adoc[] +**** xref:reference:sql/sql-clauses/over-window.adoc[] +*** xref:reference:sql/sql-data-types/index.adoc[Data Types] +**** xref:reference:sql/sql-data-types/numeric-type/index.adoc[Numeric] +***** xref:reference:sql/sql-data-types/numeric-type/numeric.adoc[] +***** xref:reference:sql/sql-data-types/numeric-type/numeric-data-type-aliases.adoc[] +**** xref:reference:sql/sql-data-types/timestamp-without-time-zone.adoc[] +**** xref:reference:sql/sql-data-types/timestamp-with-time-zone.adoc[] +**** xref:reference:sql/sql-data-types/date.adoc[] +**** xref:reference:sql/sql-data-types/time-type/index.adoc[Time] +***** xref:reference:sql/sql-data-types/time-type/time.adoc[] +***** xref:reference:sql/sql-data-types/time-type/time-operators.adoc[] +**** xref:reference:sql/sql-data-types/interval.adoc[] +**** xref:reference:sql/sql-data-types/bool.adoc[] +**** xref:reference:sql/sql-data-types/text.adoc[] +**** xref:reference:sql/sql-data-types/json.adoc[] +**** xref:reference:sql/sql-data-types/array.adoc[] +**** xref:reference:sql/sql-data-types/row.adoc[] +**** xref:reference:sql/sql-data-types/geometry.adoc[] +**** xref:reference:sql/sql-data-types/geography.adoc[] +*** xref:reference:sql/sql-functions/index.adoc[Functions] +**** xref:reference:sql/sql-functions/boolean-functions/index.adoc[Boolean] +***** xref:reference:sql/sql-functions/boolean-functions/if-function.adoc[] +***** xref:reference:sql/sql-functions/boolean-functions/is-distinct-from-operator.adoc[] +***** xref:reference:sql/sql-functions/boolean-functions/is-not-distinct-from-operator.adoc[] +**** xref:reference:sql/sql-functions/math-functions/index.adoc[Math] +***** xref:reference:sql/sql-functions/math-functions/abs.adoc[] +***** xref:reference:sql/sql-functions/math-functions/cbrt.adoc[] +***** xref:reference:sql/sql-functions/math-functions/ceil.adoc[] +***** xref:reference:sql/sql-functions/math-functions/exp.adoc[] +***** xref:reference:sql/sql-functions/math-functions/floor.adoc[] +***** xref:reference:sql/sql-functions/math-functions/greatest.adoc[] +***** xref:reference:sql/sql-functions/math-functions/least.adoc[] +***** xref:reference:sql/sql-functions/math-functions/ln.adoc[] +***** xref:reference:sql/sql-functions/math-functions/log.adoc[] +***** xref:reference:sql/sql-functions/math-functions/power.adoc[] +***** xref:reference:sql/sql-functions/math-functions/random.adoc[] +***** xref:reference:sql/sql-functions/math-functions/round.adoc[] +***** xref:reference:sql/sql-functions/math-functions/sign.adoc[] +***** xref:reference:sql/sql-functions/math-functions/sin.adoc[] +***** xref:reference:sql/sql-functions/math-functions/sinh.adoc[] +***** xref:reference:sql/sql-functions/math-functions/cosh.adoc[] +***** xref:reference:sql/sql-functions/math-functions/sqrt.adoc[] +***** xref:reference:sql/sql-functions/math-functions/to-char-from-number.adoc[] +**** xref:reference:sql/sql-functions/string-functions/index.adoc[String] +***** xref:reference:sql/sql-functions/string-functions/concat.adoc[] +***** xref:reference:sql/sql-functions/string-functions/ends-with.adoc[] +***** xref:reference:sql/sql-functions/string-functions/length.adoc[] +***** xref:reference:sql/sql-functions/string-functions/lower.adoc[] +***** xref:reference:sql/sql-functions/string-functions/position.adoc[] +***** xref:reference:sql/sql-functions/string-functions/replace.adoc[] +***** xref:reference:sql/sql-functions/string-functions/starts-with.adoc[] +***** xref:reference:sql/sql-functions/string-functions/strpos.adoc[] +***** xref:reference:sql/sql-functions/string-functions/substr.adoc[] +***** xref:reference:sql/sql-functions/string-functions/substring.adoc[] +***** xref:reference:sql/sql-functions/string-functions/upper.adoc[] +***** xref:reference:sql/sql-functions/string-functions/regex/index.adoc[Regex] +****** xref:reference:sql/sql-functions/string-functions/regex/regexp-replace.adoc[] +****** xref:reference:sql/sql-functions/string-functions/regex/regexp-match.adoc[] +****** xref:reference:sql/sql-functions/string-functions/regex/posix-regular-expressions.adoc[] +**** xref:reference:sql/sql-functions/timestamp-functions/index.adoc[Timestamp] +***** xref:reference:sql/sql-functions/timestamp-functions/current-timestamp.adoc[] +***** xref:reference:sql/sql-functions/timestamp-functions/date-trunc.adoc[] +***** xref:reference:sql/sql-functions/timestamp-functions/extract.adoc[] +***** xref:reference:sql/sql-functions/timestamp-functions/format-timestamp.adoc[] +***** xref:reference:sql/sql-functions/timestamp-functions/timestamp-micros.adoc[] +***** xref:reference:sql/sql-functions/timestamp-functions/timestamp-millis.adoc[] +***** xref:reference:sql/sql-functions/timestamp-functions/timestamp-seconds.adoc[] +***** xref:reference:sql/sql-functions/timestamp-functions/timestamp-trunc.adoc[] +***** xref:reference:sql/sql-functions/timestamp-functions/to-char.adoc[] +***** xref:reference:sql/sql-functions/timestamp-functions/to-timestamp.adoc[] +***** xref:reference:sql/sql-functions/timestamp-functions/unix-micros.adoc[] +***** xref:reference:sql/sql-functions/timestamp-functions/unix-millis.adoc[] +***** xref:reference:sql/sql-functions/timestamp-functions/unix-seconds.adoc[] +**** xref:reference:sql/sql-functions/json-functions/index.adoc[JSON] +***** xref:reference:sql/sql-functions/json-functions/json-array-extract.adoc[] +***** xref:reference:sql/sql-functions/json-functions/json-array-length.adoc[] +***** xref:reference:sql/sql-functions/json-functions/json-extract-path.adoc[] +***** xref:reference:sql/sql-functions/json-functions/json-extract-path-text.adoc[] +**** xref:reference:sql/sql-functions/aggregate-functions/index.adoc[Aggregate] +***** xref:reference:sql/sql-functions/aggregate-functions/avg.adoc[] +***** xref:reference:sql/sql-functions/aggregate-functions/bool-and.adoc[] +***** xref:reference:sql/sql-functions/aggregate-functions/bool-or.adoc[] +***** xref:reference:sql/sql-functions/aggregate-functions/count.adoc[] +***** xref:reference:sql/sql-functions/aggregate-functions/distinct.adoc[] +***** xref:reference:sql/sql-functions/aggregate-functions/for-max.adoc[] +***** xref:reference:sql/sql-functions/aggregate-functions/for-min.adoc[] +***** xref:reference:sql/sql-functions/aggregate-functions/max.adoc[] +***** xref:reference:sql/sql-functions/aggregate-functions/min.adoc[] +***** xref:reference:sql/sql-functions/aggregate-functions/sum.adoc[] +***** xref:reference:sql/sql-functions/aggregate-functions/ordered-set-aggregate-functions/index.adoc[Ordered-Set] +****** xref:reference:sql/sql-functions/aggregate-functions/ordered-set-aggregate-functions/mode.adoc[] +****** xref:reference:sql/sql-functions/aggregate-functions/ordered-set-aggregate-functions/percentile-cont.adoc[] +****** xref:reference:sql/sql-functions/aggregate-functions/ordered-set-aggregate-functions/percentile-disc.adoc[] +***** xref:reference:sql/sql-functions/aggregate-functions/statistics/index.adoc[Statistics] +****** xref:reference:sql/sql-functions/aggregate-functions/statistics/corr.adoc[] +****** xref:reference:sql/sql-functions/aggregate-functions/statistics/covar-pop.adoc[] +****** xref:reference:sql/sql-functions/aggregate-functions/statistics/covar-samp.adoc[] +****** xref:reference:sql/sql-functions/aggregate-functions/statistics/regr-avgx.adoc[] +****** xref:reference:sql/sql-functions/aggregate-functions/statistics/regr-avgy.adoc[] +****** xref:reference:sql/sql-functions/aggregate-functions/statistics/regr-count.adoc[] +****** xref:reference:sql/sql-functions/aggregate-functions/statistics/regr-intercept.adoc[] +****** xref:reference:sql/sql-functions/aggregate-functions/statistics/regr-r2.adoc[] +****** xref:reference:sql/sql-functions/aggregate-functions/statistics/regr-slope.adoc[] +****** xref:reference:sql/sql-functions/aggregate-functions/statistics/regr-sxx.adoc[] +****** xref:reference:sql/sql-functions/aggregate-functions/statistics/regr-sxy.adoc[] +****** xref:reference:sql/sql-functions/aggregate-functions/statistics/regr-syy.adoc[] +****** xref:reference:sql/sql-functions/aggregate-functions/statistics/stddev.adoc[] +****** xref:reference:sql/sql-functions/aggregate-functions/statistics/stddev-pop.adoc[] +****** xref:reference:sql/sql-functions/aggregate-functions/statistics/stddev-samp.adoc[] +****** xref:reference:sql/sql-functions/aggregate-functions/statistics/variance.adoc[] +****** xref:reference:sql/sql-functions/aggregate-functions/statistics/var-pop.adoc[] +****** xref:reference:sql/sql-functions/aggregate-functions/statistics/var-samp.adoc[] +**** xref:reference:sql/sql-functions/window-functions/index.adoc[Window] +***** xref:reference:sql/sql-functions/window-functions/avg.adoc[] +***** xref:reference:sql/sql-functions/window-functions/bool-and.adoc[] +***** xref:reference:sql/sql-functions/window-functions/bool-or.adoc[] +***** xref:reference:sql/sql-functions/window-functions/count.adoc[] +***** xref:reference:sql/sql-functions/window-functions/cume-dist.adoc[] +***** xref:reference:sql/sql-functions/window-functions/dense-rank.adoc[] +***** xref:reference:sql/sql-functions/window-functions/first-value.adoc[] +***** xref:reference:sql/sql-functions/window-functions/lag.adoc[] +***** xref:reference:sql/sql-functions/window-functions/last-value.adoc[] +***** xref:reference:sql/sql-functions/window-functions/lead.adoc[] +***** xref:reference:sql/sql-functions/window-functions/max.adoc[] +***** xref:reference:sql/sql-functions/window-functions/min.adoc[] +***** xref:reference:sql/sql-functions/window-functions/nth-value.adoc[] +***** xref:reference:sql/sql-functions/window-functions/ntile.adoc[] +***** xref:reference:sql/sql-functions/window-functions/percent-rank.adoc[] +***** xref:reference:sql/sql-functions/window-functions/rank.adoc[] +***** xref:reference:sql/sql-functions/window-functions/row-number.adoc[] +***** xref:reference:sql/sql-functions/window-functions/sum.adoc[] +**** xref:reference:sql/sql-functions/other-functions/index.adoc[Other] +***** xref:reference:sql/sql-functions/other-functions/coalesce.adoc[] +***** xref:reference:sql/sql-functions/other-functions/col-description.adoc[] +***** xref:reference:sql/sql-functions/other-functions/current-database.adoc[] +***** xref:reference:sql/sql-functions/other-functions/current-schema.adoc[] +***** xref:reference:sql/sql-functions/other-functions/has-schema-privilege.adoc[] +***** xref:reference:sql/sql-functions/other-functions/nullif.adoc[] +***** xref:reference:sql/sql-functions/other-functions/obj-description.adoc[] +***** xref:reference:sql/sql-functions/other-functions/pg-backend-pid.adoc[] +***** xref:reference:sql/sql-functions/other-functions/pg-encoding-to-char.adoc[] +***** xref:reference:sql/sql-functions/other-functions/pg-get-constraintdef.adoc[] +***** xref:reference:sql/sql-functions/other-functions/pg-get-expr.adoc[] +***** xref:reference:sql/sql-functions/other-functions/pg-get-indexdef.adoc[] +***** xref:reference:sql/sql-functions/other-functions/pg-get-statisticsobjdef-columns.adoc[] +***** xref:reference:sql/sql-functions/other-functions/pg-get-userbyid.adoc[] +***** xref:reference:sql/sql-functions/other-functions/pg-relation-is-publishable.adoc[] +***** xref:reference:sql/sql-functions/other-functions/pg-size-pretty.adoc[] +***** xref:reference:sql/sql-functions/other-functions/pg-table-is-visible.adoc[] +***** xref:reference:sql/sql-functions/other-functions/pg-table-size.adoc[] +***** xref:reference:sql/sql-functions/other-functions/pg-total-relation-size.adoc[] +***** xref:reference:sql/sql-functions/other-functions/pg-typeof.adoc[] +***** xref:reference:sql/sql-functions/other-functions/shobj-description.adoc[] +***** xref:reference:sql/sql-functions/other-functions/generate-series.adoc[] +*** xref:reference:sql/sql-operators/index.adoc[Operators] +**** xref:reference:sql/sql-operators/bitwise-shift-left.adoc[] +**** xref:reference:sql/sql-operators/bitwise-shift-right.adoc[] +*** xref:reference:sql/schema.adoc[] +*** xref:reference:sql/comment-support.adoc[] +*** xref:reference:sql/transactions.adoc[] +*** xref:reference:sql/system-virtual-tables.adoc[] +*** xref:reference:sql/system-catalogs/index.adoc[System Catalogs] +**** xref:reference:sql/system-catalogs/catalogs/pg_attrdef.adoc[] +**** xref:reference:sql/system-catalogs/catalogs/pg_attribute.adoc[] +**** xref:reference:sql/system-catalogs/catalogs/pg_authid.adoc[] +**** xref:reference:sql/system-catalogs/catalogs/pg_class.adoc[] +**** xref:reference:sql/system-catalogs/catalogs/pg_constraint.adoc[] +**** xref:reference:sql/system-catalogs/catalogs/pg_database.adoc[] +**** xref:reference:sql/system-catalogs/catalogs/pg_depend.adoc[] +**** xref:reference:sql/system-catalogs/catalogs/pg_description.adoc[] +**** xref:reference:sql/system-catalogs/catalogs/pg_index.adoc[] +**** xref:reference:sql/system-catalogs/catalogs/pg_language.adoc[] +**** xref:reference:sql/system-catalogs/catalogs/pg_namespace.adoc[] +**** xref:reference:sql/system-catalogs/catalogs/pg_proc.adoc[] +**** xref:reference:sql/system-catalogs/catalogs/pg_roles.adoc[] +**** xref:reference:sql/system-catalogs/catalogs/pg_settings.adoc[] +**** xref:reference:sql/system-catalogs/catalogs/pg_shadow.adoc[] +**** xref:reference:sql/system-catalogs/catalogs/pg_statio_user_tables.adoc[] +**** xref:reference:sql/system-catalogs/catalogs/pg_type.adoc[] +**** xref:reference:sql/system-catalogs/catalogs/pg_user.adoc[] + ** xref:reference:tiers/index.adoc[Cloud Tiers and Regions] *** xref:reference:tiers/serverless-regions.adoc[] *** xref:reference:tiers/byoc-tiers.adoc[] diff --git a/modules/reference/images/sql/join-venn.png b/modules/reference/images/sql/join-venn.png new file mode 100644 index 0000000000000000000000000000000000000000..58d544500599c6b95c4e36d978e3faa44ab1be45 GIT binary patch literal 134971 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�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 D0Sj8d literal 0 HcmV?d00001 diff --git a/modules/reference/images/sql/left-join-venn.png b/modules/reference/images/sql/left-join-venn.png new file mode 100644 index 0000000000000000000000000000000000000000..6fa338d49b4c612b941f4c03e074a9bc77458a2a GIT binary patch literal 121565 zcmeFYXHZj7+cvrZQ2|jA1f;5nNH0pS7DTB^54|Hrkxqb6R4gDwIwAxW0zxR#dr=Ib zgH!>fg(5Yy0154^_&o3T&Ya)p&zbr5jN<^itaX>`zV5Q#JkZr(r01ZAAc*n)y*v64 zbb<$hXv0q)2fxXukvI$fqkDP}<_$p%%#?prP-+@G_$8IMzQ!%6u$yZE{BX?qrp`?W zDn>Hw+tNZ*qh9y#+%ycJT0RJM|1*n_J!~Auk+9BL?90L7D||$eOKopb?o2iWA@8Fl z9pkSVe*aTYd%ItOI@2ul3&|5>{xGoeeoazErolNOQ(@C{LoS&syjS$KOb`QcW$8~w zLl~wom$a!1s?kg;c{ESrb1!|HKR{UHRJx1p1^n#xxWMi$+4$?Ynnlk2C8~=os`lY0V{Sj^1rhXLk>`wza?B$@AsIv)zs4B zCeFKBtq4I^9|3`G4oPE%-6NGeijbudkN0Afj`lyLppEnMexD!w^+7{rbe4t3_mEbvk}aOa!})LZQYVQGunZFt^W^uQ0Ht|7PhI z5f4F%^Dc~VUjMk8o-gWDxy0E(4o}IhexM@p4#kkC^u32CQHGC_02mp^LNzV9G< z`ni6r43Ktvji8#gzxYZcptriTva*5{e8A0aYF;$HCB z5~Q7DN0pUky@mG_%*#2^=ae&U_tfSy)%1o@VgU^0fkEn-WMVADtW}M?qeq;!>fCJC zOVdg&`n9!f*C`xq7Ur{@h%^bJfx?)MA)%(UuU@n5<%4y_+NQ=LGqcVSPhm;X^-amM zik}1L@N6QmKJz*SVC&ApY~o9LCZ(&hK<-B*c)L~76F9p9<57%UlkDbJF%F6o$hmSHrVbwa|35hQKks*GqB(9X!Jrqx?ta{Spd}OJ}X_3~eLniYxZSkxUR5s^W zlcxOzHbQ6~KVB&x_aFS*URou5*AVuF_-@=&2krG2jY84bBZ4cQe%szJ2rnlhJ3jjc zDs@ZOV|&W=bG85Okd0Bk^H@URdUMp?k1i^xiI!Q38qzNPbg08jPg?{tTHyVY-S_

xnJNhf?Fm9qK!fBs~^ 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 literal 0 HcmV?d00001 diff --git a/modules/reference/images/sql/outer-join-venn.png b/modules/reference/images/sql/outer-join-venn.png new file mode 100644 index 0000000000000000000000000000000000000000..4f6cee4de18af460e376c73a67b2d345b9e125a0 GIT binary patch literal 144408 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 literal 0 HcmV?d00001 diff --git a/modules/reference/images/sql/right-join-venn.png b/modules/reference/images/sql/right-join-venn.png new file mode 100644 index 0000000000000000000000000000000000000000..6035ab6542ba6a8aed577a2d09138bcc95adf81c GIT binary patch literal 125085 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%0AYYLAKBI)Nq`}II>yI`se(AWIU6;-e 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 z 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 Z_M5A|J4fL^u82ObuBv@0TgCFxe*rC;52pYC literal 0 HcmV?d00001 diff --git a/modules/reference/pages/sql/comment-support.adoc b/modules/reference/pages/sql/comment-support.adoc new file mode 100644 index 000000000..e27cf1c47 --- /dev/null +++ b/modules/reference/pages/sql/comment-support.adoc @@ -0,0 +1,61 @@ += Comment Support +:description: Redpanda SQL fully supports comments in your queries. +:page-topic-type: reference + +Redpanda SQL fully supports comments in your queries. Comments provide a way to add explanatory notes and improve the readability of queries, making it easier for developers and stakeholders to understand complex queries. + +There are two types of comments in Redpanda SQL: single-line and multi-line (block). + +== Single line comments + +A single-line comment in Redpanda SQL starts with two consecutive hyphens (--) and extends to the end of the line. Use these comments to annotate specific parts of a query with brief explanations or notes that help readers understand the query. + +=== Syntax + +[source,sql] +---- +-- This is an example single line comment +---- + +== Multi-line (block) comments + +Redpanda SQL also supports multi-line comments, often referred to as block comments. These comments begin with `/*` and end with `*/`, allowing for multi-line explanations or temporarily disabling sections of the query. + +=== Syntax + +[source,sql] +---- +/* +This is an example multi-line comment. +It can span multiple lines and is useful for providing detailed explanations. +*/ +---- + +== Comment placement + +In Redpanda SQL, single-line comments should always be placed at the end of the line they refer to, whereas multi-line comments can be positioned anywhere within the query. + +=== Comment on a single line + +[source,sql] +---- +SELECT column1, column2 -- This is an example single line comment +FROM table_name; +---- + +=== Comment on multiple lines + +[source,sql] +---- +SELECT /* comment 1 */ column1, column2 +FROM table_name /* comment 2 */ +WHERE column3 = 42 /* comment 3 */ ; +---- + +== Best practices for commenting + +To maximize the benefits of comments in Redpanda SQL queries, follow these best practices: + +* Be concise. Write clear and concise comments that provide meaningful insights into the specific parts of the query. +* Update comments during code changes. Whenever the query is modified, update the associated comments to reflect the changes accurately. +* Avoid over-commenting. While comments are helpful, excessive commenting can clutter the code and reduce readability. diff --git a/modules/reference/pages/sql/index.adoc b/modules/reference/pages/sql/index.adoc new file mode 100644 index 000000000..2e4a5c38d --- /dev/null +++ b/modules/reference/pages/sql/index.adoc @@ -0,0 +1,12 @@ += SQL Reference +:description: This section provides information about the syntax and semantics of SQL queries, clauses, data types, and functions that Redpanda SQL supports. + +This section provides information about the syntax and semantics of SQL queries, clauses, data types, and functions that Redpanda SQL supports. The information in this section is divided into groups according to the kind of operation they perform as follows: + +* xref:reference:sql/sql-statements/index.adoc[SQL Statements]. Learn how to create a request for data or information from one or more database tables using supported statements. +* xref:reference:sql/sql-clauses/index.adoc[SQL Clauses]. Learn how to write user-friendly queries and analyze data using different constraints and conditions. +* xref:reference:sql/sql-data-types/index.adoc[SQL Data Types]. Learn how to implement supported data types to run your operations, such as text, timestamp, numeric, and many more. +* xref:reference:sql/sql-functions/index.adoc[SQL Functions]. See how you can combine statements, data types, and other references into specific functions for particular tasks. +* xref:reference:sql/schema.adoc[Schema]. Learn about a logical container that holds database objects and relationships of data in a database. +* xref:reference:sql/comment-support.adoc[Comment Support]. Add comments in your queries for better documentation and collaboration. +* xref:reference:sql/transactions.adoc[Transactions]. Learn more about managing your transactions. diff --git a/modules/reference/pages/sql/redpanda-catalogs.adoc b/modules/reference/pages/sql/redpanda-catalogs.adoc new file mode 100644 index 000000000..d7e3048ab --- /dev/null +++ b/modules/reference/pages/sql/redpanda-catalogs.adoc @@ -0,0 +1,81 @@ += Redpanda Catalogs +:description: Redpanda catalogs are named connections that map Redpanda topics to queryable SQL tables. +:page-topic-type: reference + +Redpanda catalogs are named connections that let you query Redpanda topics using standard SQL. The catalog model consists of three core concepts: + +* Catalogs: Named connections to a Redpanda cluster, created with xref:reference:sql/sql-statements/create-redpanda-catalog.adoc[CREATE REDPANDA CATALOG]. +* Tables: Redpanda topics mapped as queryable SQL tables using the `catalog_name\=>table_name` syntax, created with xref:reference:sql/sql-statements/create-table.adoc[CREATE TABLE]. +* Storage connections: Named connections to external object storage such as Amazon S3, created with xref:reference:sql/sql-statements/create-storage.adoc[CREATE STORAGE]. + +NOTE: Redpanda SQL operates in read-only mode. Data mutation operations such as `INSERT`, `UPDATE`, and `DELETE` are not available. Data is ingested into Redpanda topics and made queryable through catalog mappings. + +== Typical workflow + +To query Redpanda topic data with SQL: + +. Create a catalog connection: ++ +[source,sql] +---- +CREATE REDPANDA CATALOG my_catalog +WITH ( + initial_brokers = 'broker1:9092', + schema_registry_url = 'http://schema-registry:8081' +); +---- + +. Map a topic as a table: ++ +[source,sql] +---- +CREATE TABLE my_catalog=>user_events +WITH (topic = 'user-events'); +---- + +. Query the data: ++ +[source,sql] +---- +SELECT * FROM my_catalog=>user_events LIMIT 10; +---- + +== Related statements + +[cols="<40%,<60%",options="header"] +|=== +|Statement |Description + +|xref:reference:sql/sql-statements/create-redpanda-catalog.adoc[CREATE REDPANDA CATALOG] +|Create a catalog connection to a Redpanda cluster. + +|xref:reference:sql/sql-statements/alter-redpanda-catalog.adoc[ALTER REDPANDA CATALOG] +|Modify connection properties of an existing catalog. + +|xref:reference:sql/sql-statements/create-table.adoc[CREATE TABLE] +|Map a Redpanda topic to a SQL table through a catalog. + +|xref:reference:sql/sql-statements/alter-table.adoc[ALTER TABLE] +|Modify options of an existing catalog table. + +|xref:reference:sql/sql-statements/drop-table.adoc[DROP TABLE] +|Remove a catalog table mapping. + +|xref:reference:sql/sql-statements/drop-redpanda-catalog.adoc[DROP REDPANDA CATALOG] +|Remove a Redpanda catalog connection. + +|xref:reference:sql/sql-statements/drop-storage.adoc[DROP STORAGE] +|Remove a named storage definition. + +|xref:reference:sql/sql-statements/show-tables.adoc[SHOW TABLES] +|List tables within a catalog. + +|xref:reference:sql/sql-statements/describe.adoc[DESCRIBE] +|Show details about a catalog or catalog table. + +|xref:reference:sql/sql-statements/create-storage.adoc[CREATE STORAGE] +|Create a connection to external object storage. + +|xref:reference:sql/sql-statements/alter-storage.adoc[ALTER STORAGE] +|Modify an existing storage connection. +|=== diff --git a/modules/reference/pages/sql/schema.adoc b/modules/reference/pages/sql/schema.adoc new file mode 100644 index 000000000..f30f4e2bb --- /dev/null +++ b/modules/reference/pages/sql/schema.adoc @@ -0,0 +1,228 @@ += Schemas +:description: Schemas are namespaces that organize tables and other database objects in Redpanda SQL. +:page-topic-type: reference + +A schema is a namespace that groups related database objects, including tables, views, indexes, sequences, data types, operators, and functions. Schemas let you organize objects into logical groups so their names do not collide. + +Redpanda SQL supports multiple schemas in a single database. For example, in a database named `mydb`, you might define schemas such as `auth`, `model`, and `business`. + +== Default schema + +By default, Redpanda SQL uses the `public` schema. When you reference an unqualified table name, it is equivalent to `public.table_name`. The same rule applies to `CREATE`, `DROP`, and `SELECT` statements. + +== Syntax + +=== Create a schema + +[source,sql] +---- +CREATE SCHEMA [IF NOT EXISTS] schema_name; +---- + +* `schema_name`: Name of the schema to create. +* `IF NOT EXISTS`: Optional. Prevents an error if the schema already exists. + +=== Create a table in a schema + +[source,sql] +---- +CREATE TABLE schema_name.table_name( +... +); +---- + +* `schema_name`: Name of an existing schema. +* `table_name`: Name of the table to create. + +=== Query a table in a schema + +[source,sql] +---- +SELECT * FROM schema_name.table_name; +---- + +* `schema_name`: Name of the schema. +* `table_name`: Name of the table to query. + +=== Drop a schema + +To drop an empty schema: + +[source,sql] +---- +DROP SCHEMA [IF EXISTS] schema_name; +---- + +* `schema_name`: Name of the schema to drop. +* `IF EXISTS`: Optional. Prevents an error if the schema does not exist. + +To drop a schema that contains tables, use `CASCADE` to drop the schema and its contents: + +[source,sql] +---- +DROP SCHEMA schema_name CASCADE; +---- + +== Examples + +=== Create a schema and table + +. Create a schema: ++ +[source,sql] +---- +CREATE SCHEMA app_data; +---- + +. Create a table in the `app_data` schema and insert rows: ++ +[source,sql] +---- +CREATE TABLE app_data.functions( + id int, + function_name text, + active bool +); + +INSERT INTO app_data.functions(id, function_name, active) +VALUES +('1111', 'Numeric', 'TRUE'), +('2222', 'Text', 'TRUE'), +('3333', 'Timestamp', 'TRUE'), +('4444', 'JSON', 'TRUE'), +('5555', 'Boolean', 'TRUE'); +---- + +. Verify the table contents: ++ +[source,sql] +---- +SELECT * FROM app_data.functions; +---- + +. The query returns: ++ +[source,sql] +---- ++------+---------------+---------+ +| id | function_name | active | ++------+---------------+---------+ +| 1111 | Numeric | t | +| 2222 | Text | t | +| 3333 | Timestamp | t | +| 4444 | JSON | t | +| 5555 | Boolean | t | ++------+---------------+---------+ +---- + +=== Use IF NOT EXISTS + +The `IF NOT EXISTS` option lets the `CREATE SCHEMA` query succeed without error when the schema already exists. The schema is not modified. + +==== Without IF NOT EXISTS + +. Create a schema: ++ +[source,sql] +---- +CREATE SCHEMA analytics; +---- ++ +The query returns: ++ +[source,sql] +---- +CREATE SCHEMA +---- + +. Run the same `CREATE SCHEMA` statement again. The query fails with an error: ++ +[source,sql] +---- +CREATE SCHEMA analytics; +---- ++ +The query returns: ++ +[source,sql] +---- +ERROR: Schema: analytics already exists +---- + +==== With IF NOT EXISTS + +Run the same `CREATE SCHEMA` statement with `IF NOT EXISTS`. The query succeeds without error: + +[source,sql] +---- +CREATE SCHEMA IF NOT EXISTS analytics; +---- + +[source,sql] +---- +CREATE +---- + +=== Drop a schema + +To drop a schema and all its tables, use `CASCADE`: + +[source,sql] +---- +DROP SCHEMA app_data CASCADE; +---- + +If the schema is empty, you can drop it without `CASCADE`: + +[source,sql] +---- +DROP SCHEMA app_data; +---- + +=== Use IF EXISTS + +The `IF EXISTS` option lets the `DROP SCHEMA` query succeed without error when the schema does not exist. + +==== Without IF EXISTS + +. Drop a schema: ++ +[source,sql] +---- +DROP SCHEMA analytics; +---- ++ +The query returns: ++ +[source,sql] +---- +DROP +---- + +. Run the same `DROP SCHEMA` statement again. The query fails with an error: ++ +[source,sql] +---- +DROP SCHEMA analytics; +---- ++ +The query returns: ++ +[source,sql] +---- +ERROR: schema "analytics" does not exist +---- + +==== With IF EXISTS + +Run the same `DROP SCHEMA` statement with `IF EXISTS`. The query succeeds without error: + +[source,sql] +---- +DROP SCHEMA IF EXISTS analytics; +---- + +[source,sql] +---- +DROP +---- diff --git a/modules/reference/pages/sql/sql-clauses/from/from.adoc b/modules/reference/pages/sql/sql-clauses/from/from.adoc new file mode 100644 index 000000000..481e1ee3f --- /dev/null +++ b/modules/reference/pages/sql/sql-clauses/from/from.adoc @@ -0,0 +1,240 @@ += FROM +:description: The FROM clause specifies the tables that a query reads data from. +:page-topic-type: reference + +The `FROM` clause specifies the tables that a query reads data from. + +== Syntax + +A `FROM` clause must list at least one table: + +[source,sql] +---- +query FROM table_name; +---- + +If two or more tables are listed, they are joined with one of: xref:reference:sql/sql-clauses/from/join.adoc[JOIN], xref:reference:sql/sql-clauses/from/right-join.adoc[RIGHT JOIN], xref:reference:sql/sql-clauses/from/left-join.adoc[LEFT JOIN], or xref:reference:sql/sql-clauses/from/outer-join.adoc[OUTER JOIN]. + +[source,sql] +---- +FROM table1_name +[ { JOIN + | LEFT JOIN + | RIGHT JOIN + | OUTER JOIN } table2_name +ON table1_name.column1 = table2_name.column1 ] +---- + +[NOTE] +==== +These examples use the `public` schema, the default in Redpanda SQL. For information on creating or displaying tables in other schemas, see xref:reference:sql/schema.adoc[Schema]. +==== + +== Examples + +This example uses the `FROM` clause with a single table. Given a `client` table, the goal is to retrieve the client's name and the city where the company is based. + +[source,sql] +---- +CREATE TABLE client ( + client_id int, + client_name text, + client_origin text +); +INSERT INTO client + (client_id, client_name, client_origin) +VALUES + (181891,'Toyota','Japan'), + (181892,'Google','USA'), + (181893,'Samsung','South Korea'); +---- + +[source,sql] +---- +SELECT * FROM client; +---- + +This returns: + +[source,sql] +---- ++------------+--------------+------------------+ +| client_id | client_name | client_origin | ++------------+--------------+------------------+ +| 181891 | Toyota | Japan | +| 181892 | Google | USA | +| 181893 | Samsung | South Korea | ++------------+--------------+------------------+ +---- + +. Run the following query: ++ +[source,sql] +---- +SELECT client_name, client_origin FROM client; +---- + +. The query returns: ++ +[source,sql] +---- ++--------------+------------------+ +| client_name | client_origin | ++--------------+------------------+ +| Toyota | Japan | +| Google | USA | +| Samsung | South Korea | ++--------------+------------------+ +---- + +[TIP] +==== +For multi-table queries, see: xref:reference:sql/sql-clauses/from/join.adoc[JOIN], xref:reference:sql/sql-clauses/from/right-join.adoc[RIGHT JOIN], xref:reference:sql/sql-clauses/from/left-join.adoc[LEFT JOIN], or xref:reference:sql/sql-clauses/from/outer-join.adoc[OUTER JOIN]. +==== + +== Subqueries with FROM + +The `FROM` clause can also specify a subquery. The result of the subquery becomes a new relation that the outer query can reference. + +[NOTE] +==== +You can list more than one table by separating them with a comma (`,`). This is an implicit (cross) join: `FROM t1, t2 WHERE t1.id = t2.id` is equivalent to `FROM t1 JOIN t2 ON t1.id = t2.id`. Without a `WHERE` clause, the result is the Cartesian product of all rows in both tables. +==== + +=== Syntax + +[source,sql] +---- +SELECT X.column1, X.column2, X.column3 +FROM table_2 as X, table_1 as Y +WHERE conditions (X.column, Y.column); +---- + +. The subquery in the first `FROM` clause selects the columns from the specific table using a new temporary relation (`SELECT X.column1, X.column2, X.column3 FROM`). +. Set the tables into a new temporary relation (`table_2 as X, table_1 as Y`). +. The query is evaluated, selecting only those rows from the temporary relation that fulfill the conditions stated in the `WHERE` clause. + +=== Example + +The following example finds a product whose price exceeds the average budget across all categories. + +[source,sql] +---- +CREATE TABLE product ( + id int, + product text, + category text, + price int +); +INSERT INTO product + (id, product, category, price) +VALUES + (445747,'Court vision women''s shoes nike','Shoes', 8000), + (445641,'Disney kids h&m','Shirt', 6500), + (477278,'Defacto adidas','Hat', 8500), + (481427,'Sophie shopping bag','Bag', 6500), + (411547,'Candy skirt zara','Skirt', 6500), + (488198,'Slim cut skirt hush puppies','Skirt', 7600); +---- + +[source,sql] +---- +SELECT * FROM product; +---- + +This returns: + +[source,sql] +---- ++---------+----------------------------------+-----------+--------+ +| id | product | category | price | ++---------+----------------------------------+-----------+--------+ +| 445747 | Court vision women's shoes nike | Shoes | 8000 | +| 445641 | Disney kids h&m | Shirt | 6500 | +| 477278 | Defacto adidas | Hat | 8500 | +| 481427 | Sophie shopping bag | Bag | 6500 | +| 411547 | Candy skirt zara | Skirt | 6500 | +| 488198 | Slim cut skirt hush puppies | Skirt | 7600 | ++---------+----------------------------------+-----------+--------+ +---- + +Create a `category` table: + +[source,sql] +---- +CREATE TABLE category ( + categoryName text, + budget int +); +INSERT INTO category + (categoryName, budget) +VALUES + ('Shoes', 7000), + ('Shirt', 9000), + ('Bag', 8000), + ('Skirt', 7500), + ('Hat', 7000); +---- + +[source,sql] +---- +SELECT * FROM category; +---- + +This returns: + +[source,sql] +---- ++---------------+----------+ +| categoryName | budget | ++---------------+----------+ +| Shoes | 7000 | +| Shirt | 9000 | +| Bag | 8000 | +| Skirt | 7500 | +| Hat | 7000 | ++---------------+----------+ +---- + +. Run the following query to find the average budget across all categories: ++ +[source,sql] +---- +select avg(budget) as avgBudget from category; +---- + +. The query returns the average budget across all categories: ++ +[source,sql] +---- ++--------------------+ +| avgbudget | ++--------------------+ +| 7700.000000000000 | ++--------------------+ +---- + +. Run: ++ +* The `product` table is aliased as `P` and the budget's average value from the `category` table as `C`. +* The query displays the product's name, category, and price. +* The conditions are set where the product's price exceeds the budget's average value. ++ +[source,sql] +---- +select P.product, P.category, P.price from +(select avg(budget) as avgBudget from category) as C, product as P +where P.price > C.avgBudget; +---- + +. The query returns the products with a price greater than 7700: ++ +[source,sql] +---- ++------------------------------------+-----------+----------+ +| product | category | price | ++------------------------------------+-----------+----------+ +| Court vision women's shoes nike | Shoes | 8000 | +| Defacto adidas | Hat | 8500 | ++------------------------------------+-----------+----------+ +---- diff --git a/modules/reference/pages/sql/sql-clauses/from/index.adoc b/modules/reference/pages/sql/sql-clauses/from/index.adoc new file mode 100644 index 000000000..7cca9b355 --- /dev/null +++ b/modules/reference/pages/sql/sql-clauses/from/index.adoc @@ -0,0 +1,3 @@ += FROM +:description: Reference for the FROM clause and JOIN variants in Redpanda SQL. +:page-layout: index diff --git a/modules/reference/pages/sql/sql-clauses/from/join.adoc b/modules/reference/pages/sql/sql-clauses/from/join.adoc new file mode 100644 index 000000000..410a537f6 --- /dev/null +++ b/modules/reference/pages/sql/sql-clauses/from/join.adoc @@ -0,0 +1,155 @@ += JOIN +:description: The JOIN clause combines records from two tables based on common fields. +:page-topic-type: reference + +The `JOIN` clause combines records from two tables based on common fields. + +== Syntax + +=== JOIN ... ON + +[source,sql] +---- +SELECT table_1.column_1, table_2.column_2... +FROM table_1 +JOIN table_2 +ON table_1.common_field = table_2.common_field +---- + +. `SELECT table_1.column_1, table_2.column_2...` selects the columns to display from both tables. +. `FROM table_1 JOIN table_2` represents the joined tables. +. `ON table_1.common_field = table_2.common_field` compares each row of `table_1` with each row of `table_2` to find all pairs of rows that meet the join condition. +. When the condition is met, Redpanda SQL combines column values for each matched pair of rows from `table_1` and `table_2` into a result row. + +=== JOIN ... USING + +[source,sql] +---- +SELECT column_1, column_2... +FROM table_1 +JOIN table_2 +USING (column_name [, column_name2 ...]) +---- + +The `USING` clause is a shorthand for joining tables when both tables share columns with the same name. Instead of writing `ON table_1.id = table_2.id`, you can write `USING (id)`. When joining on multiple shared columns, separate them with commas: `USING (id, name)`. + +=== Table alias + +A table alias is a temporary name given to a table, column, or expression in a query. Aliases don't change the result; they make queries easier to read. + +[source,sql] +---- +SELECT left.column_1, right.column_2... +FROM table_1 as left +JOIN table_2 as right +ON left.common_field = right.common_field +---- + +== Examples + +The following examples use two tables: `movies` and `categories`. + +Create the `movies` table: + +[source,sql] +---- +CREATE TABLE movies ( + movie_id int, + movie_name text, + category_id int +); +INSERT INTO movies + (movie_id, movie_name, category_id) +VALUES + (201011, 'The Avengers', 181893), + (200914, 'Avatar', 181894), + (201029, 'Shutter Island', 181891), + (201925, 'Tune in Your Love', 181892); +---- + +[source,sql] +---- +SELECT * FROM movies; +---- + +This returns: + +[source,sql] +---- ++------------+-----------------------+--------------+ +| movie_id | movie_name | category_id | ++------------+-----------------------+--------------+ +| 201011 | The Avengers | 181893 | +| 200914 | Avatar | 181894 | +| 201029 | Shutter Island | 181891 | +| 201925 | Tune in Your Love | 181892 | ++------------+-----------------------+--------------+ +---- + +Create the `categories` table: + +[source,sql] +---- +CREATE TABLE categories ( + id int, + category_name text +); +INSERT INTO categories + (id, category_name) +VALUES + (181891, 'Psychological Thriller'), + (181892, 'Romance'), + (181893, 'Fantasy'), + (181894, 'Science Fiction'), + (181895, 'Action'); +---- + +[source,sql] +---- +SELECT * FROM categories; +---- + +This returns: + +[source,sql] +---- ++-----------+--------------------------+ +| id | category_name | ++-----------+--------------------------+ +| 181891 | Psychological Thriller | +| 181892 | Romance | +| 181893 | Fantasy | +| 181894 | Science Fiction | +| 181895 | Action | ++-----------+--------------------------+ +---- + +A `JOIN` query against these tables: + +[source,sql] +---- +SELECT a.movie_name, c.category_name +FROM movies AS a +JOIN categories AS c +ON a.category_id = c.id; +---- + +The query returns: + +[source,sql] +---- ++-----------------------+---------------------------+ +| movie_name | category_name | ++-----------------------+---------------------------+ +| Shutter Island | Psychological Thriller | +| Tune in Your Love | Romance | +| The Avengers | Fantasy | +| Avatar | Science Fiction | ++-----------------------+---------------------------+ +---- + +The `JOIN` checks each row of the `category_id` column in the `movies` table against the `id` column of each row in the `categories` table. When the values match, Redpanda SQL creates a new result row that combines columns from both tables. + +The following Venn diagram illustrates this example: + +image::sql/join-venn.png[Venn diagram showing an inner join between the Movies and Categories tables] diff --git a/modules/reference/pages/sql/sql-clauses/from/left-join.adoc b/modules/reference/pages/sql/sql-clauses/from/left-join.adoc new file mode 100644 index 000000000..659a4741f --- /dev/null +++ b/modules/reference/pages/sql/sql-clauses/from/left-join.adoc @@ -0,0 +1,171 @@ += LEFT JOIN +:description: The LEFT JOIN returns all matching records from the left table combined with the right table. +:page-topic-type: reference + +The `LEFT JOIN` returns all matching records from the left table combined with the right table. Even if there are no matching records in the right table, the `LEFT JOIN` still returns a row in the result, with `NULL` in each column from the right table. + +[NOTE] +==== +`LEFT JOIN` is also known as `LEFT OUTER JOIN`. +==== + +== Syntax + +[source,sql] +---- +SELECT column_1, column_2... +FROM table_1 +LEFT JOIN table_2 +ON table_1.matching_field = table_2.matching_field; +---- + +In this syntax: + +. `SELECT column_1, column_2...` defines the columns from both tables where the data is to be selected. +. `FROM table_1` defines the left table as the main table in the `FROM` clause. +. `LEFT JOIN table_2` defines the right table as the table the main table joins. +. `ON table_1.matching_field = table_2.matching_field` sets the join condition after the `ON` keyword with the matching field between the two tables. + +=== Table alias + +A table alias is a temporary name given to a table in a query. Aliases don't change the result; they make queries easier to read. + +[source,sql] +---- +SELECT A.column_1, B.column_2... +FROM table_1 A +LEFT JOIN table_2 B +ON A.matching_field = B.matching_field; +---- + +== Examples + +Create the `item` table: + +[source,sql] +---- +CREATE TABLE item ( + item_no int NOT NULL, + item_name text +); + +INSERT INTO item + (item_no,item_name) +VALUES + (111,'Butter'), + (113,'Tea'), + (116,'Bread'), + (119,'Coffee'); +---- + +[source,sql] +---- +SELECT * FROM item; +---- + +This returns: + +[source,sql] +---- ++-----------+----------------+ +| item_no | item_name | ++-----------+----------------+ +| 111 | Butter | +| 113 | Tea | +| 116 | Bread | +| 119 | Coffee | ++-----------+----------------+ +---- + +Create the `invoice` table: + +[source,sql] +---- +CREATE TABLE invoice ( + inv_no int NOT NULL, + item int, + sold_qty int, + sold_price int +); + +INSERT INTO invoice + (inv_no, item, sold_qty, sold_price) +VALUES + (020219,111,3,9000), + (020220,116,6,30000), + (020221,116,2,10000), + (020222,116,1,5000), + (020223,119,5,20000), + (020224,119,4,16000); +---- + +[source,sql] +---- +SELECT * FROM invoice; +---- + +This returns: + +[source,sql] +---- ++----------+---------+-----------+-------------+ +| inv_no | item | sold_qty | sold_price | ++----------+---------+-----------+-------------+ +| 20219 | 111 | 3 | 9000 | +| 20220 | 116 | 6 | 30000 | +| 20221 | 116 | 2 | 10000 | +| 20222 | 116 | 1 | 5000 | +| 20223 | 119 | 5 | 20000 | +| 20224 | 119 | 4 | 16000 | ++----------+---------+-----------+-------------+ +---- + +A `LEFT JOIN` query against these tables: + +[source,sql] +---- +SELECT item_no, item_name, sold_qty, sold_price +FROM item +LEFT JOIN invoice +ON item.item_no = invoice.item; +---- + +* The `item` table is the left table, and the `invoice` table is the right table. +* The query combines values from the `item` table using `item_no` and matches records using the `item` column from the `invoice` table. +* When records match, Redpanda SQL creates a new row with `item_no`, `item_name`, `sold_qty`, and `sold_price` columns as defined in the `SELECT` clause. +* Otherwise, a new row is created with `NULL` values from the right table (`invoice`). + +The query returns: + +[source,sql] +---- ++-----------+-------------+------------+---------------+ +| item_no | item_name | sold_qty | sold_price | ++-----------+-------------+------------+---------------+ +| 111 | Butter | 3 | 9000 | +| 113 | Tea | null | null | +| 116 | Bread | 6 | 30000 | +| 116 | Bread | 2 | 10000 | +| 116 | Bread | 1 | 5000 | +| 119 | Coffee | 5 | 20000 | +| 119 | Coffee | 4 | 16000 | ++-----------+-------------+------------+---------------+ +---- + +Based on the data from the `item` and `invoice` tables: + +* The result matches the total items in the `item` table: four items. +* The result displays all items from the left table (`item`), even if one item has not been sold. +* Item id `111` (`Butter`) has been sold once for 3 pieces at 9000. +* Item id `113` (`Tea`) has never been sold, so the `sold_qty` and `sold_price` columns are `NULL`. +* Item id `116` (`Bread`) has been sold three times: 6 pieces at 30000, 2 pieces at 10000, and 1 piece at 5000. +* Item id `119` (`Coffee`) has been sold twice: 5 pieces at 20000 and 4 pieces at 16000. + +[TIP] +==== +An `item` can have zero or many invoices. An `invoice` belongs to zero or one `item`. +==== + +The following Venn diagram illustrates the `LEFT JOIN`: + +image::sql/left-join-venn.png[Venn diagram showing a left join between the Item and Invoice tables] diff --git a/modules/reference/pages/sql/sql-clauses/from/outer-join.adoc b/modules/reference/pages/sql/sql-clauses/from/outer-join.adoc new file mode 100644 index 000000000..862502621 --- /dev/null +++ b/modules/reference/pages/sql/sql-clauses/from/outer-join.adoc @@ -0,0 +1,220 @@ += OUTER JOIN +:description: The OUTER JOIN returns all the records from the selected fields between two tables, whether the join condition is met or not. +:page-topic-type: reference + +The `OUTER JOIN` (also known as `FULL OUTER JOIN`) returns all the records from the selected fields between the two tables (left and right), whether the join condition is met or not. + +== Inner join vs. outer join + +An `INNER JOIN` returns only matched rows. An `OUTER JOIN` returns all rows from both tables, whether matched or not. + +== Syntax + +[source,sql] +---- +SELECT column_1, column_2... +FROM table_1 +FULL OUTER JOIN table_2 +ON table_1.matching_field = table_2.matching_field; +---- + +In this syntax: + +. `SELECT column_1, column_2...` defines the columns from both tables to display data. +. `FROM table_1` represents `table_1` as the left table in the `FROM` clause. +. `FULL OUTER JOIN table_2` represents `table_2` as the right table in the `FULL OUTER JOIN` condition. +. `ON table_1.matching_field = table_2.matching_field` sets the join condition after the `ON` keyword with the matching field between the two tables. + +=== Table alias + +A table alias is a temporary name given to a table in a query. Aliases don't change the result; they make queries easier to read. + +[source,sql] +---- +SELECT A.column_1, B.column_2... +FROM table_1 A +FULL OUTER JOIN table_2 B +ON A.matching_field = B.matching_field; +---- + +[NOTE] +==== +If there are no matched records from the joined tables, `NULL` values are returned in every column of the table that does not have the matching record. +==== + +== Examples + +Create the `departments` table: + +[source,sql] +---- +CREATE TABLE departments ( + department_id int, + department_name text +); +INSERT INTO departments (department_id,department_name) +VALUES + (1001, 'Sales'), + (1002, 'Marketing'), + (1003, 'HR'), + (1004, 'Project'), + (1005, 'Product'); +---- + +[source,sql] +---- +SELECT * FROM departments; +---- + +This returns: + +[source,sql] +---- ++----------------+------------------+ +| department_id | department_name | ++----------------+------------------+ +| 1001 | Sales | +| 1002 | Marketing | +| 1003 | HR | +| 1004 | Project | +| 1005 | Product | ++----------------+------------------+ +---- + +Create the `employee` table: + +[source,sql] +---- +CREATE TABLE employee ( + employee_id int, + employee_name text, + dept_id int +); +INSERT INTO employee ( + employee_id, + employee_name, + dept_id +) +VALUES + (2001,'Tony Stark', 1002), + (2002,'Christian Bale', 1002), + (2003,'Anne Hailey', 1003), + (2004,'Wilson Cliff', 1004), + (2005,'Susan Oh', 1001), + (2006,'Julian Robert', 1001), + (2007,'Gilbert Tom', null); +---- + +[source,sql] +---- +SELECT * FROM employee; +---- + +This returns: + +[source,sql] +---- ++--------------+-------------------+------------+ +| employee_id | employee_name | dept_id | ++--------------+-------------------+------------+ +| 2001 | Tony Stark | 1002 | +| 2002 | Christian Bale | 1002 | +| 2003 | Anne Hailey | 1003 | +| 2004 | Wilson Cliff | 1004 | +| 2005 | Susan Oh | 1001 | +| 2006 | Julian Robert | 1001 | +| 2007 | Gilbert Tom | null | ++--------------+-------------------+------------+ +---- + +=== FULL OUTER JOIN + +A `FULL OUTER JOIN` query against these tables: + +[source,sql] +---- +SELECT employee_name, department_name +FROM departments +FULL OUTER JOIN employee +ON departments.department_id = employee.dept_id; +---- + +The result shows every department with an employee, the employees who work under a specific department, every department that does not have any employees, and the employees who do not belong to a specific department. + +[source,sql] +---- ++-------------------+-------------------+ +| employee_name | department_name | ++-------------------+-------------------+ +| Julian Robert | Sales | +| Susan Oh | Sales | +| Christian Bale | Marketing | +| Tony Stark | Marketing | +| Anne Hailey | HR | +| Wilson Cliff | Project | +| Gilbert Tom | null | +| null | Product | ++-------------------+-------------------+ +---- + +The following Venn diagram illustrates the `FULL OUTER JOIN`: + +image::sql/outer-join-venn.png[Venn diagram showing a full outer join between the Departments and Employee tables] + +=== FULL OUTER JOIN with WHERE clause + +==== Find departments with no employees + +. To find departments that do not have any employees, add a `WHERE` clause with `NULL`: ++ +[source,sql] +---- +SELECT employee_name, department_name +FROM departments +FULL OUTER JOIN employee +ON departments.department_id = employee.dept_id +WHERE employee_name IS NULL; +---- + +. The query returns: ++ +[source,sql] +---- ++------------------+--------------------+ +| employee_name | department_name | ++------------------+--------------------+ +| null | Product | ++------------------+--------------------+ +---- + +The result indicates that the `Product` department has no employees. + +==== Find employees with no department + +. To find employees who do not belong to any department, add a `WHERE` clause with `NULL`: ++ +[source,sql] +---- +SELECT employee_name, department_name +FROM employee +FULL OUTER JOIN departments +ON employee.dept_id = departments.department_id +WHERE department_name IS NULL; +---- + +. The query returns: ++ +[source,sql] +---- ++------------------+--------------------+ +| employee_name | department_name | ++------------------+--------------------+ +| Gilbert Tom | null | ++------------------+--------------------+ +---- + +The result shows that Gilbert Tom does not belong to any department. + +The following Venn diagram illustrates the `FULL OUTER JOIN` with `WHERE` clause filtering for NULL values: + +image::sql/outer-join-where-venn.png[Venn diagram showing a full outer join with WHERE clause filtering for NULL values] diff --git a/modules/reference/pages/sql/sql-clauses/from/right-join.adoc b/modules/reference/pages/sql/sql-clauses/from/right-join.adoc new file mode 100644 index 000000000..c882148ff --- /dev/null +++ b/modules/reference/pages/sql/sql-clauses/from/right-join.adoc @@ -0,0 +1,165 @@ += RIGHT JOIN +:description: The RIGHT JOIN returns all matching records from the right table combined with the left table. +:page-topic-type: reference + +The `RIGHT JOIN` returns all matching records from the right table combined with the left table. Even if there are no matching records in the left table, the `RIGHT JOIN` still returns a row in the result, with `NULL` in each column from the left table. + +== Syntax + +[source,sql] +---- +SELECT column_1, column_2... +FROM table_1 +RIGHT JOIN table_2 +ON table_1.matching_field = table_2.matching_field; +---- + +In this syntax: + +. `SELECT column_1, column_2...` defines the columns from both tables to display. +. `FROM table_1` defines `table_1` as the left table in the `FROM` clause. +. `RIGHT JOIN table_2` defines `table_2` as the right table in the `RIGHT JOIN` condition. +. `ON table_1.matching_field = table_2.matching_field` sets the join condition after the `ON` keyword with the matching field between the two tables. + +=== Table alias + +A table alias is a temporary name given to a table in a query. Aliases don't change the result; they make queries easier to read. + +[source,sql] +---- +SELECT A.column_1, B.column_2... +FROM table_1 A +RIGHT JOIN table_2 B +ON A.matching_field = B.matching_field; +---- + +== Examples + +Create the `customer` table: + +[source,sql] +---- +CREATE TABLE customer ( + id int NOT NULL, + customer_name text +); + +INSERT INTO customer + (id, customer_name) +VALUES + (201011,'James'), + (200914,'Harry'), + (201029,'Ellie'), + (201925,'Mary'); +---- + +[source,sql] +---- +SELECT * FROM customer; +---- + +This returns: + +[source,sql] +---- ++-----------+----------------+ +| id | customer_name | ++-----------+----------------+ +| 201011 | James | +| 200914 | Harry | +| 201029 | Ellie | +| 201925 | Mary | ++-----------+----------------+ +---- + +Create the `orders` table: + +[source,sql] +---- +CREATE TABLE orders ( + order_id int NOT NULL, + order_date date, + order_amount int, + customer_id int +); + +INSERT INTO orders + (order_id, order_date, order_amount, customer_id) +VALUES + (181893,'2021-10-08',3000,201029), + (181894,'2021-11-18',2000,201029), + (181891,'2021-09-10',9000,201011), + (181892,'2021-10-10',7000,201925), + (181897,'2022-05-27',6700,null), + (181899,'2021-07-22',4500,201011); +---- + +[source,sql] +---- +SELECT * FROM orders; +---- + +This returns: + +[source,sql] +---- ++------------+------------------+---------------+-------------+ +| order_id | order_date | order_amount | customer_id | ++------------+------------------+---------------+-------------+ +| 181893 | 2021-10-08 | 3000 | 201029 | +| 181894 | 2021-11-18 | 2000 | 201029 | +| 181891 | 2021-09-10 | 9000 | 201011 | +| 181892 | 2021-10-10 | 7000 | 201925 | +| 181897 | 2022-05-27 | 6700 | null | +| 181899 | 2021-07-22 | 4500 | 201011 | ++------------+------------------+---------------+-------------+ +---- + +A `RIGHT JOIN` query against these tables: + +[source,sql] +---- +SELECT customer_name, order_date, order_amount +FROM customer +RIGHT JOIN orders +ON customer.id = orders.customer_id; +---- + +* The `customer` table is the left table, and the `orders` table is the right table. +* The query combines values from the `orders` table using `customer_id` and matches records using the `id` column from the `customer` table. +* When records match, Redpanda SQL creates a new row with `customer_name` and `order_amount` columns as defined in the `SELECT` clause. +* Otherwise, a new row is created with `NULL` values from the left table (`customer`). + +The query returns: + +[source,sql] +---- ++------------------+----------------+-----------------+ +| customer_name | order_date | order_amount | ++------------------+----------------+-----------------+ +| James | 2021-09-10 | 9000 | +| James | 2021-07-22 | 4500 | +| Ellie | 2021-10-08 | 3000 | +| Ellie | 2021-11-18 | 2000 | +| Mary | 2021-10-10 | 7000 | +| null | 2022-05-27 | 6700 | ++------------------+----------------+-----------------+ +---- + +Based on the data from the `customer` and `orders` tables: + +* Order id `181893` matches the customer `Ellie`. +* Order id `181894` matches the customer `Ellie`. +* Order id `181891` matches the customer `James`. +* Order id `181899` matches the customer `James`. +* Order id `181892` matches the customer `Mary`. +* Order id `181897` does not match any customer, so the `customer_name` column is `NULL`. + +[NOTE] +==== +A `customer` can have zero or many `orders`. An item from `orders` belongs to zero or one `customer`. +==== + +The following Venn diagram illustrates the `RIGHT JOIN`: + +image::sql/right-join-venn.png[Venn diagram showing a right join between the Customer and Orders tables] diff --git a/modules/reference/pages/sql/sql-clauses/group-by.adoc b/modules/reference/pages/sql/sql-clauses/group-by.adoc new file mode 100644 index 000000000..5555d22dc --- /dev/null +++ b/modules/reference/pages/sql/sql-clauses/group-by.adoc @@ -0,0 +1,282 @@ += GROUP BY +:description: The GROUP BY clause returns a group of records from one or more tables that have the same values in specified columns. +:page-topic-type: reference + +The `GROUP BY` clause returns a group of records from one or more tables that have the same values in specified columns. The result of the `GROUP BY` clause returns a single row for each value of the column. + +[NOTE] +==== +You can use xref:reference:sql/sql-functions/aggregate-functions/index.adoc[aggregate functions] such as `COUNT()`, `MAX()`, `MIN()`, and `SUM()` to perform operations on the grouped values in the `SELECT` statement. +==== + +== Syntax + +[source,sql] +---- +SELECT +column_1, column_2, aggregate_function(column_3) +FROM +table_name +GROUP BY +column_1, column_2,...; +---- + +This syntax includes the following elements: + +* `SELECT column_1, column_2, aggregate_function(column_3)` defines the columns to group (`column_1, column_2`) and the column to apply an aggregate function to (`column_3`). +* `FROM table_name` defines the table to retrieve data from. +* `GROUP BY column_1, column_2,...;` lists the columns to group. + +[NOTE] +==== +Each column you group on must also appear in the `SELECT` list, and each column you group on must appear in the `GROUP BY` clause. +==== + +=== Syntax with WHERE clause + +The `GROUP BY` clause must appear after the `WHERE` clause: + +[source,sql] +---- +SELECT +column_1, column_2, aggregate_function(column_3) +FROM +table_name +WHERE +conditions +GROUP BY +column_1, column_2,...; +---- + +== Examples + +Assume there are two tables: `customer` and `orders`. + +Create the `customer` table: + +[source,sql] +---- +CREATE TABLE customer ( + cust_id int, + cust_name text +); +INSERT INTO customer + (cust_id, cust_name) +VALUES + (11001, 'Maya'), + (11003, 'Ricky'), + (11009, 'Sean'), + (11008, 'Chris'), + (11002, 'Emily'), + (11005, 'Rue'), + (11007, 'Tom'), + (11006, 'Casey'); +---- + +[source,sql] +---- +SELECT * FROM customer; +---- + +This returns: + +[source,sql] +---- ++-----------+------------+ +| cust_id | cust_name | ++-----------+------------+ +| 11001 | Maya | +| 11003 | Ricky | +| 11009 | Sean | +| 11008 | Chris | +| 11002 | Emily | +| 11005 | Rue | +| 11007 | Tom | +| 11006 | Casey | ++-----------+------------+ +---- + +Create the `orders` table: + +[source,sql] +---- +CREATE TABLE orders ( + order_id int, + order_date date, + order_prod text, + order_qty int, + order_price int, + cust_id int +); +INSERT INTO orders + (order_id, order_date, order_prod, order_qty, order_price, cust_id) +VALUES + (999191, '2021-01-08','Butter', 1, 4000, 11001), + (999192, '2021-09-30','Sugar', 1, 10000, 11002), + (999193, '2021-04-17','Sugar', 1, 10000, 11009), + (999194, '2021-08-29','Flour', 4, 20000, 11006), + (999195, '2021-05-04','Sugar', 2, 20000, 11008), + (999196, '2021-07-27','Butter', 2, 8000, 11006), + (999197, '2021-10-30','Flour', 2, 10000, 11001), + (999198, '2021-12-18','Flour', 2, 10000, 11007); +---- + +[source,sql] +---- +SELECT * FROM orders; +---- + +This returns: + +[source,sql] +---- ++------------+--------------+--------------+-------------+---------------+-----------+ +| order_id | order_date | order_prod | order_qty | order_price | cust_id | ++------------+--------------+--------------+-------------+---------------+-----------+ +| 999191 | 2021-01-08 | Butter | 1 | 4000 | 11001 | +| 999192 | 2021-09-30 | Sugar | 1 | 10000 | 11002 | +| 999193 | 2021-04-17 | Sugar | 1 | 10000 | 11009 | +| 999194 | 2021-08-29 | Flour | 4 | 20000 | 11006 | +| 999195 | 2021-05-04 | Sugar | 2 | 20000 | 11008 | +| 999196 | 2021-07-27 | Butter | 2 | 8000 | 11006 | +| 999197 | 2021-10-30 | Flour | 2 | 10000 | 11001 | +| 999198 | 2021-12-18 | Flour | 2 | 10000 | 11007 | ++------------+--------------+--------------+-------------+---------------+-----------+ +---- + +=== Basic GROUP BY + +This query returns all distinct product names from the `orders` table: + +[source,sql] +---- +SELECT order_prod +FROM orders +GROUP BY order_prod; +---- + +The query returns: + +[source,sql] +---- ++--------------+ +| order_prod | ++--------------+ +| Flour | +| Sugar | +| Butter | ++--------------+ +---- + +=== GROUP BY on multiple columns + +This example uses multiple columns in the `GROUP BY` clause: + +[source,sql] +---- +SELECT order_id, order_prod +FROM orders +GROUP BY order_id, order_prod; +---- + +The query returns: + +[source,sql] +---- ++-----------+--------------+ +| order_id | order_prod | ++-----------+--------------+ +| 999194 | Flour | +| 999191 | Butter | +| 999196 | Butter | +| 999192 | Sugar | +| 999195 | Sugar | +| 999198 | Flour | +| 999193 | Sugar | +| 999197 | Flour | ++-----------+--------------+ +---- + +=== GROUP BY with aggregate functions + +This example calculates the total amount each customer has paid for orders using the `SUM()` aggregate function: + +[source,sql] +---- +SELECT cust_id, SUM(order_price) +FROM orders +GROUP BY cust_id; +---- + +The query returns: + +[source,sql] +---- ++-----------+----------+ +| cust_id | sum | ++-----------+----------+ +| 11009 | 10000 | +| 11007 | 10000 | +| 11006 | 28000 | +| 11002 | 10000 | +| 11001 | 14000 | +| 11008 | 20000 | ++-----------+----------+ +---- + +=== GROUP BY with JOIN condition + +This query joins the `orders` table with the `customer` table and groups customers by name. It uses `COUNT()` to count the number of products each customer has purchased: + +[source,sql] +---- +SELECT C.cust_name, COUNT(order_prod) +FROM orders O +JOIN customer C ON O.cust_id = C.cust_id +GROUP BY C.cust_name; +---- + +The query returns: + +[source,sql] +---- ++------------+---------+ +| cust_name | count | ++------------+---------+ +| Tom | 1 | +| Chris | 1 | +| Casey | 2 | +| Maya | 2 | +| Sean | 1 | +| Emily | 1 | ++------------+---------+ +---- + +=== GROUP BY with date data type + +The `order_date` column uses the `DATE` data type. This example groups the order quantity and total price by date using the `DATE()` function: + +[source,sql] +---- +SELECT DATE(order_date), order_qty, SUM(order_price) +FROM orders +GROUP BY order_qty, DATE(order_date); +---- + +The query returns: + +[source,sql] +---- ++---------------+------------+---------+ +| date | order_qty | sum | ++---------------+------------+---------+ +| 2021-07-27 | 2 | 8000 | +| 2021-08-29 | 4 | 20000 | +| 2021-04-17 | 1 | 10000 | +| 2021-09-30 | 1 | 10000 | +| 2021-05-04 | 2 | 20000 | +| 2021-01-08 | 1 | 4000 | +| 2021-12-18 | 2 | 10000 | +| 2021-10-30 | 2 | 10000 | ++---------------+------------+---------+ +---- diff --git a/modules/reference/pages/sql/sql-clauses/having.adoc b/modules/reference/pages/sql/sql-clauses/having.adoc new file mode 100644 index 000000000..e5ae50e6c --- /dev/null +++ b/modules/reference/pages/sql/sql-clauses/having.adoc @@ -0,0 +1,244 @@ += HAVING +:description: The HAVING clause specifies a search condition using an aggregate function. +:page-topic-type: reference + +The `HAVING` clause specifies a search condition using an xref:reference:sql/sql-functions/aggregate-functions/index.adoc[aggregate function]. It filters the records returned from a `GROUP BY` clause that do not fulfill the specified condition. + +== Differences between WHERE and HAVING + +[cols="51%,49%",options="header"] +|=== +|WHERE |HAVING +|The `GROUP BY` clause appears after the `WHERE` clause. |The `GROUP BY` clause appears before the `HAVING` clause. +|The `WHERE` clause cannot work with an aggregate function. |The `HAVING` clause can work with an aggregate function. +|The `WHERE` clause filters individual records. |The `HAVING` clause filters groups of records. +|=== + +== Syntax + +[source,sql] +---- +SELECT column_1, column_2,... +FROM table_name +GROUP BY column_name(s) +HAVING condition_aggregate_function +---- + +This syntax includes the following elements: + +* `SELECT column_1, column_2,...` selects the columns to display. +* `FROM table_name` selects the table to retrieve data from. +* `GROUP BY column_name(s)` lists the columns to group. +* `HAVING condition_aggregate_function` provides the condition for filtering rows formed by the `GROUP BY` clause. The condition can use an aggregate function such as `SUM()`, `COUNT()`, or `MIN()`. + +== Examples + +Assume there are two tables: `student` and `score`. + +Create the `student` table: + +[source,sql] +---- +CREATE TABLE student ( + stud_id int, + stud_name text +); +INSERT INTO student + (stud_id, stud_name) +VALUES + (992831192, 'Mary'), + (992811191, 'Bobby'), + (992311195, 'Sean'), + (998311193, 'Harry'), + (998311194, 'William'), + (928311197, 'Kate'), + (928311190, 'Tom'), + (928311199, 'Sully'), + (998311196, 'Susan'); +---- + +[source,sql] +---- +SELECT * FROM student; +---- + +This returns: + +[source,sql] +---- ++------------+------------+ +| stud_id | stud_name | ++------------+------------+ +| 992831192 | Mary | +| 992811191 | Bobby | +| 992311195 | Sean | +| 998311193 | Harry | +| 998311194 | William | +| 928311197 | Kate | +| 928311190 | Tom | +| 928311199 | Sully | +| 998311196 | Susan | ++------------+------------+ +---- + +Create the `score` table: + +[source,sql] +---- +CREATE TABLE score ( + score_id int, + subject text, + score_val int, + stud_id int, + score_stat text +); +INSERT INTO score + (score_id, subject, score_val, stud_id, score_stat) +VALUES + (12221, 'Math', 90, 992811191, 'PASSED'), + (12222, 'Biology', 90, 992811191, 'PASSED'), + (12223, 'Art', 80, 992831192, 'PASSED'), + (12224, 'History', 70, 928311197, 'FAILED'), + (12225, 'Physics', 75, 928311190, 'FAILED'), + (12226, 'Art', 85, 928311197, 'PASSED'), + (12227, 'Biology', 90, 998311196, 'PASSED'), + (12228, 'Biology', 70, 928311199, 'FAILED'), + (12229, 'Physics', 80, 998311194, 'PASSED'), + (12231, 'Math', 80, 998311193, 'PASSED'), + (12232, 'History', 90, 992811191, 'PASSED'), + (12233, 'Math', 70, 998311194, 'FAILED'), + (12234, 'Math', 80, 928311190, 'PASSED'); +---- + +[source,sql] +---- +SELECT * FROM score; +---- + +This returns: + +[source,sql] +---- ++-----------+----------+------------+------------+-------------+ +| score_id | subject | score_val | stud_id | score_stat | ++-----------+----------+------------+------------+-------------+ +| 12221 | Math | 90 | 992811191 | PASSED | +| 12222 | Biology | 90 | 992811191 | PASSED | +| 12223 | Art | 80 | 992831192 | PASSED | +| 12224 | History | 70 | 928311197 | FAILED | +| 12225 | Physics | 75 | 928311190 | FAILED | +| 12226 | Art | 85 | 928311197 | PASSED | +| 12227 | Biology | 90 | 998311196 | PASSED | +| 12228 | Biology | 70 | 928311199 | FAILED | +| 12229 | Physics | 80 | 998311194 | PASSED | +| 12231 | Math | 80 | 998311193 | PASSED | +| 12232 | History | 90 | 992811191 | PASSED | +| 12233 | Math | 70 | 998311194 | FAILED | +| 12234 | Math | 80 | 928311190 | PASSED | ++-----------+----------+------------+------------+-------------+ +---- + +=== HAVING clause with AVG function + +The following example uses the `AVG` aggregate function to filter subjects with an average score greater than 80: + +[source,sql] +---- +SELECT subject +FROM score +GROUP BY subject +HAVING AVG (score_val) > 80; +---- + +The query returns: + +[source,sql] +---- ++-----------+ +| subject | ++-----------+ +| Art | +| Biology | ++-----------+ +---- + +=== HAVING clause with COUNT function + +The following query lists subjects that have more than two `PASSED` scores: + +[source,sql] +---- +SELECT SUM(CASE WHEN score_stat = 'PASSED' THEN 1 ELSE 0 END) AS passed_count, subject +FROM score +GROUP BY subject +HAVING SUM(CASE WHEN score_stat = 'PASSED' THEN 1 ELSE 0 END) > 2; +---- + +The query returns `Math` as the only subject with more than two `PASSED` values: + +[source,sql] +---- ++--------------+---------+ +| passed_count | subject | ++--------------+---------+ +| 3 | Math | ++--------------+---------+ +---- + +=== HAVING clause with MAX function + +Assume the minimum score criterion is 75. The following query finds the maximum score for each subject where the maximum is greater than 75: + +[source,sql] +---- +SELECT subject, MAX(score_val) +FROM score +GROUP BY subject +HAVING MAX(score_val)>75; +---- + +The query returns: + +[source,sql] +---- ++-----------+--------+ +| subject | max | ++-----------+--------+ +| Math | 90 | +| History | 90 | +| Physics | 80 | +| Art | 85 | +| Biology | 90 | ++-----------+--------+ +---- + +=== HAVING with JOIN condition + +To find students who have failed in their subjects, combine the `student` and `score` tables using `JOIN` and apply a condition on the `score_stat` column: + +[source,sql] +---- +SELECT stud_name, subject, score_val, score_stat +FROM student A +JOIN score C ON A.stud_id = C.stud_id +GROUP BY stud_name, subject, score_val, score_stat +HAVING score_stat = 'FAILED'; +---- + +* The `JOIN` clause combines the two tables. +* The `GROUP BY` clause filters records from both tables based on the specified columns. +* The `HAVING` clause filters the records returned from the `GROUP BY` clause according to the specified condition. + +The query returns: + +[source,sql] +---- ++------------+------------+------------+--------------+ +| stud_name | subject | score_val | score_stat | ++------------+------------+------------+--------------+ +| Kate | History | 70 | FAILED | +| Sully | Biology | 70 | FAILED | +| Tom | Physics | 75 | FAILED | +| William | Math | 70 | FAILED | ++------------+------------+------------+--------------+ +---- diff --git a/modules/reference/pages/sql/sql-clauses/index.adoc b/modules/reference/pages/sql/sql-clauses/index.adoc new file mode 100644 index 000000000..3d6333c33 --- /dev/null +++ b/modules/reference/pages/sql/sql-clauses/index.adoc @@ -0,0 +1,22 @@ += SQL clauses +:description: SQL clauses define how data is retrieved, filtered, and manipulated. +:page-topic-type: reference + +SQL clauses define how data is retrieved, filtered, and manipulated. They specify what data to include, how to organize it, and the conditions rows must meet to appear in the result set. + +Redpanda SQL supports the following clauses: + +[cols="<40%,<60%",options="header"] +|=== +|Clause |Description +|xref:reference:sql/sql-clauses/from/from.adoc[FROM] |Defines the source tables or views for the query. +|xref:reference:sql/sql-clauses/where.adoc[WHERE] |Filters rows based on specified conditions. +|xref:reference:sql/sql-clauses/group-by.adoc[GROUP BY] |Groups rows sharing common values in specified columns for aggregation. +|xref:reference:sql/sql-clauses/having.adoc[HAVING] |Filters grouped rows based on aggregate conditions. +|xref:reference:sql/sql-clauses/order-by.adoc[ORDER BY] |Sorts the result set by specified columns in ascending or descending order. +|xref:reference:sql/sql-clauses/limit.adoc[LIMIT] |Restricts the number of rows returned by the query. +|xref:reference:sql/sql-clauses/offset.adoc[OFFSET] |Skips a specified number of rows before returning results. +|xref:reference:sql/sql-clauses/set-operations/index.adoc[SET OPERATIONS] |Combines or compares results from multiple `SELECT` statements, such as `UNION`, `INTERSECT`, and `EXCEPT`. +|xref:reference:sql/sql-clauses/with.adoc[WITH] |Creates temporary named result sets (Common Table Expressions) for reuse within queries. +|xref:reference:sql/sql-clauses/over-window.adoc[OVER] |Specifies the window over which window functions operate on subsets of data. +|=== diff --git a/modules/reference/pages/sql/sql-clauses/limit.adoc b/modules/reference/pages/sql/sql-clauses/limit.adoc new file mode 100644 index 000000000..2dc24eb8c --- /dev/null +++ b/modules/reference/pages/sql/sql-clauses/limit.adoc @@ -0,0 +1,206 @@ += LIMIT +:description: The LIMIT clause restricts the number of records returned by a SELECT statement. +:page-topic-type: reference + +`LIMIT` is an optional clause used with `SELECT` statements to retrieve records from one or more tables. It specifies the number of records a query returns after filtering the data. + +== Syntax + +The `LIMIT` clause has two syntax forms that produce identical results. The first is the standard PostgreSQL form: + +[source,sql] +---- +SELECT column_list +FROM table_name +ORDER BY sort_expression +LIMIT row_count +---- + +The second is the ANSI SQL form (`FETCH NEXT ... ROWS ONLY`): + +[source,sql] +---- +SELECT column_list +FROM table_name +ORDER BY sort_expression +FETCH NEXT row_count ROWS ONLY +---- + +Where: + +* `column_list`: The columns or calculations to retrieve. +* `table_name`: The tables to retrieve records from. +* `ORDER BY`: An expression used to order the results, either ascending (`ASC`) or descending (`DESC`). +* `LIMIT row_count`: The number of rows to return based on `row_count`. + +[NOTE] +==== +You can list more than one table in the `FROM` clause. +==== + +=== Special cases + +* If `row_count` is `NULL`, the query produces the same result as a query without a `LIMIT` clause. +* If `row_count` is zero, the statement returns an empty set. + +== Examples + +This example creates a `comporders` table and inserts values into it: + +[source,sql] +---- +CREATE TABLE comporders +( + order_id int, + cust_name text, + prod_name text, + prod_price float, + status text +); + +INSERT INTO comporders +VALUES +(1002, 'Mike', 'Lenovo IdeaPad Flex 5', 600, 'PAID'), +(1003, 'Sean', 'Acer Aspire 3', 450, 'PAID'), +(1004, 'Victor', 'Microsoft Surface Laptop Go 2', 500, 'PENDING'), +(1005, 'Lewis', 'Lenovo Duet 5i', 700, 'PAID'), +(1006, 'David', 'Acer Swift 3', 640, 'PAID'), +(1007, 'Meghan', 'Lenovo IdeaPad Duet 5 Chromebook', 750, 'PAID'), +(1008, 'Harry', 'Apple iPad Air', 449, 'PENDING'), +(1009, 'Steve', 'Microsoft Surface Go 3', 680, 'PENDING'), +(1010, 'Omar', 'HP Victus 16', 800,'PAID'); +---- + +To verify the inserted values, run: + +[source,sql] +---- +SELECT * FROM comporders; +---- + +The query returns: + +[source,sql] +---- ++-----------+------------+----------------------------------+-------------+----------+ +| order_id | cust_name | prod_name | prod_price | status | ++-----------+------------+----------------------------------+-------------+----------+ +| 1002 | Mike | Lenovo IdeaPad Flex 5 | 600 | PAID | +| 1003 | Sean | Acer Aspire 3 | 450 | PAID | +| 1004 | Victor | Microsoft Surface Laptop Go 2 | 500 | PENDING | +| 1005 | Lewis | Lenovo Duet 5i | 700 | PAID | +| 1006 | David | Acer Swift 3 | 640 | PAID | +| 1007 | Meghan | Lenovo IdeaPad Duet 5 Chromebook | 750 | PAID | +| 1008 | Harry | Apple iPad Air | 449 | PENDING | +| 1009 | Steve | Microsoft Surface Go 3 | 680 | PENDING | +| 1010 | Omar | HP Victus 16 | 800 | PAID | ++-----------+------------+----------------------------------+-------------+----------+ +---- + +=== LIMIT with ORDER BY expression + +This example uses the `LIMIT` clause to get the first four orders sorted by `order_id`: + +[source,sql] +---- +SELECT order_id, prod_name, prod_price +FROM comporders +ORDER BY order_id +LIMIT 4; +---- + +The query returns: + +[source,sql] +---- ++-----------+-------------------------------+-------------+ +| order_id | prod_name | prod_price | ++-----------+-------------------------------+-------------+ +| 1002 | Lenovo IdeaPad Flex 5 | 600 | +| 1003 | Acer Aspire 3 | 450 | +| 1004 | Microsoft Surface Laptop Go 2 | 500 | +| 1005 | Lenovo Duet 5i | 700 | ++-----------+-------------------------------+-------------+ +---- + +=== LIMIT with ASC or DESC + +You can use the `LIMIT` clause to select rows with the highest or lowest values from a table. + +. To get the five most expensive orders, sort by `prod_price` in descending order (`DESC`) and use `LIMIT` to return the first five rows: ++ +[source,sql] +---- +SELECT * FROM comporders +ORDER BY prod_price DESC +LIMIT 5; +---- ++ +The query returns: ++ +[source,sql] +---- ++-----------+------------+----------------------------------+-------------+----------+ +| order_id | cust_name | prod_name | prod_price | status | ++-----------+------------+----------------------------------+-------------+----------+ +| 1010 | Omar | HP Victus 16 | 800 | PAID | +| 1007 | Meghan | Lenovo IdeaPad Duet 5 Chromebook | 750 | PAID | +| 1005 | Lewis | Lenovo Duet 5i | 700 | PAID | +| 1009 | Steve | Microsoft Surface Go 3 | 680 | PENDING | +| 1006 | David | Acer Swift 3 | 640 | PAID | ++-----------+------------+----------------------------------+-------------+----------+ +---- + +. To get the five cheapest orders, sort by `prod_price` in ascending order (`ASC`) and use `LIMIT` to return the first five rows: ++ +[source,sql] +---- +SELECT * FROM comporders +ORDER BY prod_price ASC +LIMIT 5; +---- ++ +The query returns: ++ +[source,sql] +---- ++-----------+------------+----------------------------------+-------------+----------+ +| order_id | cust_name | prod_name | prod_price | status | ++-----------+------------+----------------------------------+-------------+----------+ +| 1008 | Harry | Apple iPad Air | 449 | PENDING | +| 1003 | Sean | Acer Aspire 3 | 450 | PAID | +| 1004 | Victor | Microsoft Surface Laptop Go 2 | 500 | PENDING | +| 1002 | Mike | Lenovo IdeaPad Flex 5 | 600 | PAID | +| 1006 | David | Acer Swift 3 | 640 | PAID | ++-----------+------------+----------------------------------+-------------+----------+ +---- + +=== LIMIT with OFFSET + +In this example, the `LIMIT` and `OFFSET` clauses get five orders: + +[source,sql] +---- +SELECT * FROM comporders +LIMIT 5 OFFSET 2; +---- + +The query returns: + +[source,sql] +---- ++-----------+------------+----------------------------------+-------------+----------+ +| order_id | cust_name | prod_name | prod_price | status | ++-----------+------------+----------------------------------+-------------+----------+ +| 1004 | Victor | Microsoft Surface Laptop Go 2 | 500 | PENDING | +| 1005 | Lewis | Lenovo Duet 5i | 700 | PAID | +| 1006 | David | Acer Swift 3 | 640 | PAID | +| 1007 | Meghan | Lenovo IdeaPad Duet 5 Chromebook | 750 | PAID | +| 1008 | Harry | Apple iPad Air | 449 | PENDING | ++-----------+------------+----------------------------------+-------------+----------+ +---- + +In this result: + +* Orders with `order_id` 1002 and 1003 are not displayed because the `OFFSET` value is `2`, so the first two rows are skipped. +* Orders with `order_id` 1009 and 1010 are not displayed because the `LIMIT` value is `5`, so only five rows are returned. diff --git a/modules/reference/pages/sql/sql-clauses/offset.adoc b/modules/reference/pages/sql/sql-clauses/offset.adoc new file mode 100644 index 000000000..b61f5d1df --- /dev/null +++ b/modules/reference/pages/sql/sql-clauses/offset.adoc @@ -0,0 +1,76 @@ += OFFSET +:description: The OFFSET clause skips a specified number of records from the result set. +:page-topic-type: reference + +The `OFFSET` clause skips a specified number of records from the result set. + +== Syntax + +[source,sql] +---- +SELECT columns +FROM table_name +OFFSET num; +---- + +Where: + +* `columns`: The columns to fetch. +* `table_name`: The table to fetch records from. +* `num`: The number of records to skip. + +== Examples + +The following example uses a `salaryemp` table. + +[source,sql] +---- +SELECT * FROM salaryemp ORDER BY emp_sal; +---- + +The query returns: + +[source,sql] +---- ++-----------+------------+----------------+-------------+ +| emp_id | emp_name | emp_div | emp_sal | ++-----------+------------+----------------+-------------+ +| 1008 | Harry | Operations | 4500 | +| 1005 | Lewis | Sales | 5500 | +| 1002 | Mike | Marketing | 6000 | +| 1003 | Sean | Marketing | 6500 | +| 1009 | Steve | Marketing | 6800 | +| 1004 | Victor | Finance | 7000 | +| 1007 | Meghan | Finance | 7500 | +| 1006 | David | Marketing | 8000 | +| 1010 | Omar | Finance | 8000 | +| 1011 | David | Sales | 8200 | ++-----------+------------+----------------+-------------+ +---- + +The following query skips the first three rows and returns the next five, ordered by salary: + +[source,sql] +---- +SELECT * FROM salaryemp +ORDER BY emp_sal +LIMIT 5 OFFSET 3; +---- + +* `OFFSET 3` skips the first three rows (`Harry`, `Lewis`, `Mike`). +* `LIMIT 5` returns the next five rows. + +The query returns: + +[source,sql] +---- ++-----------+------------+----------------+-------------+ +| emp_id | emp_name | emp_div | emp_sal | ++-----------+------------+----------------+-------------+ +| 1003 | Sean | Marketing | 6500 | +| 1009 | Steve | Marketing | 6800 | +| 1004 | Victor | Finance | 7000 | +| 1007 | Meghan | Finance | 7500 | +| 1006 | David | Marketing | 8000 | ++-----------+------------+----------------+-------------+ +---- diff --git a/modules/reference/pages/sql/sql-clauses/order-by.adoc b/modules/reference/pages/sql/sql-clauses/order-by.adoc new file mode 100644 index 000000000..0305d3f97 --- /dev/null +++ b/modules/reference/pages/sql/sql-clauses/order-by.adoc @@ -0,0 +1,279 @@ += ORDER BY +:description: The ORDER BY clause sorts the rows returned by a SELECT statement. +:page-topic-type: reference + +The `ORDER BY` clause sorts the rows returned by a `SELECT` statement. + +== Syntax + +[source,sql] +---- +SELECT columns +FROM table_name +ORDER BY sort_expression1 [ASC | DESC] [NULLS FIRST | NULLS LAST]; +---- + +=== Parameters + +* `columns`: The columns to retrieve. +* `table_name`: The table to retrieve records from. +* `ORDER BY`: The expression used to order the results. +* `ASC` or `DESC`: Optional. Specifies whether results are returned in ascending or descending order. Default is `ASC`. +* `NULLS FIRST` or `NULLS LAST`: Optional. Specifies where `NULL` values appear in the sort order. `NULLS FIRST` places `NULL` values before non-null values. `NULLS LAST` places `NULL` values after non-null values. The default is `NULLS LAST` for `ASC` order and `NULLS FIRST` for `DESC` order. + +== Examples + +The following examples use a table called `salaryemp`. To create the table, run the query: + +[source,sql] +---- +CREATE TABLE salaryemp +( + emp_id int, + emp_name text, + emp_div text, + emp_sal int +); + +INSERT INTO salaryemp +VALUES +(1002, 'Mike', 'Marketing', 6000), +(1003, 'Sean', 'Marketing', 6500), +(1004, 'Victor', 'Finance', 7000), +(1005, 'Lewis', 'Sales', 5500), +(1006, 'David', 'Marketing', 8000), +(1007, 'Meghan', 'Finance', 7500), +(1008, 'Harry', 'Operations', 4500), +(1009, 'Steve', 'Marketing', 6800), +(1010, 'Omar', 'Finance', 8000), +(1011, 'David', 'Sales', 8200); +---- + +To verify the inserted values, run: + +[source,sql] +---- +SELECT * FROM salaryemp; +---- + +The query returns: + +[source,sql] +---- ++-----------+------------+----------------+-------------+ +| emp_id | emp_name | emp_div | emp_sal | ++-----------+------------+----------------+-------------+ +| 1002 | Mike | Marketing | 6000 | +| 1003 | Sean | Marketing | 6500 | +| 1004 | Victor | Finance | 7000 | +| 1005 | Lewis | Sales | 5500 | +| 1006 | David | Marketing | 8000 | +| 1007 | Meghan | Finance | 7500 | +| 1008 | Harry | Operations | 4500 | +| 1009 | Steve | Marketing | 6800 | +| 1010 | Omar | Finance | 8000 | +| 1011 | David | Sales | 8200 | ++-----------+------------+----------------+-------------+ +---- + +=== ORDER BY in ascending order + +This example uses the `ORDER BY` clause to sort employees by their division: + +[source,sql] +---- +SELECT emp_name, emp_div +FROM salaryemp +ORDER BY emp_div; +---- + +The query returns: + +[source,sql] +---- ++------------+----------------+ +| emp_name | emp_div | ++------------+----------------+ +| Victor | Finance | +| Omar | Finance | +| Meghan | Finance | +| Mike | Marketing | +| Sean | Marketing | +| David | Marketing | +| Steve | Marketing | +| Harry | Operations | +| Lewis | Sales | +| David | Sales | ++------------+----------------+ +---- + +=== ORDER BY in descending order + +The following statement selects all records from the `salaryemp` table and sorts them by `emp_sal` in descending order: + +[source,sql] +---- +SELECT * FROM salaryemp +ORDER BY emp_sal DESC; +---- + +The query returns: + +[source,sql] +---- ++-----------+------------+----------------+-------------+ +| emp_id | emp_name | emp_div | emp_sal | ++-----------+------------+----------------+-------------+ +| 1011 | David | Sales | 8200 | +| 1006 | David | Marketing | 8000 | +| 1010 | Omar | Finance | 8000 | +| 1007 | Meghan | Finance | 7500 | +| 1004 | Victor | Finance | 7000 | +| 1009 | Steve | Marketing | 6800 | +| 1003 | Sean | Marketing | 6500 | +| 1002 | Mike | Marketing | 6000 | +| 1005 | Lewis | Sales | 5500 | +| 1008 | Harry | Operations | 4500 | ++-----------+------------+----------------+-------------+ +---- + +=== ORDER BY with both ASC and DESC parameters + +The following statement selects all records from the `salaryemp` table and sorts the rows by `emp_sal` in ascending order and `emp_div` in descending order: + +[source,sql] +---- +SELECT * FROM salaryemp +ORDER BY emp_sal ASC, emp_div DESC; +---- + +The query returns: + +[source,sql] +---- ++-----------+------------+----------------+-------------+ +| emp_id | emp_name | emp_div | emp_sal | ++-----------+------------+----------------+-------------+ +| 1009 | Harry | Operations | 4500 | +| 1005 | Lewis | Sales | 5500 | +| 1002 | Mike | Marketing | 6000 | +| 1003 | Sean | Marketing | 6500 | +| 1009 | Steve | Marketing | 6800 | +| 1004 | Victor | Finance | 7000 | +| 1007 | Meghan | Finance | 7500 | +| 1006 | David | Marketing | 8000 | +| 1010 | Omar | Finance | 8000 | +| 1011 | David | Sales | 8200 | ++-----------+------------+----------------+-------------+ +---- + +=== ORDER BY with TEXT data types + +In this example, two small tables are created with `TEXT` data: + +[source,sql] +---- +CREATE TABLE strings +( + column1 text +); + +INSERT INTO strings +VALUES ('A'), ('B'), ('a'), ('b'); + +CREATE TABLE texts +( + column1 TEXT +); + +INSERT INTO texts +VALUES ('A'), ('B'), ('a'), ('b'); +---- + +When using the `ORDER BY` clause with these data types, records with uppercase letters are sorted lexicographically first, followed by records with lowercase letters. + +[source,sql] +---- +SELECT * FROM strings ORDER BY column1; +SELECT * FROM texts ORDER BY column1; +---- + +The query returns: + +[source,sql] +---- + column1 +--------- + A + B + a + b +---- + +=== ORDER BY with INTERVAL data type + +For this example, create a new table called `interval_data`: + +[source,sql] +---- +CREATE TABLE interval_data ( + duration INTERVAL +); + +INSERT INTO interval_data (duration) +VALUES + (INTERVAL '1 month 30 days 20 hours'), + (INTERVAL '2 months 20 hours'), + (INTERVAL '1 month 30 days 19 hours'), + (INTERVAL '2 months 1 hours'); +---- + +`ORDER BY` on an `INTERVAL` column sorts values by their total duration. For example, `1 month 30 days 20 hours` is greater than `2 months 1 hour` because `1 month` equals `30 days`, making the first interval equivalent to `60 days 20 hours` versus `60 days 1 hour`. + +[source,sql] +---- +SELECT * FROM interval_data ORDER BY duration; +---- + +The query returns: + +[source,sql] +---- + duration +------------------------ + 2 mons 01:00:00 + 1 mon 30 days 19:00:00 + 1 mon 30 days 20:00:00 + 2 mons 20:00:00 +---- + +This total-duration sorting also applies when mixing `hours` and `days`. For example, `24 hours 5 minutes` and `1 day 5 minutes` represent the same total duration: + +[source,sql] +---- +INSERT INTO interval_data (duration) +VALUES + (INTERVAL '24 hours 5 minutes'), + (INTERVAL '1 day 5 minutes'), + (INTERVAL '1 day 2 minutes'); +---- + +[source,sql] +---- +SELECT * FROM interval_data ORDER BY duration; +---- + +The query returns: + +[source,sql] +---- + duration +------------------------ + 1 day 00:02:00 + 24:05:00 + 1 day 00:05:00 + 2 mons 01:00:00 + 1 mon 30 days 19:00:00 + 1 mon 30 days 20:00:00 + 2 mons 20:00:00 +---- diff --git a/modules/reference/pages/sql/sql-clauses/over-window.adoc b/modules/reference/pages/sql/sql-clauses/over-window.adoc new file mode 100644 index 000000000..464df7078 --- /dev/null +++ b/modules/reference/pages/sql/sql-clauses/over-window.adoc @@ -0,0 +1,163 @@ += OVER and WINDOW +:description: Window functions use the OVER and WINDOW clauses to define the set of rows over which the function operates. +:page-topic-type: reference + +Window functions use a set of clauses to define the rows they operate over. Some of these clauses are mandatory and others are optional. + +== OVER clause + +The `OVER` clause defines a window, or user-specified set of rows, within a query result set. It is mandatory for window functions and differentiates them from other SQL functions. + +=== Syntax + +[source,sql] +---- +OVER (PARTITION BY rows1 ORDER BY rows2) +---- + +The `PARTITION BY` clause is a list of expressions interpreted in much the same way as the elements of a `GROUP BY` clause, except that they are always simple expressions and never the name or number of an output column. These expressions can also contain aggregate function calls, which are not allowed in a regular `GROUP BY` clause (windowing occurs after grouping and aggregation). + +`[ PARTITION BY expression [, ...] ]` (optional window partition) + +The `ORDER BY` clause used in the `OVER` clause is a list of expressions interpreted in much the same way as the elements of a statement-level `ORDER BY` clause, except that the expressions are always taken as simple expressions and never the name or number of an output column. + +`[ ORDER BY expression [ ASC | DESC | USING operator ] [ NULLS { FIRST | LAST } ] [, ...] ]` (optional window ordering) + +== WINDOW clause + +The optional `WINDOW` clause defines one or more named window specifications, as a `window_name` and `window_definition` pair. + +=== Syntax + +[source,sql] +---- +WINDOW window_name AS (window_definition) [, ...] +---- + +`window_name` is a name that can be referenced from `OVER` clauses or subsequent window definitions. Note the following: + +* The `window_definition` may use an `existing_window_name` to refer to a previous `window_definition` in the `WINDOW` clause, but the previous `window_definition` must not specify a frame clause. +* The `window_definition` copies the `PARTITION BY` and `ORDER BY` clauses from the previous `window_definition`, but it cannot specify its own `PARTITION BY` clause. It can specify an `ORDER BY` clause if the previous `window_definition` does not have one. + +`[ existing_window_name ] [ PARTITION BY clause ] [ ORDER BY clause ] [ frame clause ]` (all arguments are optional) + +[NOTE] +==== +A `window_definition` without arguments defines a window with all rows, without partition or ordering. +==== + +The frame clause defines the window frame for window functions that depend on the frame (not all do). The window frame is a set of related rows for each row of the query (called the current row). + +* `{ RANGE | ROWS | GROUPS } frame_start [ frame_exclusion ]` +* `{ RANGE | ROWS | GROUPS } BETWEEN frame_start AND frame_end [ frame_exclusion ]` + +[NOTE] +==== +Redpanda SQL supports `ROWS` and `RANGE` frame modes. The `GROUPS` frame mode and `frame_exclusion` are not supported. +==== + +Note the following: + +* `frame_start` and `frame_end` can be one of: `UNBOUNDED PRECEDING`, `offset PRECEDING`, `CURRENT ROW`, `offset FOLLOWING`, `UNBOUNDED FOLLOWING`. +* If `frame_end` is omitted, it defaults to `CURRENT ROW`. The following restrictions apply: +** `frame_start` cannot be `UNBOUNDED FOLLOWING`. +** `frame_end` cannot be `UNBOUNDED PRECEDING`. +** `frame_end` cannot appear earlier in the list of `frame_start` and `frame_end` options than the `frame_start` choice does. + +In `ROWS` mode, `CURRENT ROW` means that the frame starts or ends with the current row. The offset is an integer indicating that the frame starts or ends that many rows before or after the current row. + +[NOTE] +==== +The `ROWS` mode can produce unpredictable results if the `ORDER BY` ordering does not order the rows uniquely. +==== + +== Examples + +For the examples in this section, create the `winsales` table: + +[source,sql] +---- +CREATE TABLE winsales( + salesid int, + dateid date, + sellerid int, + buyerid text, + qty int, + qty_shipped int); +INSERT INTO winsales VALUES + (30001, '8/2/2003', 3, 'b', 10, 10), + (10001, '12/24/2003', 1, 'c', 10, 10), + (10005, '12/24/2003', 1, 'a', 30, null), + (40001, '1/9/2004', 4, 'a', 40, null), + (10006, '1/18/2004', 1, 'c', 10, null), + (20001, '2/12/2004', 2, 'b', 20, 20), + (40005, '2/12/2004', 4, 'a', 10, 10), + (20002, '2/16/2004', 2, 'c', 20, 20), + (30003, '4/18/2004', 3, 'b', 15, null), + (30004, '4/18/2004', 3, 'b', 20, null), + (30007, '9/7/2004', 3, 'c', 30, null); +---- + +=== OVER clause with PARTITION BY and ORDER BY + +This example uses the `OVER` clause with `PARTITION BY` and `ORDER BY`: + +[source,sql] +---- +SELECT *, + SUM(qty) OVER (PARTITION BY sellerid) AS seller_qty +FROM winsales +ORDER BY sellerid, salesid; +---- + +The query returns: + +[source,sql] +---- + salesid | dateid | sellerid | buyerid | qty | qty_shipped | seller_qty +---------+------------+----------+---------+-----+-------------+------------ + 10001 | 2003-12-24 | 1 | c | 10 | 10 | 50 + 10005 | 2003-12-24 | 1 | a | 30 | | 50 + 10006 | 2004-01-18 | 1 | c | 10 | | 50 + 20001 | 2004-02-12 | 2 | b | 20 | 20 | 40 + 20002 | 2004-02-16 | 2 | c | 20 | 20 | 40 + 30001 | 2003-08-02 | 3 | b | 10 | 10 | 75 + 30003 | 2004-04-18 | 3 | b | 15 | | 75 + 30004 | 2004-04-18 | 3 | b | 20 | | 75 + 30007 | 2004-09-07 | 3 | c | 30 | | 75 + 40001 | 2004-01-09 | 4 | a | 40 | | 50 + 40005 | 2004-02-12 | 4 | a | 10 | 10 | 50 +(11 rows) +---- + +=== OVER clause with named window + +This example uses the `OVER` clause with a named window defined in the `WINDOW` clause: + +[source,sql] +---- +SELECT *, + SUM(qty) OVER seller AS seller_qty +FROM winsales WINDOW seller AS (PARTITION BY sellerid) +ORDER BY sellerid, salesid; +---- + +The query returns: + +[source,sql] +---- + salesid | dateid | sellerid | buyerid | qty | qty_shipped | seller_qty +---------+------------+----------+---------+-----+-------------+------------ + 10001 | 2003-12-24 | 1 | c | 10 | 10 | 50 + 10005 | 2003-12-24 | 1 | a | 30 | | 50 + 10006 | 2004-01-18 | 1 | c | 10 | | 50 + 20001 | 2004-02-12 | 2 | b | 20 | 20 | 40 + 20002 | 2004-02-16 | 2 | c | 20 | 20 | 40 + 30001 | 2003-08-02 | 3 | b | 10 | 10 | 75 + 30003 | 2004-04-18 | 3 | b | 15 | | 75 + 30004 | 2004-04-18 | 3 | b | 20 | | 75 + 30007 | 2004-09-07 | 3 | c | 30 | | 75 + 40001 | 2004-01-09 | 4 | a | 40 | | 50 + 40005 | 2004-02-12 | 4 | a | 10 | 10 | 50 +(11 rows) +---- diff --git a/modules/reference/pages/sql/sql-clauses/set-operations/except.adoc b/modules/reference/pages/sql/sql-clauses/set-operations/except.adoc new file mode 100644 index 000000000..b7e9d476c --- /dev/null +++ b/modules/reference/pages/sql/sql-clauses/set-operations/except.adoc @@ -0,0 +1,249 @@ += EXCEPT +:description: The EXCEPT combines the result sets of two or more tables and retrieves rows specific to the first SELECT statement but not present in the subsequent ones. +:page-topic-type: reference + +`EXCEPT` combines the result sets of two or more tables and retrieves rows specific to the first `SELECT` statement but not present in the subsequent ones. + +== Syntax + +[source,sql] +---- +SELECT value1, value2, ... value_n +FROM table1 +EXCEPT +SELECT value1, value2, ... value_n +FROM table2; +---- + +Where: + +* `value1, value2, ... value_n`: The columns to retrieve. +* `table1, table2`: The tables to retrieve records from. + +== Examples + +Suppose you have two tables: `vehicles_2021` and `vehicles_2022`. The goal is to find the vehicle that was present in 2021 but is not present in 2022: + +[source,sql] +---- +CREATE TABLE vehicles_2021 ( + vhc_id INT, + vhc_name TEXT +); + +CREATE TABLE vehicles_2022 ( + vhc_id INT, + vhc_name TEXT +); + +INSERT INTO vehicles_2021 VALUES +(1, 'Truck'), +(2, 'Car'), +(3, 'Motorcycle'); + +INSERT INTO vehicles_2022 VALUES +(2, 'Car'), +(3, 'Bus'), +(4, 'Motorcycle'); +---- + +View the tables: + +[source,sql] +---- +SELECT * FROM vehicles_2021; +SELECT * FROM vehicles_2022; +---- + +[source,sql] +---- +vhc_id | vhc_name +--------+------------ + 1 | Truck + 2 | Car + 3 | Motorcycle + + vhc_id | vhc_name +--------+------------ + 2 | Car + 3 | Bus + 4 | Motorcycle +---- + +Use `EXCEPT` to find vehicle names present in the first table but not in the second: + +[source,sql] +---- +SELECT vhc_name FROM vehicles_2021 +EXCEPT +SELECT vhc_name FROM vehicles_2022; +---- + +The query returns vehicle names that exist in `vehicles_2021` but not in `vehicles_2022`: + +[source,sql] +---- +vhc_name +---------- + Truck +---- + +Only `Truck` is present in the first table but not the second. + +== EXCEPT ALL + +`EXCEPT ALL` finds rows specific to the first `SELECT` statement while preserving duplicate entries. + +=== Syntax + +[source,sql] +---- +SELECT value1, value2, ... value_n +FROM table1 +EXCEPT ALL +SELECT value1, value2, ... value_n +FROM table2; +---- + +Where: + +* `value1, value2, ... value_n`: The columns to retrieve. +* `table1, table2`: The tables to retrieve records from. + +=== Customers in only one marketplace + +To identify customers who have purchased products from one marketplace but not from another, create the tables and populate them with relevant data: + +[source,sql] +---- +CREATE TABLE marketplace1_transactions ( + customer_id INT, + product_id INT, + amount FLOAT +); + +CREATE TABLE marketplace2_transactions ( + customer_id INT, + product_id INT, + amount FLOAT +); + +INSERT INTO marketplace1_transactions VALUES +(101, 1, 100.00), +(102, 2, 150.00), +(103, 3, 200.00), +(104, 1, 120.00); + +INSERT INTO marketplace2_transactions VALUES +(102, 3, 180.00), +(103, 2, 160.00), +(105, 4, 90.00), +(106, 1, 110.00); +---- + +View the tables: + +[source,sql] +---- +SELECT * FROM marketplace1_transactions; +SELECT * FROM marketplace2_transactions; +---- + +[source,sql] +---- +customer_id | product_id | amount +-------------+------------+-------- + 101 | 1 | 100 + 102 | 2 | 150 + 103 | 3 | 200 + 104 | 1 | 120 + + customer_id | product_id | amount +-------------+------------+-------- + 102 | 3 | 180 + 103 | 2 | 160 + 105 | 4 | 90 + 106 | 1 | 110 +---- + +Use `EXCEPT ALL` to find customers who have purchased products from one marketplace but not from the other: + +[source,sql] +---- +SELECT customer_id FROM marketplace1_transactions +EXCEPT ALL +SELECT customer_id FROM marketplace2_transactions; +---- + +The query returns the `customer_id` values that appear in the first marketplace but not in the second: + +[source,sql] +---- +customer_id +------------- + 104 + 101 +---- + +=== Compare arrays with duplicates + +Create two tables, `left_array_values` and `right_array_values`, to hold sets of values: + +[source,sql] +---- +CREATE TABLE left_array_values ( + value INT +); + +CREATE TABLE right_array_values ( + value INT +); + +INSERT INTO left_array_values VALUES (1), (1), (3); +INSERT INTO right_array_values VALUES (1), (2); +---- + +View the contents of the two tables before performing the comparison: + +[source,sql] +---- +SELECT * FROM left_array_values; +SELECT * FROM right_array_values; +---- + +The tables contain: + +[source,sql] +---- +value +------- + 1 + 1 + 3 + + value +------- + 1 + 2 +---- + +Use `EXCEPT ALL` to compare the values, focusing on unique elements while retaining duplicate entries: + +[source,sql] +---- +SELECT value +FROM left_array_values +EXCEPT ALL +SELECT value +FROM right_array_values; +---- + +`EXCEPT ALL` compares elements pairwise, so both `1` and `3` appear in the final result: + +[source,sql] +---- +value +------- + 3 + 1 +---- diff --git a/modules/reference/pages/sql/sql-clauses/set-operations/index.adoc b/modules/reference/pages/sql/sql-clauses/set-operations/index.adoc new file mode 100644 index 000000000..0e90e8001 --- /dev/null +++ b/modules/reference/pages/sql/sql-clauses/set-operations/index.adoc @@ -0,0 +1,14 @@ += Set operations +:description: Set operations combine, compare, or contrast result sets from multiple SELECT statements. +:page-topic-type: reference + +Set operations combine, compare, or contrast result sets from multiple `SELECT` statements. Redpanda SQL supports the following operations: + +* xref:reference:sql/sql-clauses/set-operations/union.adoc[Union]: Combines two or more sets to create a new set containing all unique elements from the input sets. +* xref:reference:sql/sql-clauses/set-operations/intersect.adoc[Intersect]: Yields a new set with elements common to all input sets. +* xref:reference:sql/sql-clauses/set-operations/except.adoc[Except]: Generates a set containing elements from the first set that are not present in the second set. + +[NOTE] +==== +For all set operations, the data types of corresponding columns in the `SELECT` queries must be compatible. The order of columns is flexible as long as the columns in consecutive places are pairwise compatible. For example, `SELECT col1, col2 FROM table1 UNION SELECT col2, col1 FROM table2`. +==== diff --git a/modules/reference/pages/sql/sql-clauses/set-operations/intersect.adoc b/modules/reference/pages/sql/sql-clauses/set-operations/intersect.adoc new file mode 100644 index 000000000..b2b8eafa5 --- /dev/null +++ b/modules/reference/pages/sql/sql-clauses/set-operations/intersect.adoc @@ -0,0 +1,207 @@ += INTERSECT +:description: The INTERSECT combines the result sets of two or more SELECT statements, retrieving only the common rows between them. +:page-topic-type: reference + +`INTERSECT` combines the result sets of two or more `SELECT` statements, retrieving only the common rows between them. Unlike `UNION`, which combines all rows and removes duplicates, `INTERSECT` returns rows that appear in all `SELECT` statements. + +== Syntax + +[source,sql] +---- +SELECT value1, value2, ... value_n +FROM table1 +INTERSECT +SELECT value1, value2, ... value_n +FROM table2; +---- + +Where: + +* `value1, value2, ... value_n`: The columns to retrieve. You can also use `SELECT * FROM` to retrieve all columns. +* `table1, table2`: The tables to retrieve records from. + +== Examples + +Suppose you have two tables: `customers_old` and `customers_new`, containing customer data for different periods. To find the customers who are present in both tables: + +[source,sql] +---- +CREATE TABLE customers_old ( + customer_id INT, + customer_name TEXT +); + +CREATE TABLE customers_new ( + customer_id INT, + customer_name TEXT +); + +INSERT INTO customers_old VALUES +(1, 'Alice'), +(2, 'Bob'), +(3, 'Charlie'); + +INSERT INTO customers_new VALUES +(2, 'Bob'), +(3, 'Charlie'), +(4, 'David'); +---- + +View the inserted values: + +[source,sql] +---- +SELECT * FROM customers_old; +SELECT * FROM customers_new; +---- + +[source,sql] +---- +customer_id | customer_name +-------------+--------------- + 1 | Alice + 2 | Bob + 3 | Charlie + + customer_id | customer_name +-------------+--------------- + 2 | Bob + 3 | Charlie + 4 | David +---- + +Combine common customers using `INTERSECT`: + +[source,sql] +---- +SELECT customer_name FROM customers_old +INTERSECT +SELECT customer_name FROM customers_new; +---- + +The query returns only the names that appear in both tables: + +[source,sql] +---- +customer_name +--------------- + Bob + Charlie +---- + +Only `Bob` and `Charlie` appear in both tables. + +== INTERSECT ALL + +`INTERSECT ALL` retrieves all common rows between two or more tables, including duplicates. If a row appears in any of the `SELECT` statements multiple times, it is included in the final result set the same number of times. + +=== Syntax + +[source,sql] +---- +SELECT value1, value2, ... value_n +FROM table1 +INTERSECT ALL +SELECT value1, value2, ... value_n +FROM table2; +---- + +Where: + +* `value1, value2, ... value_n`: The columns to retrieve. You can also retrieve all columns using `SELECT * FROM`. +* `table1, table2`: The tables to retrieve records from. + +=== Example + +Create three tables of products from different years. To find the common products among all three tables, including duplicates: + +[source,sql] +---- +CREATE TABLE products_electronics2021 ( + product_id INT, + product_name TEXT +); + +CREATE TABLE products_electronics2022 ( + product_id INT, + product_name TEXT +); + +CREATE TABLE products_electronics2023 ( + product_id INT, + product_name TEXT +); + +INSERT INTO products_electronics2021 VALUES +(1, 'Laptop'), +(2, 'Phone'), +(3, 'Tablet'), +(4, 'Headphones'); + +INSERT INTO products_electronics2022 VALUES +(2, 'TV'), +(3, 'Printer'), +(4, 'Monitor'), +(5, 'Phone'); + +INSERT INTO products_electronics2023 VALUES +(3, 'Laptop'), +(4, 'Phone'), +(5, 'Oven'), +(6, 'AC'); +---- + +View the tables: + +[source,sql] +---- +SELECT * FROM products_electronics2021; +SELECT * FROM products_electronics2022; +SELECT * FROM products_electronics2023; +---- + +[source,sql] +---- +product_id | product_name +------------+-------------- + 1 | Laptop + 2 | Phone + 3 | Tablet + 4 | Headphones + + product_id | product_name +------------+-------------- + 2 | TV + 3 | Printer + 4 | Monitor + 5 | Phone + + product_id | product_name +------------+-------------- + 3 | Laptop + 4 | Phone + 5 | Oven + 6 | AC +---- + +Combine common products from all three tables using `INTERSECT ALL`: + +[source,sql] +---- +SELECT product_name FROM products_electronics2021 +INTERSECT ALL +SELECT product_name FROM products_electronics2022 +INTERSECT ALL +SELECT product_name FROM products_electronics2023; +---- + +The query returns the products common to all three tables, including duplicates: + +[source,sql] +---- +product_name +-------------- + Phone +---- + +Only `Phone` appears across all three tables. diff --git a/modules/reference/pages/sql/sql-clauses/set-operations/union.adoc b/modules/reference/pages/sql/sql-clauses/set-operations/union.adoc new file mode 100644 index 000000000..4108ade3e --- /dev/null +++ b/modules/reference/pages/sql/sql-clauses/set-operations/union.adoc @@ -0,0 +1,188 @@ += UNION +:description: The UNION combines the result sets of two or more SELECT statements, removing duplicate rows between the tables. +:page-topic-type: reference + +`UNION` combines the result sets of two or more `SELECT` statements, removing duplicate rows between the tables. + +== Syntax + +[source,sql] +---- +SELECT value1, value2, ... value_n +FROM table1 +UNION +SELECT value1, value2, ... value_n +FROM table2; +---- + +Where: + +* `value1, value2, ... value_n`: The columns to retrieve. You can also retrieve all columns using `SELECT * FROM`. +* `table1, table2`: The tables to retrieve records from. + +== Examples + +Suppose there is a table called `employees` and another table called `contractors`. The goal is to retrieve a combined list of names from both tables, excluding duplicates: + +[source,sql] +---- +CREATE TABLE employees ( + emp_id INT, + emp_name TEXT +); + +CREATE TABLE contractors ( + contractor_id INT, + contractor_name TEXT +); + +INSERT INTO employees VALUES +(1, 'John'), +(2, 'Alice'), +(3, 'Bob'); + +INSERT INTO contractors VALUES +(101, 'Alice'), +(102, 'Eve'), +(103, 'Tom'); +---- + +Verify the inserted values: + +[source,sql] +---- +SELECT * FROM employees; +SELECT * FROM contractors; +---- + +[source,sql] +---- +emp_id | emp_name +--------+---------- + 1 | John + 2 | Alice + 3 | Bob + + contractor_id | contractor_name +---------------+----------------- + 101 | Alice + 102 | Eve + 103 | Tom +---- + +Combine the values from the tables: + +[source,sql] +---- +SELECT emp_name FROM employees +UNION +SELECT contractor_name FROM contractors; +---- + +The query returns the values from both tables without duplicates: + +[source,sql] +---- +emp_name +---------- + Alice + Bob + Eve + John + Tom +---- + +The duplicate name `Alice` appears only once in the output. + +== UNION ALL + +`UNION ALL` combines the result sets of two or more `SELECT` statements, returning all rows from the queries without removing duplicates. + +=== Syntax + +[source,sql] +---- +SELECT value1, value2, ... value_n +FROM table1 +UNION ALL +SELECT value1, value2, ... value_n +FROM table2; +---- + +Where: + +* `value1, value2, ... value_n`: The columns to retrieve. You can also retrieve all columns using `SELECT * FROM`. +* `table1, table2`: The tables to retrieve records from. + +=== Example + +Suppose you have two separate tables, `sales_2022` and `sales_2023`, containing sales data for different years. To combine the sales data from both tables without removing duplicates: + +[source,sql] +---- +CREATE TABLE sales_2022 ( + transaction_id INT, + product_name TEXT, + sale_amount INT +); + +CREATE TABLE sales_2023 ( + transaction_id INT, + product_name TEXT, + sale_amount INT +); + +INSERT INTO sales_2022 VALUES +(1, 'Product A', 1000), +(2, 'Product B', 500), +(3, 'Product C', 750); + +INSERT INTO sales_2023 VALUES +(4, 'Product A', 1200), +(5, 'Product D', 800), +(6, 'Product E', 950); +---- + +Verify the inserted values: + +[source,sql] +---- +SELECT * FROM sales_2022; +SELECT * FROM sales_2023; +---- + +[source,sql] +---- +transaction_id | product_name | sale_amount +----------------+--------------+------------- + 1 | Product A | 1000 + 2 | Product B | 500 + 3 | Product C | 750 + + transaction_id | product_name | sale_amount +----------------+--------------+------------- + 4 | Product A | 1200 + 5 | Product D | 800 + 6 | Product E | 950 +---- + +Combine all values from the tables using `UNION ALL`: + +[source,sql] +---- +SELECT product_name, sale_amount FROM sales_2022 UNION ALL SELECT product_name, sale_amount FROM sales_2023; +---- + +The query returns all rows from the first table followed by all rows from the second table, including duplicates: + +[source,sql] +---- +product_name | sale_amount +--------------+------------- + Product A | 1000 + Product B | 500 + Product C | 750 + Product A | 1200 + Product D | 800 + Product E | 950 +---- diff --git a/modules/reference/pages/sql/sql-clauses/where.adoc b/modules/reference/pages/sql/sql-clauses/where.adoc new file mode 100644 index 000000000..d8e99c089 --- /dev/null +++ b/modules/reference/pages/sql/sql-clauses/where.adoc @@ -0,0 +1,239 @@ += WHERE +:description: The WHERE clause filters records based on specified conditions, returning only the rows that meet the conditions. +:page-topic-type: reference + +The `WHERE` clause filters records based on specified conditions. It excludes records that do not meet the conditions and returns only the rows that match. + +== Syntax + +[source,sql] +---- +SELECT column1, column2, ... +FROM table_name +WHERE [condition] +---- + +This syntax includes the following elements: + +* `SELECT column1, column2, ...` defines the columns where the records are displayed. +* `FROM table_name` sets the table where the records are taken from. +* `WHERE [condition]` specifies the search condition using comparison or logical operators (for example, `>`, `=`, `LIKE`). + +[TIP] +==== +The query starts with the `FROM` clause, then evaluates the `WHERE` condition, and finally runs `SELECT` on the specified columns. +==== + +== Examples + +Assume there is a table called `salary` with the following records: + +[source,sql] +---- +CREATE TABLE salary ( + empid int, + empname text, + empdept text, + empaddress text, + empsalary int +); +INSERT INTO salary + (empid, empname, empdept, empaddress, empsalary) +VALUES + (2001,'Paul','HR', 'California', null ), + (2002,'Brandon','Product', 'Norway', 15000), + (2003,'Bradley','Marketing', 'Texas', null), + (2004,'Lisa','Marketing', 'Houston', 10000), + (2005,'Emily','Marketing', 'Texas', 20000), + (2006,'Bobby','Finance', 'Seattle', 20000), + (2007,'Parker','Project', 'Texas', 45000); +---- + +[source,sql] +---- +SELECT * FROM salary; +---- + +This returns: + +[source,sql] +---- ++--------+-----------+------------+-------------+------------+ +| empid | empname | empdept | empaddress | empsalary | ++--------+-----------+------------+-------------+------------+ +| 2001 | Paul | HR | California | null | +| 2002 | Brandon | Product | Norway | 15000 | +| 2003 | Bradley | Marketing | Texas | null | +| 2004 | Lisa | Marketing | Houston | 10000 | +| 2005 | Emily | Marketing | Texas | 20000 | +| 2006 | Bobby | Finance | Seattle | 20000 | +| 2007 | Parker | Project | Texas | 45000 | ++--------+-----------+------------+-------------+------------+ +---- + +=== WHERE clause with `=` operator + +The following example uses the `=` (equal) operator to look up the employees who work in the Marketing department: + +[source,sql] +---- +SELECT empname, empdept +FROM salary +WHERE empdept = 'Marketing'; +---- + +The query returns: + +[source,sql] +---- ++------------+-------------+ +| empname | empdept | ++------------+-------------+ +| Bradley | Marketing | +| Emily | Marketing | +| Lisa | Marketing | ++------------+-------------+ +---- + +[WARNING] +==== +The value defined in the `WHERE` clause condition is case-sensitive. Specify the exact value to match. +==== + +=== WHERE clause with `!=` operator + +The following example uses the `!=` (not equal) operator to look up employees who do not live in Texas: + +[source,sql] +---- +SELECT empname, empdept, empaddress +FROM salary +WHERE empaddress != 'Texas'; +---- + +[NOTE] +==== +You can also use the `<>` operator for "not equal". +==== + +The query returns: + +[source,sql] +---- ++------------+------------+--------------+ +| empname | empdept | empaddress | ++------------+------------+--------------+ +| Paul | HR | California | +| Brandon | Product | Norway | +| Lisa | Marketing | Houston | +| Bobby | Finance | Seattle | ++------------+------------+--------------+ +---- + +=== WHERE clause with `>` operator + +The following example uses the `>` (greater than) operator to find employees with a salary above 20000: + +[source,sql] +---- +SELECT empname, empdept, empsalary +FROM salary +WHERE empsalary > 20000; +---- + +[NOTE] +==== +You can use the `<` operator for a "less than" condition. +==== + +The query returns: + +[source,sql] +---- ++------------+------------+-------------+ +| empname | empdept | empsalary | ++------------+------------+-------------+ +| Parker | Project | 45000 | ++------------+------------+-------------+ +---- + +Only Parker has a salary greater than 20000. + +=== WHERE clause with `<=` operator + +The following example uses the `<=` (less than or equal to) operator to find employees with a salary less than or equal to 15000: + +[source,sql] +---- +SELECT empname, empdept, empsalary +FROM salary +WHERE empsalary <= '15000'; +---- + +[NOTE] +==== +You can use the `>=` operator for a "greater than or equal to" condition. +==== + +The query returns: + +[source,sql] +---- ++------------+------------+-------------+ +| empname | empdept | empsalary | ++------------+------------+-------------+ +| Brandon | Product | 15000 | +| Lisa | Marketing | 10000 | ++------------+------------+-------------+ +---- + +Brandon has a salary equal to 15000 and Lisa has a salary less than 15000. + +=== WHERE clause with `LIKE` operator + +The following example uses the `LIKE` operator to retrieve employees whose first name starts with `Br`: + +[source,sql] +---- +SELECT * FROM salary +WHERE empname LIKE 'Br%'; +---- + +[NOTE] +==== +To match a string at the end instead of the start, use `LIKE '%string'`. +==== + +The query returns `Brandon` and `Bradley`: + +[source,sql] +---- ++---------+------------+--------------+--------------+-----------+ +| empid | empname | empdept | empaddress | empsalary | ++---------+------------+-------------+--------------+------------+ +| 2002 | Brandon | Product | Norway | 15000 | +| 2003 | Bradley | Marketing | Texas | null | ++---------+------------+-------------+--------------+------------+ +---- + +=== WHERE clause with `IS NULL` operator + +The following example uses the `IS NULL` operator to find employees who do not have a salary value: + +[source,sql] +---- +SELECT * FROM salary +WHERE empsalary IS NULL; +---- + +The query returns: + +[source,sql] +---- ++---------+------------+-------------+--------------+------------+ +| empid | empname | empdept | empaddress | empsalary | ++---------+------------+-------------+--------------+------------+ +| 2001 | Paul | HR | California | null | +| 2003 | Bradley | Marketing | Texas | null | ++---------+------------+-------------+--------------+------------+ +---- diff --git a/modules/reference/pages/sql/sql-clauses/with.adoc b/modules/reference/pages/sql/sql-clauses/with.adoc new file mode 100644 index 000000000..17e060dcb --- /dev/null +++ b/modules/reference/pages/sql/sql-clauses/with.adoc @@ -0,0 +1,91 @@ += WITH +:description: The WITH clause defines auxiliary statements (Common Table Expressions) for use within a larger query. +:page-topic-type: reference + +The `WITH` clause defines auxiliary statements (referred to by their alias names) for use within a larger query. These auxiliary statements are also known as Common Table Expressions (CTEs). + +== Syntax + +The `WITH` clause precedes the primary statement it is attached to and contains a list of auxiliary statements with corresponding aliases. + +[source,sql] +---- +WITH [with_statement_alias AS (with_statement_body)]+ primary_statement; +---- + +* `primary_statement`: A `SELECT`, `INSERT`, `UPDATE`, or `DELETE` statement. +* `with_statement_body`: A `SELECT` statement. It can refer to aliases defined earlier in the query. + +== Semantics + +Redpanda SQL only supports non-materialized CTEs. Each auxiliary query alias is replaced with its corresponding body at the early stages of query processing. The following query: + +[source,sql] +---- +WITH a AS (SELECT 77), b AS (SELECT * FROM a) SELECT * FROM b +---- + +is effectively turned into: + +[source,sql] +---- +SELECT * FROM (SELECT * FROM (SELECT 77) AS a) AS b +---- + +The auxiliary query gets the same alias (`AS b` part) as in the `WITH` clause. To change it, set a new alias on usage. + +[source,sql] +---- +WITH b AS (SELECT 1 AS c1) SELECT b.c1, b1.c1 FROM b CROSS JOIN b AS b1; +---- + +== Usage + +Non-materialized `WITH` clauses are useful when you want to refactor a complex query to make it more readable. You can extract subqueries or reuse them in several places, having only one definition. Each use of a query is optimized separately, specifically for how the parent query uses its results. For example: + +[source,sql] +---- +WITH math_grades AS (SELECT g_date, semester_id, grade FROM grades WHERE subject="Math") +SELECT * FROM +(SELECT AVG(grade) FROM math_grades WHERE semester_id=2137) AS avg_semester_grades, +(SELECT AVG(grade) FROM math_grades WHERE g_date >= (CURRENT_TIMESTAMP() - INTERVAL '1 y')) AS avg_year_grades +---- + +Both subqueries use the same auxiliary `math_grades` query, but each filters it using different keys. As a result, both scans only read part of the table. With a materialized CTE (not yet supported), the query engine would scan the whole table first and then filter the result twice, once for each subquery. + +== Alias context + +You cannot create more than one CTE with the same alias within a single `WITH` clause. However, nested `SELECT` statements can each have their own `WITH` clauses, creating their own contexts for defined aliases. + +[NOTE] +==== +The same alias can be defined in more than one context. +==== + +[source,sql] +---- +WITH a AS ( -- creates context 1 + SELECT 1 +) +SELECT * FROM ( + WITH a AS (SELECT 2) -- creates context 2 + SELECT * FROM a -- uses context 2 +) CROSS JOIN a; -- uses context 1 +---- + +The query returns `2, 1` as output. + +When referencing an alias, the context defined at the nested query level is used. If the nested context does not define the referenced alias, the search moves up one level and repeats until an alias definition is found. + +[source,sql] +---- +WITH a AS ( + SELECT 1 +) +SELECT * FROM ( + WITH b as (SELECT 2) + SELECT * FROM b +) CROSS JOIN b; -- error +---- + +The query returns `ERROR: relation "b" does not exist`, because `b` is not defined in this context or any of the contexts above. diff --git a/modules/reference/pages/sql/sql-data-types/array.adoc b/modules/reference/pages/sql/sql-data-types/array.adoc new file mode 100644 index 000000000..a51bbf3b7 --- /dev/null +++ b/modules/reference/pages/sql/sql-data-types/array.adoc @@ -0,0 +1,184 @@ += Array +:description: In Redpanda SQL, an array stores a collection of elements that have the same data type (any built-in data type can be used). +:page-topic-type: reference + +In Redpanda SQL, an array stores a collection of elements that have the same data type (any built-in data type can be used). + +[NOTE] +==== +Currently, the implementation is limited only to single-dimensional arrays. +==== + +== Array type declaration + +An array type can be declared by appending square brackets to the data type of its elements: + +[source,sql] +---- +CREATE TABLE movie_night ( + event_date DATE NOT NULL, + movies_planned TEXT[5] NOT NULL +); +---- + +This syntax specifies the size of the array. However, it does not enforce any limits, and the behavior is the same as for arrays of unspecified length. There is also another way to declare an array, by prepending the `ARRAY` keyword after the data type of the elements: + +[source,sql] +---- +CREATE TABLE movie_night ( + event_date DATE NOT NULL, + movies_planned TEXT ARRAY NOT NULL +); +---- + +== Array values + +You can create array literals by using the `ARRAY` keyword and combining it with the array's values enclosed in square brackets and separated by commas: + +[source,sql] +---- +ARRAY[ value1 , value2 , ... ] +---- + +You can use such a literal with, for example, `SELECT` or `INSERT INTO` statements: + +[source,sql] +---- +SELECT ARRAY['10:14:25'::time, '22:58:11'::time]; + ?column? +--------------------- + {10:14:25,22:58:11} +(1 row) + +INSERT INTO movie_night VALUES +('2024-12-01', ARRAY['Inception', 'Interstellar', 'The Prestige']); +INSERT 0 1 + +SELECT * FROM movie_night; + event_date | movies_planned +------------+----------------------------------------- + 2024-12-01 | {Inception,Interstellar,"The Prestige"} +(1 row) +---- + +You can also use a string representation of an array as another available option for array's values syntax. It requires the elements' values to be enclosed in curly braces and separated by commas: + +[source,sql] +---- +'{ value1 , value2 , ... }' +---- + +You can use such an array value representation in, for example, `INSERT INTO` statements with the `VALUES` clause: + +[source,sql] +---- +INSERT INTO movie_night VALUES ('2024-12-15', '{The Matrix, John Wick}'); +INSERT 0 1 + +SELECT * FROM movie_night; +event_date | movies_planned +------------+----------------------------------------- +2024-12-01 | {Inception,Interstellar,"The Prestige"} +2024-12-15 | {"The Matrix","John Wick"} +(2 rows) +---- + +Any element can be enclosed in double quotes and this is required, if the value contains commas or curly braces: + +[source,sql] +---- +SELECT '{"{\"key1\": 1, \"key2\": \"value\"}", NULL, true}'::json[]; + ?column? +----------------------------------------------- + {"{\"key1\":1,\"key2\":\"value\"}",NULL,true} +(1 row) +---- + +[NOTE] +==== +In this example, the double quotes which are a part of the JSON value are required to be escaped with a backslash, so that they are not mistaken with the double quote, which marks the end of the element. +==== + +== Access arrays + +You can retrieve a single element from an array using the array subscript operator. When it comes to array values indexing, the elements of an n-length array start at index `1` and end at index `n`: + +[source,sql] +---- +SELECT movies_planned, + movies_planned[1] AS first_movie, + movies_planned[3] AS third_movie +FROM movie_night; + movies_planned | first_movie | third_movie +-----------------------------------------+-------------+-------------- + {Inception,Interstellar,"The Prestige"} | Inception | The Prestige + {"The Matrix","John Wick"} | The Matrix | +(2 rows) +---- + +[NOTE] +==== +If the index exceeds the length of an array, the returned value is `NULL`. +==== +Arrays can also be accessed by using array slices. An array slice is denoted by writing `lower_bound:upper_bound`. The bounds can be omitted, in which case the slice is unbounded from a given side: + +[source,sql] +---- +SELECT movies_planned[:] as "unbounded slice", + movies_planned[1:2] AS "[1:2] slice", + movies_planned[2:] AS "[2:] slice" +FROM movie_night; + unbounded slice | [1:2] slice | [2:] slice +-----------------------------------------+----------------------------+------------------------------- + {Inception,Interstellar,"The Prestige"} | {Inception,Interstellar} | {Interstellar,"The Prestige"} + {"The Matrix","John Wick"} | {"The Matrix","John Wick"} | {"John Wick"} +(2 rows) +---- + +== Limitations + +=== Field size limit + +In Redpanda SQL, the field size limit for variable-size types is 32MB and this limit applies to arrays as well. If a value exceeds the given limit, an error is returned: + +[source,sql] +---- +CREATE TABLE tb (array_column bigint[]); +CREATE + +COPY tb FROM '/.oxla/long_array_value.csv'; +ERROR: Error in row 1, column array_column value exceeds size of 33554432 +---- + +=== Unsupported SQL clauses + +Array columns cannot be used as the key columns in `ORDER BY`, `GROUP BY`, or `JOIN` operations. You also cannot use array columns as part of the index of a table. For these operations, Redpanda SQL returns an error message: + +[source,sql] +---- +SELECT * FROM movie_night ORDER BY movies_planned; +ERROR: could not identify an ordering operator for type text[] +---- + +Arrays can still be used in `ORDER BY` or `JOIN` operations, if the array column is not the key: + +[source,sql] +---- +SELECT * FROM movie_night ORDER BY event_date ASC; + event_date | movies_planned +------------+----------------------------------------- + 2024-12-01 | {Inception,Interstellar,"The Prestige"} + 2024-12-15 | {"The Matrix","John Wick"} +(2 rows) +---- + +=== Unsupported SQL statements + +Specific SQL statements currently do not support arrays. These include: + +* `INSERT INTO` with `SELECT`: Arrays cannot be directly imported using an `INSERT INTO` with a `SELECT` statement. Instead, use the `COPY FROM CSV` command or the `INSERT INTO` statement with the `VALUES` keyword +* `UPDATE` and `DELETE`: Updating or deleting records from a table, which contains array columns is not supported +* `COPY TO`: Exporting data from array columns using the `COPY TO` command is not available. +* `CREATE INDEX`: Index on a table cannot be created on an array column. + +Any effort to use such operations with arrays will result in an error. For now, these limitations should be considered when designing tables that include array columns. diff --git a/modules/reference/pages/sql/sql-data-types/bool.adoc b/modules/reference/pages/sql/sql-data-types/bool.adoc new file mode 100644 index 000000000..6be575b41 --- /dev/null +++ b/modules/reference/pages/sql/sql-data-types/bool.adoc @@ -0,0 +1,149 @@ += Bool +:description: BOOL is a data type for expressions that return one of two possible values: true or false. +:page-topic-type: reference + +== Overview + +A `BOOL` is a data type for expressions that return one of two possible values: `true` or `false`. + +[WARNING] +==== +`BOOLEAN` is an alias for the `BOOL` data type. You can create a table using `BOOLEAN`, but Redpanda SQL stores and processes the values as `BOOL`. +==== + +== Format + +* `FALSE` +* `TRUE` + +== Examples + +The following are examples of using a bool data type: + +=== Create a table + +The following example creates a `borrowBook` table to store book borrowing records. The table contains columns for the borrow ID, book name, borrower, and a `bool` column for the returned status. + +[source,sql] +---- +CREATE TABLE borrowBook ( + borrowID INT, + bookName TEXT, + borrower TEXT, + returnedStat BOOL NOT NULL +); +INSERT INTO borrowBook (borrowID,bookName, borrower, returnedStat) +VALUES + (101, 'The Silent Patient', 'Mike', TRUE), + (201, 'Malibu Rising', 'Jean', TRUE), + (301, 'The Guest List', 'Mark', FALSE), + (401, 'The Four Winds', 'Cliff', TRUE), + (501, 'The Vanishing Half: A Novel', 'Sarah', TRUE), + (601, 'Red, White & Royal Blue', 'Anna', FALSE), + (701, 'The Duke and I', 'Blake', FALSE), + (801, 'The Lord of the Rings', 'Sandra', FALSE); +---- + +The `borrowBook` table has been successfully created after executing the query: + +[source,sql] +---- +COMPLETE +INSERT 0 8 +---- + +=== Display the table + +Run the `SELECT` statement to get all records from the `borrowBook` table: + +[source,sql] +---- +SELECT * FROM borrowBook; +---- + +This returns the following result: + +[source,sql] +---- ++-----------+---------------------------------+------------+---------------+ +| borrowid | bookname | borrower | returnedstat | ++-----------+---------------------------------+------------+---------------+ +| 101 | The Silent Patient | Mike | t | +| 201 | Malibu Rising | Jean | t | +| 301 | The Guest List | Mark | f | +| 401 | The Four Winds | Cliff | t | +| 501 | The Vanishing Half: A Novel | Sarah | t | +| 601 | Red, White & Royal Blue | Anna | f | +| 701 | The Duke and I | Blake | f | +| 801 | The Lord of the Rings | Sandra | f | ++-----------+---------------------------------+------------+---------------+ +---- + +=== List of the returned books + +This example retrieves all the books that have already been returned: + +[source,sql] +---- +SELECT * FROM borrowbook +WHERE returnedstat= 'true'; +---- + +The query returns the following results: + +[source,sql] +---- ++-----------+---------------------------------+------------+---------------+ +| borrowid | bookname | borrower | returnedstat | ++-----------+---------------------------------+------------+---------------+ +| 101 | The Silent Patient | Mike | t | +| 201 | Malibu Rising | Jean | t | +| 401 | The Four Winds | Cliff | t | +| 501 | The Vanishing Half: A Novel | Sarah | t | ++-----------+---------------------------------+------------+---------------+ +---- + +=== List of the unreturned books + +To acquire all of the book records that haven't been returned yet, run the `SELECT` statement with a specified `WHERE` condition as `false`: + +[source,sql] +---- +SELECT * FROM borrowbook +WHERE returnedstat= 'false'; +---- + +The query returns the following results: + +[source,sql] +---- ++-----------+---------------------------------+------------+---------------+ +| borrowid | bookname | borrower | returnedstat | ++-----------+---------------------------------+------------+---------------+ +| 301 | The Guest List | Mark | f | +| 601 | Red, White & Royal Blue | Anna | f | +| 701 | The Duke and I | Blake | f | +| 801 | The Lord of the Rings | Sandra | f | ++-----------+---------------------------------+------------+---------------+ +---- + +=== Check a book's return status + +This example finds the returned status of the book "The Lord of the Rings" by executing the `SELECT` statement with a specified column in the `WHERE` clause: + +[source,sql] +---- +SELECT * FROM borrowbook +WHERE bookname = 'The Lord of the Rings'; +---- + +This query filters all records based on the specified conditions, showing that Sandra hasn't returned the book yet: + +[source,sql] +---- ++-----------+---------------------------------+------------+---------------+ +| borrowid | bookname | borrower | returnedstat | ++-----------+---------------------------------+------------+---------------+ +| 801 | The Lord of the Rings | Sandra | f | ++-----------+---------------------------------+------------+---------------+ +---- diff --git a/modules/reference/pages/sql/sql-data-types/date.adoc b/modules/reference/pages/sql/sql-data-types/date.adoc new file mode 100644 index 000000000..f8e2d18dd --- /dev/null +++ b/modules/reference/pages/sql/sql-data-types/date.adoc @@ -0,0 +1,69 @@ += Date +:description: The DATE data type stores calendar dates without a time zone in Redpanda SQL. +:page-topic-type: reference + +The `DATE` data type stores calendar dates without a time zone. Use it to store and insert date values. + +[NOTE] +==== +The date value is stored without the time zone. +==== + +== Format + +[source,sql] +---- +YYYY-MM-DD +---- + +* `YYYY` - Four-digit year +* `MM` - One / two-digit month +* `DD` - One / two-digit day + +== Examples + +In this example, the `emp_submission` table consists of the candidate ID, candidate name, the submitted department, and a submission date with a `DATE` data type. + +[source,sql] +---- +CREATE TABLE emp_submission ( + candidate_ID INT, + candidate_Name TEXT, + sub_dept TEXT, + sub_date DATE +); + +INSERT INTO emp_submission (candidate_ID, candidate_Name, sub_dept, sub_date) +VALUES +(8557411, 'Kumar', 'HR', '2022-05-01'), +(8557421, 'Ricky', 'HR', '2022-01-09'), +(8557451, 'Alice', 'Finance', '2022-08-02'), +(8557461, 'Angel', 'Product', '2012-04-16'), +(8557431, 'Joan', 'Finance', '2022-02-02'), +(8557471, 'Cody', 'Product', '2022-03-20'), +(8557491, 'Liam', 'Product', '2022-06-15'); +---- + +Now that the data has been inserted, execute the following `SELECT` statement: + +[source,sql] +---- +SELECT * FROM emp_submission; +---- + +The following is the result of the `SELECT` statement where the values in the `sub_date` column have `DATE` data type: + +[source,sql] +---- ++---------------+------------------+------------+---------------+ +| candidate_id | candidate_name | sub_dept | sub_date | ++---------------+------------------+------------+---------------+ +| 8557411 | Kumar | HR | 2022-05-01 | +| 8557421 | Ricky | HR | 2022-01-09 | +| 8557451 | Alice | Finance | 2022-08-02 | +| 8557461 | Angel | Product | 2012-04-16 | +| 8557431 | Joan | Finance | 2022-02-02 | +| 8557471 | Cody | Product | 2022-03-20 | +| 8557491 | Liam | Product | 2022-06-15 | ++---------------+------------------+------------+---------------+ +---- diff --git a/modules/reference/pages/sql/sql-data-types/geography.adoc b/modules/reference/pages/sql/sql-data-types/geography.adoc new file mode 100644 index 000000000..17dfc3c3f --- /dev/null +++ b/modules/reference/pages/sql/sql-data-types/geography.adoc @@ -0,0 +1,75 @@ += Geography +:description: The GEOGRAPHY data type stores geodetic (spherical) spatial point values using the WGS84 coordinate system (SRID 4326). +:page-topic-type: reference + +The `GEOGRAPHY` data type stores geodetic (spherical) spatial point values using the WGS84 coordinate system (SRID 4326). Unlike xref:reference:sql/sql-data-types/geometry.adoc[GEOMETRY], which uses planar coordinates, `GEOGRAPHY` interprets coordinates as longitude and latitude on the Earth's surface, and distance calculations return results in meters. + +[NOTE] +==== +Redpanda SQL supports only `POINT` geographies. Multi-part geometries such as `POLYGON`, `LINESTRING`, and `MULTIPOINT` are not supported. +==== + +== Format + +`GEOGRAPHY` values can be specified in the following formats: + +* WKT: `POINT(longitude latitude)` (SRID defaults to 4326) +* EWKT: `SRID=4326;POINT(longitude latitude)` +* EWKB: A hex-encoded binary string (50 hex characters, includes SRID) + +[source,sql] +---- +SELECT GEOGRAPHY 'POINT(-73.9857 40.7484)'; +---- + +== SRID handling + +`GEOGRAPHY` always uses SRID 4326 (WGS84). If you specify a different SRID, an error is returned. + +== Casting + +`GEOGRAPHY` supports the following casts: + +* `GEOGRAPHY` -> `TEXT`: Returns EWKB hex string +* `TEXT` -> `GEOGRAPHY`: Parses WKT or EWKB string +* `GEOGRAPHY` -> `GEOMETRY`: Removes SRID +* `GEOMETRY` -> `GEOGRAPHY`: Adds SRID=4326 + +[NOTE] +==== +Casting between `GEOGRAPHY` and `POINT` is not supported. +==== + +== Functions + +The following functions work with `GEOGRAPHY` values: + +[cols="2,3,1",options="header"] +|=== +|Function |Description |Return type +|`ST_ASTEXT(geography)` |Returns the WKT representation |`text` +|`ST_ASTEXT(geography, max_digits)` |Returns the WKT representation with limited decimal digits |`text` +|`ST_ASEWKT(geography)` |Returns the Extended WKT representation (includes SRID) |`text` +|`ST_ASEWKT(geography, max_digits)` |Returns the Extended WKT representation with limited decimal digits |`text` +|`ST_DISTANCE(geography, geography)` |Returns the geodetic distance in meters using the WGS84 ellipsoid |`double precision` +|`ST_DISTANCE(geography, geography, use_spheroid)` |Returns the geodetic distance in meters. Set `use_spheroid` to `false` for a faster spherical approximation. |`double precision` +|=== + +== Examples + +Calculate the distance in meters between two geographic points (New York City and London): + +[source,sql] +---- +SELECT ST_DISTANCE( + GEOGRAPHY 'POINT(-73.9857 40.7484)', + GEOGRAPHY 'POINT(-0.1278 51.5074)' +) AS distance_meters; +---- + +[source,sql] +---- + distance_meters +------------------- + 5570222.179854498 +---- diff --git a/modules/reference/pages/sql/sql-data-types/geometry.adoc b/modules/reference/pages/sql/sql-data-types/geometry.adoc new file mode 100644 index 000000000..85e023708 --- /dev/null +++ b/modules/reference/pages/sql/sql-data-types/geometry.adoc @@ -0,0 +1,77 @@ += Geometry +:description: The GEOMETRY data type stores planar (Cartesian) spatial point values as two double-precision coordinates. +:page-topic-type: reference + +The `GEOMETRY` data type stores planar (Cartesian) spatial point values as two double-precision coordinates. It uses https://en.wikipedia.org/wiki/Well-known_text_representation_of_geometry[Well-Known Text (WKT)^] and https://en.wikipedia.org/wiki/Well-known_text_representation_of_geometry#Well-known_binary[Well-Known Binary (WKB)^] formats for input and output. + +[NOTE] +==== +Redpanda SQL supports only `POINT` geometries. Multi-part geometries such as `POLYGON`, `LINESTRING`, and `MULTIPOINT` are not supported. +==== + +== Format + +`GEOMETRY` values can be specified in the following formats: + +* WKT: `POINT(x y)` (space-separated coordinates) +* EWKT: `SRID=4326;POINT(x y)` (SRID is accepted but ignored for `GEOMETRY`) +* EWKB: A hex-encoded binary string (42 hex characters) + +[source,sql] +---- +SELECT GEOMETRY 'POINT(0.1234 5.6789)'; +---- + +== Differences between GEOMETRY and GEOGRAPHY + +[cols="1,1",options="header"] +|=== +|GEOMETRY |GEOGRAPHY +|Uses a Cartesian (planar) coordinate system |Uses a geodetic (spherical) coordinate system +|SRID is ignored |SRID is always 4326 (WGS84) +|`ST_DISTANCE` returns Euclidean distance |`ST_DISTANCE` returns distance in meters +|=== + +== Casting + +`GEOMETRY` supports the following casts: + +* `GEOMETRY` -> `TEXT`: Returns WKB hex string +* `TEXT` -> `GEOMETRY`: Parses WKT or EWKB string +* `GEOMETRY` -> `GEOGRAPHY`: Adds SRID=4326 +* `GEOGRAPHY` -> `GEOMETRY`: Removes SRID +* `GEOMETRY` -> `POINT`: Converts to `(x,y)` format +* `POINT` -> `GEOMETRY`: Converts to WKB format + +== Functions + +The following functions work with `GEOMETRY` values: + +[cols="1,2,1",options="header"] +|=== +|Function |Description |Return type +|`ST_ASTEXT(geometry)` |Returns the WKT representation of the geometry |`text` +|`ST_ASTEXT(geometry, max_digits)` |Returns the WKT representation with limited decimal digits |`text` +|`ST_ASEWKT(geometry)` |Returns the Extended WKT representation |`text` +|`ST_ASEWKT(geometry, max_digits)` |Returns the Extended WKT representation with limited decimal digits |`text` +|`ST_DISTANCE(geometry, geometry)` |Returns the Euclidean distance between two geometry points |`double precision` +|=== + +== Examples + +[source,sql] +---- +SELECT + ST_ASTEXT(GEOMETRY 'POINT(1.5 2.5)') AS wkt, + ST_DISTANCE( + GEOMETRY 'POINT(0 0)', + GEOMETRY 'POINT(3 4)' + ) AS distance; +---- + +[source,sql] +---- + wkt | distance +----------------+---------- + POINT(1.5 2.5) | 5 +---- diff --git a/modules/reference/pages/sql/sql-data-types/index.adoc b/modules/reference/pages/sql/sql-data-types/index.adoc new file mode 100644 index 000000000..9d21e1fa6 --- /dev/null +++ b/modules/reference/pages/sql/sql-data-types/index.adoc @@ -0,0 +1,38 @@ += SQL data types +:description: Redpanda SQL supports a wide range of data types, each designed to handle specific types of data efficiently. +:page-topic-type: reference + +Redpanda SQL supports a wide range of data types, each designed to handle specific types of data efficiently. + +The following table summarizes the data types supported by Redpanda SQL: + +[width="100%",cols="<48%,<29%,<23%",options="header",] +|=== +|Data Type |Definition |Format +|xref:reference:sql/sql-data-types/numeric-type/numeric.adoc#int-type[INT] |32-bit signed integer |one or more digits "`0`" to "`9`" +|xref:reference:sql/sql-data-types/numeric-type/numeric.adoc#bigint-type[BIGINT] |64-bit signed integer |large numeric/decimal value +|xref:reference:sql/sql-data-types/numeric-type/numeric.adoc#real-type[REAL] |32-bit floating point number |`float(n)` +|xref:reference:sql/sql-data-types/numeric-type/numeric.adoc#double-precision-type[DOUBLE PRECISION] |64-bit floating point number |`decimal(p, s)` +|xref:reference:sql/sql-data-types/timestamp-without-time-zone.adoc[TIMESTAMP WITHOUT TIME ZONE] |Time and date values without a time zone |`YYYY-MM-DD [HH:MM:SS[.SSSSSS]]` +|xref:reference:sql/sql-data-types/timestamp-with-time-zone.adoc[TIMESTAMP WITH TIME ZONE] |Date and time values, including the time zone information |`YYYY-MM-DD HH:MM:SS.SSSSSS{plus}TZ` +|xref:reference:sql/sql-data-types/date.adoc[DATE] |Date value |`YYYY-MM-DD` +|xref:reference:sql/sql-data-types/time-type/time.adoc[TIME] |Time values without any date information |`HH:MM:SS[.SSSSSS]` +|xref:reference:sql/sql-data-types/interval.adoc[INTERVAL] |Encodes a span of time |`year-month (YYYY-MM); day-time (DD HH:MM:SS)` +|xref:reference:sql/sql-data-types/bool.adoc[BOOL] |Boolean value |`True` or `False` +|xref:reference:sql/sql-data-types/text.adoc[TEXT] |UTF8 encoded string with Unicode support |'`text`' +|xref:reference:sql/sql-data-types/json.adoc[JSON] |A value in JSON standard format |`variable_name JSON` +|xref:reference:sql/sql-data-types/array.adoc[ARRAY] |An array of a specific data type |`'{value1, value2, value3}'::data_type[]` +|xref:reference:sql/sql-data-types/row.adoc[ROW] |A composite value containing fields of different types |`ROW(value1, value2, ...)` +|xref:reference:sql/sql-data-types/geometry.adoc[GEOMETRY] |A spatial data type for planar (Cartesian) point values |`GEOMETRY 'POINT(x y)'` +|xref:reference:sql/sql-data-types/geography.adoc[GEOGRAPHY] |A spatial data type for geodetic (spherical) point values using WGS84 |`GEOGRAPHY 'POINT(lon lat)'` +|=== + +[WARNING] +==== +When performing operations on numeric or temporal types, overflows can lead to undefined behavior, resulting in unexpected values or errors. Ensure input values are within the allowed range for each numeric type to prevent overflows. This can occur during arithmetic operations or function execution (for example, `AVG()`), where the result does not fit the result type. Using larger data types such as `BIGINT` can help mitigate overflow risks. +==== + +[NOTE] +==== +Explicit casting between types can cause data loss due to altered precision or magnitude, such as truncating fractional seconds in `TIME` or silently clipping out-of-range values. Verify input ranges to prevent unintended data loss. +==== diff --git a/modules/reference/pages/sql/sql-data-types/interval.adoc b/modules/reference/pages/sql/sql-data-types/interval.adoc new file mode 100644 index 000000000..d8ed544c0 --- /dev/null +++ b/modules/reference/pages/sql/sql-data-types/interval.adoc @@ -0,0 +1,176 @@ += Interval +:description: The Interval data type represents periods between dates or times, which can be precisely calculated and expressed through various units. +:page-topic-type: reference + +The Interval data type represents periods between dates or times, which can be precisely calculated and expressed through various units. Those can be combined and include additional options for different interval calculations. +== Syntax + +The syntax for specifying an interval is as follows: + +[source,sql] +---- +SELECT INTERVAL 'quantity unit [quantity unit...] [direction]' [OPTION] +---- + +[cols="1,3",options="header"] +|=== +|Parameter |Description + +|`quantity` +|The value representing the number of units. + +|`unit` +a|Year, month, day, hour, and minute. Abbreviations, short forms, and dash format are supported. Plural forms are also acceptable (for example, months, days, weeks). + +|`direction` +|An optional parameter: `ago` or empty string. + +|`OPTION` +|Additional options when parsing interval. +|=== + +[NOTE] +==== +For arithmetic and comparison operations, Redpanda SQL assumes `1 month = 30 days` and `1 day = 24 hours`. However, adding 30 days to a timestamp is not always equivalent to adding 1 month, because calendar months have different durations. +==== + +== Supported units and abbreviations + +[cols=",",options="header",] +|=== +|*Unit* |*Abbreviations* +|Millennium |- +|Century |- +|Decade |- +|Year |`y`, `yr`, `yrs` +|Month |- +|Week |- +|Day |`d` +|Hour |`h`, `hr`, `hrs` +|Minute |`min`, `mins`, `m` +|Second |`s`, `sec`, `secs` +|Millisecond |`ms` +|Microsecond |- +|=== + +== Options for interval parsing + +* `YEAR`, `MONTH`, `DAY`, `HOUR`, `MINUTE`, `SECOND` +* `YEAR TO MONTH`, `DAY TO HOUR`, `DAY TO MINUTE`, `DAY TO SECOND`, `HOUR TO MINUTE`, `HOUR TO SECOND`, `MINUTE TO SECOND` + +== Examples + +=== Select interval with multiple units + +This example calculates an interval by combining multiple units of time. + +[source,sql] +---- +SELECT INTERVAL '5 years 4 months 2 weeks 3 days 5 hours 10 minutes 25 seconds' as "Interval"; +---- + +[source,sql] +---- + Interval +--------------------------------- + 5 years 4 mons 17 days 05:10:25 +(1 row) +---- + +=== Use abbreviations + +This example shows how to use abbreviated units for time intervals. + +[source,sql] +---- +SELECT INTERVAL '10 yr 8 months 2 weeks 6 days 5 hrs 10 min 20 s as "Interval"; +---- + +[source,sql] +---- + Interval +---------------------------------- + 10 years 8 mons 20 days 05:10:20 +(1 row) +---- + +=== Use dash format +[source,sql] +---- +SELECT INTERVAL '1-2 3 DAYS 04:05:06.070809' as "Interval"; +---- + +[source,sql] +---- + Interval +-------------------------------------- + 1 year 2 mons 3 days 04:05:06.070809 +(1 row) +---- + +=== Parse intervals using specific units + +By running the following code, the output shows everything up to minutes and ignores seconds and milliseconds. + +[source,sql] +---- +SELECT INTERVAL '1-2 5 DAYS 07:08:06.040809' MINUTE as "Interval"; +---- + +[source,sql] +---- + Interval +------------------------------- + 1 year 2 mons 5 days 07:08:00 +(1 row) +---- + +=== Display specific range only + +Executing the following query results in only years and months being displayed, excluding days, hours, minutes, and seconds from the input. + +[source,sql] +---- +SELECT INTERVAL '2-4 5 DAYS 04:05:06.070809' YEAR TO MONTH as "Interval"; +---- + +[source,sql] +---- + Interval +---------------- + 2 years 4 mons +(1 row) +---- + +=== Extract data from interval + +To extract interval numbers from a timestamp, use the `EXTRACT()` function: + +[source,sql] +---- +SELECT EXTRACT (field FROM interval) +---- + +* `field`: Supports time units, such as `YEAR`, `MONTH`, `DAY`, and `HOUR`. +* `interval`: Specified timestamp. + +[source,sql] +---- +SELECT EXTRACT (MINUTE +FROM INTERVAL '2 hours 30 minutes'); +---- + +The output returns only the minutes part: + +[source,sql] +---- + extract +------------ + 30 +(1 row) +---- + +[NOTE] +==== +If you query a field that is not specified in the timestamp, the output is `0`. +==== diff --git a/modules/reference/pages/sql/sql-data-types/json.adoc b/modules/reference/pages/sql/sql-data-types/json.adoc new file mode 100644 index 000000000..0e0bf966f --- /dev/null +++ b/modules/reference/pages/sql/sql-data-types/json.adoc @@ -0,0 +1,80 @@ += JSON +:description: JSON stands for JavaScript Object Notation. +:page-topic-type: reference + +== Overview + +JSON stands for JavaScript Object Notation. It is an open standard format with key-value pairs to transport data between a server and a web application. + +== Syntax + +The JSON data type in Redpanda SQL has the following syntax: + +[source,sql] +---- +variable_name JSON +---- + +== Examples + +=== Create a table + +First, create the `orders` table using the following command: + +[source,sql] +---- +CREATE TABLE orders ( + orders_Detail JSON +); +---- + +This creates a table with the `orders_Detail` column to store key-value pairs of data. + +=== Insert data + +Next, insert data into the orders table as follows: + +[source,sql] +---- +INSERT INTO orders (orders_Detail) +VALUES +('{ "customer": "Dean Smith", "items": {"product": "cup","qty": 2}}'), +('{ "customer": "Sissy Kate", "items": {"product": "knife","qty": 1}}'), +('{ "customer": "Emma Stone", "items": {"product": "spoon","qty": 4}}'), +('{ "customer": "Chris Bale", "items": {"product": "fork","qty": 5}}'), +('{ "customer": "Mike Stuart", "items": {"product": "spatula","qty": 2}}'); +---- + +This inserts data values where `orders_Detail` has the following keys: + +* `customer`: Stores the customer's data. +* `items`: Stores the order details, including `product` and `qty`. + +=== Retrieve data + +Use the `SELECT` command to retrieve the orders table's data. + +[source,sql] +---- +SELECT * FROM orders; +---- + +The query returns the following output: + +[source,sql] +---- ++--------------------------------------------------------------------------+ +| orders_detail | ++--------------------------------------------------------------------------+ +| {"customer":"Dean Smith","items":{"qty":2.000000,"product":"cup"}} | +| {"customer":"Sissy Kate","items":{"product":"knife","qty":1.000000}} | +| {"customer":"Emma Stone","items":{"qty":4.000000,"product":"spoon"}} | +| {"customer":"Chris Bale","items":{"product":"fork","qty":5.000000}} | +| {"customer":"Mike Stuart","items":{"qty":2.000000,"product":"spatula"}} | ++--------------------------------------------------------------------------+ +---- + +[TIP] +==== +It is normal for the JSON type's result to look disordered. +==== diff --git a/modules/reference/pages/sql/sql-data-types/numeric-type/index.adoc b/modules/reference/pages/sql/sql-data-types/numeric-type/index.adoc new file mode 100644 index 000000000..1951c000e --- /dev/null +++ b/modules/reference/pages/sql/sql-data-types/numeric-type/index.adoc @@ -0,0 +1,3 @@ += Numeric Types +:description: Reference for numeric data types in Redpanda SQL. +:page-layout: index diff --git a/modules/reference/pages/sql/sql-data-types/numeric-type/numeric-data-type-aliases.adoc b/modules/reference/pages/sql/sql-data-types/numeric-type/numeric-data-type-aliases.adoc new file mode 100644 index 000000000..4085015d2 --- /dev/null +++ b/modules/reference/pages/sql/sql-data-types/numeric-type/numeric-data-type-aliases.adoc @@ -0,0 +1,91 @@ += Numeric Data Type - Aliases +:description: Aliases for the primary numeric data types in Redpanda SQL, mapped to their primary types during processing. +:page-topic-type: reference + +Redpanda SQL accepts aliases that you can use interchangeably with the primary data types. During processing, Redpanda SQL maps these aliases to their corresponding primary data types. + +The following sections describe the numeric data type aliases: + +== INTEGER alias + +The `INTEGER` alias is an alternative name for the `INT` data type. For example, the following two queries are functionally the same: + +[source,sql] +---- +CREATE TABLE ExampleTable ( + id INTEGER, +); + +-- Functionally the same as the previous table +CREATE TABLE AnotherTable ( + id INT, +); +---- + +[NOTE] +==== +Even though you write `INTEGER`, Redpanda SQL stores and treats the data as `INT`. +==== + +== LONG alias + +The `LONG` alias is often used to represent larger integer values. For example: + +[source,sql] +---- +CREATE TABLE LargeValues ( + value LONG, +); + +-- Functionally the same as the previous table +CREATE TABLE LargeValuesEquivalent ( + value BIGINT, +); +---- + +[NOTE] +==== +Any usage of `LONG` is stored and treated as `BIGINT`. +==== + +== FLOAT alias + +The `FLOAT` alias corresponds to the `REAL` data type. For example: + +[source,sql] +---- +CREATE TABLE FloatExample ( + price FLOAT, +); + +-- Functionally the same as the previous table +CREATE TABLE FloatEquivalent ( + price REAL, +); +---- + +[NOTE] +==== +When you use `FLOAT`, it's stored and treated as `REAL`. +==== + +== DOUBLE alias + +The `DOUBLE` alias defines `DOUBLE PRECISION` floating-point numbers. For example: + +[source,sql] +---- +CREATE TABLE DoubleExample ( + measurement DOUBLE, +); + +-- Functionally the same as the previous table +CREATE TABLE DoubleEquivalent ( + measurement DOUBLE PRECISION, +); +---- + +[NOTE] +==== +When you use `DOUBLE`, it's stored and treated as `DOUBLE PRECISION`. +==== diff --git a/modules/reference/pages/sql/sql-data-types/numeric-type/numeric.adoc b/modules/reference/pages/sql/sql-data-types/numeric-type/numeric.adoc new file mode 100644 index 000000000..943e19e66 --- /dev/null +++ b/modules/reference/pages/sql/sql-data-types/numeric-type/numeric.adoc @@ -0,0 +1,304 @@ += Numeric +:description: The INT data type represents whole numbers without decimal points. +:page-topic-type: reference + +== Int type + +The `INT` data type represents whole numbers without decimal points. It is a 32-bit signed integer with a range from -2147483648 to 2147483647. + +=== Format + +[source,sql] +---- +column_name INT +---- + +=== Example + +The following is an example of how to create a column using an `INT` type. + +[source,sql] +---- +CREATE TABLE cities ( + city_id INT, + cityname TEXT, + population INT +); +INSERT INTO cities (city_id, cityname, population) +VALUES +(8557411, 'New York', 8419000), +(8557421, 'London', 8982000), +(8557451, 'Hongkong', 7482000), +(8557491, 'Seoul', 9776000); +---- + +To display the table, run the query: + +[source,sql] +---- +SELECT * FROM cities; +---- + +This returns the following result. + +[source,sql] +---- + city_id | cityname | population +---------+----------+------------ + 8557411 | New York | 8419000 + 8557421 | London | 8982000 + 8557451 | Hongkong | 7482000 + 8557491 | Seoul | 9776000 +(4 rows) +---- + +== Bigint type + +The `BIGINT` data type stores large whole numbers that exceed the `INT` range. It is a 64-bit signed integer with a range from -9223372036854775808 to 9223372036854775807. + +=== Format + +[source,sql] +---- +column_name BIGINT +---- + +=== Example + +The following is an example of how to create a column using the `BIGINT` type: + +[source,sql] +---- +CREATE TABLE galaxies ( + galaxy_name TEXT, + star BIGINT +); +INSERT INTO galaxies (galaxy_name, star) +VALUES +('Milky Way', 100000000000), +('Cigar', 30000000000), +('Andromeda', 1000000000000), +('Cosmos', 2000000000000000000); +---- + +To display the table, run the query: + +[source,sql] +---- +SELECT * FROM galaxies; +---- + +The query returns the following output: + +[source,sql] +---- + galaxy_name | star +-------------+--------------------- + Milky Way | 100000000000 + Cigar | 30000000000 + Andromeda | 1000000000000 + Cosmos | 2000000000000000000 +(4 rows) +---- + +== Real type + +The `REAL` data type is a 32-bit floating-point number compliant with the IEEE 754 binary32 format. + +=== Format + +[source,sql] +---- +column_name REAL +---- + +=== Example + +==== Create a table + +The following example creates a table with a `REAL` column type. + +[source,sql] +---- +CREATE TABLE numbers ( + column_1 REAL +); + +INSERT into numbers (column_1) +VALUES (1.234568); +---- + +Display the table with the following query. + +[source,sql] +---- +SELECT * FROM numbers; +---- + +The stored value is shown in the following output. + +[source,sql] +---- + column_1 +---------- + 1.234568 +(1 row) +---- + +==== Rounding + +Rounding might happen if the precision of an input number is too high. + +[source,sql] +---- +CREATE TABLE numbers1 ( +column_1 REAL +); + +INSERT into numbers1 (column_1) +VALUES (1.2345689); +---- + +Display the table with the following query. + +[source,sql] +---- +SELECT * FROM numbers1; +---- + +The following output shows the value after rounding. + +[source,sql] +---- + column_1 +---------- + 1.234569 +(1 row) +---- + +==== Create a table with numbers exceeding the range + +The `REAL` type only stores 32-bit floating-point numbers. In this example, the input numbers exceed the range. + +[source,sql] +---- +CREATE TABLE numbers2 ( + column_1 REAL +); + +INSERT into numbers2 (column_1) +VALUES (1.2345682991822); +---- + +Display the table with the following query. + +[source,sql] +---- +SELECT * FROM numbers2; +---- + +The final output will only return numbers that match the range. + +[source,sql] +---- + column_1 +----------- + 1.2345684 +(1 row) +---- + +== Double precision type + +The `DOUBLE PRECISION` data type is a 64-bit floating-point number compliant with the IEEE 754 binary64 format. + +=== Format + +[source,sql] +---- +column_name DOUBLE PRECISION +---- + +=== Example + +==== Create a table + +The following example creates a table with a `DOUBLE PRECISION` type column. + +[source,sql] +---- +CREATE TABLE numbersdouble ( + column_1 DOUBLE PRECISION +); + +INSERT into numbersdouble (column_1) +VALUES (1.234568817283122); +---- + +Display the table with the following query. + +[source,sql] +---- +SELECT * FROM numbersdouble; +---- + +The following is the output. + +[source,sql] +---- + column_1 +------------------- + 1.234568817283122 +(1 row) +---- + +==== Rounding + +Rounding might happen if the precision of an input number is too high. + +[source,sql] +---- +CREATE TABLE numbersdouble1 ( + column_1 DOUBLE PRECISION +); + +INSERT into numbersdouble1 (column_1) +VALUES (1.234568817283122773); +---- + +Display the table with the following query. + +[source,sql] +---- +SELECT * FROM numbersdouble1; +---- + +The following output shows the value after rounding. + +[source,sql] +---- + column_1 +-------------------- + 1.2345688172831228 +(1 row) +---- + +== Scientific notation support + +Redpanda SQL supports scientific notation for floating-point types. This feature supports expressions like 1.1e{plus}3, 1e-20, 1.1e02, and similar in your queries. + +=== Example + +[source,sql] +---- +SELECT 1.1e+3, 1e-20, 1.1e02; +---- + +The query returns: + +[source,sql] +---- + ?column? | ?column? | ?column? +----------+----------+---------- + 1100 | 1e-20 | 110 +(1 row) +---- diff --git a/modules/reference/pages/sql/sql-data-types/row.adoc b/modules/reference/pages/sql/sql-data-types/row.adoc new file mode 100644 index 000000000..bc55a2dac --- /dev/null +++ b/modules/reference/pages/sql/sql-data-types/row.adoc @@ -0,0 +1,77 @@ += ROW +:description: The ROW data type represents a composite value containing one or more fields of different types. +:page-topic-type: reference + +The `ROW` data type represents a composite value (also known as a struct or record) containing one or more fields of different types. + +== Syntax + +[source,sql] +---- +ROW(expression [, ...]) +(expression, expression [, ...]) +---- + +The explicit `ROW` keyword is required for single-element composites. For two or more elements, the `ROW` keyword is optional and the parenthesized list is treated as an implicit tuple. + +== Examples + +=== Create a ROW with multiple values + +[source,sql] +---- +SELECT ROW(1, 'hello', 3.14); +---- + +[source,sql] +---- + row +----------------- + (1,"hello",3.14) +(1 row) +---- + +=== Use implicit tuple syntax + +[source,sql] +---- +SELECT (1, 2, 3); +---- + +[source,sql] +---- + row +--------- + (1,2,3) +(1 row) +---- + +=== Create a nested ROW + +[source,sql] +---- +SELECT ROW(1, ROW(2, 3)); +---- + +[source,sql] +---- + row +----------- + (1,"(2,3)") +(1 row) +---- + +=== Create an empty ROW + +[source,sql] +---- +SELECT ROW(); +---- + +[source,sql] +---- + row +----- + () +(1 row) +---- diff --git a/modules/reference/pages/sql/sql-data-types/text.adoc b/modules/reference/pages/sql/sql-data-types/text.adoc new file mode 100644 index 000000000..1e939f028 --- /dev/null +++ b/modules/reference/pages/sql/sql-data-types/text.adoc @@ -0,0 +1,126 @@ += Text +:description: The text data type is a UTF8-encoded text with Unicode support, which stores a sequence of characters (text). +:page-topic-type: reference + +The text data type is a UTF8-encoded text with Unicode support, which stores a sequence of characters (text). + +== Examples + +Create an employee table with a text data type in each column: + +[source,sql] +---- +CREATE TABLE employee ( + employeeName text, + employeeDept text, + employeeRole text +); +INSERT INTO employee (employeeName, employeeDept, employeeRole) +VALUES ('John','Finance','Staff'), + ('Maya','Product','Staff'), + ('Jane','Finance','Staff'), + ('Phil','HR','Manager'); +---- + +[TIP] +==== +Insert the text value between the single quotes `' '`. +==== +The following output shows the created table: + +[source,sql] +---- ++---------------+---------------+---------------+ +| employeename | employeedept | employeerole | ++---------------+---------------+---------------+ +| John | Finance | Staff | +| Maya | Product | Staff | +| Jane | Finance | Staff | +| Phil | HR | Manager | ++---------------+---------------+---------------+ +---- + +== Text with SUBSTR function + +The `substr()` function extracts a specific number of characters from a text. + +=== Syntax + +[source,sql] +---- +substr( text, start_position, length ) +---- + +The syntax includes the following parameters: + +* `text`is the specified text. +* `start_position` is the starting position, specifying the part from which the substring is returned. Use an integer value. +* `length` determines the number of characters to extract. Use one or more characters. + +[NOTE] +==== +The first position in the `text` is 1. +==== + +=== Example + +Insert a value into the text column. + +[source,sql] +---- +SELECT substr('Watermelon',6,5) AS "Fruit"; +---- + +The following output shows the result: + +[source,sql] +---- ++-------------+ +| Fruit | ++-------------+ +| melon | ++-------------+ +---- + +== Text with LENGTH function + +The `length()` function returns the number of characters in a text. + +[NOTE] +==== +The number of characters might be different from the byte length. +==== + +=== Syntax + +The length function will take a text as a parameter. + +[source,sql] +---- +LENGTH (text); +---- + +=== Example + +Insert a value into the text column. + +[source,sql] +---- +SELECT LENGTH ('UNITED STATES'); +---- + +The following output shows the result. + +[source,sql] +---- ++---------+ +| f | ++---------+ +| 13 | ++---------+ +---- + +[NOTE] +==== +The `length()` function will also count spaces. +==== diff --git a/modules/reference/pages/sql/sql-data-types/time-type/index.adoc b/modules/reference/pages/sql/sql-data-types/time-type/index.adoc new file mode 100644 index 000000000..a96a32040 --- /dev/null +++ b/modules/reference/pages/sql/sql-data-types/time-type/index.adoc @@ -0,0 +1,3 @@ += Time Types +:description: Reference for time data types and operators in Redpanda SQL. +:page-layout: index diff --git a/modules/reference/pages/sql/sql-data-types/time-type/time-operators.adoc b/modules/reference/pages/sql/sql-data-types/time-type/time-operators.adoc new file mode 100644 index 000000000..5bca19a53 --- /dev/null +++ b/modules/reference/pages/sql/sql-data-types/time-type/time-operators.adoc @@ -0,0 +1,379 @@ += Time operators +:description: Time operators in Redpanda SQL perform various operations on dates, times, and intervals. +:page-topic-type: reference + +Time operators in Redpanda SQL perform various operations on dates, times, and intervals. + +== DATE {plus} INTEGER + +Add a specific number of days to a date. + +Example: + +[source,sql] +---- +select date '2022-03-15' + 14 as "result"; +---- + +The result is the date 14 days after '`2022-03-15`': + +[source,sql] +---- + result +------------ + 2022-03-29 +---- + +=== INTEGER {plus} DATE + +Adding and multiplying time operators can also be done in reverse order. For example, you can add a number of days to a date in the format of `Integer {plus} Date`. + +[source,sql] +---- +select 14 + date '2022-03-15' AS "result"; +---- + +This produces the same result: 14 days after '`2022-03-15`' is '`2022-03-29`': + +[source,sql] +---- + result +------------ + 2022-03-29 +---- + +== DATE {plus} INTERVAL + +Add a specified interval to a date. + +Example: + +[source,sql] +---- +select date '2022-03-15' + interval '3 months' as "result"; +---- + +The result is the date three months after '`2022-03-15`': + +[source,sql] +---- + result +---------------------------- + 2022-06-15 00:00:00.000000 +---- + +== DATE - INTEGER + +Subtract a certain number of days from a date. + +Example: + +[source,sql] +---- +select date '2022-03-15' - 7 as "result"; +---- + +The result is the date 7 days before '`2022-03-15`': + +[source,sql] +---- + result +------------ + 2022-03-08 +---- + +== DATE - INTERVAL + +Subtract a specified interval from a date. + +Example: + +[source,sql] +---- +select date '2022-03-15' - interval '2 hour' as "result"; +---- + +The result is the timestamp two hours before '`2022-03-15`': + +[source,sql] +---- + result +---------------------------- + 2022-03-14 22:00:00.000000 +---- + +== DATE - DATE + +Subtract dates. + +Example: + +[source,sql] +---- +select date '2023-03-15' - date '2023-01-10' as "result"; +---- + +The number of days elapsed between '`2023-03-15`' and '`2023-01-10`' is 64 days. + +[source,sql] +---- + result +-------- + 64 +---- + +== DATE {plus} TIME + +Add a time-of-day to a date. + +Example: + +[source,sql] +---- +select date '2010-05-20' + time '02:00' as "result"; +---- + +The result is a timestamp combining the date and time: + +[source,sql] +---- + result +---------------------------- + 2010-05-20 02:00:00.000000 +---- + +== TIME {plus} INTERVAL + +Add a certain interval to a given time. + +Example: + +[source,sql] +---- +select time '12:30' + interval '1 hour' as "result"; +---- + +The result is the time one hour after '`12:30`': + +[source,sql] +---- + result +----------------- + 13:30:00.000000 +---- + +== TIME - INTERVAL + +Subtract a specified interval from a given time. + +Example: + +[source,sql] +---- +select time '18:45' - interval '45 minutes' as "result"; +---- + +The result is the time 18:00: + +[source,sql] +---- + result +----------------- + 18:00:00.000000 +---- + +== TIME - TIME + +Get a time difference by subtracting one time from another. + +Example: + +[source,sql] +---- +select time '10:00' - TIME '08:20' as "result"; +---- + +In this example, the time difference between the two provided times is 1 hour and 40 minutes. + +[source,sql] +---- + result +----------------- + 01:40:00.000000 +---- + +== TIMESTAMP {plus} INTERVAL + +Add a timestamp and an interval. + +Example: + +[source,sql] +---- +select timestamp '2021-01-05 12:00:00' + interval '5 days' as "result"; +---- + +The result is a new timestamp, 5 days after '`2021-01-05 12:00:00`': + +[source,sql] +---- + result +---------------------------- + 2021-01-10 12:00:00.000000 +---- + +== TIMESTAMP - INTERVAL + +Subtract an interval from a timestamp. + +Example: + +[source,sql] +---- +select timestamp '2022-01-04 12:00:00' - interval '3 days' as "result"; +---- + +In this example, it subtracts 3 days from '`2022-01-04 12:00:00`'. + +[source,sql] +---- + result +---------------------------- + 2022-01-01 12:00:00.000000 +---- + +== TIMESTAMP - TIMESTAMP + +Get an interval by subtracting one timestamp from another. + +Example: + +[source,sql] +---- +select timestamp '2022-01-05 18:30:00' - timestamp '2022-01-01 12:00:00' as "result"; +---- + +It gives the interval between the two timestamps, 102 hours and 30 minutes. + +[source,sql] +---- + result +------------------ + 102:30:00.000000 +---- + +== INTERVAL {plus} INTERVAL + +Add intervals. + +Example: + +[source,sql] +---- +select interval '2 months 2 days' + interval '6 days' as "result"; +---- + +It adds 6 days to 2 days, resulting in a total of 2 months and 8 days. + +[source,sql] +---- + result +--------------- + 2 mons 8 days +---- + +== INTERVAL - INTERVAL + +Subtract intervals. + +Example: + +[source,sql] +---- +select interval '2 months' - interval '20 days' as "result"; +---- + +It subtracts 20 days from 2 months. + +[source,sql] +---- + result +----------------- + 2 mons -20 days +---- + +== INTERVAL * INTEGER + +Multiply an interval by an integer. + +Example: + +[source,sql] +---- +select interval '2 hours' * 3 as "result"; +---- + +It multiplies '`2 hours`' by 3, the result is 6 hours. + +[source,sql] +---- + result +----------------- + 06:00:00.000000 +---- + +== INTERVAL * DOUBLE PRECISION + +Multiply an interval by a scalar. + +Example: + +[source,sql] +---- +select interval '2 hours' * 1.5 as "result"; +---- + +It multiplies '`2 hours`' by 1.5, and returns 3 hours. + +[source,sql] +---- + result +----------------- + 03:00:00.000000 +---- + +== INTERVAL / NUMBER + +Divide an interval by an integer or scalar. + +=== Divide by an integer + +[source,sql] +---- +select interval '1 hour' / 2 as "result"; +---- + +It divides '`1 hour`' by 2, and returns 30 minutes. + +[source,sql] +---- + result +----------------- + 00:30:00.000000 +---- + +=== Divide by a scalar + +[source,sql] +---- +select interval '2 hours' / 1.5 as "result"; +---- + +It divides '`2 hours`' by 1.5, and returns 1 hour 20 minutes. + +[source,sql] +---- + result +----------------- + 01:20:00.000000 +---- diff --git a/modules/reference/pages/sql/sql-data-types/time-type/time.adoc b/modules/reference/pages/sql/sql-data-types/time-type/time.adoc new file mode 100644 index 000000000..8f825d1ca --- /dev/null +++ b/modules/reference/pages/sql/sql-data-types/time-type/time.adoc @@ -0,0 +1,68 @@ += Time +:description: The TIME data type in Redpanda SQL stores time values without any date information. +:page-topic-type: reference + +The `TIME` data type in Redpanda SQL stores time values without any date information. It represents a specific time of day, independent of any time zone or date. + +== Format + +The format for the TIME data type is as follows: + +[source,sql] +---- +HH:MM:SS[.SSSSSS] +---- + +* `HH`: One or two-digit hour (valid values from 00 to 23). +* `MM`: One or two-digit minutes (valid values from 00 to 59). +* `SS`: One or two-digit seconds (valid values from 00 to 59). +* `[.SSSSSS]` : Optional fractional seconds, with up to six decimal places (microsecond precision). + +== Examples + +=== Create a schedule table + +The following example creates a table to manage employee schedules, containing their names and the time they are scheduled to start work. The `start_time` column uses the `TIME` data type. + +[source,sql] +---- +CREATE TABLE employee_schedule ( + employee_name TEXT, + start_time TIME +); + +INSERT INTO employee_schedule (employee_name, start_time) +VALUES +('John Doe', '08:30:00'), +('Jane Smith', '09:00:00'), +('Michael Johnson', '10:15:00'); +---- + +The table has been successfully created after executing the query: + +[source,sql] +---- +COMPLETE +INSERT 0 3 +---- + +=== View the employee schedule + +To view all employee schedules in the `employee_schedule` table, use the `SELECT` statement. + +[source,sql] +---- +SELECT * FROM employee_schedule; +---- + +The output displays the employee names and their corresponding scheduled start times: + +[source,sql] +---- + employee_name | start_time +-----------------+----------------- + John Doe | 08:30:00.000000 + Jane Smith | 09:00:00.000000 + Michael Johnson | 10:15:00.000000 +(3 rows) +---- diff --git a/modules/reference/pages/sql/sql-data-types/timestamp-with-time-zone.adoc b/modules/reference/pages/sql/sql-data-types/timestamp-with-time-zone.adoc new file mode 100644 index 000000000..3bc7ba95c --- /dev/null +++ b/modules/reference/pages/sql/sql-data-types/timestamp-with-time-zone.adoc @@ -0,0 +1,151 @@ += Timestamp with Time Zone +:description: Redpanda SQL provides you with two data types for handling timestamps: +:page-topic-type: reference + +Redpanda SQL provides two data types for handling timestamps: + +* xref:reference:sql/sql-data-types/timestamp-without-time-zone.adoc[Timestamp without time zone]: Stores both date and time values. +* Timestamp with time zone: Stores date and time values and processes time zone information during operations. +** During an `INSERT` operation, the time zone is ignored. The date and time are stored without considering the time zone. +** During a `SELECT` operation, the time zone information from the user's session is also ignored. The data is returned exactly as it is stored without adjusting the time zone. + +[NOTE] +==== +All user sessions have a local timezone associated with them, affecting how `timestamp with time zone` values are displayed. The timezone information is not stored in the database. Every time a user requests a value of this type, Redpanda SQL converts from UTC to the user's local timezone before displaying it. +==== + +[NOTE] +==== +Redpanda SQL relies on timezone information served by the host machine's operating system. It must be up-to-date to ensure correct timestamp conversions, date calculations, and compliance with regional time changes such as daylight saving adjustments. +==== + +== Format + +The `timestamp with time zone` data type has the following format: + +[source,sql] +---- +YYYY-MM-DD HH:MM:SS.SSSSSS+TZ +---- + +* `YYYY`: Four-digit year. +* `MM`: One or two-digit month. +* `DD`: One or two-digit day. +* `HH`: One or two-digit hour (valid values from 00 to 23). +* `MM`: One or two-digit minutes (valid values from 00 to 59). +* `SS`: One or two-digit seconds (valid values from 00 to 59). +* `.SSSSSS`: Optional fractional digits, up to six (microsecond precision). +* `+TZ`: Time zone offset in the format +/-HH:MM (for example, +05:30, -08:00). + +== Examples + +=== Create a table + +The following example creates a table named `event_log` that consists of a timestamp without a time zone and a timestamp with time zone columns. The values in the `event_timestamp_tz` are in the "Europe/Moscow" timezone. + +[source,sql] +---- +CREATE TABLE events_log ( + event_name TEXT, + event_timestamp TIMESTAMP WITHOUT TIME ZONE, + event_timestamp_tz TIMESTAMP WITH TIME ZONE +); +INSERT INTO events_log (event_name, event_timestamp, event_timestamp_tz) +VALUES + ('Event 1', '2023-07-27 12:30:00', '2023-07-27 12:30:00+03:00'), + ('Event 2', '2023-07-27 08:45:00', '2023-07-27 08:45:00+03:00'), + ('Event 3', '2023-07-27 20:15:00', '2023-07-27 20:15:00+03:00'); +---- + +The table has been successfully created after executing the query: + +[source,sql] +---- +COMPLETE +INSERT 0 3 +---- + +=== Display the table + +Run the `SELECT` statement to get all records of the table: + +[source,sql] +---- +SELECT event_timestamp, event_timestamp_tz +FROM events_log; +---- + +This returns the following result. Notice that the `event_timestamp_tz` is converted to UTC timezone. + +[source,sql] +---- + event_timestamp | event_timestamp_tz +----------------------------+--------------------------------- + 2023-07-27 12:30:00.000000 | 2023-07-27 09:30:00.000000+0000 + 2023-07-27 08:45:00.000000 | 2023-07-27 05:45:00.000000+0000 + 2023-07-27 20:15:00.000000 | 2023-07-27 17:15:00.000000+0000 +(3 rows) +---- + +=== Order table by timestamp + +To sort the events based on the `event_timestamp` column and display the corresponding UTC in the `event_timestamp_tz` column, run the query: + +[source,sql] +---- +SELECT + event_timestamp, + event_timestamp_tz, + event_timestamp AT TIME ZONE 'UTC' AS utc_time +FROM + events_log +ORDER BY + event_timestamp; +---- + +This query retrieves the `event_timestamp` and `event_timestamp_tz` columns and calculates the corresponding UTC time using the `AT TIME ZONE 'UTC'` operator. + +The results are ordered based on the `event_timestamp` column, producing a sorted list of events with their corresponding local and UTC times. + +[source,sql] +---- + event_timestamp | event_timestamp_tz | utc_time +----------------------------+---------------------------------+--------------------------------- + 2023-07-27 08:45:00.000000 | 2023-07-27 05:45:00.000000+0000 | 2023-07-27 08:45:00.000000+0000 + 2023-07-27 12:30:00.000000 | 2023-07-27 09:30:00.000000+0000 | 2023-07-27 12:30:00.000000+0000 + 2023-07-27 20:15:00.000000 | 2023-07-27 17:15:00.000000+0000 | 2023-07-27 20:15:00.000000+0000 +(3 rows) +---- + +== AT TIME ZONE operator + +The `AT TIME ZONE` operator in timestamp with time zone converts the given timestamp with time zone to the new time zone, with no time zone designation. + +=== Syntax + +[source,sql] +---- +SELECT TIMESTAMP WITH TIME ZONE 'timestamp' AT TIME ZONE 'TIME_ZONE'; +---- + +* `timestamp`: The date and time value with the time zone. +* `TIME_ZONE`: The target time zone to which Redpanda SQL converts the timestamp. The user's time zone is fixed to UTC. + +=== Example + +In this example, a specified timestamp with time zone is converted into the UTC timezone. + +[source,sql] +---- +SELECT TIMESTAMP WITH TIME ZONE '2023-03-04 10:29:90-05' AT TIME ZONE 'UTC'; +---- + +The result is a timestamp without a time zone: + +[source,sql] +---- + timezone +---------------------------- + 2023-03-04 15:30:30.000000 +(1 row) +---- diff --git a/modules/reference/pages/sql/sql-data-types/timestamp-without-time-zone.adoc b/modules/reference/pages/sql/sql-data-types/timestamp-without-time-zone.adoc new file mode 100644 index 000000000..6c7c4eb0f --- /dev/null +++ b/modules/reference/pages/sql/sql-data-types/timestamp-without-time-zone.adoc @@ -0,0 +1,210 @@ += Timestamp Without Time Zone +:description: The timestamp data type stores time and date values without a time zone. +:page-topic-type: reference + +== Overview + +The timestamp data type stores time and date values without a time zone. It represents a fixed time, independent of any time zone or applied globally. + +== Format + +[source,sql] +---- +YYYY-MM-DD [HH:MM:SS[.SSSSSS]] +---- + +* `YYYY`: Four-digit year. +* `MM`: One / two-digit month. +* `DD`: One / two-digit day. +* `HH`: One / two-digit hour (valid values from 00 to 23). +* `MM`: One / two-digit minutes (valid values from 00 to 59). +* `SS`: One / two-digit seconds (valid values from 00 to 59). +* `[.SSSSSS]`: Up to six fractional digits (microsecond precision). + +[NOTE] +==== +Fractional digits are the digits after the decimal point ( . ) +==== + +== Examples + +=== Create a table + +The following example creates a `visitor` table to store visitor data in an office building. It consists of the visitor's name, the purpose of the visit, company, time, and date, which uses the `Timestamp` data type. + +[source,sql] +---- +CREATE TABLE visitors ( + visitorName TEXT, + visitPurp TEXT, + visitComp TEXT, + visitDate TIMESTAMP WITHOUT TIME ZONE +); +INSERT INTO visitors (visitorName, visitPurp, visitComp, visitDate) +VALUES + ('Peter', 'Interview', 'Apple', '2022-01-10 09:12:40'), + ('Will', 'Meeting', 'McKesson', '2022-01-29 11:28:02'), + ('Max', 'Meeting', 'McKesson', '2022-02-11 10:19:10'), + ('Dustin', 'Meeting', 'CVS Health', '2022-03-18 14:24:08'), + ('Lizzy', 'Meeting', 'CVS Health', '2022-04-23 13:10:09'), + ('Evy', 'Interview', 'Apple', '2022-05-01 08:45:50'); +---- + +The `visitors` table has been successfully created after executing the query: + +[source,sql] +---- +COMPLETE +INSERT 0 6 +---- + +=== Display the table + +Run the `SELECT` statement to get all records of the `visitors` table: + +[source,sql] +---- +SELECT * FROM visitors; +---- + +This returns the following result: + +[source,sql] +---- ++--------------+--------------+---------------+-----------------------+ +| visitorName | visitPurp | visitComp | visitDate | ++--------------+--------------+---------------+-----------------------+ +| Peter | Interview | Apple | 2022-01-10 09:12:40 | +| Will | Meeting | McKesson | 2022-01-29 11:28:02 | +| Max | Meeting | McKesson | 2022-02-11 10:19:10 | +| Dustin | Meeting | CVS Health | 2022-03-18 14:24:08 | +| Lizzy | Meeting | CVS Health | 2022-04-23 13:10:09 | +| Evy | Interview | Apple | 2022-05-01 08:45:50 | ++--------------+--------------+---------------+-----------------------+ +---- + +=== Look for a specific timestamp + +The following example retrieves records with a specified timestamp: + +[source,sql] +---- +SELECT * FROM visitors +WHERE visitDate = '2022-04-23 13:10:09'; +---- + +The query returns the following results: + +[source,sql] +---- ++--------------+--------------+---------------+-----------------------+ +| visitorName | visitPurp | visitComp | visitDate | ++--------------+--------------+---------------+-----------------------+ +| Lizzy | Meeting | CVS Health | 2022-04-23 13:10:09 | ++--------------+--------------+---------------+-----------------------+ +---- + +=== Insert a value that exceeds the standard format + +The time in timestamp has a standard format; that is, for minutes only valid for values from 00 to 59. + +The following example inserts a new record into the visitors table with a value of `60`, which exceeds the standard seconds format. + +[source,sql] +---- +INSERT INTO visitors (visitorName, visitPurp, visitComp, visitDate) +VALUES + ('Jolly', 'Survey', 'Apple', '2022-01-10 09:12:60'); +---- + +[source,sql] +---- +INSERT 0 1 + +Query returned successfully in 135 msec. +---- + +Verify the result by running the following `select` statement: + +[source,sql] +---- +SELECT * FROM visitors +WHERE visitorName = 'Jolly'; +---- + +The seconds are displayed as `00` because `60` adds 1 minute to the minutes' value. + +[source,sql] +---- ++--------------+--------------+---------------+-----------------------+ +| visitorName | visitPurp | visitComp | visitDate | ++--------------+--------------+---------------+-----------------------+ +| Jolly | Survey | Apple | 2022-01-10 09:13:00 | ++--------------+--------------+---------------+-----------------------+ +---- + +== AT TIME ZONE operator + +The `AT TIME ZONE` operator converts the input timestamp to the target time zone specified in the query. Additionally, the timestamp you inputted will always be presented in the user's local timezone (currently set as UTC). + +[WARNING] +==== +The result type of this operator differs from the input: it produces a timestamp with a time zone. +==== + +=== Syntax + +To use the `AT TIME ZONE` operator, you can follow this syntax: + +[source,sql] +---- +SELECT TIMESTAMP 'input_timestamp' AT TIME ZONE 'TIME_ZONE'; +---- + +Here's what each element means: + +* `input_timestamp`: This represents the date and time value you want to convert. The user's time zone is fixed to UTC. +* `TIME_ZONE`: The target time zone to which Redpanda SQL converts the timestamp. + +=== Example 1 + +Suppose you have a timestamp and want to convert it into the MST time zone: + +[source,sql] +---- +SELECT TIMESTAMP '2001-02-16 10:28:30' AT TIME ZONE 'MST'; +---- + +The result is a timestamp with the time zone adjusted to MST: + +[source,sql] +---- + f +--------------------------------- + 2001-02-16 17:28:30.000000+0000 +(1 row) +---- + +=== Example 2 + +Using the xref:reference:sql/sql-data-types/timestamp-without-time-zone.adoc[visitors] table, the following query retrieves a list of visit dates in the MST time zone: + +[source,sql] +---- +SELECT visitDate, visitDate AT TIME ZONE 'MST' as "visitDateMST" FROM visitors; +---- + +This query returns a list of two columns: `visitDate` displays the timestamps without a time zone, and `visitDateMST` stores the timestamps converted to the MST time zone. + +[source,sql] +---- + visitdate | visitDateMST +----------------------------+--------------------------------- + 2022-01-10 09:12:40.000000 | 2022-01-10 16:12:40.000000+0000 + 2022-01-29 11:28:02.000000 | 2022-01-29 18:28:02.000000+0000 + 2022-02-11 10:19:10.000000 | 2022-02-11 17:19:10.000000+0000 + 2022-03-18 14:24:08.000000 | 2022-03-18 21:24:08.000000+0000 + 2022-04-23 13:10:09.000000 | 2022-04-23 20:10:09.000000+0000 + 2022-05-01 08:45:50.000000 | 2022-05-01 15:45:50.000000+0000 +(6 rows) +---- diff --git a/modules/reference/pages/sql/sql-functions/aggregate-functions/avg.adoc b/modules/reference/pages/sql/sql-functions/aggregate-functions/avg.adoc new file mode 100644 index 000000000..64a0d336e --- /dev/null +++ b/modules/reference/pages/sql/sql-functions/aggregate-functions/avg.adoc @@ -0,0 +1,145 @@ += AVG +:description: The AVG() function calculates the average value of records. +:page-topic-type: reference + +The `AVG()` function calculates the average value of records. The supported input and return types are listed in the following table: + +[cols=",",options="header",] +|=== +|Input type |Return type +|`INTEGER` |`DOUBLE PRECISION` +|`BIGINT` |`DOUBLE PRECISION` +|`REAL` |`DOUBLE PRECISION` +|`DOUBLE PRECISION` |`DOUBLE PRECISION` +|=== + +[NOTE] +==== +If the input type is 32-bit, then the result is 64-bit +==== +Special cases: Returns NaN if the input contains a NaN. + +== Examples + +This example uses an `orders` table that stores details of the purchase transactions: + +[source,sql] +---- +CREATE TABLE orders ( + orderid int, + custname text, + orderproduct text, + ordertotal real +); +INSERT INTO orders (orderid, custname, orderproduct, ordertotal) +VALUES +(9557411, 'Maya', 'Jeans', 10.5), +(9557421, 'Aaron', 'T-Shirt', 9.2), +(9557451, 'Alex', 'Hat', 10.8), +(9557311, 'Will', 'Hat', 8.5), +(9557321, 'Will', 'T-Shirt', 12.15), +(9557351, 'Maya', 'T-Shirt', 9.5), +(9557221, 'Maya', 'Jeans', 11.02), +(9557251, 'Alex', 'Jeans', 11.09), +(9557231, 'Aaron', 'Hat', 14.56), +(9557281, 'Aaron', 'Hat', 12.15), +(9557291, 'Will', 'T-Shirt', 13.1); +---- + +[source,sql] +---- +SELECT * FROM orders; +---- + +This query shows the following table: + +[source,sql] +---- ++----------+-----------+---------------+-------------+ +| orderid | custname | orderproduct | ordertotal | ++----------+-----------+---------------+-------------+ +| 9557411 | Maya | Jeans | 10.5 | +| 9557421 | Aaron | T-Shirt | 9.2 | +| 9557451 | Alex | Hat | 10.8 | +| 9557311 | Will | Hat | 8.5 | +| 9557321 | Will | T-Shirt | 12.15 | +| 9557351 | Maya | T-Shirt | 9.5 | +| 9557221 | Maya | Jeans | 11.02 | +| 9557251 | Alex | Jeans | 11.09 | +| 9557231 | Aaron | Hat | 14.56 | +| 9557281 | Aaron | Hat | 12.15 | +| 9557291 | Will | T-Shirt | 13.1 | ++----------+-----------+---------------+-------------+ +---- + +=== AVG() with a single expression + +The first example calculates the average amount of all orders that customers have paid: + +[source,sql] +---- +SELECT AVG(ordertotal) AS "Order Total Average" +FROM orders; +---- + +This returns the following output: + +[source,sql] +---- ++---------------------+ +| Order Total Average | ++---------------------+ +| 11.142727331681685 | ++---------------------+ +---- + +=== AVG() with a GROUP BY clause + +The following example uses the `AVG()` function and `GROUP BY` clause to calculate the average amount paid by each customer: + +* First, the `GROUP BY` clause divides orders into groups based on customers +* Then, the `AVG` function is applied to each group. + +[source,sql] +---- +SELECT custname AS "Customer", AVG (ordertotal) AS "Total Price Average" +FROM orders +GROUP BY custname; +---- + +The query returns: + +[source,sql] +---- ++-----------+----------------------+ +| Customer | Total Price Average | ++-----------+----------------------+ +| Aaron | 11.96999994913737 | +| Alex | 10.945000171661377 | +| Will | 11.25 | +| Maya | 10.34000015258789 | ++-----------+----------------------+ +---- + +You can use the cast operator like`::NUMERIC(10,2)` to add two decimal numbers after the comma: + +[source,sql] +---- +SELECT custname AS "Customer", AVG (ordertotal)::NUMERIC(10,2) AS "Total Price Average" +FROM orders +GROUP BY custname; +---- + +The result will trim and round two numbers after the comma: + +[source,sql] +---- ++-----------+----------------------+ +| Customer | Total Price Average | ++-----------+----------------------+ +| Aaron | 11.97 | +| Alex | 10.95 | +| Will | 11.25 | +| Maya | 10.34 | ++-----------+----------------------+ +---- diff --git a/modules/reference/pages/sql/sql-functions/aggregate-functions/bool-and.adoc b/modules/reference/pages/sql/sql-functions/aggregate-functions/bool-and.adoc new file mode 100644 index 000000000..a6eeab627 --- /dev/null +++ b/modules/reference/pages/sql/sql-functions/aggregate-functions/bool-and.adoc @@ -0,0 +1,111 @@ += BOOL_AND +:description: The BOOL_AND() function calculates all the boolean values in the aggregated group, which will have these results: +:page-topic-type: reference + +The `BOOL_AND()` function calculates all the boolean values in the aggregated group, which will have these results: + +* `true` if all the values are `true` for every row. +* `false` if at least one row in the group is `false`. + +The input and the return type must be in `BOOL`. + +[NOTE] +==== +`NULL` values are not aggregated, so it returns `NULL` if there are zero input rows. +==== + +== Examples + +This example uses a payment table that stores details of the orders, whether the order has been paid or unpaid by the customer: + +[source,sql] +---- +CREATE TABLE payment ( + orderid int, + custname text, + orderproduct text, + ordertotal real, + paid boolean +); +INSERT INTO payment (orderid, custname, orderproduct, ordertotal, paid) +VALUES +(9557411, 'Maya', 'Jeans', 10.5, true), +(9557421, 'Aaron', 'T-Shirt', 9.2, true), +(9557451, 'Alex', 'Hat', 10.8, true), +(9557311, 'Will', 'Hat', 8.5, true), +(9557321, 'Will', 'T-Shirt', 12.15, true), +(9557351, 'Maya', 'T-Shirt', 9.5, true), +(9557221, 'Maya', 'Jeans', 11.02, true), +(9557251, 'Alex', 'Jeans', 11.09, true), +(9557231, 'Aaron', 'Hat', 14.56, false), +(9557281, 'Aaron', 'Hat', 12.15, true), +(9557291, 'Will', 'T-Shirt', 13.1, true); +---- + +[source,sql] +---- +SELECT * FROM payment; +---- + +This query shows the following table: + +[source,sql] +---- ++----------+-----------+---------------+-------------+-------+ +| orderid | custname | orderproduct | ordertotal | paid | ++----------+-----------+---------------+-------------+-------+ +| 9557411 | Maya | Jeans | 10.5 | t | +| 9557421 | Aaron | T-Shirt | 9.2 | t | +| 9557451 | Alex | Hat | 10.8 | t | +| 9557311 | Will | Hat | 8.5 | t | +| 9557321 | Will | T-Shirt | 12.15 | t | +| 9557351 | Maya | T-Shirt | 9.5 | t | +| 9557221 | Maya | Jeans | 11.02 | t | +| 9557251 | Alex | Jeans | 11.09 | t | +| 9557231 | Aaron | Hat | 14.56 | f | +| 9557281 | Aaron | Hat | 12.15 | t | +| 9557291 | Will | T-Shirt | 13.1 | t | ++----------+-----------+---------------+-------------+-------+ +---- + +=== `BOOL_AND` with a false result + +To find out if all customers have paid for their orders, run the query: + +[source,sql] +---- +SELECT BOOL_AND(paid) AS "final_result" FROM payment; +---- + +In the `BOOL_AND` function, if there is at least one `FALSE` value, the overall result is `FALSE`. The output shows that there is an order that hasn't been paid. + +[source,sql] +---- ++--------------+ +| final_result | ++--------------+ +| f | ++--------------+ +---- + +=== `BOOL_AND` with a true result + +To find out if Maya has paid for her orders, run the query: + +[source,sql] +---- +SELECT BOOL_AND(paid) AS Maya_Paid +FROM payment +WHERE custname ='Maya'; +---- + +In the `BOOL_AND` function, if all values are `TRUE`, then the overall result is `TRUE`. The output shows that Maya has paid all her orders. + +[source,sql] +---- ++-----------+ +| maya_paid | ++-----------+ +| t | ++-----------+ +---- diff --git a/modules/reference/pages/sql/sql-functions/aggregate-functions/bool-or.adoc b/modules/reference/pages/sql/sql-functions/aggregate-functions/bool-or.adoc new file mode 100644 index 000000000..3864fb744 --- /dev/null +++ b/modules/reference/pages/sql/sql-functions/aggregate-functions/bool-or.adoc @@ -0,0 +1,111 @@ += BOOL_OR +:description: The BOOL_OR() function calculates all the boolean values in the aggregated group, which will have these results: +:page-topic-type: reference + +The `BOOL_OR()` function calculates all the boolean values in the aggregated group, which will have these results: + +* `false` if all the values are `false` for every row. +* `true` if at least one row in the group is true. + +The input and the return type must be in `BOOL`. + +[NOTE] +==== +`NULL` values are not aggregated, so it returns `NULL` if there are zero input rows. +==== + +== Examples + +This example uses a payment table that stores details of the orders, whether the order has been paid or unpaid by the customer: + +[source,sql] +---- +CREATE TABLE payment ( + orderid int, + custname text, + orderproduct text, + ordertotal real, + paid boolean +); +INSERT INTO payment (orderid, custname, orderproduct, ordertotal, paid) +VALUES +(9557411, 'Maya', 'Jeans', 10.5, false), +(9557421, 'Aaron', 'T-Shirt', 9.2, false), +(9557451, 'Alex', 'Hat', 10.8, false), +(9557311, 'Will', 'Hat', 8.5, true), +(9557321, 'Will', 'T-Shirt', 12.15, false), +(9557351, 'Maya', 'T-Shirt', 9.5, true), +(9557221, 'Maya', 'Jeans', 11.02, false), +(9557251, 'Alex', 'Jeans', 11.09, false), +(9557231, 'Aaron', 'Hat', 14.56, false), +(9557281, 'Aaron', 'Hat', 12.15, false), +(9557291, 'Will', 'T-Shirt', 13.1, false); +---- + +[source,sql] +---- +SELECT * FROM payment; +---- + +This query shows the following table: + +[source,sql] +---- ++----------+-----------+---------------+-------------+--------+ +| orderid | custname | orderproduct | ordertotal | paid | ++----------+-----------+---------------+-------------+--------+ +| 9557411 | Maya | Jeans | 10.5 | f | +| 9557421 | Aaron | T-Shirt | 9.2 | f | +| 9557451 | Alex | Hat | 10.8 | f | +| 9557311 | Will | Hat | 8.5 | t | +| 9557321 | Will | T-Shirt | 12.15 | f | +| 9557351 | Maya | T-Shirt | 9.5 | t | +| 9557221 | Maya | Jeans | 11.02 | f | +| 9557251 | Alex | Jeans | 11.09 | f | +| 9557231 | Aaron | Hat | 14.56 | f | +| 9557281 | Aaron | Hat | 12.15 | f | +| 9557291 | Will | T-Shirt | 13.1 | f | ++----------+-----------+---------------+-------------+--------+ +---- + +=== `BOOL_OR` with a true result + +To find out if all customers have paid for their orders, run the query: + +[source,sql] +---- +SELECT BOOL_OR(paid) AS "final_result" FROM payment; +---- + +If there is at least one `TRUE` value, the overall result is `TRUE`. The output shows that some order has been paid regardless of the other unpaid orders. + +[source,sql] +---- ++--------------+ +| final_result | ++--------------+ +| t | ++--------------+ +---- + +=== `BOOL_OR` with a false result + +To find out if Aaron has paid for his orders, run the query: + +[source,sql] +---- +SELECT BOOL_OR(paid) AS aaron_paid +FROM payment +WHERE custname ='Aaron'; +---- + +If all values are `FALSE`, then the overall result is `FALSE`. The output shows that Aaron hasn't paid for all his orders. + +[source,sql] +---- ++------------+ +| aaron_paid | ++------------+ +| f | ++------------+ +---- diff --git a/modules/reference/pages/sql/sql-functions/aggregate-functions/count.adoc b/modules/reference/pages/sql/sql-functions/aggregate-functions/count.adoc new file mode 100644 index 000000000..9f3d8f5cb --- /dev/null +++ b/modules/reference/pages/sql/sql-functions/aggregate-functions/count.adoc @@ -0,0 +1,138 @@ += COUNT +:description: The COUNT() function retrieves the number of records that match a specific condition. +:page-topic-type: reference + +The `COUNT()` function retrieves the number of records that match a specific condition. It works with any data type supported by Redpanda SQL and returns a `BIGINT`. + +[NOTE] +==== +The output will indicate the total number of rows in a table, regardless of the input types. +==== + +== Examples + +This example uses an orders table that stores details of the purchase transactions: + +[source,sql] +---- +CREATE TABLE orders ( + orderid int, + custname text, + orderproduct text, + ordertotal real +); +INSERT INTO orders (orderid, custname, orderproduct, ordertotal) +VALUES +(9557411, 'Maya', 'Jeans', 10.5), +(9557421, 'Aaron', 'T-Shirt', 9.2), +(9557451, 'Alex', 'Hat', 10.8), +(9557311, 'Will', 'Hat', 8.5), +(9557321, 'Will', 'T-Shirt', 12.15), +(9557351, 'Maya', 'T-Shirt', 9.5), +(9557221, 'Maya', 'Jeans', 11.02), +(9557251, 'Alex', 'Jeans', 11.09), +(9557231, 'Aaron', 'Hat', 14.56), +(9557281, 'Aaron', 'Hat', 12.15), +(9557291, 'Will', 'T-Shirt', 13.1); +---- + +[source,sql] +---- +SELECT * FROM orders; +---- + +This query shows the following table: + +[source,sql] +---- ++----------+-----------+---------------+-------------+ +| orderid | custname | orderproduct | ordertotal | ++----------+-----------+---------------+-------------+ +| 9557411 | Maya | Jeans | 10.5 | +| 9557421 | Aaron | T-Shirt | 9.2 | +| 9557451 | Alex | Hat | 10.8 | +| 9557311 | Will | Hat | 8.5 | +| 9557321 | Will | T-Shirt | 12.15 | +| 9557351 | Maya | T-Shirt | 9.5 | +| 9557221 | Maya | Jeans | 11.02 | +| 9557251 | Alex | Jeans | 11.09 | +| 9557231 | Aaron | Hat | 14.56 | +| 9557281 | Aaron | Hat | 12.15 | +| 9557291 | Will | T-Shirt | 13.1 | ++----------+-----------+---------------+-------------+ +---- + +=== `COUNT()` with a single expression + +This example returns the number of all orders in the orders table: + +[source,sql] +---- +SELECT COUNT(*) FROM orders; +---- + +The query returns: + +[source,sql] +---- ++-------+ +| count | ++-------+ +| 11 | ++-------+ +---- + +=== `COUNT()` with a `GROUP BY` clause + +This example will combine the `COUNT()` function and the `GROUP BY` clause. + +* The `GROUP BY` clause groups the orders based on the customer's name. +* The `COUNT()` function counts the orders for each customer. + +[source,sql] +---- +SELECT custname, COUNT (orderid) +FROM orders +GROUP BY custname; +---- + +The query returns: + +[source,sql] +---- ++-----------+--------+ +| custname | count | ++-----------+--------+ +| Aaron | 3 | +| Alex | 2 | +| Will | 3 | +| Maya | 3 | ++-----------+--------+ +---- + +=== `COUNT()` with a `HAVING` clause + +This example combines the `COUNT()` function and the `HAVING` clause to apply a specific condition to find customers who have made more than two orders: + +[source,sql] +---- +SELECT custname, COUNT (orderid) +FROM orders +GROUP BY custname +HAVING COUNT (orderid) > 2; +---- + +* The `GROUP BY` clause groups the orders based on the customer's name. +* The `HAVING` clause will filter only customers with more than two order IDs. +* The `COUNT()` function counts the orders for each customer. + +[source,sql] +---- ++-----------+-------+ +| custname | count | ++-----------+-------+ +| Aaron | 3 | +| Will | 3 | +| Maya | 3 | ++-----------+-------+ +---- diff --git a/modules/reference/pages/sql/sql-functions/aggregate-functions/distinct.adoc b/modules/reference/pages/sql/sql-functions/aggregate-functions/distinct.adoc new file mode 100644 index 000000000..e48a01fcb --- /dev/null +++ b/modules/reference/pages/sql/sql-functions/aggregate-functions/distinct.adoc @@ -0,0 +1,210 @@ += DISTINCT +:description: When using aggregation functions, they can contain the DISTINCT keyword. +:page-topic-type: reference + +When using aggregation functions, they can contain the `DISTINCT` keyword. It acts as a qualifier for them, to ensure that only unique values are being processed. Syntax: + +[source,sql] +---- +aggregation function (DISTINCT expression [clause] ...) ... +---- + +`DISTINCT` keyword can be combined with the following aggregate functions: + +* `AVG()` +* `COUNT()` +* `MAX()` +* `MIN()` +* `SUM()` + +All functions listed in this section operate on the same input and return types, that are supported by their counterparts without any qualifiers. They can be grouped without any limitations, provided that they utilise a single `DISTINCT` keyword. + +== Examples + +This section focuses on a few examples that showcase sample usage of these concepts. They are based on creation of the following tables: + +[source,sql] +---- +CREATE TABLE customer ( + customer_id int, + cust_name text +); +INSERT INTO customer + (customer_id, cust_name) +VALUES + (11112, 'Alex'), + (11113, 'Aaron'), + (11114, 'Alice'), + (11115, 'Nina'), + (11116, 'Rosy'), + (11117, 'Martha'), + (11118, 'John'); + +CREATE TABLE rental ( + rental_id int, + rental_date timestamp, + return_date timestamp, + car text, + customer_id int, + total_price int +); +INSERT INTO rental (rental_id, rental_date, return_date, car, customer_id, total_price) +VALUES +(8557411, '2022-04-02 09:10:19', '2022-04-10 10:15:05', 'Audi', 11112, 1400), +(8557421, '2022-04-06 07:00:30', '2022-04-19 07:10:19', 'BMW', 11115, 2000), +(8557451, '2022-04-19 08:00:20', '2022-04-24 08:05:00', 'Cadillac', 11112, 1000), +(8557311, '2022-05-11 09:15:28', '2022-05-18 09:00:18', 'Audi', 11115, 1500), +(8557321, '2022-05-20 10:12:22', '2022-05-28 10:08:48', 'Audi', 11113, 1500), +(8557351, '2022-06-10 12:18:09', '2022-06-20 18:12:23', 'Cadillac', 11114, 1200), +(8557221, '2022-06-17 14:02:02', '2022-06-20 14:17:02', 'Chevrolet', 11112, 1300), +(8557251, '2022-07-12 05:19:49', '2022-07-19 07:15:28', 'Chevrolet', 11116, 1400), +(8557231, '2022-08-09 09:29:08', '2022-08-24 09:30:58', 'Cadillac', 11114, 2000), +(8557291, '2022-08-18 15:15:20', '2022-09-01 15:30:19', 'BMW', 11117, 3000); +---- + +The created tables: + +[source,sql] +---- +SELECT * FROM customer; + ++-------------+-----------+ +| customer_id | cust_name | ++-------------+-----------+ +| 11112 | Alex | +| 11113 | Aaron | +| 11114 | Alice | +| 11115 | Nina | +| 11116 | Rosy | +| 11117 | Martha | +| 11118 | John | ++-------------+-----------+ + +SELECT * FROM rental; + ++------------+---------------------+---------------------+-----------+---------------+-------------+ +| rental_id | rental_date | return_date | car | customer_id | total_price | ++------------+---------------------+---------------------+-----------+---------------+-------------+ +| 8557411 | 2022-04-02 09:10:19 | 2022-04-10 10:15:05 | Audi | 11112 | 1400 | +| 8557421 | 2022-04-06 07:00:30 | 2022-04-19 07:10:19 | BMW | 11115 | 2000 | +| 8557451 | 2022-04-19 08:00:20 | 2022-04-24 08:05:00 | Cadillac | 11112 | 1000 | +| 8557311 | 2022-05-11 09:15:28 | 2022-05-18 09:00:18 | Audi | 11115 | 1500 | +| 8557321 | 2022-05-20 10:12:22 | 2022-05-28 10:08:48 | Audi | 11113 | 1500 | +| 8557351 | 2022-06-10 12:18:09 | 2022-06-20 18:12:23 | Cadillac | 11114 | 1200 | +| 8557221 | 2022-06-17 14:02:02 | 2022-06-20 14:17:02 | Chevrolet | 11112 | 1300 | +| 8557251 | 2022-07-12 05:19:49 | 2022-07-19 07:15:28 | Chevrolet | 11116 | 1400 | +| 8557231 | 2022-08-09 09:29:08 | 2022-08-24 09:30:58 | Cadillac | 11114 | 2000 | +| 8557291 | 2022-08-18 15:15:20 | 2022-09-01 15:30:19 | BMW | 11117 | 3000 | ++------------+---------------------+---------------------+-----------+---------------+-------------+ +---- + +=== `DISTINCT` combined with `COUNT` function + +The following example uses `DISTINCT` qualifier combined with `COUNT()` function to calculate the number of unique car brands in rentals: + +[source,sql] +---- +SELECT COUNT (DISTINCT car) AS number_of_car_brands +FROM rental; +---- + +This returns the following output: + +[source,sql] +---- ++----------------------+ +| number_of_car_brands | ++----------------------+ +| 4 | ++----------------------+ +---- + +This example uses the `DISTINCT` qualifier combined with `COUNT()` function to calculate the number of rentals by each customer: + +[source,sql] +---- +SELECT c.cust_name AS customer_name, COUNT (DISTINCT r.rental_id) AS rental_count +FROM rental r +JOIN customer c ON r.customer_id = c.customer_id +GROUP BY c.cust_name; +---- + +This calculates the `rental_count` by each `customer_name`: + +[source,sql] +---- ++----------------+--------------+ +| customer_name | rental_count | ++----------------+--------------+ +| Nina | 2 | +| Aaron | 1 | +| Alice | 2 | +| Martha | 1 | +| Alex | 3 | +| Rosy | 1 | ++----------------+--------------+ +---- + +=== `DISTINCT` combined with `MAX()` function + +The following example uses `DISTINCT` qualifier combined with `MAX()` function to find maximum single spending per each customer, dropping any repeated transactions: + +[source,sql] +---- +SELECT c.cust_name AS customer_name, + MAX (DISTINCT r.total_price) AS max_spending +FROM rental r +JOIN customer c ON r.customer_id = c.customer_id +GROUP BY c.cust_name; +---- + +The query returns: + +[source,sql] +---- ++---------------+--------------+ +| customer_name | max_spending | ++---------------+--------------+ +| Martha | 3000 | +| Rosy | 1400 | +| Alex | 1400 | +| Alice | 2000 | +| Nina | 2000 | +| Aaron | 1500 | ++---------------+--------------+ +---- + +=== `DISTINCT` combined with `SUM()` function + +The following example compares the sum of unique revenues versus the sum of all revenues in rental data: + +[source,sql] +---- +SELECT + SUM (DISTINCT r.total_price) AS unique_revenue, + SUM (r.total_price) AS total_revenue +FROM rental r; +---- + +The query returns: + +[source,sql] +---- ++----------------+---------------+ +| unique_revenue | total_revenue | ++----------------+---------------+ +| 11400 | 16300 | ++----------------+---------------+ +---- + +The result may help to understand what is the impact of repeating transactions on total revenue. + +== Limitations + +There is one use case that is not currently supported: + +* Aggregation functions with `DISTINCT` keyword used as an argument of an expression, for example, +[source,sql] +---- +SELECT 1 + COUNT(DISTINCT col) FROM table +---- diff --git a/modules/reference/pages/sql/sql-functions/aggregate-functions/for-max.adoc b/modules/reference/pages/sql/sql-functions/aggregate-functions/for-max.adoc new file mode 100644 index 000000000..b0e22450a --- /dev/null +++ b/modules/reference/pages/sql/sql-functions/aggregate-functions/for-max.adoc @@ -0,0 +1,116 @@ += FOR_MAX() +:description: The FOR_MAX() function searches for a maximum in a specific column and returns a value related to that maximum from another column. +:page-topic-type: reference + +The `FOR_MAX()` function searches for a maximum in a specific column and returns a value related to that maximum from another column. + +== Syntax + +[source,sql] +---- +FOR_MAX(metric, value) +---- + +== Arguments + +* `metric`: Must be one of the following data types: `INT`, `LONG`, `FLOAT`, `DOUBLE`, `DATE` or `TIMESTAMP`. +* `value`: Can be any data type except `TEXT`. + +The `FOR_MAX()` function returns `NULL` in the following situations: + +* There are no input rows +* The `metric` column contains only `NULL` values +* The `value` corresponding to the metric minimum value is `NULL` + +This function also returns `NaN` (not-a-number) if the input contains a `NaN`. + +== Examples + +The following examples use a `payment` table that stores customer payment records, including any applied discounts: + +[source,sql] +---- +CREATE TABLE payments ( + paymentid int, + customer_name text, + price real, + discount real +); +INSERT INTO payments (paymentid, customer_name, price, discount) +VALUES +(1, 'Alex', 280.12, 0.1), +(2, NULL, 35.75, NULL), +(3, 'Alex', 45.1, 0.05), +(4, 'Alex', NULL, 0.4), +(5, 'John', NULL, 0.1), +(6, 'Bob', 50.45, 0.07), +(7, 'Bob', 120.5, 0.0); +---- + +To view the `payments` table content, run the query: + +[source,sql] +---- +SELECT * FROM payments; +---- + +[source,sql] +---- ++-----------+---------------+--------+----------+ +| paymentid | customer_name | price | discount | ++-----------+---------------+--------+----------+ +| 2 | | 35.75 | | +| 4 | Alex | | 0.4 | +| 3 | Alex | 45.1 | 0.05 | +| 1 | Alex | 280.12 | 0.1 | +| 6 | Bob | 50.45 | 0.07 | +| 5 | John | | 0.1 | +| 7 | Bob | 120.5 | 0 | ++-----------+---------------+--------+----------+ +---- + +=== `FOR_MAX()` basic usage + +To determine the price associated with the highest discount, run the code: + +[source,sql] +---- +SELECT FOR_MAX(discount, price) AS for_lowest_discount +FROM payments; +---- + +The query returns the following output: + +[source,sql] +---- ++---------------------+ +| for_lowest_discount | ++---------------------+ +| | ++---------------------+ +---- + +=== `FOR_MAX()` with `GROUP BY` clause + +This example uses a `GROUP BY` clause to group customers and then uses the `FOR_MAX()` function to get a discount for the highest price paid by each customer: + +[source,sql] +---- +SELECT customer_name, FOR_MAX(price, discount) AS discount +FROM payments +GROUP BY customer_name; +---- + +The query returns the following output: + +[source,sql] +---- ++---------------+----------+ +| customer_name | discount | ++---------------+----------+ +| | | +| Bob | 0 | +| Alex | 0.1 | +| John | | ++---------------+----------+ +---- diff --git a/modules/reference/pages/sql/sql-functions/aggregate-functions/for-min.adoc b/modules/reference/pages/sql/sql-functions/aggregate-functions/for-min.adoc new file mode 100644 index 000000000..e7791ceda --- /dev/null +++ b/modules/reference/pages/sql/sql-functions/aggregate-functions/for-min.adoc @@ -0,0 +1,114 @@ += FOR_MIN() +:description: The FOR_MIN() function searches for a minimum in a specific column and returns a value related to that minimum from another column. +:page-topic-type: reference + +The `FOR_MIN()` function searches for a minimum in a specific column and returns a value related to that minimum from another column. + +== Syntax + +[source,sql] +---- +FOR_MIN(metric, value) +---- + +== Arguments + +* `metric`: Must be one of the following data types: `INT`, `LONG`, `FLOAT`, `DOUBLE`, `DATE` or `TIMESTAMP`. +* `value`: Can be any data type except `TEXT`. + +The `FOR_MIN()` function returns `NULL` in the following situations: + +* There are no input rows +* The `metric` column contains only `NULL` values +* The `value` corresponding to the metric minimum value is `NULL` + +This function also returns `NaN` (not-a-number) if the input contains a `NaN`. + +== Examples + +The following examples use a `payment` table that stores customer payment records, including any applied discounts: + +[source,sql] +---- +CREATE TABLE payments ( + paymentid int, + customer_name text, + price real, + discount real); + +INSERT INTO + payments (paymentid, customer_name, price, discount) +VALUES + (1, 'Alex', 280.12, 0.1), + (2, NULL, 35.75, NULL), + (3, 'Alex', 45.1, 0.05), + (4, 'Alex', NULL, 0.4), + (5, 'John', NULL, 0.1), + (6, 'Bob', 50.45, 0.07), + (7, 'Bob', 120.5, 0.0); +---- + +To view the `payments` table content, run the query: + +[source,sql] +---- +SELECT * FROM payments; +---- + +[source,sql] +---- + paymentid | customer_name | price | discount +-----------+---------------+--------+---------- + 1 | Alex | 280.12 | 0.1 + 2 | | 35.75 | + 3 | Alex | 45.1 | 0.05 + 4 | Alex | | 0.4 + 5 | John | | 0.1 + 6 | Bob | 50.45 | 0.07 + 7 | Bob | 120.5 | 0 +(7 rows) +---- + +=== `FOR_MIN()` basic usage + +To determine the price associated with the lowest discount applied across all payments, run the query: + +[source,sql] +---- +SELECT FOR_MIN(discount, price) AS for_lowest_discount FROM payments; +---- + +The query returns the following output: + +[source,sql] +---- + for_lowest_discount +--------------------- + 120.5 +(1 row) +---- + +=== `FOR_MIN()` with `GROUP BY` clause + +To determine the discount associated with the lowest price paid by each customer, use the `GROUP BY` clause with the `FOR_MIN()` function: + +[source,sql] +---- +SELECT customer_name, + FOR_MIN(price, discount) AS discount +FROM payments +GROUP BY customer_name; +---- + +This query returns the following output: + +[source,sql] +---- +customer_name | discount +---------------+---------- + Bob | 0.07 + Alex | 0.05 + | + John | +(4 rows) +---- diff --git a/modules/reference/pages/sql/sql-functions/aggregate-functions/index.adoc b/modules/reference/pages/sql/sql-functions/aggregate-functions/index.adoc new file mode 100644 index 000000000..57e401e94 --- /dev/null +++ b/modules/reference/pages/sql/sql-functions/aggregate-functions/index.adoc @@ -0,0 +1,29 @@ += Overview +:description: Aggregate functions compute a single result from a set of input values. + +Aggregate functions compute a single result from a set of input values. Redpanda SQL supports the following aggregate functions: + +[width="100%",cols="36%,64%",options="header",] +|=== +|Function Name |Description +|xref:reference:sql/sql-functions/aggregate-functions/sum.adoc[SUM] |Calculates and returns the sum of all values +|xref:reference:sql/sql-functions/aggregate-functions/min.adoc[MIN] |Calculates and returns the minimum value +|xref:reference:sql/sql-functions/aggregate-functions/for-min.adoc[FOR_MIN] |Calculates and returns a value corresponding to the minimal metric in the same row from a set of values +|xref:reference:sql/sql-functions/aggregate-functions/max.adoc[MAX] |Calculates and returns the maximum value +|xref:reference:sql/sql-functions/aggregate-functions/for-max.adoc[FOR_MAX] |Calculates and Returns a value corresponding to the maximum metric in the same row from a set of values +|xref:reference:sql/sql-functions/aggregate-functions/avg.adoc[AVG] |Calculates and returns the average value +|xref:reference:sql/sql-functions/aggregate-functions/count.adoc[COUNT] |Counts the number of rows +|xref:reference:sql/sql-functions/aggregate-functions/bool-and.adoc[BOOL_AND] |Calculates the boolean of all the boolean values in the aggregated group. `FALSE` if at least one of aggregated rows is `FALSE` +|xref:reference:sql/sql-functions/aggregate-functions/bool-or.adoc[BOOL_OR] |Calculates the boolean of all the boolean values in the aggregated group. `TRUE` if at least one of aggregated rows is `TRUE` +|=== + +[width="100%",cols="36%,64%",options="header",] +|=== +|Function qualifier |Description +|xref:reference:sql/sql-functions/aggregate-functions/distinct.adoc[DISTINCT] |Allows aggregation functions to operate on a distinct set of values within a column +|=== + +[TIP] +==== +You can utilize the aggregate functions with the `GROUP BY` and `HAVING` clauses in the `SELECT` statement. +==== diff --git a/modules/reference/pages/sql/sql-functions/aggregate-functions/max.adoc b/modules/reference/pages/sql/sql-functions/aggregate-functions/max.adoc new file mode 100644 index 000000000..256158401 --- /dev/null +++ b/modules/reference/pages/sql/sql-functions/aggregate-functions/max.adoc @@ -0,0 +1,118 @@ += MAX +:description: MAX() is a function that returns the maximum value from a set of records. +:page-topic-type: reference + +`MAX()` is a function that returns the maximum value from a set of records. + +== Syntax + +The syntax for this function is: + +[source,sql] +---- +MAX(column_name) +---- + +This function's output data type will always be the same as the input one, however it returns `NULL` if there are no records or input consists of `NULL` values and it also returns `NaN` if the input contains a `NaN`. + +== Examples + +The following examples use a movies table that stores movie details, such as movie's title, category, and IMDb rating. + +[source,sql] +---- +CREATE TABLE movies ( + movieid int, + moviename text, + moviecategory text, + imdbrating real +); +INSERT INTO movies (movieid, moviename, moviecategory, imdbrating) +VALUES +(8557411, 'The Shawshank Redemption', 'Drama', 9.4), +(8557421, 'Life Is Beautiful', 'Romance', 8.4), +(8557451, 'The Godfather', 'Crime', 9.3), +(8557311, 'Prisoners', 'Thriller', 8.5), +(8557321, 'Inception', 'Science Fiction', 9), +(8557351, 'The Dark Knight', 'Action', 9.2), +(8557221, 'Coco', 'Drama', 8.2), +(8557251, 'The Sixth Sense', 'Horror', 8.1), +(8557231, 'Kill Bill: Vol. 1', 'Action', 8.1), +(8557281, 'The Notebook', 'Romance', 7.8), +(8557291, 'Forrest Gump', 'Drama', 8); +---- + +[source,sql] +---- +SELECT * FROM movies; +---- + +The query returns: + +[source,sql] +---- ++---------+--------------------------+-----------------+-------------+ +| movieid | moviename | moviecategory | imdbrating | ++---------+--------------------------+-----------------+-------------+ +| 8557411 | The Shawshank Redemption | Drama | 9.4 | +| 8557421 | Life Is Beautiful | Romance | 8.4 | +| 8557451 | The Godfather | Crime | 9.3 | +| 8557311 | Prisoners | Thriller | 8.5 | +| 8557321 | Inception | Science Fiction | 9 | +| 8557351 | The Dark Knight | Action | 9.2 | +| 8557221 | Coco | Drama | 8.2 | +| 8557251 | The Sixth Sense | Horror | 8.1 | +| 8557231 | Kill Bill: Vol. 1 | Action | 8.1 | +| 8557281 | The Notebook | Romance | 7.8 | +| 8557291 | Forrest Gump | Drama | 8 | ++---------+--------------------------+-----------------+-------------+ +---- + +=== `MAX()` with a single expression + +For example, you might want to know what is the highest rating among all stored movies: + +[source,sql] +---- +SELECT MAX(imdbRating) AS "Highest Rating" +FROM movies; +---- + +[source,sql] +---- ++-----------------+ +| Highest Rating | ++-----------------+ +| 9.4 | ++-----------------+ +---- + +=== `MAX()` with GROUP BY clause + +This example uses a `MAX()` function to get the highest rating in each movie category and the results are ordered by the rating in ascending order. + +[source,sql] +---- +SELECT + movieCategory AS "Movie Category", + MAX(imdbRating) AS "Highest Rating" +FROM movies +GROUP BY movieCategory +ORDER BY MAX(imdbRating) ASC; +---- + +This returns the highest rating from a group of `movieCategory`: + +[source,bash] +---- + Movie Category | Highest Rating +-----------------+---------------- + Horror | 8.1 + Romance | 8.4 + Thriller | 8.5 + Science Fiction | 9 + Action | 9.2 + Crime | 9.3 + Drama | 9.4 +(7 rows) +---- diff --git a/modules/reference/pages/sql/sql-functions/aggregate-functions/min.adoc b/modules/reference/pages/sql/sql-functions/aggregate-functions/min.adoc new file mode 100644 index 000000000..ec0a57bdb --- /dev/null +++ b/modules/reference/pages/sql/sql-functions/aggregate-functions/min.adoc @@ -0,0 +1,118 @@ += MIN +:description: MIN() is a function that returns the minimum value from a set of records. +:page-topic-type: reference + +`MIN()` is a function that returns the minimum value from a set of records. + +== Syntax + +The syntax for this function is: + +[source,sql] +---- +MIN(column_name) +---- + +This function's output data type will always be the same as the input one, however it returns `NULL` if there are no records or input consists of `NULL` values and it also returns `NaN` if the input contains a `NaN`. + +== Examples + +The following examples use a movies table that stores movie details, such as movie's title, category, and IMDb rating. + +[source,sql] +---- +CREATE TABLE movies ( + movieid int, + moviename text, + moviecategory text, + imdbrating real +); +INSERT INTO movies (movieid, moviename, moviecategory, imdbrating) +VALUES +(8557411, 'The Shawshank Redemption', 'Drama', 9.4), +(8557421, 'Life Is Beautiful', 'Romance', 8.4), +(8557451, 'The Godfather', 'Crime', 9.3), +(8557311, 'Prisoners', 'Thriller', 8.5), +(8557321, 'Inception', 'Science Fiction', 9), +(8557351, 'The Dark Knight', 'Action', 9.2), +(8557221, 'Coco', 'Drama', 8.2), +(8557251, 'The Sixth Sense', 'Horror', 8.1), +(8557231, 'Kill Bill: Vol. 1', 'Action', 8.1), +(8557281, 'The Notebook', 'Romance', 7.8), +(8557291, 'Forrest Gump', 'Drama', 8); +---- + +[source,sql] +---- +SELECT * FROM movies; +---- + +The query returns: + +[source,sql] +---- ++---------+--------------------------+-----------------+-------------+ +| movieid | moviename | moviecategory | imdbrating | ++---------+--------------------------+-----------------+-------------+ +| 8557411 | The Shawshank Redemption | Drama | 9.4 | +| 8557421 | Life Is Beautiful | Romance | 8.4 | +| 8557451 | The Godfather | Crime | 9.3 | +| 8557311 | Prisoners | Thriller | 8.5 | +| 8557321 | Inception | Science Fiction | 9 | +| 8557351 | The Dark Knight | Action | 9.2 | +| 8557221 | Coco | Drama | 8.2 | +| 8557251 | The Sixth Sense | Horror | 8.1 | +| 8557231 | Kill Bill: Vol. 1 | Action | 8.1 | +| 8557281 | The Notebook | Romance | 7.8 | +| 8557291 | Forrest Gump | Drama | 8 | ++---------+--------------------------+-----------------+-------------+ +---- + +=== `MIN()` with a single expression + +For example, you might want to know what is the lowest rating of all stored movies: + +[source,sql] +---- +SELECT MIN(imdbRating) AS "Lowest Rating" +FROM movies; +---- + +[source,sql] +---- ++----------------+ +| Lowest Rating | ++----------------+ +| 7.8 | ++----------------+ +---- + +=== `MIN()` with `GROUP BY` clause + +This example uses a `GROUP BY` clause to group the movie categories, then uses the `MIN()` function to get the lowest rating in each movie category and arrange the results in ascending order. + +[source,sql] +---- +SELECT + movieCategory AS "Movie Category", + MIN(imdbRating) AS "Lowest Rating" +FROM movies +GROUP BY movieCategory +ORDER BY MIN(imdbRating) ASC; +---- + +The query returns: + +[source,bash] +---- + Movie Category | Lowest Rating +-----------------+--------------- + Romance | 7.8 + Drama | 8 + Horror | 8.1 + Action | 8.1 + Thriller | 8.5 + Science Fiction | 9 + Crime | 9.3 +(7 rows) +---- diff --git a/modules/reference/pages/sql/sql-functions/aggregate-functions/ordered-set-aggregate-functions/index.adoc b/modules/reference/pages/sql/sql-functions/aggregate-functions/ordered-set-aggregate-functions/index.adoc new file mode 100644 index 000000000..9fbdd7853 --- /dev/null +++ b/modules/reference/pages/sql/sql-functions/aggregate-functions/ordered-set-aggregate-functions/index.adoc @@ -0,0 +1,3 @@ += Ordered-Set Aggregate Functions +:description: Reference for ordered-set aggregate functions in Redpanda SQL. +:page-layout: index diff --git a/modules/reference/pages/sql/sql-functions/aggregate-functions/ordered-set-aggregate-functions/mode.adoc b/modules/reference/pages/sql/sql-functions/aggregate-functions/ordered-set-aggregate-functions/mode.adoc new file mode 100644 index 000000000..963fad42b --- /dev/null +++ b/modules/reference/pages/sql/sql-functions/aggregate-functions/ordered-set-aggregate-functions/mode.adoc @@ -0,0 +1,71 @@ += MODE() +:description: MODE() is an ordered-set aggregate function that returns the most frequently occurring value (the mode) from a set of values. +:page-topic-type: reference + +`MODE()` is an ordered-set aggregate function that returns the most frequently occurring value (the mode) from a set of values. + +== Syntax + +[source,sql] +---- +MODE() WITHIN GROUP (ORDER BY order_list) +---- + +[NOTE] +==== +Null values are ignored during the calculation. If `NULL` is the most frequent value, the function returns the second most common value. +==== + +== Parameters + +* `()`: This function does not take any arguments, but the parentheses are required. + +== Examples + +The following example uses a simplified version of the `film` table from the link:https://www.postgresql.org/ftp/projects/pgFoundry/dbsamples/pagila/pagila/[Pagila database^], containing only the `title`, `length` and `rating` columns. + +[source,sql] +---- +DROP TABLE IF EXISTS film; +CREATE TABLE film ( + title text NOT NULL, + length int, + rating text +); +INSERT INTO film(title, length, rating) VALUES + ('ATTRACTION NEWTON', 83, 'PG-13'), + ('CHRISTMAS MOONSHINE', 150, 'NC-17'), + ('DANGEROUS UPTOWN', 121, 'PG'), + ('KILL BROTHERHOOD', 54, 'G'), + ('HALLOWEEN NUTS', 47, 'PG-13'), + ('HOURS RAGE', 122, 'NC-17'), + ('PIANIST OUTFIELD', 136, 'NC-17'), + ('PICKUP DRIVING', 77, 'G'), + ('INDEPENDENCE HOTEL', 157, 'NC-17'), + ('PRIVATE DROP', 106, 'PG'), + ('SAINTS BRIDE', 125, 'G'), + ('FOREVER CANDIDATE', 131, 'NC-17'), + ('MILLION ACE', 142, 'PG-13'), + ('SLEEPY JAPANESE', 137, 'PG'), + ('WRATH MILE', 176, 'NC-17'), + ('YOUTH KICK', 179, 'NC-17'), + ('CLOCKWORK PARADISE', 143, 'PG-13'); +---- + +The following query retrieves the most frequent ratings found in the film table: + +[source,sql] +---- +SELECT MODE() + WITHIN GROUP (ORDER BY rating) +FROM film; +---- + +The query returns: + +[source,sql] +---- +| mode | +|-------| +| NC-17 | +---- diff --git a/modules/reference/pages/sql/sql-functions/aggregate-functions/ordered-set-aggregate-functions/percentile-cont.adoc b/modules/reference/pages/sql/sql-functions/aggregate-functions/ordered-set-aggregate-functions/percentile-cont.adoc new file mode 100644 index 000000000..0c48e7641 --- /dev/null +++ b/modules/reference/pages/sql/sql-functions/aggregate-functions/ordered-set-aggregate-functions/percentile-cont.adoc @@ -0,0 +1,98 @@ += PERCENTILE_CONT() +:description: PERCENTILE_CONT() is an ordered-set aggregate function used to compute continuous percentiles from a set of values. +:page-topic-type: reference + +`PERCENTILE_CONT()` is an ordered-set aggregate function used to compute continuous percentiles from a set of values. The continuous percentile returns an interpolated value based on the distribution of the input data, while multiple continuous percentiles return an array of results matching the shape of the `fractions` parameter with each non-null element replaced by the value corresponding to that percentile. + +== Syntax + +The syntax for this function is: + +[tabs] +==== +Continuous Percentile:: ++ +[source,sql] +---- +PERCENTILE_CONT(fraction) WITHIN GROUP (ORDER BY order_list) +---- ++ +[NOTE] +==== +This function is often used in conjunction with the `WITHIN GROUP` clause to specify how to order the data before calculating the percentile. +==== ++ +Parameters + +* `fraction`: Decimal value between 0 and 1 representing the desired percentile (for example, 0.25 for the 25th percentile). + +Multiple Continuous Percentile:: ++ +[source,sql] +---- +PERCENTILE_CONT(fractions) WITHIN GROUP (ORDER BY order_list) +---- ++ +[NOTE] +==== +This function is often used in conjunction with the `WITHIN GROUP` clause to specify how to order the data before calculating the percentile. +==== ++ +Parameters + +* `fractions`: Array of decimal values between 0 and 1 representing the desired percentiles (for example, `ARRAY[0.25, 0.50, 0.75, 0.90]`). + +==== + +== Examples + +The following example uses a simplified version of the `film` table from the link:https://www.postgresql.org/ftp/projects/pgFoundry/dbsamples/pagila/pagila/[Pagila database^], containing only the `title`, `length` and `rating` columns. + +[source,sql] +---- +DROP TABLE IF EXISTS film; +CREATE TABLE film ( + title text NOT NULL, + length int, + rating text +); +INSERT INTO film(title, length, rating) VALUES + ('ATTRACTION NEWTON', 83, 'PG-13'), + ('CHRISTMAS MOONSHINE', 150, 'NC-17'), + ('DANGEROUS UPTOWN', 121, 'PG'), + ('KILL BROTHERHOOD', 54, 'G'), + ('HALLOWEEN NUTS', 47, 'PG-13'), + ('HOURS RAGE', 122, 'NC-17'), + ('PIANIST OUTFIELD', 136, 'NC-17'), + ('PICKUP DRIVING', 77, 'G'), + ('INDEPENDENCE HOTEL', 157, 'NC-17'), + ('PRIVATE DROP', 106, 'PG'), + ('SAINTS BRIDE', 125, 'G'), + ('FOREVER CANDIDATE', 131, 'NC-17'), + ('MILLION ACE', 142, 'PG-13'), + ('SLEEPY JAPANESE', 137, 'PG'), + ('WRATH MILE', 176, 'NC-17'), + ('YOUTH KICK', 179, 'NC-17'), + ('CLOCKWORK PARADISE', 143, 'PG-13'); +---- + +This query calculates the median film length within each rating category. + +[source,sql] +---- +SELECT rating, PERCENTILE_CONT(0.50) WITHIN GROUP (ORDER BY length) AS "50th percentile" FROM film +GROUP BY rating; +---- + +The query returns: + +[source,sql] +---- + rating | 50th percentile +--------+----------------- + PG-13 | 112.5 + PG | 121 + NC-17 | 150 + G | 77 +(4 rows) +---- diff --git a/modules/reference/pages/sql/sql-functions/aggregate-functions/ordered-set-aggregate-functions/percentile-disc.adoc b/modules/reference/pages/sql/sql-functions/aggregate-functions/ordered-set-aggregate-functions/percentile-disc.adoc new file mode 100644 index 000000000..23a6f9bfc --- /dev/null +++ b/modules/reference/pages/sql/sql-functions/aggregate-functions/ordered-set-aggregate-functions/percentile-disc.adoc @@ -0,0 +1,98 @@ += PERCENTILE_DISC() +:description: PERCENTILE_DISC() is an ordered-set aggregate function used to compute discrete percentiles from a set of values. +:page-topic-type: reference + +`PERCENTILE_DISC()` is an ordered-set aggregate function used to compute discrete percentiles from a set of values. The discrete percentile returns the first input value, which position in the ordering equals or exceeds the specified fraction, while multiple discrete percentiles return an array of results matching the shape of the fractions parameter, with each non-null element being replaced by the input value corresponding to that percentile. + +== Syntax + +The syntax for this function is: + +[tabs] +==== +Discrete Percentile:: ++ +[source,sql] +---- +PERCENTILE_DISC(fraction) WITHIN GROUP (ORDER BY order_list) +---- ++ +[NOTE] +==== +If multiple values share the same rank at the specified percentile, `PERCENTILE_DISC()` returns the first one encountered in the ordering. +==== ++ +Parameters + +* `fraction`: Decimal value between 0 and 1 representing the desired percentile (for example, 0.25 for the 25th percentile). + +Multiple Discrete Percentile:: ++ +[source,sql] +---- +PERCENTILE_DISC(fractions) WITHIN GROUP (ORDER BY order_list) +---- ++ +[NOTE] +==== +If multiple values share the same rank at the specified percentile, `PERCENTILE_DISC` returns the first one encountered in the ordering. +==== ++ +Parameters + +* `fractions`: Array of decimal values between 0 and 1 representing the desired percentiles (for example, `ARRAY[0.25, 0.50, 0.75, 0.90]`). + +==== + +== Examples + +The following example uses a simplified version of the `film` table from the link:https://www.postgresql.org/ftp/projects/pgFoundry/dbsamples/pagila/pagila/[Pagila database^], containing only the `title`, `length` and `rating` columns. + +[source,sql] +---- +DROP TABLE IF EXISTS film; +CREATE TABLE film ( + title text NOT NULL, + length int, + rating text +); +INSERT INTO film(title, length, rating) VALUES + ('ATTRACTION NEWTON', 83, 'PG-13'), + ('CHRISTMAS MOONSHINE', 150, 'NC-17'), + ('DANGEROUS UPTOWN', 121, 'PG'), + ('KILL BROTHERHOOD', 54, 'G'), + ('HALLOWEEN NUTS', 47, 'PG-13'), + ('HOURS RAGE', 122, 'NC-17'), + ('PIANIST OUTFIELD', 136, 'NC-17'), + ('PICKUP DRIVING', 77, 'G'), + ('INDEPENDENCE HOTEL', 157, 'NC-17'), + ('PRIVATE DROP', 106, 'PG'), + ('SAINTS BRIDE', 125, 'G'), + ('FOREVER CANDIDATE', 131, 'NC-17'), + ('MILLION ACE', 142, 'PG-13'), + ('SLEEPY JAPANESE', 137, 'PG'), + ('WRATH MILE', 176, 'NC-17'), + ('YOUTH KICK', 179, 'NC-17'), + ('CLOCKWORK PARADISE', 143, 'PG-13'); +---- + +The following query calculates the quartile, median and the third quartile of film lengths: + +[source,sql] +---- +SELECT rating, percentile_disc(ARRAY[0.25, 0.5, 0.75]) WITHIN GROUP (ORDER BY length) AS "quartiles" FROM film +GROUP BY rating; +---- + +The query returns: + +[source,sql] +---- + rating | quartiles +--------+--------------- + G | {54,77,125} + PG | {106,121,137} + PG-13 | {47,83,142} + NC-17 | {131,150,176} +(4 rows) +---- diff --git a/modules/reference/pages/sql/sql-functions/aggregate-functions/statistics/corr.adoc b/modules/reference/pages/sql/sql-functions/aggregate-functions/statistics/corr.adoc new file mode 100644 index 000000000..e8c589cfd --- /dev/null +++ b/modules/reference/pages/sql/sql-functions/aggregate-functions/statistics/corr.adoc @@ -0,0 +1,70 @@ += CORR() +:description: The CORR() aggregate function calculates the Pearson correlation coefficient between two sets of number pairs. +:page-topic-type: reference + +The `CORR()` aggregate function calculates the Pearson correlation coefficient between two sets of number pairs. This function measures the linear relationship between two variables, providing a value between -1 and 1. + +== Syntax + +The syntax for this function is: + +[source,sql] +---- +CORR(y, x) +---- + +== Parameters + +* `y`: Variable being predicted. +* `x`: Variable used for prediction. + +== Examples + +The following example uses a simplified version of the `film` table from the Pagila database, containing only the `title`, `length` and `rating` columns. The complete schema for the `film` table can be found on the link:https://www.postgresql.org/ftp/projects/pgFoundry/dbsamples/pagila/pagila/[Pagila^] database website. + +[source,sql] +---- +DROP TABLE IF EXISTS film; +CREATE TABLE film ( + title text NOT NULL, + length int, + rating int +); +INSERT INTO film(title, length, rating) VALUES + ('ATTRACTION NEWTON', 83, 5), + ('CHRISTMAS MOONSHINE', 150, 7), + ('DANGEROUS UPTOWN', 121, 4), + ('KILL BROTHERHOOD', 54, 3), + ('HALLOWEEN NUTS', 47, 5), + ('HOURS RAGE', 122, 7), + ('PIANIST OUTFIELD', 136, 7), + ('PICKUP DRIVING', 77, 3), + ('INDEPENDENCE HOTEL', 157, 7), + ('PRIVATE DROP', 106, 4), + ('SAINTS BRIDE', 125, 3), + ('FOREVER CANDIDATE', 131, 7), + ('MILLION ACE', 142, 5), + ('SLEEPY JAPANESE', 137, 4), + ('WRATH MILE', 176, 7), + ('YOUTH KICK', 179, 7), + ('CLOCKWORK PARADISE', 143, 5); +---- + +The following query uses the `CORR()` function to calculate the correlation between film length and rating: + +[source,sql] +---- +SELECT + CORR(length, rating) AS CorrelationCoefficient +FROM film; +---- + +The query returns: + +[source,sql] +---- + correlationcoefficient +------------------------ + 0.6190587870867634 +(1 row) +---- diff --git a/modules/reference/pages/sql/sql-functions/aggregate-functions/statistics/covar-pop.adoc b/modules/reference/pages/sql/sql-functions/aggregate-functions/statistics/covar-pop.adoc new file mode 100644 index 000000000..1391e0912 --- /dev/null +++ b/modules/reference/pages/sql/sql-functions/aggregate-functions/statistics/covar-pop.adoc @@ -0,0 +1,70 @@ += COVAR_POP() +:description: The COVAR_POP() aggregate function calculates the population covariance between two sets of number pairs. +:page-topic-type: reference + +The `COVAR_POP()` aggregate function calculates the population covariance between two sets of number pairs. This function measures how much two variables change together, providing insight into their linear relationship. + +== Syntax + +The syntax for this function is: + +[source,sql] +---- +COVAR_POP(y, x) +---- + +== Parameters + +* `y`: Variable being predicted. +* `x`: Variable used for prediction. + +== Examples + +The following example uses a simplified version of the `film` table from the Pagila database, containing only the `title`, `length` and `rating` columns. The complete schema for the `film` table can be found on the link:https://www.postgresql.org/ftp/projects/pgFoundry/dbsamples/pagila/pagila/[Pagila^] database website. + +[source,sql] +---- +DROP TABLE IF EXISTS film; +CREATE TABLE film ( + title text NOT NULL, + length int, + rating int +); +INSERT INTO film(title, length, rating) VALUES + ('ATTRACTION NEWTON', 83, 5), + ('CHRISTMAS MOONSHINE', 150, 7), + ('DANGEROUS UPTOWN', 121, 4), + ('KILL BROTHERHOOD', 54, 3), + ('HALLOWEEN NUTS', 47, 5), + ('HOURS RAGE', 122, 7), + ('PIANIST OUTFIELD', 136, 7), + ('PICKUP DRIVING', 77, 3), + ('INDEPENDENCE HOTEL', 157, 7), + ('PRIVATE DROP', 106, 4), + ('SAINTS BRIDE', 125, 3), + ('FOREVER CANDIDATE', 131, 7), + ('MILLION ACE', 142, 5), + ('SLEEPY JAPANESE', 137, 4), + ('WRATH MILE', 176, 7), + ('YOUTH KICK', 179, 7), + ('CLOCKWORK PARADISE', 143, 5); +---- + +The following query uses the `COVAR_POP()` function to calculate the covariance between film length and rating: + +[source,sql] +---- +SELECT + COVAR_POP(length, rating) AS Covariance +FROM film; +---- + +The query returns: + +[source,sql] +---- + covariance +------------------- + 36.02768166089963 +(1 row) +---- diff --git a/modules/reference/pages/sql/sql-functions/aggregate-functions/statistics/covar-samp.adoc b/modules/reference/pages/sql/sql-functions/aggregate-functions/statistics/covar-samp.adoc new file mode 100644 index 000000000..7857f8149 --- /dev/null +++ b/modules/reference/pages/sql/sql-functions/aggregate-functions/statistics/covar-samp.adoc @@ -0,0 +1,71 @@ += COVAR_SAMP +:description: The COVAR_SAMP() aggregate function calculates the sample covariance between two sets of number pairs. +:page-topic-type: reference + +The `COVAR_SAMP()` aggregate function calculates the sample covariance between two sets of number pairs. This function measures how changes in one variable relate linearly to changes in another variable within a sample dataset. + +== Syntax + +The syntax for this function is: + +[source,sql] +---- +COVAR_SAMP(y, x) +---- + +== Parameters + +* `y`: Variable being predicted. +* `x`: Variable used for prediction. + +== Examples + +This example uses a simplified version of the `film` table from the Pagila database, containing only the `title`, `length` and `rating` columns. The complete schema for the `film` table can be found on the link:https://www.postgresql.org/ftp/projects/pgFoundry/dbsamples/pagila/pagila/[Pagila^] database website. + +[source,sql] +---- +DROP TABLE IF EXISTS film; +CREATE TABLE film ( + title text NOT NULL, + length int, + rating int +); +INSERT INTO film(title, length, rating) VALUES + ('ATTRACTION NEWTON', 83, 5), + ('CHRISTMAS MOONSHINE', 150, 7), + ('DANGEROUS UPTOWN', 121, 4), + ('KILL BROTHERHOOD', 54, 3), + ('HALLOWEEN NUTS', 47, 5), + ('HOURS RAGE', 122, 7), + ('PIANIST OUTFIELD', 136, 7), + ('PICKUP DRIVING', 77, 3), + ('INDEPENDENCE HOTEL', 157, 7), + ('PRIVATE DROP', 106, 4), + ('SAINTS BRIDE', 125, 3), + ('FOREVER CANDIDATE', 131, 7), + ('MILLION ACE', 142, 5), + ('SLEEPY JAPANESE', 137, 4), + ('WRATH MILE', 176, 7), + ('YOUTH KICK', 179, 7), + ('CLOCKWORK PARADISE', 143, 5); +---- + +The following query uses the `COVAR_SAMP()` function to calculate the sample covariance between film `length` and `rating` where `rating` is greater than or equal to 4: + +[source,sql] +---- +SELECT + COVAR_SAMP(length, rating) AS SampleCovariance +FROM film +WHERE rating >= 4; +---- + +The query returns: + +[source,sql] +---- + samplecovariance +-------------------- + 23.087912087912066 +(1 row) +---- diff --git a/modules/reference/pages/sql/sql-functions/aggregate-functions/statistics/index.adoc b/modules/reference/pages/sql/sql-functions/aggregate-functions/statistics/index.adoc new file mode 100644 index 000000000..9cd3aa026 --- /dev/null +++ b/modules/reference/pages/sql/sql-functions/aggregate-functions/statistics/index.adoc @@ -0,0 +1,27 @@ += Overview +:description: Aggregate functions for statistics are typically used for statistical analysis. + +Aggregate functions for statistics are typically used for statistical analysis. Redpanda SQL supports the following functions: + +[width="100%",cols="42%,58%",options="header",] +|=== +|Functions |Description +|xref:reference:sql/sql-functions/aggregate-functions/statistics/corr.adoc[CORR] |Calculates the Pearson correlation coefficient between two sets of number pairs +|xref:reference:sql/sql-functions/aggregate-functions/statistics/covar-pop.adoc[COVAR_POP] |Calculates the population covariance between two sets of number pairs +|xref:reference:sql/sql-functions/aggregate-functions/statistics/covar-samp.adoc[COVAR_SAMP] |Calculates the sample covariance between two sets of number pairs +|xref:reference:sql/sql-functions/aggregate-functions/statistics/regr-avgx.adoc[REGR_AVGX] |Calculates the average of the independent variable (sum(X)/N) +|xref:reference:sql/sql-functions/aggregate-functions/statistics/regr-avgy.adoc[REGR_AVGY] |Calculates the average of the dependent variable (sum(Y)/N) +|xref:reference:sql/sql-functions/aggregate-functions/statistics/regr-count.adoc[REGR_COUNT] |Calculates the number of input rows in which both expressions are non-null +|xref:reference:sql/sql-functions/aggregate-functions/statistics/regr-intercept.adoc[REGR_INTERCEPT] |Calculates the y-intercept of the univariate linear regression line for a group of data points +|xref:reference:sql/sql-functions/aggregate-functions/statistics/regr-r2.adoc[REGR_R2] |Calculates the coefficient of determination (R2) for a linear regression model +|xref:reference:sql/sql-functions/aggregate-functions/statistics/regr-slope.adoc[REGR_SLOPE] |Calculates slope of the least-squares-fit linear equation determined by the (X, Y) pairs +|xref:reference:sql/sql-functions/aggregate-functions/statistics/regr-sxx.adoc[REGR_SXX] |Calculates the sum(X2) - sum(X)2/N ("`sum of squares`" of the independent variable) +|xref:reference:sql/sql-functions/aggregate-functions/statistics/regr-sxy.adoc[REGR_SXY] |Calculates the sum(X_Y) - sum(X)_ sum(Y)/N ("`sum of products`" of independent times dependent variable) +|xref:reference:sql/sql-functions/aggregate-functions/statistics/regr-syy.adoc[REGR_SYY] |Calculates the sum(Y2) - sum(Y)2/N ("`sum of squares`" of the dependent variable) +|xref:reference:sql/sql-functions/aggregate-functions/statistics/stddev.adoc[STDDEV] |Calculates the sample standard deviation of a set of numeric values +|xref:reference:sql/sql-functions/aggregate-functions/statistics/stddev-pop.adoc[STDDEV_POP] |Calculates the population standard deviation of the input values +|xref:reference:sql/sql-functions/aggregate-functions/statistics/stddev-samp.adoc[STDDEV_SAMP] |Calculates the sample standard deviation of the input values +|xref:reference:sql/sql-functions/aggregate-functions/statistics/variance.adoc[VARIANCE] |Calculates the the sample variance of a set of numeric values. +|xref:reference:sql/sql-functions/aggregate-functions/statistics/var-pop.adoc[VAR_POP] |Calculates the population variance of the input values (square of the population standard deviation) +|xref:reference:sql/sql-functions/aggregate-functions/statistics/var-samp.adoc[VAR_SAMP] |Calculates the sample variance of the input values (square of the sample standard deviation) +|=== diff --git a/modules/reference/pages/sql/sql-functions/aggregate-functions/statistics/regr-avgx.adoc b/modules/reference/pages/sql/sql-functions/aggregate-functions/statistics/regr-avgx.adoc new file mode 100644 index 000000000..e5ae969f7 --- /dev/null +++ b/modules/reference/pages/sql/sql-functions/aggregate-functions/statistics/regr-avgx.adoc @@ -0,0 +1,70 @@ += REGR_AVGX() +:description: The REGR_AVGX() aggregate function calculates the average of the independent variable (x) for non-null pairs of dependent (y) and independent (x) vari +:page-topic-type: reference + +The `REGR_AVGX()` aggregate function calculates the average of the independent variable (x) for non-null pairs of dependent (y) and independent (x) variables. This function is commonly used in linear regression analysis to compute the mean of the independent variable where both variables are not NULL. + +== Syntax + +The syntax for this function is: + +[source,sql] +---- +REGR_AVGX(y, x) +---- + +== Parameters + +* `y`: Variable being predicted. +* `x`: Variable used for prediction. + +== Examples + +The following example uses a simplified version of the `film` table from the Pagila database, containing only the `title`, `length` and `rating` columns. The complete schema for the `film` table can be found on the link:https://www.postgresql.org/ftp/projects/pgFoundry/dbsamples/pagila/pagila/[Pagila^] database website. + +[source,sql] +---- +DROP TABLE IF EXISTS film; +CREATE TABLE film ( + title text NOT NULL, + length int, + rating int +); +INSERT INTO film(title, length, rating) VALUES + ('ATTRACTION NEWTON', 83, 5), + ('CHRISTMAS MOONSHINE', 150, 7), + ('DANGEROUS UPTOWN', 121, 4), + ('KILL BROTHERHOOD', 54, 3), + ('HALLOWEEN NUTS', 47, 5), + ('HOURS RAGE', 122, 7), + ('PIANIST OUTFIELD', 136, 7), + ('PICKUP DRIVING', 77, 3), + ('INDEPENDENCE HOTEL', 157, 7), + ('PRIVATE DROP', 106, 4), + ('SAINTS BRIDE', 125, 3), + ('FOREVER CANDIDATE', 131, 7), + ('MILLION ACE', 142, 5), + ('SLEEPY JAPANESE', 137, 4), + ('WRATH MILE', 176, 7), + ('YOUTH KICK', 179, 7), + ('CLOCKWORK PARADISE', 143, 5); +---- + +The following query uses the `REGR_AVGX()` function to calculate the average rating for films where both `length` and `rating` are not NULL: + +[source,sql] +---- +SELECT + REGR_AVGX(length, rating) AS AverageRating +FROM film; +---- + +The query returns: + +[source,sql] +---- + averagerating +------------------- + 5.294117647058823 +(1 row) +---- diff --git a/modules/reference/pages/sql/sql-functions/aggregate-functions/statistics/regr-avgy.adoc b/modules/reference/pages/sql/sql-functions/aggregate-functions/statistics/regr-avgy.adoc new file mode 100644 index 000000000..979b523b5 --- /dev/null +++ b/modules/reference/pages/sql/sql-functions/aggregate-functions/statistics/regr-avgy.adoc @@ -0,0 +1,70 @@ += REGR_AVGY() +:description: The REGR_AVGY() aggregate function calculates the mean of the dependent variable (y) for non-null pairs of dependent (y) and independent (x) variables +:page-topic-type: reference + +The `REGR_AVGY()` aggregate function calculates the mean of the dependent variable (y) for non-null pairs of dependent (y) and independent (x) variables. This function is used in linear regression analysis to compute the average value of the dependent variable where both variables are not NULL. + +== Syntax + +The syntax for this function is: + +[source,sql] +---- +REGR_AVGY(y, x) +---- + +== Parameters + +* `y`: Variable being predicted. +* `x`: Variable used for prediction. + +== Examples + +The following example uses a simplified version of the `film` table from the Pagila database, containing only the `title`, `length` and `rating` columns. The complete schema for the `film` table can be found on the link:https://www.postgresql.org/ftp/projects/pgFoundry/dbsamples/pagila/pagila/[Pagila^] database website. + +[source,sql] +---- +DROP TABLE IF EXISTS film; +CREATE TABLE film ( + title text NOT NULL, + length int, + rating int +); +INSERT INTO film(title, length, rating) VALUES + ('ATTRACTION NEWTON', 83, 5), + ('CHRISTMAS MOONSHINE', 150, 7), + ('DANGEROUS UPTOWN', 121, 4), + ('KILL BROTHERHOOD', 54, 3), + ('HALLOWEEN NUTS', 47, 5), + ('HOURS RAGE', 122, 7), + ('PIANIST OUTFIELD', 136, 7), + ('PICKUP DRIVING', 77, 3), + ('INDEPENDENCE HOTEL', 157, 7), + ('PRIVATE DROP', 106, 4), + ('SAINTS BRIDE', 125, 3), + ('FOREVER CANDIDATE', 131, 7), + ('MILLION ACE', 142, 5), + ('SLEEPY JAPANESE', 137, 4), + ('WRATH MILE', 176, 7), + ('YOUTH KICK', 179, 7), + ('CLOCKWORK PARADISE', 143, 5); +---- + +The following query uses the `REGR_AVGY()` function to calculate the mean of the dependent variable (`rating`) for rows where both `rating` and `length` are not NULL: + +[source,sql] +---- +SELECT + REGR_AVGY(rating, length) AS AverageRating +FROM film; +---- + +The query returns: + +[source,sql] +---- + averagerating +------------------- + 5.294117647058823 +(1 row) +---- diff --git a/modules/reference/pages/sql/sql-functions/aggregate-functions/statistics/regr-count.adoc b/modules/reference/pages/sql/sql-functions/aggregate-functions/statistics/regr-count.adoc new file mode 100644 index 000000000..bc3141da4 --- /dev/null +++ b/modules/reference/pages/sql/sql-functions/aggregate-functions/statistics/regr-count.adoc @@ -0,0 +1,70 @@ += REGR_COUNT() +:description: The REGR_COUNT() aggregate function calculates the number of non-null value pairs for a dependent variable (y) and an independent variable (x). +:page-topic-type: reference + +The `REGR_COUNT()` aggregate function calculates the number of non-null value pairs for a dependent variable (y) and an independent variable (x). This function is used in linear regression analysis to determine the number of valid data points available for computation. + +== Syntax + +The syntax for this function is: + +[source,sql] +---- +REGR_COUNT(y, x) +---- + +== Parameters + +* `y`: Variable being predicted. +* `x`: Variable used for prediction. + +== Examples + +The following example uses a simplified version of the `film` table from the Pagila database, containing only the `title`, `length` and `rating` columns. The complete schema for the `film` table can be found on the link:https://www.postgresql.org/ftp/projects/pgFoundry/dbsamples/pagila/pagila/[Pagila^] database website. + +[source,sql] +---- +DROP TABLE IF EXISTS film; +CREATE TABLE film ( + title text NOT NULL, + length int, + rating int +); +INSERT INTO film(title, length, rating) VALUES + ('ATTRACTION NEWTON', 83, 5), + ('CHRISTMAS MOONSHINE', 150, 7), + ('DANGEROUS UPTOWN', 121, 4), + ('KILL BROTHERHOOD', 54, 3), + ('HALLOWEEN NUTS', 47, 5), + ('HOURS RAGE', 122, 7), + ('PIANIST OUTFIELD', 136, 7), + ('PICKUP DRIVING', 77, 3), + ('INDEPENDENCE HOTEL', 157, 7), + ('PRIVATE DROP', 106, 4), + ('SAINTS BRIDE', 125, 3), + ('FOREVER CANDIDATE', 131, 7), + ('MILLION ACE', 142, 5), + ('SLEEPY JAPANESE', 137, 4), + ('WRATH MILE', 176, 7), + ('YOUTH KICK', 179, 7), + ('CLOCKWORK PARADISE', 143, 5); +---- + +The following query uses the `REGR_COUNT()` function to count the number of rows where both `rating` and `length` are not NULL: + +[source,sql] +---- +SELECT + REGR_COUNT(rating, length) AS NonNullPairsCount +FROM film; +---- + +The query returns: + +[source,sql] +---- + nonnullpairscount +------------------- + 17 +(1 row) +---- diff --git a/modules/reference/pages/sql/sql-functions/aggregate-functions/statistics/regr-intercept.adoc b/modules/reference/pages/sql/sql-functions/aggregate-functions/statistics/regr-intercept.adoc new file mode 100644 index 000000000..4aa70b705 --- /dev/null +++ b/modules/reference/pages/sql/sql-functions/aggregate-functions/statistics/regr-intercept.adoc @@ -0,0 +1,70 @@ += REGR_INTERCEPT() +:description: The REGR_INTERCEPT() aggregate function calculates the y-intercept of the univariate linear regression line for a group of data points, where the depe +:page-topic-type: reference + +The `REGR_INTERCEPT()` aggregate function calculates the y-intercept of the univariate linear regression line for a group of data points, where the dependent variable is (y) and the independent variable is (x). The intercept is the point where the regression line crosses the y-axis when x=0. + +== Syntax + +The syntax for this function is: + +[source,sql] +---- +REGR_INTERCEPT(y, x) +---- + +== Parameters + +* `y`: Variable being predicted. +* `x`: Variable used for prediction. + +== Examples + +The following example uses a simplified version of the `film` table from the Pagila database, containing only the `title`, `length` and `rating` columns. The complete schema for the `film` table can be found on the link:https://www.postgresql.org/ftp/projects/pgFoundry/dbsamples/pagila/pagila/[Pagila^] database website. + +[source,sql] +---- +DROP TABLE IF EXISTS film; +CREATE TABLE film ( + title text NOT NULL, + length int, + rating int +); +INSERT INTO film(title, length, rating) VALUES + ('ATTRACTION NEWTON', 83, 5), + ('CHRISTMAS MOONSHINE', 150, 7), + ('DANGEROUS UPTOWN', 121, 4), + ('KILL BROTHERHOOD', 54, 3), + ('HALLOWEEN NUTS', 47, 5), + ('HOURS RAGE', 122, 7), + ('PIANIST OUTFIELD', 136, 7), + ('PICKUP DRIVING', 77, 3), + ('INDEPENDENCE HOTEL', 157, 7), + ('PRIVATE DROP', 106, 4), + ('SAINTS BRIDE', 125, 3), + ('FOREVER CANDIDATE', 131, 7), + ('MILLION ACE', 142, 5), + ('SLEEPY JAPANESE', 137, 4), + ('WRATH MILE', 176, 7), + ('YOUTH KICK', 179, 7), + ('CLOCKWORK PARADISE', 143, 5); +---- + +The following query uses the `REGR_INTERCEPT()` function to calculate the y-intercept of the regression line for valid pairs of `rating` and `length`: + +[source,sql] +---- +SELECT + REGR_INTERCEPT(rating, length) AS YIntercept +FROM film; +---- + +The query returns: + +[source,sql] +---- + yintercept +-------------------- + 2.1055200882495355 +(1 row) +---- diff --git a/modules/reference/pages/sql/sql-functions/aggregate-functions/statistics/regr-r2.adoc b/modules/reference/pages/sql/sql-functions/aggregate-functions/statistics/regr-r2.adoc new file mode 100644 index 000000000..20ca8bf02 --- /dev/null +++ b/modules/reference/pages/sql/sql-functions/aggregate-functions/statistics/regr-r2.adoc @@ -0,0 +1,70 @@ += REGR_R2() +:description: The REGR_R2() aggregate function calculates the coefficient of determination (R2) for a linear regression model. +:page-topic-type: reference + +The `REGR_R2()` aggregate function calculates the coefficient of determination (R2) for a linear regression model. The R2 value indicates how well the independent variable (x) explains the variability of the dependent variable (y). + +== Syntax + +The syntax for this function is: + +[source,sql] +---- +REGR_R2(y, x) +---- + +== Parameters + +* `y`: Variable being predicted. +* `x`: Variable used for prediction. + +== Examples + +This example uses a simplified version of the `film` table from the Pagila database, containing only the `title`, `length` and `rating` columns. The complete schema for the `film` table can be found on the link:https://www.postgresql.org/ftp/projects/pgFoundry/dbsamples/pagila/pagila/[Pagila^] database website. + +[source,sql] +---- +DROP TABLE IF EXISTS film; +CREATE TABLE film ( + title text NOT NULL, + length int, + rating int +); +INSERT INTO film(title, length, rating) VALUES + ('ATTRACTION NEWTON', 83, 5), + ('CHRISTMAS MOONSHINE', 150, 7), + ('DANGEROUS UPTOWN', 121, 4), + ('KILL BROTHERHOOD', 54, 3), + ('HALLOWEEN NUTS', 47, 5), + ('HOURS RAGE', 122, 7), + ('PIANIST OUTFIELD', 136, 7), + ('PICKUP DRIVING', 77, 3), + ('INDEPENDENCE HOTEL', 157, 7), + ('PRIVATE DROP', 106, 4), + ('SAINTS BRIDE', 125, 3), + ('FOREVER CANDIDATE', 131, 7), + ('MILLION ACE', 142, 5), + ('SLEEPY JAPANESE', 137, 4), + ('WRATH MILE', 176, 7), + ('YOUTH KICK', 179, 7), + ('CLOCKWORK PARADISE', 143, 5); +---- + +The following query uses the `REGR_R2()` function to calculate the coefficient of determination (R2) for valid pairs of `rating` and `length`: + +[source,sql] +---- +SELECT + REGR_R2(rating, length) AS coefficientOfDetermination +FROM film; +---- + +The query returns: + +[source,sql] +---- + coefficientofdetermination +---------------------------- + 0.3832337818693347 +(1 row) +---- diff --git a/modules/reference/pages/sql/sql-functions/aggregate-functions/statistics/regr-slope.adoc b/modules/reference/pages/sql/sql-functions/aggregate-functions/statistics/regr-slope.adoc new file mode 100644 index 000000000..54df6656a --- /dev/null +++ b/modules/reference/pages/sql/sql-functions/aggregate-functions/statistics/regr-slope.adoc @@ -0,0 +1,70 @@ += REGR_SLOPE() +:description: The REGR_SLOPE() aggregate function calculates the slope of the regression line for a linear relationship between a dependent variable (y) and an inde +:page-topic-type: reference + +The `REGR_SLOPE()` aggregate function calculates the slope of the regression line for a linear relationship between a dependent variable (y) and an independent variable (x). The slope represents the rate of change in `y` for every unit increase in `x`. This function is used in regression analysis to quantify the strength and direction of a linear relationship. + +== Syntax + +The syntax for this function is: + +[source,sql] +---- +REGR_SLOPE(y, x) +---- + +== Parameters + +* `y`: Variable being predicted. +* `x`: Variable used for prediction. + +== Examples + +The following example uses a simplified version of the `film` table from the Pagila database, containing only the `title`, `length` and `rating` columns. The complete schema for the `film` table can be found on the link:https://www.postgresql.org/ftp/projects/pgFoundry/dbsamples/pagila/pagila/[Pagila^] database website. + +[source,sql] +---- +DROP TABLE IF EXISTS film; +CREATE TABLE film ( + title text NOT NULL, + length int, + rating int +); +INSERT INTO film(title, length, rating) VALUES + ('ATTRACTION NEWTON', 83, 5), + ('CHRISTMAS MOONSHINE', 150, 7), + ('DANGEROUS UPTOWN', 121, 4), + ('KILL BROTHERHOOD', 54, 3), + ('HALLOWEEN NUTS', 47, 5), + ('HOURS RAGE', 122, 7), + ('PIANIST OUTFIELD', 136, 7), + ('PICKUP DRIVING', 77, 3), + ('INDEPENDENCE HOTEL', 157, 7), + ('PRIVATE DROP', 106, 4), + ('SAINTS BRIDE', 125, 3), + ('FOREVER CANDIDATE', 131, 7), + ('MILLION ACE', 142, 5), + ('SLEEPY JAPANESE', 137, 4), + ('WRATH MILE', 176, 7), + ('YOUTH KICK', 179, 7), + ('CLOCKWORK PARADISE', 143, 5); +---- + +The following query uses the `REGR_SLOPE()` function to calculate the slope of the regression line for valid pairs of `rating` and `length`: + +[source,sql] +---- +SELECT + REGR_SLOPE(rating, length) AS Slope +FROM film; +---- + +The query returns: + +[source,sql] +---- + slope +---------------------- + 0.025985694391063227 +(1 row) +---- diff --git a/modules/reference/pages/sql/sql-functions/aggregate-functions/statistics/regr-sxx.adoc b/modules/reference/pages/sql/sql-functions/aggregate-functions/statistics/regr-sxx.adoc new file mode 100644 index 000000000..3df266edb --- /dev/null +++ b/modules/reference/pages/sql/sql-functions/aggregate-functions/statistics/regr-sxx.adoc @@ -0,0 +1,70 @@ += REGR_SXX() +:description: The REGR_SXX() aggregate function calculates the sum of squares of deviations for the independent variable (x) in a linear regression analysis. +:page-topic-type: reference + +The `REGR_SXX()` aggregate function calculates the sum of squares of deviations for the independent variable (x) in a linear regression analysis. This value represents the variability of the independent variable and is a key component in calculating the slope and other regression statistics. + +== Syntax + +The syntax for this function is: + +[source,sql] +---- +REGR_SXX(y, x) +---- + +== Parameters + +* `y`: Variable being predicted. +* `x`: Variable used for prediction. + +== Examples + +The following example uses a simplified version of the `film` table from the Pagila database, containing only the `title`, `length` and `rating` columns. The complete schema for the `film` table can be found on the link:https://www.postgresql.org/ftp/projects/pgFoundry/dbsamples/pagila/pagila/[Pagila^] database website. + +[source,sql] +---- +DROP TABLE IF EXISTS film; +CREATE TABLE film ( + title text NOT NULL, + length int, + rating int +); +INSERT INTO film(title, length, rating) VALUES + ('ATTRACTION NEWTON', 83, 5), + ('CHRISTMAS MOONSHINE', 150, 7), + ('DANGEROUS UPTOWN', 121, 4), + ('KILL BROTHERHOOD', 54, 3), + ('HALLOWEEN NUTS', 47, 5), + ('HOURS RAGE', 122, 7), + ('PIANIST OUTFIELD', 136, 7), + ('PICKUP DRIVING', 77, 3), + ('INDEPENDENCE HOTEL', 157, 7), + ('PRIVATE DROP', 106, 4), + ('SAINTS BRIDE', 125, 3), + ('FOREVER CANDIDATE', 131, 7), + ('MILLION ACE', 142, 5), + ('SLEEPY JAPANESE', 137, 4), + ('WRATH MILE', 176, 7), + ('YOUTH KICK', 179, 7), + ('CLOCKWORK PARADISE', 143, 5); +---- + +The following query uses the `REGR_SXX()` function to calculate the sum of squares of deviations for the independent variable `length`: + +[source,sql] +---- +SELECT + REGR_SXX(rating, length) AS SumOfSquaresX +FROM film; +---- + +The query returns: + +[source,sql] +---- + sumofsquaresx +------------------ + 23569.5294117647 +(1 row) +---- diff --git a/modules/reference/pages/sql/sql-functions/aggregate-functions/statistics/regr-sxy.adoc b/modules/reference/pages/sql/sql-functions/aggregate-functions/statistics/regr-sxy.adoc new file mode 100644 index 000000000..45cac9d69 --- /dev/null +++ b/modules/reference/pages/sql/sql-functions/aggregate-functions/statistics/regr-sxy.adoc @@ -0,0 +1,70 @@ += REGR_SXY() +:description: The REGR_SXY() aggregate function calculates the sum of products od deviations for the dependent variable (y) and the independent variable (x) in a li +:page-topic-type: reference + +The `REGR_SXY()` aggregate function calculates the sum of products od deviations for the dependent variable (y) and the independent variable (x) in a linear regression analysis. This value represents the covariance-like term used to compute the slope of the regression line. + +== Syntax + +The syntax for this function is: + +[source,sql] +---- +REGR_SXY(y, x) +---- + +== Parameters + +* `y`: Variable being predicted. +* `x`: Variable used for prediction. + +== Examples + +This example uses a simplified version of the `film` table from the Pagila database, containing only the `title`, `length` and `rating` columns. The complete schema for the `film` table can be found on the link:https://www.postgresql.org/ftp/projects/pgFoundry/dbsamples/pagila/pagila/[Pagila^] database website. + +[source,sql] +---- +DROP TABLE IF EXISTS film; +CREATE TABLE film ( + title text NOT NULL, + length int, + rating int +); +INSERT INTO film(title, length, rating) VALUES + ('ATTRACTION NEWTON', 83, 5), + ('CHRISTMAS MOONSHINE', 150, 7), + ('DANGEROUS UPTOWN', 121, 4), + ('KILL BROTHERHOOD', 54, 3), + ('HALLOWEEN NUTS', 47, 5), + ('HOURS RAGE', 122, 7), + ('PIANIST OUTFIELD', 136, 7), + ('PICKUP DRIVING', 77, 3), + ('INDEPENDENCE HOTEL', 157, 7), + ('PRIVATE DROP', 106, 4), + ('SAINTS BRIDE', 125, 3), + ('FOREVER CANDIDATE', 131, 7), + ('MILLION ACE', 142, 5), + ('SLEEPY JAPANESE', 137, 4), + ('WRATH MILE', 176, 7), + ('YOUTH KICK', 179, 7), + ('CLOCKWORK PARADISE', 143, 5); +---- + +This query uses the `REGR_SXY()` function to calculate the sum of products of deviations for non-null pair of `rating` and `length`: + +[source,sql] +---- +SELECT + REGR_SXY(rating, length) AS SumOfSquaresXY +FROM film; +---- + +The query returns: + +[source,sql] +---- + sumofsquaresxy +------------------- + 612.4705882352937 +(1 row) +---- diff --git a/modules/reference/pages/sql/sql-functions/aggregate-functions/statistics/regr-syy.adoc b/modules/reference/pages/sql/sql-functions/aggregate-functions/statistics/regr-syy.adoc new file mode 100644 index 000000000..002e90046 --- /dev/null +++ b/modules/reference/pages/sql/sql-functions/aggregate-functions/statistics/regr-syy.adoc @@ -0,0 +1,70 @@ += REGR_SYY() +:description: The REGR_SYY() aggregate function calculates the sum of squares of deviations for the dependent variable (y) in a linear regression analysis. +:page-topic-type: reference + +The `REGR_SYY()` aggregate function calculates the sum of squares of deviations for the dependent variable (y) in a linear regression analysis. + +== Syntax + +The syntax for this function is: + +[source,sql] +---- +REGR_SYY(y, x) +---- + +== Parameters + +* `y`: Variable being predicted. +* `x`: Variable used for prediction. + +== Examples + +The following example uses a simplified version of the `film` table from the Pagila database, containing only the `title`, `length` and `rating` columns. The complete schema for the `film` table can be found on the link:https://www.postgresql.org/ftp/projects/pgFoundry/dbsamples/pagila/pagila/[Pagila^] database website. + +[source,sql] +---- +DROP TABLE IF EXISTS film; +CREATE TABLE film ( + title text NOT NULL, + length int, + rating int +); +INSERT INTO film(title, length, rating) VALUES + ('ATTRACTION NEWTON', 83, 5), + ('CHRISTMAS MOONSHINE', 150, 7), + ('DANGEROUS UPTOWN', 121, 4), + ('KILL BROTHERHOOD', 54, 3), + ('HALLOWEEN NUTS', 47, 5), + ('HOURS RAGE', 122, 7), + ('PIANIST OUTFIELD', 136, 7), + ('PICKUP DRIVING', 77, 3), + ('INDEPENDENCE HOTEL', 157, 7), + ('PRIVATE DROP', 106, 4), + ('SAINTS BRIDE', 125, 3), + ('FOREVER CANDIDATE', 131, 7), + ('MILLION ACE', 142, 5), + ('SLEEPY JAPANESE', 137, 4), + ('WRATH MILE', 176, 7), + ('YOUTH KICK', 179, 7), + ('CLOCKWORK PARADISE', 143, 5); +---- + +The following query uses the `REGR_SYY()` function to calculate the sum of squares of deviation for the dependent variable `rating`: + +[source,sql] +---- +SELECT + REGR_SYY(rating, length) AS SumOfSquaresY +FROM film; +---- + +The query returns: + +[source,sql] +---- + sumofsquaresy +-------------------- + 41.529411764705856 +(1 row) +---- diff --git a/modules/reference/pages/sql/sql-functions/aggregate-functions/statistics/stddev-pop.adoc b/modules/reference/pages/sql/sql-functions/aggregate-functions/statistics/stddev-pop.adoc new file mode 100644 index 000000000..abf8a982e --- /dev/null +++ b/modules/reference/pages/sql/sql-functions/aggregate-functions/statistics/stddev-pop.adoc @@ -0,0 +1,69 @@ += STDDEV_POP() +:description: The STDDEV_POP() aggregate function calculates the population stardard deviation of a set of numeric values. +:page-topic-type: reference + +The `STDDEV_POP()` aggregate function calculates the population stardard deviation of a set of numeric values. + +== Syntax + +The syntax for this function is: + +[source,sql] +---- +STDDEV_POP(expression) +---- + +== Parameters + +* `expression`: Numeric expression or column. + +== Examples + +The following example uses a simplified version of the `film` table from the Pagila database, containing only the `title`, `length` and `rating` columns. The complete schema for the `film` table can be found on the link:https://www.postgresql.org/ftp/projects/pgFoundry/dbsamples/pagila/pagila/[Pagila^] database website. + +[source,sql] +---- +DROP TABLE IF EXISTS film; +CREATE TABLE film ( + title text NOT NULL, + length int, + rating int +); +INSERT INTO film(title, length, rating) VALUES + ('ATTRACTION NEWTON', 83, 5), + ('CHRISTMAS MOONSHINE', 150, 7), + ('DANGEROUS UPTOWN', 121, 4), + ('KILL BROTHERHOOD', 54, 3), + ('HALLOWEEN NUTS', 47, 5), + ('HOURS RAGE', 122, 7), + ('PIANIST OUTFIELD', 136, 7), + ('PICKUP DRIVING', 77, 3), + ('INDEPENDENCE HOTEL', 157, 7), + ('PRIVATE DROP', 106, 4), + ('SAINTS BRIDE', 125, 3), + ('FOREVER CANDIDATE', 131, 7), + ('MILLION ACE', 142, 5), + ('SLEEPY JAPANESE', 137, 4), + ('WRATH MILE', 176, 7), + ('YOUTH KICK', 179, 7), + ('CLOCKWORK PARADISE', 143, 5); +---- + +The following query uses the `STDDEV_POP()` function to calculate the population standard deviation for the `length` column: + +[source,sql] +---- +SELECT + STDDEV_POP(length) AS LengthPopStdDev +FROM film; +---- + +The query returns: + +[source,sql] +---- + lengthpopstddev +------------------- + 37.23496886764368 +(1 row) +---- diff --git a/modules/reference/pages/sql/sql-functions/aggregate-functions/statistics/stddev-samp.adoc b/modules/reference/pages/sql/sql-functions/aggregate-functions/statistics/stddev-samp.adoc new file mode 100644 index 000000000..c50947cf0 --- /dev/null +++ b/modules/reference/pages/sql/sql-functions/aggregate-functions/statistics/stddev-samp.adoc @@ -0,0 +1,70 @@ += STDDEV_SAMP() +:description: The STDDEV_SAMP() aggregate function calculates the sample standard deviation of a set of numeric values. +:page-topic-type: reference + +The `STDDEV_SAMP()` aggregate function calculates the sample standard deviation of a set of numeric values. This function measures how much the values deviate from their mean, assuming the data is a sample of a larger population. + +== Syntax + +The syntax for this function is: + +[source,sql] +---- +STDDEV_SAMP(expression) +---- + +== Parameters + +* `expression`: Numeric expression or column. + +== Examples + +The following example uses a simplified version of the `film` table from the Pagila database, containing only the `title`, `length` and `rating` columns. The complete schema for the `film` table can be found on the link:https://www.postgresql.org/ftp/projects/pgFoundry/dbsamples/pagila/pagila/[Pagila^] database website. + +[source,sql] +---- +DROP TABLE IF EXISTS film; +CREATE TABLE film ( + title text NOT NULL, + length int, + rating int +); +INSERT INTO film(title, length, rating) VALUES + ('ATTRACTION NEWTON', 83, 5), + ('CHRISTMAS MOONSHINE', 150, 7), + ('DANGEROUS UPTOWN', 121, 4), + ('KILL BROTHERHOOD', 54, 3), + ('HALLOWEEN NUTS', 47, 5), + ('HOURS RAGE', 122, 7), + ('PIANIST OUTFIELD', 136, 7), + ('PICKUP DRIVING', 77, 3), + ('INDEPENDENCE HOTEL', 157, 7), + ('PRIVATE DROP', 106, 4), + ('SAINTS BRIDE', 125, 3), + ('FOREVER CANDIDATE', 131, 7), + ('MILLION ACE', 142, 5), + ('SLEEPY JAPANESE', 137, 4), + ('WRATH MILE', 176, 7), + ('YOUTH KICK', 179, 7), + ('CLOCKWORK PARADISE', 143, 5); +---- + +The following query uses the `STDDEV_SAMP()` function to calculate the sample standard deviation for the `length` column where `rating` is greater than or equal to 4: + +[source,sql] +---- +SELECT + STDDEV_SAMP(length) AS LengthSampleStdDev +FROM film +WHERE rating >= 4; +---- + +The query returns: + +[source,sql] +---- + lengthsamplestddev +-------------------- + 34.92503746251735 +(1 row) +---- diff --git a/modules/reference/pages/sql/sql-functions/aggregate-functions/statistics/stddev.adoc b/modules/reference/pages/sql/sql-functions/aggregate-functions/statistics/stddev.adoc new file mode 100644 index 000000000..c261397bd --- /dev/null +++ b/modules/reference/pages/sql/sql-functions/aggregate-functions/statistics/stddev.adoc @@ -0,0 +1,69 @@ += STDDEV() +:description: The STDDEV() aggregate function calculates the sample standard deviation of a set of numeric values. +:page-topic-type: reference + +The `STDDEV()` aggregate function calculates the sample standard deviation of a set of numeric values. Standard deviation measures the dispersion or spread of data points around the mean. + +== Syntax + +The syntax for this function is: + +[source,sql] +---- +STDDEV(expression) +---- + +== Parameters + +* `expression`: Numeric expression or column. + +== Examples + +The following example uses a simplified version of the `film` table from the Pagila database, containing only the `title`, `length` and `rating` columns. The complete schema for the `film` table can be found on the link:https://www.postgresql.org/ftp/projects/pgFoundry/dbsamples/pagila/pagila/[Pagila^] database website. + +[source,sql] +---- +DROP TABLE IF EXISTS film; +CREATE TABLE film ( + title text NOT NULL, + length int, + rating int +); +INSERT INTO film(title, length, rating) VALUES + ('ATTRACTION NEWTON', 83, 5), + ('CHRISTMAS MOONSHINE', 150, 7), + ('DANGEROUS UPTOWN', 121, 4), + ('KILL BROTHERHOOD', 54, 3), + ('HALLOWEEN NUTS', 47, 5), + ('HOURS RAGE', 122, 7), + ('PIANIST OUTFIELD', 136, 7), + ('PICKUP DRIVING', 77, 3), + ('INDEPENDENCE HOTEL', 157, 7), + ('PRIVATE DROP', 106, 4), + ('SAINTS BRIDE', 125, 3), + ('FOREVER CANDIDATE', 131, 7), + ('MILLION ACE', 142, 5), + ('SLEEPY JAPANESE', 137, 4), + ('WRATH MILE', 176, 7), + ('YOUTH KICK', 179, 7), + ('CLOCKWORK PARADISE', 143, 5); +---- + +The following query uses the `STDDEV()` function to calculate the sample standard deviation for the `length` column: + +[source,sql] +---- +SELECT + STDDEV(length) AS LengthStdDev +FROM film; +---- + +The query returns: + +[source,sql] +---- + lengthstddev +------------------- + 38.38092740197003 +(1 row) +---- diff --git a/modules/reference/pages/sql/sql-functions/aggregate-functions/statistics/var-pop.adoc b/modules/reference/pages/sql/sql-functions/aggregate-functions/statistics/var-pop.adoc new file mode 100644 index 000000000..a3be4550d --- /dev/null +++ b/modules/reference/pages/sql/sql-functions/aggregate-functions/statistics/var-pop.adoc @@ -0,0 +1,69 @@ += VAR_POP() +:description: The VAR_POP() aggregate function calculates the population variance of a set of numeric values. +:page-topic-type: reference + +The `VAR_POP()` aggregate function calculates the population variance of a set of numeric values. + +== Syntax + +The syntax for this function is: + +[source,sql] +---- +VAR_POP(expression) +---- + +== Parameters + +* `expression`: Numeric expression or column. + +== Examples + +The following example uses a simplified version of the `film` table from the Pagila database, containing only the `title`, `length` and `rating` columns. The complete schema for the `film` table can be found on the link:https://www.postgresql.org/ftp/projects/pgFoundry/dbsamples/pagila/pagila/[Pagila^] database website. + +[source,sql] +---- +DROP TABLE IF EXISTS film; +CREATE TABLE film ( + title text NOT NULL, + length int, + rating int +); +INSERT INTO film(title, length, rating) VALUES + ('ATTRACTION NEWTON', 83, 5), + ('CHRISTMAS MOONSHINE', 150, 7), + ('DANGEROUS UPTOWN', 121, 4), + ('KILL BROTHERHOOD', 54, 3), + ('HALLOWEEN NUTS', 47, 5), + ('HOURS RAGE', 122, 7), + ('PIANIST OUTFIELD', 136, 7), + ('PICKUP DRIVING', 77, 3), + ('INDEPENDENCE HOTEL', 157, 7), + ('PRIVATE DROP', 106, 4), + ('SAINTS BRIDE', 125, 3), + ('FOREVER CANDIDATE', 131, 7), + ('MILLION ACE', 142, 5), + ('SLEEPY JAPANESE', 137, 4), + ('WRATH MILE', 176, 7), + ('YOUTH KICK', 179, 7), + ('CLOCKWORK PARADISE', 143, 5); +---- + +The following query uses the `VAR_POP()` function to calculate the population variance for the `length` column: + +[source,sql] +---- +SELECT + VAR_POP(length) AS LengthPopulationVariance +FROM film; +---- + +The query returns: + +[source,sql] +---- + lengthpopulationvariance +-------------------------- + 1386.442906574394 +(1 row) +---- diff --git a/modules/reference/pages/sql/sql-functions/aggregate-functions/statistics/var-samp.adoc b/modules/reference/pages/sql/sql-functions/aggregate-functions/statistics/var-samp.adoc new file mode 100644 index 000000000..d3992438c --- /dev/null +++ b/modules/reference/pages/sql/sql-functions/aggregate-functions/statistics/var-samp.adoc @@ -0,0 +1,70 @@ += VAR_SAMP() +:description: The VAR_SAMP() aggregate function calculates the sample variance of a set of numeric values. +:page-topic-type: reference + +The `VAR_SAMP()` aggregate function calculates the sample variance of a set of numeric values. This function measures the spread of data points around the mean, assuming the data is a sample of a larger population. + +== Syntax + +The syntax for this function is: + +[source,sql] +---- +VAR_SAMP(expression) +---- + +== Parameters + +* `expression`: Numeric expression or column. + +== Examples + +The following example uses a simplified version of the `film` table from the Pagila database, containing only the `title`, `length` and `rating` columns. The complete schema for the `film` table can be found on the link:https://www.postgresql.org/ftp/projects/pgFoundry/dbsamples/pagila/pagila/[Pagila^] database website. + +[source,sql] +---- +DROP TABLE IF EXISTS film; +CREATE TABLE film ( + title text NOT NULL, + length int, + rating int +); +INSERT INTO film(title, length, rating) VALUES + ('ATTRACTION NEWTON', 83, 5), + ('CHRISTMAS MOONSHINE', 150, 7), + ('DANGEROUS UPTOWN', 121, 4), + ('KILL BROTHERHOOD', 54, 3), + ('HALLOWEEN NUTS', 47, 5), + ('HOURS RAGE', 122, 7), + ('PIANIST OUTFIELD', 136, 7), + ('PICKUP DRIVING', 77, 3), + ('INDEPENDENCE HOTEL', 157, 7), + ('PRIVATE DROP', 106, 4), + ('SAINTS BRIDE', 125, 3), + ('FOREVER CANDIDATE', 131, 7), + ('MILLION ACE', 142, 5), + ('SLEEPY JAPANESE', 137, 4), + ('WRATH MILE', 176, 7), + ('YOUTH KICK', 179, 7), + ('CLOCKWORK PARADISE', 143, 5); +---- + +The following query uses the `VAR_SAMP()` function to calculate the sample variance for the `length` column where `rating` is greater than or equal to 4: + +[source,sql] +---- +SELECT + VAR_SAMP(length) AS LengthSampleVariance +FROM film +WHERE rating >= 4; +---- + +The query returns: + +[source,sql] +---- + lengthsamplevariance +---------------------- + 1219.7582417582407 +(1 row) +---- diff --git a/modules/reference/pages/sql/sql-functions/aggregate-functions/statistics/variance.adoc b/modules/reference/pages/sql/sql-functions/aggregate-functions/statistics/variance.adoc new file mode 100644 index 000000000..c5616d5ee --- /dev/null +++ b/modules/reference/pages/sql/sql-functions/aggregate-functions/statistics/variance.adoc @@ -0,0 +1,69 @@ += VARIANCE() +:description: The VARIANCE() aggregate function calculate the sample variance of a set of numeric values. +:page-topic-type: reference + +The `VARIANCE()` aggregate function calculate the sample variance of a set of numeric values. Variance measures the spread of data points around the mean, providing insight into how much the values deviate from the average. + +== Syntax + +The syntax for this function is: + +[source,sql] +---- +VARIANCE(expression) +---- + +== Parameters + +* `expression`: Numeric expression or column. + +== Examples + +The following example uses a simplified version of the `film` table from the Pagila database, containing only the `title`, `length` and `rating` columns. The complete schema for the `film` table can be found on the link:https://www.postgresql.org/ftp/projects/pgFoundry/dbsamples/pagila/pagila/[Pagila^] database website. + +[source,sql] +---- +DROP TABLE IF EXISTS film; +CREATE TABLE film ( + title text NOT NULL, + length int, + rating int +); +INSERT INTO film(title, length, rating) VALUES + ('ATTRACTION NEWTON', 83, 5), + ('CHRISTMAS MOONSHINE', 150, 7), + ('DANGEROUS UPTOWN', 121, 4), + ('KILL BROTHERHOOD', 54, 3), + ('HALLOWEEN NUTS', 47, 5), + ('HOURS RAGE', 122, 7), + ('PIANIST OUTFIELD', 136, 7), + ('PICKUP DRIVING', 77, 3), + ('INDEPENDENCE HOTEL', 157, 7), + ('PRIVATE DROP', 106, 4), + ('SAINTS BRIDE', 125, 3), + ('FOREVER CANDIDATE', 131, 7), + ('MILLION ACE', 142, 5), + ('SLEEPY JAPANESE', 137, 4), + ('WRATH MILE', 176, 7), + ('YOUTH KICK', 179, 7), + ('CLOCKWORK PARADISE', 143, 5); +---- + +The following query uses the `VARIANCE()` function to calculate the variance for the `length` column: + +[source,sql] +---- +SELECT + VARIANCE(length) AS LengthVariance +FROM film; +---- + +The query returns: + +[source,sql] +---- + lengthvariance +-------------------- + 1473.0955882352937 +(1 row) +---- diff --git a/modules/reference/pages/sql/sql-functions/aggregate-functions/sum.adoc b/modules/reference/pages/sql/sql-functions/aggregate-functions/sum.adoc new file mode 100644 index 000000000..5d6bd8156 --- /dev/null +++ b/modules/reference/pages/sql/sql-functions/aggregate-functions/sum.adoc @@ -0,0 +1,253 @@ += SUM +:description: SUM() calculates the sum of values from stored records. +:page-topic-type: reference + +`SUM()` calculates the sum of values from stored records. `SUM()` doesn't consider `NULL` in the calculation, and it returns `NULL` instead of zero if the executed statement returns no rows. + +The supported input and return types are listed in the following table. + +[cols=",",options="header",] +|=== +|Input type |Return type +|INT |LONG +|LONG |LONG +|FLOAT |DOUBLE +|DOUBLE |DOUBLE +|INTERVAL |INTERVAL +|=== + +[NOTE] +==== +If the input type is 32-bit, then the result is 64-bit. +==== + +== Examples + +The following two sample tables are used in these examples: + +*customer table* + +[source,sql] +---- +CREATE TABLE customer ( + customer_id int, + cust_name text +); +INSERT INTO customer + (customer_id, cust_name) +VALUES + (11112, 'Alex'), + (11113, 'Aaron'), + (11114, 'Alice'), + (11115, 'Nina'), + (11116, 'Rosy'), + (11117, 'Martha'), + (11118, 'John'); +---- + +[source,sql] +---- +SELECT * FROM customer; +---- + +This creates the following table: + +[source,sql] +---- ++-------------+-----------+ +| customer_id | cust_name | ++-------------+-----------+ +| 11112 | Alex | +| 11113 | Aaron | +| 11114 | Alice | +| 11115 | Nina | +| 11116 | Rosy | +| 11117 | Martha | +| 11118 | John | ++-------------+-----------+ +---- + +*rental table* + +[source,sql] +---- +CREATE TABLE rental ( + rental_id int, + rental_date timestamp, + return_date timestamp, + car text, + customer_id int, + total_price int +); +INSERT INTO rental (rental_id, rental_date, return_date, car, customer_id, total_price) +VALUES +(8557411, '2022-04-02 09:10:19', '2022-04-10 10:15:05', 'Audi', 11112, 1400), +(8557421, '2022-04-06 07:00:30', '2022-04-19 07:10:19', 'BMW', 11115, 2000), +(8557451, '2022-04-19 08:00:20', '2022-04-24 08:05:00', 'Cadillac', 11112, 1000), +(8557311, '2022-05-11 09:15:28', '2022-05-18 09:00:18', 'Audi', 11115, 1500), +(8557321, '2022-05-20 10:12:22', '2022-05-28 10:08:48', 'Audi', 11113, 1500), +(8557351, '2022-06-10 12:18:09', '2022-06-20 18:12:23', 'Cadillac', 11114, 1200), +(8557221, '2022-06-17 14:02:02', '2022-06-20 14:17:02', 'Chevrolet', 11112, 1300), +(8557251, '2022-07-12 05:19:49', '2022-07-19 07:15:28', 'Chevrolet', 11116, 1400), +(8557231, '2022-08-09 09:29:08', '2022-08-24 09:30:58', 'Cadillac', 11114, 2000), +(8557291, '2022-08-18 15:15:20', '2022-09-01 15:30:19', 'BMW', 11117, 3000); +---- + +[source,sql] +---- +SELECT * FROM rental; +---- + +The rental table stores the details for car rental: + +[source,sql] +---- ++------------+---------------------+---------------------+-----------+---------------+-------------+ +| rental_id | rental_date | return_date | car | customer_id | total_price | ++------------+---------------------+---------------------+-----------+---------------+-------------+ +| 8557411 | 2022-04-02 09:10:19 | 2022-04-10 10:15:05 | Audi | 11112 | 1400 | +| 8557421 | 2022-04-06 07:00:30 | 2022-04-19 07:10:19 | BMW | 11115 | 2000 | +| 8557451 | 2022-04-19 08:00:20 | 2022-04-24 08:05:00 | Cadillac | 11112 | 1000 | +| 8557311 | 2022-05-11 09:15:28 | 2022-05-18 09:00:18 | Audi | 11115 | 1500 | +| 8557321 | 2022-05-20 10:12:22 | 2022-05-28 10:08:48 | Audi | 11113 | 1500 | +| 8557351 | 2022-06-10 12:18:09 | 2022-06-20 18:12:23 | Cadillac | 11114 | 1200 | +| 8557221 | 2022-06-17 14:02:02 | 2022-06-20 14:17:02 | Chevrolet | 11112 | 1300 | +| 8557251 | 2022-07-12 05:19:49 | 2022-07-19 07:15:28 | Chevrolet | 11116 | 1400 | +| 8557231 | 2022-08-09 09:29:08 | 2022-08-24 09:30:58 | Cadillac | 11114 | 2000 | +| 8557291 | 2022-08-18 15:15:20 | 2022-09-01 15:30:19 | BMW | 11117 | 3000 | ++------------+---------------------+---------------------+-----------+---------------+-------------+ +---- + +=== `SUM()` in `SELECT` statement + +The following example uses the `SUM()` function to calculate the total rent price of all `rental_id`: + +[source,sql] +---- +SELECT SUM (total_price) AS total +FROM rental +---- + +This returns a sum value of the `total_price`: + +[source,sql] +---- ++--------+ +| total | ++--------+ +| 16300 | ++--------+ +---- + +=== `SUM()` with a `NULL` result + +The following example uses the `SUM()` function to calculate the total rent price of the `customer_id = 11118.` + +[source,sql] +---- +SELECT SUM (total_price) AS total +FROM rental +WHERE customer_id = 11118; +---- + +Since no records in the `rental` table have the `customer_id = 11118`, the `SUM()` function returns a `NULL`. + +[source,sql] +---- ++--------+ +| total | ++--------+ +| null | ++--------+ +---- + +=== `SUM()` with `GROUP BY` clause + +You can use the `GROUP BY` clause to group the records in the table and apply the `SUM()` function to each group afterward. + +The following example uses the `SUM()` function and the `GROUP BY` clause to calculate the total price paid by each customer: + +[source,sql] +---- +SELECT customer_id, +SUM (total_price) AS total_spend +FROM rental +GROUP BY customer_id; +---- + +This calculates the `total_price` from a group of `customer_id`: + +[source,sql] +---- ++--------------+--------------+ +| customer_id | total_spend | ++--------------+--------------+ +| 11115 | 3500 | +| 11117 | 3000 | +| 11116 | 1400 | +| 11113 | 1500 | +| 11112 | 3700 | +| 11114 | 3200 | ++--------------+--------------+ +---- + +=== `SUM()` with `HAVING` clause + +You can use the `SUM()` function with the `HAVING` clause to filter out the sum of groups based on a specific condition: + +[source,sql] +---- +SELECT + customer_id, + SUM (total_price) AS total_spend +FROM rental +GROUP BY customer_id +HAVING SUM(total_price) >= 3000; +---- + +This returns the customers who spent greater than or equal to 3000: + +[source,sql] +---- ++--------------+--------------+ +| customer_id | total_spend | ++--------------+--------------+ +| 11115 | 3500 | +| 11117 | 3000 | +| 11112 | 3700 | +| 11114 | 3200 | ++--------------+--------------+ +---- + +=== `SUM()` with multiple expression + +The example uses the following: + +* `SUM()` function to calculate total rental days. +* `JOIN` clause to combine the rental table with the customer table. +* `GROUP BY` group a result-set based on the customers' names. + +[source,sql] +---- +SELECT s.cust_name, SUM(return_date - rental_date ) AS rental_period +FROM rental AS r +JOIN customer AS s +ON r.customer_id = s.customer_id +GROUP BY cust_name; +---- + +The output displays the customers' names with their total rental period. + +[source,sql] +---- ++------------+-------------------+ +| cust_name | rental_period | ++------------+-------------------+ +| Aaron | 7 days 23:56:26 | +| Martha | 14 days 00:14:59 | +| Rosy | 7 days 01:55:39 | +| Nina | 19 days 23:54:39 | +| Alex | 16 days 01:24:26 | +| Alice | 25 days 05:56:04 | ++------------+-------------------+ +---- diff --git a/modules/reference/pages/sql/sql-functions/boolean-functions/if-function.adoc b/modules/reference/pages/sql/sql-functions/boolean-functions/if-function.adoc new file mode 100644 index 000000000..b6243e4db --- /dev/null +++ b/modules/reference/pages/sql/sql-functions/boolean-functions/if-function.adoc @@ -0,0 +1,144 @@ += IF() +:description: The IF() function returns one value if the condition is TRUE and another value if the condition is FALSE. +:page-topic-type: reference + +The `IF()` function returns one value if the condition is `TRUE` and another value if the condition is `FALSE`. + +== Syntax + +[source,sql] +---- +IF(expression, true_result, else_result) +---- + +The `expression` must evaluate to a Boolean (`TRUE` or `FALSE`). + +== Examples + +=== IF() with a table + +The following example uses a `test_result` table to determine which participants passed and failed: + +[source,sql] +---- +CREATE TABLE test_result ( + applicant_id int, + name text, + score int +); + +INSERT INTO test_result VALUES +(78765,'Mike Aoki',677), +(78786,'Julie Grahams',650), +(78986,'Alexandra Jones',450), +(79742,'Lucas Moore',487), +(79769,'Augustine Harkness',572); +---- + +View the table: + +[source,sql] +---- +SELECT * FROM test_result; +---- + +The query returns: + +[source,sql] +---- ++---------------+--------------------+--------+ +| applicant_id | name | score | ++---------------+--------------------+--------+ +| 78765 | Mike Aoki | 677 | +| 78786 | Julie Grahams | 650 | +| 78986 | Alexandra Jones | 450 | +| 79742 | Lucas Moore | 487 | +| 79769 | Augustine Harkness | 572 | ++---------------+--------------------+--------+ +---- + +. The following query returns `'PASSED'` if the score is 500 or greater, and `'NOT PASSED'` otherwise: ++ +[source,sql] +---- +SELECT name, IF(score>=500, 'PASSED', 'NOT PASSED') FROM test_result; +---- + +. The query returns: ++ +[source,sql] +---- ++--------------------+-------------+ +| name | case | ++--------------------+-------------+ +| Mike Aoki | PASSED | +| Julie Grahams | PASSED | +| Alexandra Jones | NOT PASSED | +| Lucas Moore | NOT PASSED | +| Augustine Harkness | PASSED | ++--------------------+-------------+ +---- + +=== IF() with expressions as return value + +The following example uses a `deptcost` table to determine which departments exceeded the budget: + +[source,sql] +---- +CREATE TABLE deptcost ( + dept text, + budget int, + actual int, + status text +); +INSERT INTO deptcost VALUES +('Finance', 800,677,'within budget'), +('HR', 700,930,'over budget'), +('Marketing', 500,677,'over budget'), +('Project', 720,700,'within budget'), +('Sales', 910,860,'within budget'); +---- + +View the table: + +[source,sql] +---- +SELECT * FROM deptcost; +---- + +The query returns: + +[source,sql] +---- ++-----------+--------+--------+---------------+ +| dept | budget | actual | status | ++-----------+--------+--------+---------------+ +| Finance | 800 | 677 | within budget | +| HR | 700 | 930 | over budget | +| Marketing | 500 | 677 | over budget | +| Project | 720 | 700 | within budget | +| Sales | 910 | 860 | within budget | ++-----------+--------+--------+---------------+ +---- + +. The following query returns the budget difference if `actual` is less than `budget`, and `0` otherwise: ++ +[source,sql] +---- +SELECT dept, IF(actual < budget, budget - actual, 0) FROM deptcost; +---- + +. The query returns: ++ +[source,sql] +---- ++-----------+-----+ +| dept | f | ++-----------+-----+ +| Finance | 123 | +| HR | 0 | +| Marketing | 0 | +| Project | 20 | +| Sales | 50 | ++-----------+-----+ +---- diff --git a/modules/reference/pages/sql/sql-functions/boolean-functions/index.adoc b/modules/reference/pages/sql/sql-functions/boolean-functions/index.adoc new file mode 100644 index 000000000..2f26c20eb --- /dev/null +++ b/modules/reference/pages/sql/sql-functions/boolean-functions/index.adoc @@ -0,0 +1,3 @@ += Boolean Functions +:description: Reference for boolean functions and operators in Redpanda SQL. +:page-layout: index diff --git a/modules/reference/pages/sql/sql-functions/boolean-functions/is-distinct-from-operator.adoc b/modules/reference/pages/sql/sql-functions/boolean-functions/is-distinct-from-operator.adoc new file mode 100644 index 000000000..3253f0ab0 --- /dev/null +++ b/modules/reference/pages/sql/sql-functions/boolean-functions/is-distinct-from-operator.adoc @@ -0,0 +1,149 @@ += IS DISTINCT FROM Operator +:description: The IS DISTINCT FROM operator compares two values, considering them distinct even when both are NULL. +:page-topic-type: reference + +The `IS DISTINCT FROM` operator compares two values, considering them distinct even when both are `NULL`. It returns `TRUE` if the two values are different and `FALSE` if they are the same, including the case where both values are `NULL`. + +== Syntax + +The syntax for this function is: + +[source,sql] +---- +value1 IS DISTINCT FROM value2 +---- + +Where: + +* `value1` is the first value for comparison. +* `value2` is the second value for comparison. + +== Examples + +=== Basic usage + +Consider this example, which compares two values: + +*Example 1* + +[source,sql] +---- +SELECT NULL IS DISTINCT FROM NULL AS "Result"; +---- + +The preceding query returns the output: + +[source,sql] +---- + Result +-------- + f +---- + +*Example 2* + +[source,sql] +---- +SELECT 10 IS DISTINCT FROM 20 AS "Result"; +---- + +The preceding query returns the output: + +[source,sql] +---- + Result +-------- + t +---- + +*Example 3* + +[source,sql] +---- +SELECT 10 IS DISTINCT FROM 10 AS "Result"; +---- + +The preceding query returns the output: + +[source,sql] +---- + Result +-------- + f +---- + +=== Compare NULL values + +In this example, `NULL` values are compared using the `IS DISTINCT FROM` operator: + +*Example 1* + +[source,sql] +---- +SELECT NULL IS DISTINCT FROM 10 AS "Result"; +---- + +The preceding query returns the output: + +[source,sql] +---- + Result +-------- + t +---- + +*Example 2* + +[source,sql] +---- +SELECT 10 IS DISTINCT FROM NULL AS "Result"; +---- + +The preceding query returns the output: + +[source,sql] +---- + Result +-------- + t +---- + +=== Track inventory variations + +Suppose there is a table named `inventory_changes` that tracks changes in the quantities of products in a warehouse. The table has the structure: + +[source,sql] +---- +CREATE TABLE inventory_changes ( + product_id INT, + change_date DATE, + change_quantity INT +); + +INSERT INTO inventory_changes VALUES +(101, '2023-08-01', 50), +(102, '2023-08-01', 0), +(101, '2023-08-02', -15), +(103, '2023-08-03', 30), +(102, '2023-08-04', 0); +---- + +To retrieve records where the change quantity is distinct from zero, use the `IS DISTINCT FROM` operator. + +[source,sql] +---- +SELECT * +FROM inventory_changes +WHERE change_quantity IS DISTINCT FROM 0; +---- + +The result of the query does not include the 0 values: + +[source,sql] +---- + product_id | change_date | change_quantity +------------+-------------+----------------- + 101 | 2023-08-01 | 50 + 101 | 2023-08-02 | -15 + 103 | 2023-08-03 | 30 +---- diff --git a/modules/reference/pages/sql/sql-functions/boolean-functions/is-not-distinct-from-operator.adoc b/modules/reference/pages/sql/sql-functions/boolean-functions/is-not-distinct-from-operator.adoc new file mode 100644 index 000000000..296a2d8a9 --- /dev/null +++ b/modules/reference/pages/sql/sql-functions/boolean-functions/is-not-distinct-from-operator.adoc @@ -0,0 +1,151 @@ += IS NOT DISTINCT FROM Operator +:description: The IS NOT DISTINCT FROM operator is a counterpart to IS DISTINCT FROM. +:page-topic-type: reference + +== Overview + +The `IS NOT DISTINCT FROM` operator is a counterpart to `IS DISTINCT FROM`. + +It compares two values, treating them as equal even when they are both `NULL`. This operator returns `TRUE` if the two values are the same, including the case where both values are `NULL` and `FALSE` if they are different. + +== Syntax + +The syntax for this function is: + +[source,sql] +---- +value1 IS NOT DISTINCT FROM value2 +---- + +Where: + +* `value1` is the first value for comparison. +* `value2` is the second value for comparison. + +== Examples + +=== Basic usage + +Consider this example, which compares two values: + +*Example 1* + +[source,sql] +---- +SELECT 45 IS NOT DISTINCT FROM 45 AS "Result"; +---- + +The preceding query returns the output: + +[source,sql] +---- + Result +-------- + t +---- + +*Example 2* + +[source,sql] +---- +SELECT 60 IS NOT DISTINCT FROM 30 AS "Result"; +---- + +The preceding query returns: + +[source,sql] +---- + Result +-------- + f +---- + +*Example 3* + +[source,sql] +---- +SELECT NULL IS NOT DISTINCT FROM NULL AS "Result"; +---- + +The preceding query returns: + +[source,sql] +---- + Result +-------- + t +---- + +=== Compare NULL values + +In this example, NULL values are compared using the IS NOT DISTINCT FROM operator: + +*Example 1* + +[source,sql] +---- +SELECT NULL IS NOT DISTINCT FROM 80 AS "Result"; +---- + +The preceding query returns: + +[source,sql] +---- + Result +-------- + f +---- + +*Example 2* + +[source,sql] +---- +SELECT 5 IS NOT DISTINCT FROM NULL AS "Result"; +---- + +The preceding query returns: + +[source,sql] +---- + Result +-------- + f +---- + +=== Analyze data completeness + +Suppose there is a table named customer_contacts that stores customer contact information. + +[source,sql] +---- +CREATE TABLE customer_contacts ( + customer_id INT, + email TEXT, + phone TEXT +); + +INSERT INTO customer_contacts VALUES +(101, 'john@example.com', NULL), +(102, NULL, '+1234567890'), +(103, 'jane@example.com', '+9876543210'), +(104, NULL, NULL), +(105, 'alex@example.com', '+5555555555'); +---- + +The objective is to retrieve records from this table where an email address or a phone number is available for contacting the customers. + +[source,sql] +---- +SELECT * +FROM customer_contacts +WHERE email IS NOT DISTINCT FROM phone; +---- + +This query retrieves all rows from the `customer_contacts table` where the email and phone are NULL. The result shows that the customer with `customer_id 104` has no phone number or email address. + +[source,sql] +---- + customer_id | email | phone +-------------+-------+------- + 104 | | +---- diff --git a/modules/reference/pages/sql/sql-functions/index.adoc b/modules/reference/pages/sql/sql-functions/index.adoc new file mode 100644 index 000000000..5a75d5cd2 --- /dev/null +++ b/modules/reference/pages/sql/sql-functions/index.adoc @@ -0,0 +1,18 @@ += SQL Functions +:description: Redpanda SQL supports the following function types for querying and transforming data. + +Redpanda SQL supports the following function types for querying and transforming data: + + +[width="100%",cols="<42%,<58%",options="header",] +|=== +|Function Name |Description +|xref:reference:sql/sql-functions/boolean-functions/if-function.adoc[BOOLEAN FUNCTIONS] |Evaluate logical conditions and return `TRUE`, `FALSE` OR `NULL` +|xref:reference:sql/sql-functions/math-functions/index.adoc[MATH, TRIGONOMETRIC, AND HYPERBOLIC FUNCTIONS] |Perform mathematical operations on numeric data, including rounding, exponentiation, and trigonometric calculations +|xref:reference:sql/sql-functions/string-functions/index.adoc[STRING FUNCTIONS] |Manipulate string data for text processing, including concatenation, substring extraction and case conversion +|xref:reference:sql/sql-functions/timestamp-functions/index.adoc[TIMESTAMP FUNCTIONS] |Handle data and time values including extracting components, adding intervals and comparing timestamps +|xref:reference:sql/sql-functions/json-functions/index.adoc[JSON FUNCTIONS] |Manipulate and query JSON data stored in the database, including extracting values and creating JSON objects +|xref:reference:sql/sql-functions/aggregate-functions/index.adoc[AGGREGATE FUNCTIONS] |Summarize a set of values and return a single result, such as calculating sums, averages and counts +|xref:reference:sql/sql-functions/window-functions/index.adoc[WINDOW FUNCTIONS] |Operate over a subset of rows defined by a windowing clause, enabling ranking, aggregation and row numbering within result sets +|xref:reference:sql/sql-functions/other-functions/index.adoc[OTHER FUNCTIONS] |Includes a variety of specialized functions not categorized elsewhere +|=== diff --git a/modules/reference/pages/sql/sql-functions/json-functions/index.adoc b/modules/reference/pages/sql/sql-functions/json-functions/index.adoc new file mode 100644 index 000000000..2967a5903 --- /dev/null +++ b/modules/reference/pages/sql/sql-functions/json-functions/index.adoc @@ -0,0 +1,24 @@ += Overview +:description: Redpanda SQL provides functions to query and manipulate JSON data. + +Redpanda SQL provides functions to query and manipulate JSON data: + +[width="100%",cols="53%,47%",options="header",] +|=== +|*Functions* |*Description* +|xref:reference:sql/sql-functions/json-functions/json-extract-path.adoc[JSON_EXTRACT_PATH()] |Extracts a JSON sub-object at the specified path. +|xref:reference:sql/sql-functions/json-functions/json-extract-path-text.adoc[JSON_EXTRACT_PATH_TEXT()] |Returns text referenced by a series of path elements in a JSON string or JSON body. +|xref:reference:sql/sql-functions/json-functions/json-array-length.adoc[JSON_ARRAY_LENGTH()] |Returns the number of elements in the outer array of a JSON string or JSON body. +|xref:reference:sql/sql-functions/json-functions/json-array-extract.adoc[JSON_ARRAY_EXTRACT()] |Returns the JSON array as a set of JSON values. +|=== + +Use operators to specify conditions when using JSON functions. Redpanda SQL also supports the following JSON operators: + +[width="100%",cols="12%,46%,42%",options="header",] +|=== +|*Operators* |*Description* |*Example* +|-> |Gets and returns the element of the JSON array. |`'[{"a":"cab"},{"b":"bac"},{"c":"abc"}]'::json -> 2` +|-> |Gets and returns the JSON object field. |`'{"a": {"b":"abc"}}'::json -> 'a'` +|->> |Gets and returns the element of the JSON array as text. |`'[11,22,33]'::json ->> 2` +|->> |Gets and returns the JSON object field as text. |`'{"a":13,"b":33}'::json ->> 'b'` +|=== diff --git a/modules/reference/pages/sql/sql-functions/json-functions/json-array-extract.adoc b/modules/reference/pages/sql/sql-functions/json-functions/json-array-extract.adoc new file mode 100644 index 000000000..b0a8bef5e --- /dev/null +++ b/modules/reference/pages/sql/sql-functions/json-functions/json-array-extract.adoc @@ -0,0 +1,84 @@ += JSON_ARRAY_EXTRACT +:description: The JSON_ARRAY_EXTRACT() function returns the JSON array as a set of JSON values. +:page-topic-type: reference + +== Overview + +The `JSON_ARRAY_EXTRACT()` function returns the JSON array as a set of JSON values. + +== Syntax + +The `JSON_ARRAY_EXTRACT()` has the following basic syntax. + +[source,sql] +---- +JSON_ARRAY_EXTRACT('json_array'::JSON,id); +---- + +`JSON_ARRAY_EXTRACT()` requires the following parameters: + +* `json_array`: The array to extract. +* `::JSON`: Argument indicating that the query is of type JSON. +* `id`: ID of the element to extract. It is read in an array format that starts with 0. + +=== Another option + +`JSON_ARRAY_EXTRACT` can also be achieved with the `->` operator, as shown in the following syntax: + +[source,sql] +---- +SELECT 'from_json'::JSON -> path; +---- + +* `from_json`: The JSON value from which to extract. +* `::JSON`: A symbol that casts the string literal to a JSON type. +* `path`: Key of the field to extract. + +== Examples + +=== Basic JSON_ARRAY_EXTRACT() function + +. The following example extracts a JSON array as a JSON set. ++ +[source,sql] +---- +SELECT JSON_ARRAY_EXTRACT('["Bougenvile", 2, 12, "Lily"]'::JSON,3); +---- ++ +*or* ++ +[source,sql] +---- +SELECT ('["Bougenvile", 2, 12, "Lily"]'::JSON -> 3); +---- + +. The extracted array will look like the following. ++ +[source,sql] +---- ++------------+ +| f | ++------------+ +| "Lily" | ++------------+ +---- + +=== Extract element of JSON array as text + +. This example extracts the element of the JSON array as text with the `->>` operator. ++ +[source,sql] +---- +SELECT ('["Bougenvile", 2, 12, "Lily"]'::JSON ->> 1); +---- + +. Output: ++ +[source,sql] +---- ++------------+ +| f | ++------------+ +| 2.000000 | ++------------+ +---- diff --git a/modules/reference/pages/sql/sql-functions/json-functions/json-array-length.adoc b/modules/reference/pages/sql/sql-functions/json-functions/json-array-length.adoc new file mode 100644 index 000000000..956f54d3c --- /dev/null +++ b/modules/reference/pages/sql/sql-functions/json-functions/json-array-length.adoc @@ -0,0 +1,78 @@ += JSON_ARRAY_LENGTH +:description: The JSON_ARRAY_LENGTH() function returns the length of a specified JSON array. +:page-topic-type: reference + +The `JSON_ARRAY_LENGTH()` function returns the length of a specified JSON array. + +== Syntax + +This function has the following basic syntax. + +[source,sql] +---- +JSON_ARRAY_LENGTH(arrayval JSON) +---- + +The required argument for this function is `arrayval`. It represents the JSON array for which to count the length. + +== Examples + +=== Get a JSON array length with a JSON value + +The following example returns the number of elements in the array: + +[source,sql] +---- +SELECT JSON_ARRAY_LENGTH('[4, 7, 10, 11, 14, {"vegetables":"spinach","fruits":"melon"}, {"a":"b"}]'); +---- + +This function returns the following result: + +[source,sql] +---- ++-------+ +| f | ++-------+ +| 7 | ++-------+ +---- + +=== Get a JSON array length with a number + +The following example returns the number of elements in the array. + +[source,sql] +---- +SELECT JSON_ARRAY_LENGTH('[1, 2, [3, 4]]'); +---- + +The query returns: + +[source,sql] +---- ++-------+ +| f | ++-------+ +| 3 | ++-------+ +---- + +=== JSON array length where the array is NULL or empty + +This example shows that an empty JSON array returns 0. + +[source,sql] +---- +SELECT JSON_ARRAY_LENGTH('[]'); +---- + +An empty array returns 0: + +[source,sql] +---- ++-------+ +| f | ++-------+ +| 0 | ++-------+ +---- diff --git a/modules/reference/pages/sql/sql-functions/json-functions/json-extract-path-text.adoc b/modules/reference/pages/sql/sql-functions/json-functions/json-extract-path-text.adoc new file mode 100644 index 000000000..17b25dfcf --- /dev/null +++ b/modules/reference/pages/sql/sql-functions/json-functions/json-extract-path-text.adoc @@ -0,0 +1,66 @@ += JSON_EXTRACT_PATH_TEXT +:description: The JSON_EXTRACT_PATH_TEXT() function extracts JSON nested value from a specified JSON value according to the defined path. +:page-topic-type: reference + +The `JSON_EXTRACT_PATH_TEXT()` function extracts JSON nested value from a specified JSON value according to the defined path. + +[NOTE] +==== +This function may be similar to the `JSON_EXTRACT_PATH()`. This function returns a value of type text instead of type JSON. +==== + +== Syntax + +The `JSON_EXTRACT_PATH_TEXT()` syntax is as follows: + +[source,sql] +---- +JSON_EXTRACT_PATH_TEXT(from_json JSON, path TEXT[]) +---- + +The required arguments are: + +* `from_json`: The JSON value to extract. +* `path`: The path to extract. + +=== Another option + +Redpanda SQL also provides and supports the use of operators in queries. Here's the syntax: + +[source,sql] +---- +SELECT 'from_json'::JSON ->> 'path'; +---- + +* `from_json`: The JSON value from which to extract. +* `::JSON`: A symbol that casts the text literal to a JSON type. +* `path`: Key of the field to extract. + +== Examples + +. This example shows how to use the `JSON_EXTRACT_PATH_TEXT()` function to extract values ​​from a JSON object at a specified index. ++ +Run the query: ++ +[source,sql] +---- +SELECT JSON_EXTRACT_PATH_TEXT('{"a": "Oxla", "b": {"x": 1.234, "y": 4.321}}', 'a') AS "result a"; +---- ++ +*or* ++ +[source,sql] +---- +SELECT '{"a": "Oxla", "b": {"x": 1.234, "y": 4.321}}'::JSON ->> 'a' AS "result a"; +---- + +. The `JSON_EXTRACT_PATH_TEXT()` function extracts the values and returns the output: ++ +[source,sql] +---- ++------------+ +| result a | ++------------+ +| Oxla | ++------------+ +---- diff --git a/modules/reference/pages/sql/sql-functions/json-functions/json-extract-path.adoc b/modules/reference/pages/sql/sql-functions/json-functions/json-extract-path.adoc new file mode 100644 index 000000000..1d2888640 --- /dev/null +++ b/modules/reference/pages/sql/sql-functions/json-functions/json-extract-path.adoc @@ -0,0 +1,79 @@ += JSON_EXTRACT_PATH +:description: JSON_EXTRACT_PATH() function extracts JSON nested value from a specified path. +:page-topic-type: reference + +`JSON_EXTRACT_PATH()` function extracts JSON nested value from a specified path. + +== Syntax + +The syntax of the `JSON_EXTRACT_PATH()` function is: + +[source,sql] +---- +JSON_EXTRACT_PATH(from_json JSON, path TEXT[]) +---- + +* `from_json`: The JSON value from which to extract. +* `path`: The path to extract. + +=== Another option + +Redpanda SQL also provides and supports the use of operators in queries: + +[source,sql] +---- +SELECT 'from_json'::JSON -> 'path'; +---- + +* `from_json`: The JSON value from which to extract. +* `::JSON`: A symbol that casts the text literal to a JSON type. +* `path`: Key of the field to extract. + +== Examples + +These examples display how `JSON_EXTRACT_PATH()` extracts the "`oxla`" JSON sub-object from the specified path. + +. Use the query: ++ +[source,sql] +---- +SELECT JSON_EXTRACT_PATH('{"f2":{"f3":1},"f4":{"f5":99,"f6":"oxla"}}', 'f4', 'f6'); +---- ++ +*or* ++ +[source,sql] +---- +SELECT '{"f2":{"f3":1},"f4":{"f5":99,"f6":"oxla"}}'::JSON -> 'f4' -> 'f6'; +---- ++ +The query results: ++ +[source,sql] +---- ++---------+ +| f | ++---------+ +| "oxla" | ++---------+ +---- + +. Run the query: ++ +[source,sql] +---- +SELECT + JSON_EXTRACT_PATH('{"a": 1, "b": {"x": "subtract", "y": "plus"}}', 'b', 'x') AS "bx", + JSON_EXTRACT_PATH('{"a": 1, "b": {"x": "multiply", "y": "divide"}}', 'b', 'y') AS "by"; +---- ++ +The query returns: ++ +[source,sql] +---- ++---------------+-------------+ +| bx | by | ++---------------+-------------+ +| "subtract" | "divide" | ++---------------+-------------+ +---- diff --git a/modules/reference/pages/sql/sql-functions/math-functions/abs.adoc b/modules/reference/pages/sql/sql-functions/math-functions/abs.adoc new file mode 100644 index 000000000..d0b9413f0 --- /dev/null +++ b/modules/reference/pages/sql/sql-functions/math-functions/abs.adoc @@ -0,0 +1,107 @@ += ABS +:description: The ABS() function returns an absolute number (for example, the positive value of a number). +:page-topic-type: reference + +The `ABS()` function returns an absolute number (for example, the positive value of a number). The data type of the returned value will depend on the data type of the value passed to the `ABS()` function. + +== Syntax + +Here's the syntax for the `ABS()`function: + +[source,sql] +---- +ABS(x) +---- + +The `ABS()` function requires one argument: + +* `x`: An expression that evaluates to a number. + +[NOTE] +==== +The *ABS()* function will return the negation of the negative numbers. +==== + +== Examples + +=== Absolute value of a negative number + +This example demonstrates how to use the `ABS()` function to obtain the absolute value of a negative number: + +[source,sql] +---- +SELECT ABS(-10.25); +---- + +This returns an absolute value of the passed argument: + +[source,sql] +---- ++--------+ +| f | ++--------+ +| 10.25 | ++--------+ +---- + +=== ABS() function with an expression + +This example demonstrates how to use the `ABS()` function with an expression to obtain the absolute value of the result: + +[source,sql] +---- +SELECT ABS( 100 - 250); +---- + +The result of this statement is *-150*. However, the output is *150*, as 150 is the positive version of -150. + +[source,sql] +---- ++------+ +| f | ++------+ +| 150 | ++------+ +---- + +=== Use the ABS() function with a table + +This example demonstrates how to use the `ABS()` function with a table to obtain the absolute values of all numbers in a specific column: + +. First, create a table named absTable containing an *_initialValue_* column with some positive and negative values: ++ +[source,sql] +---- +CREATE TABLE absTable(initialValue float); + +INSERT INTO absTable(initialValue) +VALUES +(550), +(-210), +(72.12), +(-87.93), +(-0.0); +---- + +. Next, use this query to find the absolute value of all numbers: ++ +[source,sql] +---- +SELECT initialValue, ABS(initialValue) AS absoluteValue +FROM absTable; +---- + +. This query retrieves all values in the *"`initialValue`"* column and their absolute values in the *"`absoluteValue`"* column. The output will look something like this: ++ +[source,sql] +---- ++---------------+----------------+ +| initialValue | absoluteValue | ++---------------+----------------+ +| 550 | 550 | +| -210 | 210 | +| 72.12 | 72.12 | +| -87.93 | 87.93 | +| -0 | 0 | ++---------------+----------------+ +---- diff --git a/modules/reference/pages/sql/sql-functions/math-functions/cbrt.adoc b/modules/reference/pages/sql/sql-functions/math-functions/cbrt.adoc new file mode 100644 index 000000000..c9e65bf73 --- /dev/null +++ b/modules/reference/pages/sql/sql-functions/math-functions/cbrt.adoc @@ -0,0 +1,137 @@ += CBRT +:description: The CBRT() function calculates and returns the cube root of a given number. +:page-topic-type: reference + +The `CBRT()` function calculates and returns the cube root of a given number. In mathematical terms, for a number _x_, its cube root _y_ is determined by the equation _y³ = x_. + +== Syntax + +The syntax for the `CBRT()` function is: + +[source,sql] +---- +CBRT(number) +---- + +Where: + +* `number`: A required value representing the number for which to calculate the cube root. It can be a positive or negative whole number, a decimal, or even an expression that evaluates to a number. + +For example, expressions like `SELECT CBRT(some_column) from test_table`, assuming `some_column` contains a numeric value. + +[NOTE] +==== +*Return Value:* + - Returns `NULL` if the argument is `NULL`. + - Returns an error if the input parameter is not a numeric type. +==== + +== Examples + +These examples show how to use the `CBRT()` function: + +=== Basic cube root calculation + +Consider the example: + +[source,sql] +---- + SELECT CBRT(125); +---- + +The result of this query will be: + +[source,sql] +---- + cbrt +------ + 5 +---- + +=== Cube root of a negative value + +To calculate the cube root of a negative number, use the `CBRT()` function as shown: + +[source,sql] +---- + SELECT CBRT(-125); +---- + +Result: + +[source,sql] +---- + cbrt +------ + -5 +---- + +=== Cube root of decimal result + +For calculations with decimal numbers: + +[source,sql] +---- +SELECT CBRT(32); +---- + +The result is a decimal value: + +[source,sql] +---- + cbrt +------------------- + 3.174802103936399 +---- + +=== Cube root of decimal input + +In this scenario, fractional seconds are incorporated into the argument: + +[source,sql] +---- +SELECT CBRT(0.12815); +---- + +The result will be the cube root of the provided decimal value. + +[source,sql] +---- + cbrt +------------ + 0.50416523 +---- + +=== Handle incorrect argument + +When a non-numeric argument is provided, the `CBRT()` function works as shown here: + +[source,sql] +---- +SELECT CBRT('abc'); +---- + +The function returns an error and the result is not valid. + +[source,sql] +---- +invalid input syntax for type double precision: "abc" +---- + +=== CBRT operator (`||/(x)`) + +This example uses the CBRT operator (`||/(x)`) to calculate the cube root of a number: + +[source,sql] +---- +SELECT ||/(1728) AS cbrt_operator; +---- + +This example calculates the cube root of 1728 using the CBRT operator. The result of this query will be: + +[source,sql] +---- + cbrt_operator +-------------------- + 12.000000000000002 +---- diff --git a/modules/reference/pages/sql/sql-functions/math-functions/ceil.adoc b/modules/reference/pages/sql/sql-functions/math-functions/ceil.adoc new file mode 100644 index 000000000..49fd4a73f --- /dev/null +++ b/modules/reference/pages/sql/sql-functions/math-functions/ceil.adoc @@ -0,0 +1,106 @@ += CEIL +:description: The CEIL() function returns the nearest positive or negative integer value greater than or equal to the provided decimal input number. +:page-topic-type: reference + +The `CEIL()` function returns the nearest positive or negative integer value greater than or equal to the provided decimal input number. + +== Syntax + +The syntax of the `CEIL()` function is: + +[source,sql] +---- +CEIL(x) +---- + +The `CEIL()` function requires one argument: + +* `x`: A positive or a negative decimal number (or an expression that evaluates to a decimal number). + +== Examples + +=== Round up a positive decimal value + +This example demonstrates how the `CEIL()` function rounds up a positive decimal value: + +[source,sql] +---- +SELECT CEIL (300.55); +---- + +This returns 301, as it is the nearest integer value greater than 300.55. + +[source,sql] +---- ++------+ +| f | ++------+ +| 301 | ++------+ +---- + +=== Round up a negative decimal value + +This example demonstrates how the `CEIL()` function rounds up a negative decimal value: + +[source,sql] +---- +SELECT CEIL(-89.9) AS "Ceil"; +---- + +The output of this statement is -89, as -89 is the nearest integer value greater than or equal to -89.9. + +[source,sql] +---- ++-------+ +| Ceil | ++-------+ +| -89 | ++-------+ +---- + +=== Use the `CEIL()` function with a table + +This example demonstrates how to use the `CEIL()` function with a table to round up values in a specific column: + +. First, create a table called *_CeilRecords_*: ++ +[source,sql] +---- +CREATE TABLE CeilRecords (numbers float); + +INSERT INTO CeilRecords(numbers) +VALUES + (-28.85), + (-9.4), + (0.87), + (78.16), + (42.16); +---- ++ +This statement creates a table called *"`CeilRecords`"* with a column called *"`numbers`"* and insert 5 decimal values into it. + +. Retrieve and round up the value for all records in the *numbers* column: ++ +[source,sql] +---- +SELECT *, CEIL(numbers) AS CeilValue FROM CeilRecords; +---- ++ +The final result will contain: ++ +* A *numbers* column with initial decimal values. +* A *CeilValue* column with rounded-up integer values. ++ +[source,sql] +---- ++---------+------------+ +| numbers | CeilValue | ++---------+------------+ +| -28.85 | -28 | +| -9.4 | -9 | +| 0.87 | 1 | +| 78.16 | 79 | +| 42.16 | 43 | ++---------+------------+ +---- diff --git a/modules/reference/pages/sql/sql-functions/math-functions/cosh.adoc b/modules/reference/pages/sql/sql-functions/math-functions/cosh.adoc new file mode 100644 index 000000000..357143aaa --- /dev/null +++ b/modules/reference/pages/sql/sql-functions/math-functions/cosh.adoc @@ -0,0 +1,69 @@ += COSH +:description: The COSH() function returns the hyperbolic cosine of a specified numeric argument. +:page-topic-type: reference + +The `COSH()` function returns the hyperbolic cosine of a specified numeric argument. + +== Syntax + +[source,sql] +---- +COSH(x) +---- + +== Arguments + +* `x`: A positive or negative number of type `real` or `double precision`. + +== Return type + +Same as the input type (`real` or `double precision`). + +== Examples + +=== Positive value + +[source,sql] +---- +SELECT COSH(1); +---- + +[source,sql] +---- + cosh +-------------------- + 1.5430806348152437 +(1 row) +---- + +=== Zero + +[source,sql] +---- +SELECT COSH(0); +---- + +[source,sql] +---- + cosh +------ + 1 +(1 row) +---- + +=== Negative value + +`COSH` is symmetric, so `COSH(-x)` returns the same result as `COSH(x)`: + +[source,sql] +---- +SELECT COSH(-1); +---- + +[source,sql] +---- + cosh +-------------------- + 1.5430806348152437 +(1 row) +---- diff --git a/modules/reference/pages/sql/sql-functions/math-functions/exp.adoc b/modules/reference/pages/sql/sql-functions/math-functions/exp.adoc new file mode 100644 index 000000000..c4712341b --- /dev/null +++ b/modules/reference/pages/sql/sql-functions/math-functions/exp.adoc @@ -0,0 +1,80 @@ += EXP +:description: The EXP() function returns the exponential value of a number specified in the argument. +:page-topic-type: reference + +The `EXP()` function returns the exponential value of a number specified in the argument. + +== Syntax + +The syntax for the `EXP()` is: + +[source,sql] +---- +EXP(number); +---- + +Where: + +* `number`: The number for which to calculate the exponential value. Equivalent to the formula `e^number`. + +== Examples + +The examples here show how the `EXP()` function works. + +=== Basic usage + +This example uses the `EXP()` function with positive and negative values. + +[source,sql] +---- +SELECT EXP(0) AS "EXP of 0", + EXP(1) AS "EXP of 1", + EXP(2) AS "EXP of 2", + EXP(-1) AS "EXP of -1", + EXP(-2) AS "EXP of -2"; +---- + +The query returns: + +[source,sql] +---- +EXP of 0 | EXP of 1 | EXP of 2 | EXP of -1 | EXP of -2 +----------+-------------------+------------------+---------------------+-------------------- + 1 | 2.718281828459045 | 7.38905609893065 | 0.36787944117144233 | 0.1353352832366127 +---- + +=== Use `EXP()` with fractions + +This case uses the `EXP()` function with a fractional argument. + +[source,sql] +---- +SELECT EXP(3.2); +---- + +Here is the result: + +[source,sql] +---- + exp +-------------------- + 24.532531366911574 +---- + +=== Use `EXP()` with expressions + +This example uses the `EXP()` function with expressions. + +[source,sql] +---- +SELECT EXP(5 * 5); +---- + +The query returns: + +[source,sql] +---- + exp +------------------- + 72004899337.38588 +---- diff --git a/modules/reference/pages/sql/sql-functions/math-functions/floor.adoc b/modules/reference/pages/sql/sql-functions/math-functions/floor.adoc new file mode 100644 index 000000000..c25092e54 --- /dev/null +++ b/modules/reference/pages/sql/sql-functions/math-functions/floor.adoc @@ -0,0 +1,96 @@ += FLOOR +:description: The FLOOR() returns a number rounded down that is less than or equal to the specified argument. +:page-topic-type: reference + +The `FLOOR()` returns a number rounded down that is less than or equal to the specified argument. + +== Syntax + +The syntax for the `FLOOR()` function in Redpanda SQL is: + +[source,sql] +---- +FLOOR(x) +---- + +The `FLOOR()` function requires one argument: + +`x`: A positive or a negative decimal number (or an expression that evaluates to a decimal number). + +== Examples + +=== Round down a positive decimal value + +This example demonstrates how the `FLOORL()` function rounds down a positive decimal value: + +[source,sql] +---- +SELECT FLOOR(345.6765467); +---- + +This returns 345 as it is the closest value smaller than the argument. + +[source,sql] +---- ++------+ +| f    | ++------+ +| 345  | ++------+ +---- + +=== Round down a negative decimal value + +This example demonstrates how the `FLOORL()` function rounds down a negative decimal value: + +[source,sql] +---- +SELECT FLOOR(-0.987657); +---- + +The result is the nearest integer smaller than or equal to the specified argument. + +[source,sql] +---- ++-------+ +| f | ++-------+ +| -1    | ++-------+ +---- + +=== Use the FLOOR() function with a table + +This example demonstrates how to use the `FLOOR()` function with a table to round down values in a specific column: + +. Create a new table called *FloorRecords* with double-precision values: ++ +[source,sql] +---- +CREATE TABLE FloorRecords (numbers float); +INSERT INTO FloorRecords VALUES (3.987), (4.325), (-0.76), (-22.57); +---- + +. Retrieve the table with its values: ++ +[source,sql] +---- +SELECT * ,FLOOR(numbers) AS Floorvalue FROM FloorRecords; +---- + +. Result: ++ +* *numbers,* the column with the initial double-precision values. +* *FloorValue*, the column with the rounded-down values. ++ +[source,sql] +---- ++------------+---------------+ +| numbers | Floorvalue | ++------------+---------------+ +| 3.987 | 3 | +| 4.325 | 4 | +| -0.76 | -1 | +| -22.57 | -23 | ++------------+---------------+ +---- diff --git a/modules/reference/pages/sql/sql-functions/math-functions/greatest.adoc b/modules/reference/pages/sql/sql-functions/math-functions/greatest.adoc new file mode 100644 index 000000000..464f0fc0c --- /dev/null +++ b/modules/reference/pages/sql/sql-functions/math-functions/greatest.adoc @@ -0,0 +1,159 @@ += GREATEST +:description: The GREATEST() function extracts the greatest or largest value from a set of values. +:page-topic-type: reference + +The `GREATEST()` function returns the greatest value from a set of values. The arguments must be of compatible types. For example, comparing a text value with a number returns an error. + +== Syntax + +The syntax for this function is: + +[source,sql] +---- +GREATEST(value_1, [value_n]) +---- + +Where: + +* `value_1`: Represents the first value. +* `value_n`: Represents one or more additional values, separated by commas. + +[NOTE] +==== +* `NULL` values within the expressions are ignored. +* The result is `NULL` if all expressions evaluate to `NULL`. +==== + +== Examples + +=== Basic usage + +[source,sql] +---- +SELECT GREATEST(3,5,8,9,10); +---- + +The query returns `10`, the greatest value among the provided values. + +[source,sql] +---- +greatest +--------- + 10 +---- + +=== String comparison + +String comparison is also supported: + +[source,sql] +---- +SELECT GREATEST('apple', 'banana', 'cherry'); +---- + +The query returns `'cherry'`, the greatest string in lexicographic order. + +[source,sql] +---- +greatest +---------- + cherry +---- + +=== Handle NULL values + +`NULL` values are ignored when determining the greatest value: + +[source,sql] +---- +SELECT GREATEST (5,null,9); +---- + +The query returns `9`, the greatest non-NULL value. + +[source,sql] +---- +greatest +---------- + 9 +---- + +=== Positive and negative numbers + +Negative numbers can also be compared: + +[source,sql] +---- +SELECT GREATEST (4,-4,-8,8); +---- + +The query returns `8`, the greatest value among the provided numbers. + +[source,sql] +---- +greatest +---------- + 8 +---- + +=== Use table data + +You can also use `GREATEST()` to find the greatest value across columns. For example, create a table named `Student` that stores student names and scores. + +[source,sql] +---- +CREATE TABLE Student( + Student_name TEXT, + Student_Class TEXT, + Subject1 INT, + Subject2 INT, + Subject3 INT, + Subject4 INT +); + +INSERT INTO + Student(Student_name, Student_Class, Subject1, Subject2, Subject3, Subject4) +VALUES + ('Sayan', 'Junior', 81, 90, 86, 92 ), + ('Nitin', 'Junior', 90, 84, 88, 91 ), + ('Aniket', 'Senior', 81, 80, 87, 95 ), + ('Abdur', 'Junior', 85, 90, 80, 90 ), + ('Sanjoy', 'Senior', 88, 82, 84, 90 ); +---- + +Use the `SELECT` statement to view all the records: + +[source,sql] +---- +SELECT * FROM Student; +---- + +[source,sql] +---- +student_name | student_class | subject1 | subject2 | subject3 | subject4 +--------------+---------------+----------+----------+----------+---------- + Sayan | Junior | 81 | 90 | 86 | 92 + Nitin | Junior | 90 | 84 | 88 | 91 + Aniket | Senior | 81 | 80 | 87 | 95 + Abdur | Junior | 85 | 90 | 80 | 90 + Sanjoy | Senio | 88 | 82 | 84 | 90 +---- + +Find the greatest mark for each student across all subjects: + +[source,sql] +---- +Select Student_name, GREATEST(Subject1, Subject2, Subject3, Subject4) AS Greatest_Mark +FROM Student; +---- + +[source,sql] +---- +student_name | greatest_mark +--------------+--------------- + Sayan | 92 + Nitin | 91 + Aniket | 95 + Abdur | 90 + Sanjoy | 90 +---- diff --git a/modules/reference/pages/sql/sql-functions/math-functions/index.adoc b/modules/reference/pages/sql/sql-functions/math-functions/index.adoc new file mode 100644 index 000000000..ef1a60eec --- /dev/null +++ b/modules/reference/pages/sql/sql-functions/math-functions/index.adoc @@ -0,0 +1,61 @@ += Overview +:description: Mathematical, trigonometric, and hyperbolic functions in Redpanda SQL are designed to perform mathematical calculations and manipulate numeric values. +:page-topic-type: reference + +Mathematical, trigonometric, and hyperbolic functions in Redpanda SQL are designed to perform mathematical calculations and manipulate numeric values. + +== Mathematical functions + +[width="100%",cols="47%,53%",options="header",] +|=== +|Function |Description +|xref:reference:sql/sql-functions/math-functions/abs.adoc[ABS()] |This function returns the absolute value of an argument, regardless of whether it is positive or negative +|xref:reference:sql/sql-functions/math-functions/cbrt.adoc[CBRT()] |This function returns the cube root of a given number +|xref:reference:sql/sql-functions/math-functions/ceil.adoc[CEIL()] |This function rounds up to the nearest positive or negative integer value greater than or equal to the argument +|xref:reference:sql/sql-functions/math-functions/exp.adoc[EXP()] |This function returns the exponential value of a number specified in the argument +|xref:reference:sql/sql-functions/math-functions/floor.adoc[FLOOR()] |This function returns a number rounded down that is less than or equal to the specified argument +|xref:reference:sql/sql-functions/math-functions/greatest.adoc[GREATEST()] |This function extracts the greatest or largest value from a set of values. +|xref:reference:sql/sql-functions/math-functions/least.adoc[LEAST()] |This function returns the least or smallest value in a list of values +|xref:reference:sql/sql-functions/math-functions/ln.adoc[LN()] |This function returns the exponential value of its argument +|xref:reference:sql/sql-functions/math-functions/log.adoc[LOG()] |This function returns the base-10 logarithm or logarithm of the specified base of a given number +|xref:reference:sql/sql-functions/math-functions/power.adoc[POWER()] |This function returns the value of a number raised to the power of another number specified in the arguments +|xref:reference:sql/sql-functions/math-functions/random.adoc[RANDOM()] |This function returns a random number between 0 (inclusive) and 1 (exclusive) +|xref:reference:sql/sql-functions/math-functions/round.adoc[ROUND()] |This function rounds numbers to the nearest integer or to a specified number of decimal places +|xref:reference:sql/sql-functions/math-functions/sign.adoc[SIGN()] |This function returns -1 for negative arguments, 1 for positive arguments or 0 if the argument is 0 +|xref:reference:sql/sql-functions/math-functions/sqrt.adoc[SQRT()] |This function returns the square root of its argument +|xref:reference:sql/sql-functions/math-functions/to-char-from-number.adoc[TO_CHAR() from Number] |Formats a number into a string using a given format +|=== + +== Trigonometric functions + +These trigonometric functions take arguments and return values of type `double precision` and `real`. + +[width="100%",cols="6%,32%,26%,36%",options="header",] +|=== +|*Function* |*Description* |*Syntax* |*Example* +|`acos` |Calculates the inverse cosine of a given argument, where the output is expressed in radians. |`acos(argument)` |`select acos(1);` Returns: `0` +|`acosd` |Calculates the inverse cosine of a given argument, where the output is expressed in degrees. |`acosd(argument)` |`select acosd(0.5);` Returns: `60` +|`asin` |Calculates the inverse sine of a given argument, where the output is expressed in radians. |`asin(argument)` |`select asin(1);` Returns: `1.5707963267948966` +|`asind` |Calculates the inverse sine of a given argument, where the output is expressed in degrees. |`asind(argument)` |`select asind(0.5);` Returns: `30` +|`atan` |Calculates the inverse tangent of a given argument, where the output is expressed in radians. |`atan(argument)` |`select atan(1);` Returns: `0.7853965` +|`atand` |Calculates the inverse tangent of a given argument, where the output is expressed in degrees. |`atand(argument)` |`select atand(1);` Returns: `44.99990469434657` +|`atan2` |Calculates the inverse tangent of y/x, where the output is expressed in radians. |`atan2(y_value, x_value)` `y_value` and `x_value` are in double precision type. |`select atan2(1, 0);` Returns: `1.5707963267948966` +|`atan2d` |Calculates the inverse tangent of y/x, where the output is expressed in degrees. |`atan2d(y_value, x_value)` `y_value` and `x_value` are in double precision type. |`select atan2d(1, 0);` Returns: `90` +|`cos` |Calculates the cosine of a given argument, where the argument is in radians. |`cos(argument)` |`select cos(0);` Returns: `1` +|`cosd` |Calculates the cosine of a given argument, where the argument is in degrees. |`cosd(argument)` |`select cosd(60);` Returns: `0.5000000000000001` +|`cot` |Calculates the cotangent of a given argument, where the argument is in radians. |`cot(argument)` |`select cot(0.5);` Returns: `1.8304877` +|`cotd` |Calculates the cotangent of a given argument, where the argument is in degrees. |`cotd(argument)` |`select cotd(45);` Returns: `1.0000000000000002` +|xref:reference:sql/sql-functions/math-functions/sin.adoc[sin] |Calculates the sine of a given argument, where the argument is in radians. |`sin(argument)` |`select sin(1);` Returns: `0.8414709848078965` +|`sind` |Calculates the sine of a given argument, where the argument is in degrees. |`sind(argument)` |`select sind(30);` Returns: `0.49999999999999994` +|`tan` |Calculates the tangent of a given argument, where the argument is in radians. |`tan(argument)` |`select tan(1);` Returns: `1.5574077246549023` +|`tand` |Calculates the tangent of a given argument, where the argument is in degrees. |`tand(argument)` |`select tand(45);` Returns: `0.9999999999999999` +|=== + +== Hyperbolic functions + +[width="100%",cols="6%,32%,26%,36%",options="header",] +|=== +|*Function* |*Description* |*Syntax* |*Example* +|xref:reference:sql/sql-functions/math-functions/cosh.adoc[cosh] |Calculates the hyperbolic cosine of a given argument. |`cosh(argument)` |`select cosh(1);` Returns: `1.5430806348152437` +|xref:reference:sql/sql-functions/math-functions/sinh.adoc[sinh] |Calculates the hyperbolic sine of a given argument. |`sinh(argument)` |`select sinh(1);` Returns: `1.1752011936438014` +|=== diff --git a/modules/reference/pages/sql/sql-functions/math-functions/least.adoc b/modules/reference/pages/sql/sql-functions/math-functions/least.adoc new file mode 100644 index 000000000..3e4f97964 --- /dev/null +++ b/modules/reference/pages/sql/sql-functions/math-functions/least.adoc @@ -0,0 +1,146 @@ += LEAST +:description: The LEAST() function returns the least or smallest value in a list of values. +:page-topic-type: reference + +The `LEAST()` function returns the least or smallest value in a list of values. It needs at least one argument to work with. If different types are mixed, like a text and a number, it returns an error. + +For example, comparing the greatest value among 4, "`two`", and 9 would result in an error. + +== Syntax + +The syntax for the `LEAST()` function is: + +[source,sql] +---- +LEAST(value_1, [value_n]) +---- + +Where: + +* `value_1`: Represents the first value. +* `value_n`: Represents one or more additional values, separated by commas. + +[NOTE] +==== +*Info:* + * `NULL` values in the list are ignored. + - The result will be `NULL` if all the expressions evaluate to `NULL`. +==== + +== Examples + +These examples show how to use the `LEAST()` function: + +=== Basic usage + +Consider the example: + +[source,sql] +---- +SELECT LEAST(3,5,8,9,10); +---- + +The query will return `3`, the smallest value among the provided values. + +[source,sql] +---- + least +------- + 3 +---- + +=== String comparison + +String comparison is also supported: + +[source,sql] +---- +SELECT LEAST('a','b','c','aa'); +---- + +In this case, the result will be `'a'`, as it is the smallest string. + +[source,sql] +---- + least +------- + a +---- + +=== Handle NULL values + +`NULL` values are ignored when determining the smallest value: + +[source,sql] +---- +SELECT LEAST (5,null,9); +---- + +The result will be the smallest non-NULL value, which is `5`. + +[source,sql] +---- + least +------- + 5 +---- + +=== Negative numbers + +Negative numbers can also be compared: + +[source,sql] +---- +SELECT LEAST (4,-4,-8,8); +---- + +This query will return `-8`, the smallest value among the provided numbers. + +[source,sql] +---- + least +------- + -8 +---- + +=== Use table data + +Suppose there is a table named `grades` containing columns `x`, `y`, and `z`. + +[source,sql] +---- +CREATE TABLE grades ( + name TEXT, + x INT, + y INT, + z INT +); + +INSERT INTO grades (name, x, y, z) +VALUES + ('Jane', 50, 0, 70), + ('Rio', 60, 30, 80), + ('John', 60, 60, 86), + ('Rose', 80, 90, 88), + ('Gary', 100, 80, 90); +---- + +To find the smallest value among these columns, use the query: + +[source,sql] +---- +SELECT *, LEAST(x, y, z) AS least_grade FROM grades; +---- + +This query will add a new column named `least_grade` to the result, displaying the smallest value among columns `x`, `y`, and `z`. + +[source,sql] +---- + name | x | y | z | least_grade +------+-----+----+----+------------- + Jane | 50 | 0 | 70 | 0 + Rio | 60 | 30 | 80 | 30 + John | 60 | 60 | 86 | 60 + Rose | 80 | 90 | 88 | 80 + Gary | 100 | 80 | 90 | 80 +---- diff --git a/modules/reference/pages/sql/sql-functions/math-functions/ln.adoc b/modules/reference/pages/sql/sql-functions/math-functions/ln.adoc new file mode 100644 index 000000000..38d815873 --- /dev/null +++ b/modules/reference/pages/sql/sql-functions/math-functions/ln.adoc @@ -0,0 +1,79 @@ += LN +:description: The LN() function returns the natural logarithm of its argument. +:page-topic-type: reference + +The `LN()` function returns the natural logarithm of its argument. + +[NOTE] +==== +`LN()` does not accept negative numbers or zero. +==== + +== Syntax + +The syntax of the `LN()` function is: + +[source,sql] +---- +LN (x) +---- + +`x`: A positive number, or an expression that evaluates to a positive number. + +== Examples + +=== Basic LN() function + +The following example returns the natural logarithm of `7.87653`: + +[source,sql] +---- +SELECT LN(7.87653); +---- + +The query returns: + +[source,sql] +---- ++-------------+ +| f | ++-------------+ +| 2.0638874 | ++-------------+ +---- + +=== Use LN() function with a table + +This example combines the `LN()` function with a `CREATE TABLE` statement to obtain natural logarithmic values of a specific column: + +. Create a new table named `LNtable` with an integer `initValue` column. ++ +[source,sql] +---- +CREATE TABLE LNtable(initValue int); +INSERT INTO LNtable(initValue) +VALUES (75), (18), (28); +---- + +. Run this query to get the logarithm output of the column: ++ +[source,sql] +---- +SELECT * ,LN(initValue) AS lnValue FROM LNtable; +---- + +. The query returns the initial value and its natural logarithm: ++ +* `initValue`: The original integer values. +* `lnValue`: The natural logarithm values. ++ +[source,sql] +---- ++------------+---------------------------+ +| initValue | lnValue | ++------------+---------------------------+ +| 75 | 4.31748811353631 | +| 18 | 2.8903717578961645 | +| 28 | 3.332204510175204 | ++------------+---------------------------+ +---- diff --git a/modules/reference/pages/sql/sql-functions/math-functions/log.adoc b/modules/reference/pages/sql/sql-functions/math-functions/log.adoc new file mode 100644 index 000000000..f653edec0 --- /dev/null +++ b/modules/reference/pages/sql/sql-functions/math-functions/log.adoc @@ -0,0 +1,173 @@ += LOG +:description: The LOG() function returns the base-10 logarithm or logarithm of the specified base of a given number. +:page-topic-type: reference + +The `LOG()` function returns the base-10 logarithm or logarithm of the specified base of a given number. + +== Syntax + +This example illustrates the syntax of the `LOG()` function: + +[source,sql] +---- +-- base-10 logarithm +LOG(number) + +-- logarithm of number +LOG(base, number) +---- + +Where: + +* `base`: The base number. It must be greater than 0 and not equal to 1. +* `number`: The number logarithm to obtain. It must be a positive number and greater than 0. + +== Examples +=== Base-10 logarithm + +==== Basic usage + +In this case, the `LOG()` function calculates the base-10 logarithm of a specified number. + +[source,sql] +---- +SELECT LOG(2), LOG(2.5); +---- + +The query returns: + +[source,sql] +---- + log | log +--------------------+--------- + 0.3010299956639812 | 0.39794 +---- + +==== Negative value + +In this example, the `LOG()` function is applied to negative numbers. + +[source,sql] +---- +SELECT LOG(-1); +---- + +Any input of negative values returns a `NaN` result. + +[source,sql] +---- + log +----- + NaN +---- + +==== NULL value + +The `LOG()` function will return `NULL` if the argument is `NULL`. + +[source,sql] +---- +SELECT LOG(NULL); +---- + +A null result is returned when an argument passed is null. + +[source,sql] +---- + log +----- +---- + +==== Zero value + +In this example, the `LOG()` takes zero as an argument. + +[source,sql] +---- +SELECT LOG(0); +---- + +The query returns: + +[source,sql] +---- + log +----------- + -Infinity +---- + +=== Logarithm with custom base + +==== Basic usage + +In this case, the `LOG()` function calculates the logarithm of a specified number. + +[source,sql] +---- +SELECT LOG(4, 16), + LOG(0.7, 0.8), + LOG(0.5, 10), + LOG(1, NULL); +---- + +The query returns: + +[source,sql] +---- + log | log | log | log +-----+------------+-----------+----- + 2 | 0.62562156 | -3.321928 | +---- + +==== Use with a table + +Consider a database table called *_data_* with the records: + +[source,sql] +---- +CREATE TABLE data ( + data_column TEXT, + x REAL, + y REAL +); + +INSERT INTO data (data_column, x, y) VALUES +('Data 1', 0.5, 2), +('Data 2', 1, 2), +('Data 3', 5, 2), +('Data 4', 10, 10), +('Data 5', 50, 10); + +SELECT * FROM data; +---- + +[source,sql] +---- + data_column | x | y +-------------+-----+---- + Data 1 | 0.5 | 2 + Data 2 | 1 | 2 + Data 3 | 5 | 2 + Data 4 | 10 | 10 + Data 5 | 50 | 10 +---- + +Use the `LOG()` function to calculate the logarithm of column *_x_* (as a base) and column _y_ (as a number): + +[source,sql] +---- +SELECT *, LOG(y, x) AS LOG_Value FROM data; +---- + +The query returns: + +[source,sql] +---- + data_column | x | y | log_value +-------------+-----+----+----------- + Data 1 | 0.5 | 2 | -1 + Data 2 | 1 | 2 | 0 + Data 3 | 5 | 2 | 2.321928 + Data 4 | 10 | 10 | 1 + Data 5 | 50 | 10 | 1.69897 +---- diff --git a/modules/reference/pages/sql/sql-functions/math-functions/power.adoc b/modules/reference/pages/sql/sql-functions/math-functions/power.adoc new file mode 100644 index 000000000..8ed02bdb1 --- /dev/null +++ b/modules/reference/pages/sql/sql-functions/math-functions/power.adoc @@ -0,0 +1,93 @@ += POWER +:description: The POWER() function calculates the value of a number raised to the power of another number specified in the arguments. +:page-topic-type: reference + +The `POWER()` function calculates the value of a number raised to the power of another number specified in the arguments. + +== Syntax + +This example shows the syntax of the `POWER()` function: + +[source,sql] +---- +POWER(a,b) +---- + +Where: + +* `a`: The base number. +* `b`: The exponent to which the base number is raised. + +== Examples +=== Basic usage + +In this case, the `POWER()` function calculates the result of raising one number to the power of another. + +[source,sql] +---- +SELECT POWER(3, 4) AS "Example 1", + POWER(7, 3) AS "Example 2"; +---- + +The query returns: + +[source,sql] +---- + Example 1 | Example 2 +-----------+----------- + 81 | 343 +---- + +=== Use `POWER()` with negative values + +In this case, the `POWER()` function is applied to negative numbers. + +[source,sql] +---- +SELECT POWER(-4, -5), POWER(-1, -2), POWER(-6, -7); +---- + +The query returns: + +[source,sql] +---- + power | power | power +-------+-------+------- + -1024 | 1 | 0 +---- + +=== Use `POWER()` with floating-point numbers + +In this example, use the `POWER()` function to calculate 2.5 raised to the power of 3.0. + +[source,sql] +---- +SELECT POWER(2.5, 3.0) AS power_result; +---- + +The result, 15.625, is the value obtained by raising 2.5 to the third power. + +[source,sql] +---- + power_result +-------------- + 15.625 +---- + +=== Zero to the power of zero + +This case shows that 0 expression raised to the power of 0 returns 1. + +[source,sql] +---- +SELECT POWER(0, 0); +---- + +The query returns: + +[source,sql] +---- + power +------- + 1 +---- diff --git a/modules/reference/pages/sql/sql-functions/math-functions/random.adoc b/modules/reference/pages/sql/sql-functions/math-functions/random.adoc new file mode 100644 index 000000000..74e96b67a --- /dev/null +++ b/modules/reference/pages/sql/sql-functions/math-functions/random.adoc @@ -0,0 +1,82 @@ += RANDOM +:description: The RANDOM() function in Redpanda SQL generates a random number within a defined range. +:page-topic-type: reference + +The `RANDOM()` function in Redpanda SQL generates a random number within a defined range. By default, the range is between 0 (inclusive) and 1 (exclusive), resulting in a value greater than or equal to 0 and less than 1. + +== Syntax + +The syntax for generating a random integer or floating-point number using the `RANDOM()` function is: + +[source,sql] +---- +RANDOM() +---- + +[NOTE] +==== +There are no parameters or arguments for the `RANDOM()` function. +==== + +== Examples + +=== Generate a random number + +The RANDOM() function generates a random number greater than or equal to zero but less than one by default. Use this syntax to retrieve a random number: + +[source,sql] +---- +SELECT RANDOM(); +---- + +The result is a random number greater than 0 and less than 1. However, it will never return the maximum value of 1. + +[source,sql] +---- ++-----------------------+ +| f | ++-----------------------+ +| 0.9122627193276355 | ++-----------------------+ +---- + +=== Generate a random decimal number within a range + +To generate a random decimal number between two values: + +[source,sql] +---- +SELECT RANDOM()*(b-a)+a; +---- + +Where: + +* *"`a`"* represents the lower bound of the range. +* *"`b`"* represents the upper bound of the range. + +The return value will be a random floating-point number greater than or equal to a and less than b. + +*Example* + +To generate a random decimal number greater than or equal to 10 and less than 25: + +[source,sql] +---- +SELECT RANDOM()*(25 - 10)+10; +---- + +This example shows how to retrieve a random number: + +[source,sql] +---- ++-----------------------+ +| f | ++-----------------------+ +| 18.156098711616043 | ++-----------------------+ +---- + +[WARNING] +==== +It is important to note that the function will never return the maximum value of b. +==== diff --git a/modules/reference/pages/sql/sql-functions/math-functions/round.adoc b/modules/reference/pages/sql/sql-functions/math-functions/round.adoc new file mode 100644 index 000000000..fd8d1bec7 --- /dev/null +++ b/modules/reference/pages/sql/sql-functions/math-functions/round.adoc @@ -0,0 +1,63 @@ += ROUND +:description: The ROUND() function rounds numbers using round half to even method (bankers rounding). +:page-topic-type: reference + +The `ROUND()` function rounds numbers using round half to even method (bankers rounding). + +== Syntax + +[source,sql] +---- +ROUND(number) +ROUND(number, scale) +---- + +== Arguments + +* `number`: The number to round. It can be positive, negative, or zero, and it can be an xref:reference:sql/sql-data-types/numeric-type/numeric.adoc[Integer] or a xref:reference:sql/sql-data-types/numeric-type/numeric.adoc[Double Precision]. +* `scale`: Optional. An integer specifying the number of decimal places to round to. When omitted, the function rounds to the nearest integer. A negative scale rounds to the left of the decimal point (for example, `ROUND(1234, -2)` returns `1200`). + +== Examples + +=== Round to integer + +In this example, the function rounds decimal numbers to integers: + +[source,sql] +---- +SELECT + round(28.11) AS "round(28.11)", + round(12.51) AS "round(12.51)", + round(-9.11) AS "round(-9.11)", + round(102.5) AS "round(102.5)", + round(101.5) AS "round(101.5)", + round(-40.51) AS "round(-40.51)"; +---- + +The query will return the nearest integer for all provided values. + +[source,sql] +---- + round(28.11) | round(12.51) | round(-9.11) | round(102.5) | round(101.5) | round(-40.51) +--------------+--------------+--------------+--------------+---------------+--------------- + 28 | 13 | -9 | 102 | 102 | -41 +---- + +=== Round to a specific number of decimal places + +Use the two-argument form to specify the number of decimal places: + +[source,sql] +---- +SELECT + round(3.14159, 2) AS "round(3.14159, 2)", + round(123.456, 1) AS "round(123.456, 1)", + round(99.995, 2) AS "round(99.995, 2)"; +---- + +[source,sql] +---- + round(3.14159, 2) | round(123.456, 1) | round(99.995, 2) +-------------------+-------------------+------------------ + 3.14 | 123.5 | 100 +---- diff --git a/modules/reference/pages/sql/sql-functions/math-functions/sign.adoc b/modules/reference/pages/sql/sql-functions/math-functions/sign.adoc new file mode 100644 index 000000000..832285e5b --- /dev/null +++ b/modules/reference/pages/sql/sql-functions/math-functions/sign.adoc @@ -0,0 +1,105 @@ += SIGN +:description: The SIGN() function returns a sign of an argument. +:page-topic-type: reference + +The `SIGN()` function returns a sign of an argument. The returned values are -1 if the argument is less than zero, 1 if the argument is greater than zero, 0 if the argument is equal to zero. + +== Syntax + +The syntax for the `SIGN()` function is: + +[source,sql] +---- +SIGN(x) +---- + +The `SIGN()` function requires one argument: + +* `x`: An expression that evaluates to a number. + +== Examples + +=== Sign of a number + +This example demonstrates how the `SIGN()` function can be used to obtain the sign of a number: + +[source,sql] +---- +SELECT + SIGN(0.1) AS "SIGN(0.1)", + SIGN(999) AS "SIGN(999)", + SIGN(0) AS "SIGN(0)", + SIGN(-0) AS "SIGN(-0)"; +---- + +The query will return the signs of the passed arguments: + +[source,sql] +---- + SIGN(0.1) | SIGN(999) | SIGN(0) | SIGN(-0) +-----------+-----------+---------+---------- + 1 | 1 | 0 | 0 +---- + +Note: `-0` is accepted as an argument and is equal to zero + +=== SIGN() function with an expression + +This example demonstrates how to use the `SIGN()` function with an expression: + +[source,sql] +---- +SELECT SIGN(100 - 200); +---- + +will return the sign of the expression evaluation: + +[source,sql] +---- + sign +------ + -1 +------ +---- + +=== Use the SIGN() function with a table + +This example demonstrates how to use the `SIGN()` function with a table to obtain the absolute values of all numbers in a specific column: + +. Create a table signTable containing an *_value_* column with some positive, negative and equal to zero values: ++ +[source,sql] +---- +CREATE TABLE signTable(value float); + +INSERT INTO signTable(value) +VALUES +(1000), +(-200), +(0), +(0.22), +(-12.3), +(-0.0); +---- + +. Use this query to find the sign of all inserted values: ++ +[source,sql] +---- +SELECT value, SIGN(value) AS sign +FROM signTable; +---- + +. Result: ++ +[source,sql] +---- + value | sign +-------+------ + 1000 | 1 + -200 | -1 + 0 | 0 + 0.22 | 1 + -12.3 | -1 + -0 | 0 +---- diff --git a/modules/reference/pages/sql/sql-functions/math-functions/sin.adoc b/modules/reference/pages/sql/sql-functions/math-functions/sin.adoc new file mode 100644 index 000000000..a2020100d --- /dev/null +++ b/modules/reference/pages/sql/sql-functions/math-functions/sin.adoc @@ -0,0 +1,134 @@ += SIN +:description: SIN() is a numeric function that returns the trigonometric sine value of a specified angle in radians. +:page-topic-type: reference + +`SIN()` is a numeric function that returns the trigonometric sine value of a specified angle in radians. + +== Syntax + +The syntax of the `SIN()` function is: + +[source,sql] +---- +SIN (x) +---- + +The `SIN()` function requires one argument: + +`x`: A positive or a negative angle (or an expression that evaluates to an angle). + +== Examples + +=== Sine a positive value + +This example uses the `SIN()` function with a positive angle as the argument. + +[source,sql] +---- +SELECT SIN(5); +---- + +This returns the sine value of 5. + +[source,sql] +---- ++-----------------------+ +| f | ++-----------------------+ +| -0.9589242746631385 | ++-----------------------+ +---- + +=== Sine a negative value + +This example shows the `SIN()` function with a negative angle as the argument: + +[source,sql] +---- +SELECT SIN(-3); +---- + +The query returns: + +[source,sql] +---- ++----------------------+ +| f | ++----------------------+ +| -0.1411200080598672 | ++----------------------+ +---- + +=== Sine a fraction value + +This example shows the `SIN()` function with a fractional value as the argument: + +[source,sql] +---- +SELECT SIN(5.8732); +---- + +The query returns: + +[source,sql] +---- ++----------------------+ +| f | ++----------------------+ +| -0.3985959081271079 | ++----------------------+ +---- + +=== Sine with an expression + +The `SIN()` function can also include an expression: + +[source,sql] +---- +SELECT sin(8.5 * 2.3); +---- + +The query returns: + +[source,sql] +---- ++-----------------------+ +| f | ++-----------------------+ +| 0.6445566903363104 | ++-----------------------+ +---- + +=== Use the `SIN()` function with a table + +This example combines the `SIN()` function with a `CREATE TABLE` statement to obtain the sine values of a specific column: + +. Create a new table named *sineTable* containing the *initialValue* column. Input some values with the negative and positive angles into the column. ++ +[source,sql] +---- +CREATE TABLE sineTable(initialValue int); +INSERT INTO sineTable(initialValue) +VALUES (-5),(18), (0),(-27); +---- + +. Run this query to get the sine value output: ++ +[source,sql] +---- +SELECT * ,SIN(initialValue) AS sinValue FROM sineTable; +---- + +. The final result will have the *initialValue* column with the source value and the *sinValue* column with their calculated sine values. ++ +[source,sql] +---- ++---------------+-------------------------------+ +| initialvalue | sinvalue | ++---------------+-------------------------------+ +| -5 | 0.9589242746631385 | +| 18 | -0.7509872467716762 | +| 0 | 0 | +| -27 | -0.956375928404503 | ++---------------+-------------------------------+ +---- diff --git a/modules/reference/pages/sql/sql-functions/math-functions/sinh.adoc b/modules/reference/pages/sql/sql-functions/math-functions/sinh.adoc new file mode 100644 index 000000000..d1c2fed27 --- /dev/null +++ b/modules/reference/pages/sql/sql-functions/math-functions/sinh.adoc @@ -0,0 +1,82 @@ += SINH +:description: The SINH() function returns the hyperbolic sine of a specified numeric argument. +:page-topic-type: reference + +The `SINH()` function returns the hyperbolic sine of a specified numeric argument. + +== Syntax + +[source,sql] +---- +SINH(x) +---- + +== Arguments + +* `x`: A positive or negative number of type `real` or `double precision`. + +== Return type + +Same as the input type (`real` or `double precision`). + +== Examples + +=== Positive value + +[source,sql] +---- +SELECT SINH(1); +---- + +[source,sql] +---- + sinh +-------------------- + 1.1752011936438014 +(1 row) +---- + +=== Negative value + +[source,sql] +---- +SELECT SINH(-1); +---- + +[source,sql] +---- + sinh +--------------------- + -1.1752011936438014 +(1 row) +---- + +=== Zero + +[source,sql] +---- +SELECT SINH(0); +---- + +[source,sql] +---- + sinh +------ + 0 +(1 row) +---- + +=== Expression argument + +[source,sql] +---- +SELECT SINH(2.5 * 0.4); +---- + +[source,sql] +---- + sinh +-------------------- + 1.1752011936438014 +(1 row) +---- diff --git a/modules/reference/pages/sql/sql-functions/math-functions/sqrt.adoc b/modules/reference/pages/sql/sql-functions/math-functions/sqrt.adoc new file mode 100644 index 000000000..5a047ba11 --- /dev/null +++ b/modules/reference/pages/sql/sql-functions/math-functions/sqrt.adoc @@ -0,0 +1,118 @@ += SQRT +:description: The SQRT() function returns the square root of a given positive number. +:page-topic-type: reference + +The `SQRT()` function returns the square root of a given positive number. + +== Syntax + +The syntax for the `SQRT()` function in Redpanda SQL is: + +[source,sql] +---- +SQRT(x) +---- + +The `SQRT()` function requires one argument: + +* `x`: A positive number or an expression that evaluates to a positive number. + +== Examples + +=== SQRT() a positive value + +This example demonstrates how to find the square root of a positive integer with `SQRT()`: + +[source,sql] +---- +SELECT SQRT(81); +---- + +The returned result: + +[source,sql] +---- ++-----+ +| f | ++-----+ +| 9 | ++-----+ +---- + +=== SQRT() with an expression + +This example shows how to use the `SQRT()` function to find the square root of the result of an expression: + +[source,sql] +---- +SELECT SQRT(60 + 4); +---- + +The result of this statement is the square root of 64: + +[source,sql] +---- ++-----+ +| f | ++-----+ +| 8 | ++-----+ +---- + +=== SQRT() with double precision result + +In addition to integers, Redpanda SQL also supports calculating square roots with floating-point numbers as the outcome, as shown in this example: + +[source,sql] +---- +SELECT SQRT(70); +---- + +The output of this statement is 8.3666, which is the square root of 70 with double precision: + +[source,sql] +---- ++----------+ +| f | ++----------+ +| 8.3666 | ++----------+ +---- + +=== SQRT() a negative number + +This example demonstrates how attempting to use the `SQRT()` function with a negative value returns an error: + +[source,sql] +---- +SELECT SQRT(-25); +---- + +As the `SQRT()` function only accepts positive numbers, it returns a *_NaN (Not a Number)_* result for the square root of -25: + +[source,sql] +---- ++-------+ +| f | ++-------+ +| NaN | ++-------+ +---- + +=== SQRT operator (`|/(x)`) + +This example uses the SQRT operator (`|/(x)`) to calculate the square root of a number: + +[source,sql] +---- +SELECT |/(169) AS sqrt_operator; +---- + +This example calculates the square root of 169 using the SQRT operator. The result of this query will be: + +[source,sql] +---- + sqrt_operator +--------------- + 13 +---- diff --git a/modules/reference/pages/sql/sql-functions/math-functions/to-char-from-number.adoc b/modules/reference/pages/sql/sql-functions/math-functions/to-char-from-number.adoc new file mode 100644 index 000000000..0e385e94f --- /dev/null +++ b/modules/reference/pages/sql/sql-functions/math-functions/to-char-from-number.adoc @@ -0,0 +1,136 @@ += TO_CHAR from Number +:description: The TO_CHAR function formats a number into a string using a given format. +:page-topic-type: reference + +The `TO_CHAR` function formats a number into a string using a given format. + +== Syntax + +The syntax for this function is: + +[source,sql] +---- +TO_CHAR(value, format_string) +---- + +Parameters in the syntax include: + +* `value`: Number to format as a string. +* `format`: The format of the input string. + +== Format + +The format string supports these template patterns (case-insensitive): + +[width="100%",cols="16%,84%",options="header",] +|=== +|*Pattern* |*Description* +|`9` |Digit position (may be dropped if insignificant) +|`0` |Digit position (never dropped) +|`.` |Decimal point +|`,` |Group (thousands) separator +|`D` |Decimal point +|`G` |Group separator +|`S` |Plus/minus sign directly before or after a number +|`PL` |Plus sign in the specified position (for negative numbers) +|`MI` |Minus sign in specified position (for positive numbers) +|`SG` |Plus/minus sign in the specified position. +|=== + +=== Limitations + +* All text inside double quote `"\{text}"` will not be considered a pattern. +* The quote character `""` will not appear in the result string. +* Any text that does not match any pattern is preserved in the result string. + +== Examples + +=== Format with leading zeros + +The query formats 123.456 with leading zeros using the pattern '`00000.00000`'. + +[source,sql] +---- +SELECT TO_CHAR(123.456, '00000.00000'); +---- + +The query returns: + +[source,sql] +---- + to_char +-------------- + 00123.45600 +---- + +=== Format with variable length + +The query formats the number 123.456 with a variable-length pattern '`99999.99999`'. + +[source,sql] +---- +SELECT TO_CHAR(123.456, '99999.99999'); +---- + +The query returns: + +[source,sql] +---- + to_char +-------------- + 123.45600 +---- + +=== Format with group + +The query formats the number 123456 with grouping separators using the pattern '`9,999,999,999`'. + +[source,sql] +---- +SELECT TO_CHAR(123456, '9,999,999,999'); +---- + +The query returns: + +[source,sql] +---- + to_char +---------------- + 123,456 +---- + +=== Format with negative number + +The query formats the number -123 with a custom pattern including the sign. + +[source,sql] +---- +SELECT TO_CHAR(-123, '"Number formatted with pattern:000S":{000S}'); +---- + +The output shows the custom-formatted number. + +[source,sql] +---- + to_char +------------------------------------------- + Number formatted with pattern:000S:{123-} +---- + +=== Format with sign + +The query formats the number -123.456 with a custom pattern including the sign and separated integer. + +[source,sql] +---- +SELECT TO_CHAR(-123.456, '"Sing is: "SG" integer part is: "999", mantissa part is: ".999'); +---- + +The query returns: + +[source,sql] +---- + to_char +--------------------------------------------------------- + Sing is: - integer part is: 123, mantissa part is: .456 +---- diff --git a/modules/reference/pages/sql/sql-functions/other-functions/coalesce.adoc b/modules/reference/pages/sql/sql-functions/other-functions/coalesce.adoc new file mode 100644 index 000000000..3c9bbe870 --- /dev/null +++ b/modules/reference/pages/sql/sql-functions/other-functions/coalesce.adoc @@ -0,0 +1,191 @@ += coalesce() +:description: The COALESCE() function returns the first non-NULL argument from a list of arguments. +:page-topic-type: reference + +The `COALESCE()` function returns the first non-NULL argument from a list of arguments. After finding the first non-NULL argument, the function stops evaluating the remaining arguments. + +[NOTE] +==== +If all arguments are NULL, `COALESCE()` returns NULL. +==== + +== Syntax + +The syntax for this function is: + +[source,sql] +---- +COALESCE (argument_1, argument_2, …); +---- + +Key points from the syntax: + +* `COALESCE()` requires a minimum of two inputs. +* It can take an unlimited number of arguments. +* Evaluation occurs sequentially from left to right, stopping at the first non-null value. + +== Examples + +Here are some examples to illustrate the application of `COALESCE()`: + +=== Return the first non-null value + +In this example, a set of values is provided. The `COALESCE()` function returns the first non-null value from this set. + +[source,sql] +---- +SELECT COALESCE(9, 3, 8, 7, 1); +---- + +The result will be `9`, the first value without NULL among the provided options. + +[source,sql] +---- + coalesce +---------- + 9 +---- + +=== Handle NULL value as the last argument + +Include NULL as the final argument and check the query output. + +[source,sql] +---- +Select COALESCE(3,4,5,9,10,NULL); +---- + +The function output is `3` because it returns the first non-null value. + +[source,sql] +---- + coalesce +---------- + 3 +---- + +=== Handle NULL value as the first argument + +Consider NULL as the first argument in this example: + +[source,sql] +---- +Select COALESCE(NULL,1,5,7,9,2); +---- + +The query returns `1`, as it is the first non-null value of the argument. + +[source,sql] +---- + coalesce +---------- + 1 +---- + +=== Handle multiple NULL values + +In this query, NULL appears in the first, second, fourth, and last positions: + +[source,sql] +---- +Select COALESCE(NULL, NULL ,3, NULL, 7,9,4,5, NULL); +---- + +The `COALESCE()` function ignores the first two NULLs and returns the first non-null value, `3`. It does not process the subsequent NULL values. + +[source,sql] +---- + coalesce +---------- + 3 +---- + +=== Handle all NULL values + +Assume that the given values are entirely composed of nulls. + +[source,sql] +---- +Select COALESCE(NULL, NULL ,NULL, NULL); +---- + +In this case, the `COALESCE()` function returns an empty value (NULL). + +[source,sql] +---- + coalesce +---------- +---- + +=== `COALESCE()` with table data + +Consider the `employee_absent` table, which comprises a mix of NULL and non-null values: + +[source,sql] +---- +CREATE TABLE employee_absent ( + emp_name TEXT, + emp_dept TEXT, + absent TEXT +); + +INSERT INTO employee_absent (emp_name, emp_dept, absent) +VALUES + ('Alice', 'Finance', 'absent'), + ('Bob', 'Operations', 'absent'), + ('Carol', 'Finance', 'absent'), + ('David', 'HR', NULL), + ('Emily', 'HR', NULL); +---- + +Use the `SELECT` statement to display all the records: + +[source,sql] +---- +SELECT * FROM employee_absent; +---- + +[source,sql] +---- + emp_name | emp_dept | absent +----------+------------+-------- + Alice | Finance | absent + Bob | Operations | absent + Carol | Finance | absent + David | HR | + Emily | HR | +---- + +The following query uses the `COALESCE()` function on the `absent` column. It retrieves names and absences (with `out of office` for NULL values) for each employee. + +[source,sql] +---- +SELECT emp_name, COALESCE(absent, 'out of office') AS DisplayAbsent FROM employee_absent; +---- + +[source,sql] +---- + emp_name | displayabsent +----------+--------------- + Alice | absent + Bob | absent + Carol | absent + David | out of office + Emily | out of office +---- + +=== Error output in `COALESCE()` + +When specifying arguments with different datatypes, they should be convertible. + +[source,sql] +---- +Select Coalesce ('x',NULL,1); +---- + +If the datatypes cannot be converted, the `COALESCE()` function generates an error. + +[source,sql] +---- +ERROR: invalid input syntax for type integer: "x" +---- diff --git a/modules/reference/pages/sql/sql-functions/other-functions/col-description.adoc b/modules/reference/pages/sql/sql-functions/other-functions/col-description.adoc new file mode 100644 index 000000000..ea518193c --- /dev/null +++ b/modules/reference/pages/sql/sql-functions/other-functions/col-description.adoc @@ -0,0 +1,27 @@ += col_description() +:description: The link:https://www.postgresql.org/docs/current/functions-info.html#FUNCTIONS-INFO-COMMENT[col_description()^] is a comment information function that +:page-topic-type: reference + +The link:https://www.postgresql.org/docs/current/functions-info.html#FUNCTIONS-INFO-COMMENT[col_description()^] is a comment information function that retrieves the comment associated with a specified table column. + +== Syntax + +The syntax for this function is: + +.... +col_description (table_oid, column_number) → NULL +.... + +== Parameters + +* link:https://www.postgresql.org/docs/current/datatype-oid.html[table_oid^]: specifies the object identifier (OID) of the table containing the column from which to retrieve the comment +* link:https://www.postgresql.org/docs/current/datatype-numeric.html#DATATYPE-INT[column_number^]: indicates the ordinal position of the column within the table (starting from 1 for the first column) + +[NOTE] +==== +It is important to note that the column number must be provided as an object identifier (OID), which can be achieved by casting the table name to `regclass` +==== + +== Restrictions + +* This function always returns `NULL` if there are no parameters specified. diff --git a/modules/reference/pages/sql/sql-functions/other-functions/current-database.adoc b/modules/reference/pages/sql/sql-functions/other-functions/current-database.adoc new file mode 100644 index 000000000..d03d11c75 --- /dev/null +++ b/modules/reference/pages/sql/sql-functions/other-functions/current-database.adoc @@ -0,0 +1,34 @@ += current_database() +:description: The link:https://www.postgresql.org/docs/current/functions-info.html#FUNCTIONS-INFO-SESSION[current_database()^] is a session information function that +:page-topic-type: reference + +The link:https://www.postgresql.org/docs/current/functions-info.html#FUNCTIONS-INFO-SESSION[current_database()^] is a session information function that returns the current database's name. + +== Syntax + +The syntax for this function is: + +[source,sql] +---- +SELECT current_database(); +---- + +== Examples + +This example shows how to obtain the name of the currently connected database: + +[source,sql] +---- +SELECT current_database(); +---- + +The query returns the output: + +[source,sql] +---- ++------------+ +| f       | ++------------+ +| Oxla   | ++------------+ +---- diff --git a/modules/reference/pages/sql/sql-functions/other-functions/current-schema.adoc b/modules/reference/pages/sql/sql-functions/other-functions/current-schema.adoc new file mode 100644 index 000000000..7d5a152dd --- /dev/null +++ b/modules/reference/pages/sql/sql-functions/other-functions/current-schema.adoc @@ -0,0 +1,45 @@ += current_schema() +:description: The link:https://www.postgresql.org/docs/current/functions-info.html#FUNCTIONS-INFO-SESSION[current_schema()^] is a session information function that r +:page-topic-type: reference + +The link:https://www.postgresql.org/docs/current/functions-info.html#FUNCTIONS-INFO-SESSION[current_schema()^] is a session information function that returns the name of the first existing schema. + +== Syntax + +There are two available syntax versions of `current_schema()` function: + +.Version 1 +[source,sql] +---- +SELECT current_schema(); +---- + +.Version 2 +[source,sql] +---- +SELECT current_schema; +---- +[NOTE] +==== +Returns `NULL` if none of the schemas from `search_path` exist +==== + +== Examples + +This example shows how to get the current schema name using this function: + +[source,sql] +---- +SELECT current_schema(); +---- + +Example output: + +[source,sql] +---- ++------------+ +| f | ++------------+ +| public | ++------------+ +---- diff --git a/modules/reference/pages/sql/sql-functions/other-functions/generate-series.adoc b/modules/reference/pages/sql/sql-functions/other-functions/generate-series.adoc new file mode 100644 index 000000000..b89735a55 --- /dev/null +++ b/modules/reference/pages/sql/sql-functions/other-functions/generate-series.adoc @@ -0,0 +1,115 @@ += GENERATE_SERIES +:description: The GENERATE_SERIES function generates a set of values from start to stop with an optional step increment. +:page-topic-type: reference + +The `GENERATE_SERIES` function generates a set of values from a start value to a stop value with an optional step increment. Use it as a table function in the `FROM` clause. + +== Syntax + +[source,sql] +---- +GENERATE_SERIES(start, stop) +GENERATE_SERIES(start, stop, step) +---- + +== Arguments + +* `start`: The first value in the series. Type: `BIGINT`. +* `stop`: The last value in the series (inclusive). Type: `BIGINT`. +* `step`: Optional. The increment between values. Defaults to `1`. Use a negative value to generate a descending series. Type: `BIGINT`. + +If `step` is positive and `start` is greater than `stop`, an empty set is returned. If `step` is negative and `start` is less than `stop`, an empty set is returned. + +== Examples + +=== Generate an ascending series + +[source,sql] +---- +SELECT * FROM GENERATE_SERIES(1, 5); +---- + +[source,sql] +---- + generate_series +----------------- + 1 + 2 + 3 + 4 + 5 +(5 rows) +---- + +=== Generate a series with a custom step + +[source,sql] +---- +SELECT * FROM GENERATE_SERIES(0, 10, 2); +---- + +[source,sql] +---- + generate_series +----------------- + 0 + 2 + 4 + 6 + 8 + 10 +(6 rows) +---- + +=== Generate a descending series + +[source,sql] +---- +SELECT * FROM GENERATE_SERIES(5, 1, -1); +---- + +[source,sql] +---- + generate_series +----------------- + 5 + 4 + 3 + 2 + 1 +(5 rows) +---- + +=== Filter a series with WHERE + +[source,sql] +---- +SELECT * FROM GENERATE_SERIES(1, 10) WHERE generate_series % 2 = 0; +---- + +[source,sql] +---- + generate_series +----------------- + 2 + 4 + 6 + 8 + 10 +(5 rows) +---- + +=== Aggregate a series + +[source,sql] +---- +SELECT SUM(generate_series) FROM GENERATE_SERIES(1, 100); +---- + +[source,sql] +---- + sum +------ + 5050 +(1 row) +---- diff --git a/modules/reference/pages/sql/sql-functions/other-functions/has-schema-privilege.adoc b/modules/reference/pages/sql/sql-functions/other-functions/has-schema-privilege.adoc new file mode 100644 index 000000000..6f1e18e13 --- /dev/null +++ b/modules/reference/pages/sql/sql-functions/other-functions/has-schema-privilege.adoc @@ -0,0 +1,70 @@ += has_schema_privilege() +:description: The has_schema_privilege() function checks whether the current user has specific privileges on a schema. +:page-topic-type: reference + +The link:https://www.postgresql.org/docs/current/functions-info.html#FUNCTIONS-INFO-ACCESS[`has_schema_privilege()`^] function is an access privilege inquiry function that checks whether the current user has specific privileges on a schema. + +== Syntax + +The `has_schema_privilege` function has two available syntax versions: + +[source,sql] +---- +SELECT has_schema_privilege('user', 'schema', 'privilege'); +---- + +[source,sql] +---- +SELECT has_schema_privilege('schema', 'privilege'); +---- + +No matter which syntax version you choose, the `has_schema_privilege()` function always returns `TRUE (t)`. + +== Parameters + +* `schema`: Name of the schema to check privileges for (can be any string value or string columns from other tables). +* `user`: Name of the user who has the privileges (can be any string value). +* `privilege`: Specifies the specific privilege to check for in the schema. The function currently supports `create` and `usage`. + +[NOTE] +==== +The comparison for the `privilege` is case-insensitive, so you can use lowercase or uppercase notation for the privilege name. +==== + +== Examples + +=== Check for CREATE privilege + +This example uses the `has_schema_privilege()` function to determine whether the current user has the `create` privilege on a schema named `public`: + +[source,sql] +---- +SELECT has_schema_privilege('public', 'create'); +---- + +The query returns `TRUE`, which means that the current user has a `create` privilege on the `public` schema. + +[source,sql] +---- + has_schema_privilege +---------------------- + t +---- + +=== Check for USAGE privilege + +Use the `has_schema_privilege()` function to check for the `usage` privilege on a schema. For example, to check if the current user can create objects in the "`*public*`" schema, run: + +[source,sql] +---- +SELECT has_schema_privilege('cahyo', 'public', 'USAGE'); +---- + +The query returns `TRUE`, which means the current user has `usage` privilege on the `public` schema. + +[source,sql] +---- + has_schema_privilege +---------------------- + t +---- diff --git a/modules/reference/pages/sql/sql-functions/other-functions/index.adoc b/modules/reference/pages/sql/sql-functions/other-functions/index.adoc new file mode 100644 index 000000000..663c440cd --- /dev/null +++ b/modules/reference/pages/sql/sql-functions/other-functions/index.adoc @@ -0,0 +1,31 @@ += Other functions +:description: Reference for other Redpanda SQL functions, including coalesce, nullif, and PostgreSQL system information functions. +:page-topic-type: reference + +Besides xref:reference:sql/sql-functions/math-functions/index.adoc[math], xref:reference:sql/sql-functions/aggregate-functions/index.adoc[aggregate], xref:reference:sql/sql-functions/window-functions/index.adoc[window], xref:reference:sql/sql-functions/string-functions/index.adoc[string], xref:reference:sql/sql-functions/timestamp-functions/index.adoc[timestamp], and xref:reference:sql/sql-functions/json-functions/index.adoc[JSON] functions, Redpanda SQL also supports these functions: + +[cols="46%,54%",options="header",] +|=== +|Function |Description +|xref:reference:sql/sql-functions/other-functions/coalesce.adoc[coalesce()] |Returns the first argument that is not NULL, while the remaining arguments from the first non-null argument are not evaluated. +|xref:reference:sql/sql-functions/other-functions/current-database.adoc[current_database()] |Returns the current database's name. +|xref:reference:sql/sql-functions/other-functions/current-schema.adoc[current_schema()] |Returns the schema's name (first in the search path). +|xref:reference:sql/sql-functions/other-functions/has-schema-privilege.adoc[has_schema_privilege()] |Checks whether the current user has specific privileges on a schema. +|xref:reference:sql/sql-functions/other-functions/nullif.adoc[nullif()] |Replaces a given value with NULL if it matches a specific criterion. +|xref:reference:sql/sql-functions/other-functions/pg-get-expr.adoc[pg_get_expr()] |Retrieves the internal form of an individual expression (such as the default value for a column). +|xref:reference:sql/sql-functions/other-functions/pg-total-relation-size.adoc[pg_total_relation_size()] |Retrieves the size of a table. +|xref:reference:sql/sql-functions/other-functions/pg-typeof.adoc[pg_typeof()] |Retrieves the data type of any given value. +|xref:reference:sql/sql-functions/other-functions/pg-encoding-to-char.adoc[pg_encoding_to_char()] |Converts an encoding internal identifier to a human-readable name. +|xref:reference:sql/sql-functions/other-functions/pg-get-indexdef.adoc[pg_get_indexdef()] |Reconstructs the PostgreSQL command used to retrieve the definition of a specified index. +|xref:reference:sql/sql-functions/other-functions/pg-get-userbyid.adoc[pg_get_userbyid()] |Retrieves the name of a user (role) given its unique identifier (OID). +|xref:reference:sql/sql-functions/other-functions/pg-relation-is-publishable.adoc[pg_relation_is_publishable()] |Determines whether a specified relation (table) can be published in a publication. +|xref:reference:sql/sql-functions/other-functions/pg-size-pretty.adoc[pg_size_pretty()] |Converts sizes in bytes into a human-readable format. +|xref:reference:sql/sql-functions/other-functions/pg-table-size.adoc[pg_table_size()] |Retrieves the size of a specific table, including its associated storage components but excluding indexes. +|xref:reference:sql/sql-functions/other-functions/pg-table-is-visible.adoc[pg_table_is_visible()] |Checks whether a specified table (or other database object) is visible in the current schema search path. +|xref:reference:sql/sql-functions/other-functions/pg-get-constraintdef.adoc[pg_get_constraintdef()] |Retrieves the definition of a specific constraint in a human-readable format. +|xref:reference:sql/sql-functions/other-functions/pg-get-statisticsobjdef-columns.adoc[pg_get_statisticsobjdef_columns()] |Retrieves the definitions of columns associated with a specified statistics object. +|xref:reference:sql/sql-functions/other-functions/obj-description.adoc[obj_description()] |Returns the comment associated with a specific database object. +|xref:reference:sql/sql-functions/other-functions/col-description.adoc[col_description()] |Retrieves the comment associated with a specified table column based on its name. +|xref:reference:sql/sql-functions/other-functions/shobj-description.adoc[shobj_description()] |Retrieves the comment associated with a shared database object. +|xref:reference:sql/sql-functions/other-functions/pg-backend-pid.adoc[pg_backend_pid()] |Returns the process ID (PID) of the node handling the current session. +|=== diff --git a/modules/reference/pages/sql/sql-functions/other-functions/nullif.adoc b/modules/reference/pages/sql/sql-functions/other-functions/nullif.adoc new file mode 100644 index 000000000..d7b160cc7 --- /dev/null +++ b/modules/reference/pages/sql/sql-functions/other-functions/nullif.adoc @@ -0,0 +1,194 @@ += nullif() +:description: The NULLIF() function replaces a given value with NULL if it matches a specific criterion. +:page-topic-type: reference + +The `NULLIF()` function replaces a given value with NULL if it matches a specific criterion. + +== Syntax + +[source,sql] +---- +NULLIF(argument_1,argument_2); +---- + +The `NULLIF` function takes two arguments: + +* The first argument is the value to evaluate. +* The second argument is the value to treat as NULL if the first argument matches it. + +[TIP] +==== +If the first argument matches the second argument, the `NULLIF()` function returns `NULL`. Otherwise, it returns the first argument as-is. +==== + +== Examples + +=== Handle equal values + +In this case, the `NULLIF` function compares the values 4 and 4. + +[source,sql] +---- +SELECT NULLIF (4, 4); +---- + +The result is `NULL` because the two values being compared are equal (4 = 4). + +[source,sql] +----- + if +---- +----- + +=== Handle different values + +This example uses the `NULLIF` function to manage different values. + +[source,sql] +---- +SELECT NULLIF (9, 0); +---- + +The result is `9` because the second value in the `NULLIF` function is 0 (the two values are not equal). + +[source,sql] +---- + if + + 9 +---- + +=== String comparison + +In this case, the `NULLIF` function compares the strings `L` and `O`. + +[source,sql] +---- +SELECT NULLIF ('L', 'O'); +---- + +The result is `L` because the two strings being compared (`L` and `O`) are not equal. Therefore, the function returns the first string. + +[source,sql] +---- + if + + L +---- + +=== Handle default values + +Suppose there is an `employees` table with columns for `name` and `salary`. This query retrieves employee names and their adjusted salaries, where a salary of 0 is replaced with NULL: + +[source,sql] +---- +CREATE TABLE employees ( + name TEXT, + salary INT +); + +INSERT INTO employees (name, salary) +VALUES + ('John', 50000), + ('Jane', 0), + ('Roy', 0), + ('NEil', 0), + ('Michael', 75000); +---- + +View the records: + +[source,sql] +---- +SELECT * FROM employees; +---- + +[source,sql] +---- + name | salary +---------+-------- + John | 50000 + Jane | 0 + Roy | 0 + NEil | 0 + Michael | 75000 +---- + +This query retrieves employee names and their adjusted salaries, where a salary of 0 is replaced with NULL: + +[source,sql] +---- +SELECT name, NULLIF(salary, 0) AS adjusted_salary +FROM employees; +---- + +The `NULLIF` function checks whether the `salary` value is 0. If it is, the function returns NULL. Otherwise, it returns the original `salary` value. + +[source,sql] +---- + name | adjusted_salary +---------+----------------- + John | 50000 + Jane | + Roy | + NEil | + Michael | 75000 +---- + +=== Avoid division by zero + +Suppose there is a `fractions` table with columns, a `numerator` and a `denominator`. + +[source,sql] +---- +CREATE TABLE fractions ( + numerator INT, + denominator INT +); + +INSERT INTO fractions (numerator, denominator) +VALUES + (10, 2), + (20, 0), + (15, 3), + (75, 0), + (15, 3); +---- + +View the table: + +[source,sql] +---- +SELECT * FROM fractions; +---- + +[source,sql] +---- + numerator | denominator +-----------+------------- + 10 | 2 + 20 | 0 + 15 | 3 + 75 | 0 + 15 | 3 +---- + +Here, the `NULLIF` function is applied to the `denominator` column. If the `denominator` is 0, the function returns NULL, avoiding division by zero. + +[source,sql] +---- +SELECT numerator, denominator, numerator / NULLIF(denominator, 0) AS "result" FROM fractions; +---- + +The result is shown in the result column. + +[source,sql] +---- + numerator | denominator | result +-----------+-------------+-------- + 10 | 2 | 5 + 20 | 0 | + 15 | 3 | 5 + 75 | 0 | + 15 | 3 | 5 +---- diff --git a/modules/reference/pages/sql/sql-functions/other-functions/obj-description.adoc b/modules/reference/pages/sql/sql-functions/other-functions/obj-description.adoc new file mode 100644 index 000000000..77bb441f1 --- /dev/null +++ b/modules/reference/pages/sql/sql-functions/other-functions/obj-description.adoc @@ -0,0 +1,21 @@ += obj_description() +:description: The obj_description() function returns the comment associated with a specific database object. +:page-topic-type: reference + +The link:https://www.postgresql.org/docs/current/functions-info.html#FUNCTIONS-INFO-COMMENT[`obj_description()`^] is a comment information function that returns the comment associated with a specific database object. + +== Syntax + +[source,text] +---- +obj_description (object_oid, catalog_name) → NULL +---- + +== Parameters + +* link:https://www.postgresql.org/docs/current/datatype-oid.html[`object_oid`^]: Specifies the object identifier (OID) of the database object to retrieve the comment for. +* link:https://www.postgresql.org/docs/current/catalogs.html[`catalog_name`^]: Specifies the name of the system catalog that contains the object. + +== Restrictions + +* This function always returns `NULL` if there are no parameters specified. diff --git a/modules/reference/pages/sql/sql-functions/other-functions/pg-backend-pid.adoc b/modules/reference/pages/sql/sql-functions/other-functions/pg-backend-pid.adoc new file mode 100644 index 000000000..3f3983c7b --- /dev/null +++ b/modules/reference/pages/sql/sql-functions/other-functions/pg-backend-pid.adoc @@ -0,0 +1,12 @@ += pg_backend_pid() +:description: The pg_backend_pid() function returns the process ID (PID) of the server process handling the current session. +:page-topic-type: reference + +The link:https://www.postgresql.org/docs/current/functions-info.html#FUNCTIONS-INFO-SESSION[`pg_backend_pid()`^] is a session information function that returns the process ID (PID) of the server process handling the current session. It is useful for identifying the backend process associated with a specific database connection, allowing for monitoring and task management. + +== Syntax + +[source,sql] +---- +pg_backend_pid() +---- diff --git a/modules/reference/pages/sql/sql-functions/other-functions/pg-encoding-to-char.adoc b/modules/reference/pages/sql/sql-functions/other-functions/pg-encoding-to-char.adoc new file mode 100644 index 000000000..0c7b79879 --- /dev/null +++ b/modules/reference/pages/sql/sql-functions/other-functions/pg-encoding-to-char.adoc @@ -0,0 +1,48 @@ += pg_encoding_to_char() +:description: The pg_encoding_to_char() function converts an encoding internal identifier to a human-readable name. +:page-topic-type: reference + +The link:https://www.postgresql.org/docs/current/functions-info.html#FUNCTIONS-INFO-CATALOG[`pg_encoding_to_char()`^] is a system catalog information function that converts an encoding internal identifier to a human-readable name. + +== Syntax + +[source,sql] +---- +pg_encoding_to_char(number) +---- + +== Parameters + +* `number`: Specifies the integer value representing the encoding identifier. + +== Examples + +[source,sql] +---- +SELECT pg_encoding_to_char(1); + + pg_encoding_to_char +--------------------- + EUC_JP +(1 row) +---- + +[source,sql] +---- +SELECT pg_encoding_to_char(0); + + pg_encoding_to_char +--------------------- + SQL_ASCII +(1 row) +---- + +[source,sql] +---- +SELECT pg_encoding_to_char(-1); + + pg_encoding_to_char +--------------------- + +(1 row) +---- diff --git a/modules/reference/pages/sql/sql-functions/other-functions/pg-get-constraintdef.adoc b/modules/reference/pages/sql/sql-functions/other-functions/pg-get-constraintdef.adoc new file mode 100644 index 000000000..38c96b973 --- /dev/null +++ b/modules/reference/pages/sql/sql-functions/other-functions/pg-get-constraintdef.adoc @@ -0,0 +1,21 @@ += pg_get_constraintdef() +:description: The pg_get_constraintdef() function retrieves the definition of a specific constraint in a human-readable format. +:page-topic-type: reference + +The link:https://www.postgresql.org/docs/current/functions-info.html#FUNCTIONS-INFO-CATALOG[`pg_get_constraintdef()`^] is a system catalog information function that retrieves the definition of a specific constraint in a human-readable format. + +== Syntax + +[source,text] +---- +pg_get_constraintdef (constraint_oid [, pretty_bool]) → NULL +---- + +== Parameters + +* link:https://www.postgresql.org/docs/current/catalog-pg-constraint.html[`constraint_oid`^]: Specifies the object identifier (OID) of the constraint to retrieve the definition for. +* link:https://www.postgresql.org/docs/current/datatype-boolean.html[`pretty_bool`^]: Controls whether to format the output in a human-readable way. + +== Restrictions + +* This function always returns `NULL` if there are no parameters specified. diff --git a/modules/reference/pages/sql/sql-functions/other-functions/pg-get-expr.adoc b/modules/reference/pages/sql/sql-functions/other-functions/pg-get-expr.adoc new file mode 100644 index 000000000..553dca1b7 --- /dev/null +++ b/modules/reference/pages/sql/sql-functions/other-functions/pg-get-expr.adoc @@ -0,0 +1,73 @@ += pg_get_expr() +:description: The pg_get_expr() function retrieves the internal form of an individual expression, such as the default value for a column. +:page-topic-type: reference + +The link:https://www.postgresql.org/docs/current/functions-info.html#FUNCTIONS-INFO-CATALOG[`pg_get_expr()`^] is a system catalog information function that retrieves the internal form of an individual expression, such as the default value for a column. + +== Syntax + +The `pg_get_expr()` function has two available syntax versions: + +[source,sql] +---- +SELECT pg_get_expr('expr_text', relation_oid); +---- + +[source,sql] +---- +SELECT pg_get_expr('expr_text', relation_oid, pretty_bool); +---- + +Both versions of the `pg_get_expr()` function return an empty string `""`. + +== Parameters + +* `expr_text`: Expression to obtain the internal representation for (can be any string value). +* `relation_oid`: OID (object identifier) of the table the expression belongs to (integer type). +* `pretty_bool`: Boolean value determining whether to format the expression in a more human-readable format (`TRUE`) or not (`FALSE`). + +== Examples + +First, create a sample table named *employees*: + +[source,sql] +---- +CREATE TABLE employees ( + id INT, + name TEXT, + salary TEXT +); +---- + +Then get the OID of the table: + +[source,sql] +---- +SELECT oid FROM pg_class WHERE relname = 'employees'; +---- + +[source,sql] +---- + oid +------ + 1018 +---- + +Retrieve the internal form for the `salary` column using the `pg_get_expr()` function: + +[source,sql] +---- +-- Version 1 +SELECT pg_get_expr('salary', 1018); + +-- Version 2 +SELECT pg_get_expr('salary', 1018, TRUE); +---- + +Either query returns: + +[source,sql] +---- + pg_get_expr +------------- +---- diff --git a/modules/reference/pages/sql/sql-functions/other-functions/pg-get-indexdef.adoc b/modules/reference/pages/sql/sql-functions/other-functions/pg-get-indexdef.adoc new file mode 100644 index 000000000..70f8ba3c5 --- /dev/null +++ b/modules/reference/pages/sql/sql-functions/other-functions/pg-get-indexdef.adoc @@ -0,0 +1,68 @@ += pg_get_indexdef() +:description: The pg_get_indexdef() function reconstructs the PostgreSQL command used to retrieve the definition of a specified index. +:page-topic-type: reference + +The link:https://www.postgresql.org/docs/current/functions-info.html#FUNCTIONS-INFO-CATALOG[`pg_get_indexdef()`^] is a system catalog information function that reconstructs the PostgreSQL command used to retrieve the definition of a specified index. + +== Syntax + +The `pg_get_indexdef()` function has two available syntax versions: + +[source,sql] +---- +pg_get_indexdef(index_oid, column_oid) +---- + +[source,sql] +---- +pg_get_indexdef(index_oid, column_oid, pretty_bool) +---- + +== Parameters + +The parameters required to execute this function: + +* `index_oid`: Specifies the object identifier (OID) of the index. +* `column_oid`: Indicates the column number within the index (starting from 1). +* `pretty_bool`: Controls whether to format the output in a human-readable way. + +== Examples + +Create a sample table and an index for it: + +[source,sql] +---- +CREATE TABLE sample_table(col int); +CREATE INDEX sample_index ON sample_table(col); +---- + +Once that is done, get the OID of the index: + +[source,sql] +---- +SELECT oid FROM pg_class WHERE relname = 'sample_index'; +---- + +[source,sql] +---- + oid +------ + 16387 +---- + +Retrieve the index definition: + +[source,sql] +---- +SELECT pg_get_indexdef(16387); +---- + +The query returns the reconstructed definition: + +[source,sql] +---- + pg_get_indexdef +------------------------------------------------------- + CREATE INDEX sample_index ON public.sample_table(col) +(1 row) +---- diff --git a/modules/reference/pages/sql/sql-functions/other-functions/pg-get-statisticsobjdef-columns.adoc b/modules/reference/pages/sql/sql-functions/other-functions/pg-get-statisticsobjdef-columns.adoc new file mode 100644 index 000000000..92f3be8d7 --- /dev/null +++ b/modules/reference/pages/sql/sql-functions/other-functions/pg-get-statisticsobjdef-columns.adoc @@ -0,0 +1,16 @@ += pg_get_statisticsobjdef_columns() +:description: The pg_get_statisticsobjdef_columns() function retrieves information about the columns associated with an extended statistics object. +:page-topic-type: reference + +The link:https://www.postgresql.org/docs/current/functions-info.html#FUNCTIONS-INFO-CATALOG[`pg_get_statisticsobjdef_columns()`^] is a system catalog information function that retrieves information about the columns associated with an extended statistics object. + +== Syntax + +[source,text] +---- +pg_get_statisticsobjdef_columns() → NULL +---- + +== Restrictions + +* This function always returns `NULL` if there are no parameters specified. diff --git a/modules/reference/pages/sql/sql-functions/other-functions/pg-get-userbyid.adoc b/modules/reference/pages/sql/sql-functions/other-functions/pg-get-userbyid.adoc new file mode 100644 index 000000000..992b0638b --- /dev/null +++ b/modules/reference/pages/sql/sql-functions/other-functions/pg-get-userbyid.adoc @@ -0,0 +1,53 @@ += pg_get_userbyid() +:description: The pg_get_userbyid() function retrieves the name of a user (role) given its unique identifier (OID). +:page-topic-type: reference + +The link:https://www.postgresql.org/docs/current/functions-info.html#FUNCTIONS-INFO-CATALOG[`pg_get_userbyid()`^] is a system catalog information function that retrieves the name of a user (role) given its unique identifier (OID). + +== Syntax + +[source,sql] +---- +pg_get_userbyid(role_oid) +---- + +== Parameters + +* `role_oid`: Specifies the object identifier (OID) of the user. + +== Examples + +Get the OIDs of all the users: + +[source,sql] +---- +SELECT id,name FROM oxla_internal.oxla_role; +---- + +The query returns the list of users with their IDs (OIDs): + +[source,sql] +---- + id | name +----+--------- + 1 | oxla + 2 | other_user +(2 rows) +---- + +Translate the OID to a role name: + +[source,sql] +---- +SELECT pg_get_userbyid(2); +---- + +The query returns: + +[source,sql] +---- + pg_get_userbyid +----------------- + other_user +(1 row) +---- diff --git a/modules/reference/pages/sql/sql-functions/other-functions/pg-relation-is-publishable.adoc b/modules/reference/pages/sql/sql-functions/other-functions/pg-relation-is-publishable.adoc new file mode 100644 index 000000000..6eb8dc11e --- /dev/null +++ b/modules/reference/pages/sql/sql-functions/other-functions/pg-relation-is-publishable.adoc @@ -0,0 +1,36 @@ += pg_relation_is_publishable() +:description: Use the pg_relation_is_publishable() function to determine whether a specified relation (table) can be published in a publication. +:page-topic-type: reference + +Use the`pg_relation_is_publishable()` function to determine whether a specified relation (table) can be published in a link:https://www.postgresql.org/docs/current/logical-replication-publication.html[publication^]. + +== Syntax + +[source,sql] +---- +pg_relation_is_publishable(table_name_or_oid) +---- + +The function returns `false` for every existing table and `NULL` for any non-existing table. + +== Parameters + +* `table_name_or_oid`: Specifies the object identifier (OID) of a table or its name. + +== Examples + +[source,sql] +---- +SELECT pg_relation_is_publishable('existing_table'); + pg_relation_is_publishable +---------------------------- + f +---- + +[source,sql] +---- +SELECT pg_relation_is_publishable(16386); + pg_relation_is_publishable +---------------------------- + f +---- diff --git a/modules/reference/pages/sql/sql-functions/other-functions/pg-size-pretty.adoc b/modules/reference/pages/sql/sql-functions/other-functions/pg-size-pretty.adoc new file mode 100644 index 000000000..2262794f8 --- /dev/null +++ b/modules/reference/pages/sql/sql-functions/other-functions/pg-size-pretty.adoc @@ -0,0 +1,36 @@ += pg_size_pretty() +:description: The pg_size_pretty() function converts sizes in bytes into a human-readable format. +:page-topic-type: reference + +The link:https://www.postgresql.org/docs/current/functions-admin.html[`pg_size_pretty()`^] is a database object management function that converts sizes in bytes into a human-readable format. + +== Syntax + +[source,sql] +---- +pg_size_pretty(size) +---- + +== Parameters + +* `size`: Specifies the size in bytes to convert. + +== Examples + +[source,sql] +---- +SELECT pg_size_pretty(100); + pg_size_pretty +---------------- + 100 bytes +(1 row) +---- + +[source,sql] +---- +SELECT pg_size_pretty(1000000); + pg_size_pretty +---------------- + 977 kB +(1 row) +---- diff --git a/modules/reference/pages/sql/sql-functions/other-functions/pg-table-is-visible.adoc b/modules/reference/pages/sql/sql-functions/other-functions/pg-table-is-visible.adoc new file mode 100644 index 000000000..15503ef3d --- /dev/null +++ b/modules/reference/pages/sql/sql-functions/other-functions/pg-table-is-visible.adoc @@ -0,0 +1,41 @@ += pg_table_is_visible() +:description: The pg_table_is_visible() function checks whether a specified table or other database object is visible in the current schema search path. +:page-topic-type: reference + +The link:https://www.postgresql.org/docs/current/functions-admin.html[`pg_table_is_visible()`^] is a schema visibility inquiry function that checks whether a specified table or other database object is visible in the current schema search path. + +== Syntax + +[source,sql] +---- +pg_table_is_visible(table_or_index_oid) +---- + +== Parameters + +* `table_or_index_oid`: Specifies the object identifier (OID) of a table or its name. + +== Examples + +[source,sql] +---- +SELECT pg_table_is_visible(-1); + pg_table_is_visible +---------------------------- +---- + +[source,sql] +---- +SELECT pg_table_is_visible(16386); + pg_table_is_visible +---------------------------- + t +---- + +[source,sql] +---- +SELECT pg_table_is_visible(16381); + pg_table_is_visible +---------------------------- + f +---- diff --git a/modules/reference/pages/sql/sql-functions/other-functions/pg-table-size.adoc b/modules/reference/pages/sql/sql-functions/other-functions/pg-table-size.adoc new file mode 100644 index 000000000..b1b2de662 --- /dev/null +++ b/modules/reference/pages/sql/sql-functions/other-functions/pg-table-size.adoc @@ -0,0 +1,16 @@ += pg_table_size() +:description: The pg_table_size() function retrieves the size of a specific table, including its associated storage components but excluding indexes. +:page-topic-type: reference + +link:https://www.postgresql.org/docs/current/functions-admin.html[`pg_table_size()`^] is a system administration function that retrieves the size of a specific table, including its associated storage components but excluding indexes. + +== Syntax + +[source,sql] +---- +pg_table_size(regclass) +---- + +== Parameters + +* `regclass`: Name or object identifier (OID) of the table. diff --git a/modules/reference/pages/sql/sql-functions/other-functions/pg-total-relation-size.adoc b/modules/reference/pages/sql/sql-functions/other-functions/pg-total-relation-size.adoc new file mode 100644 index 000000000..cad5a6544 --- /dev/null +++ b/modules/reference/pages/sql/sql-functions/other-functions/pg-total-relation-size.adoc @@ -0,0 +1,55 @@ += pg_total_relation_size() +:description: The pg_total_relation_size() function retrieves the size of a table and is useful for monitoring storage requirements. +:page-topic-type: reference + +The link:https://www.postgresql.org/docs/current/functions-admin.html[`pg_total_relation_size()`^] is a database object size function that retrieves the size of a table and is useful for monitoring the storage requirements. + +== Syntax + +[source,sql] +---- +pg_total_relation_size('relation_name'); +---- + +It returns the size of the specified table in bytes. + +== Parameters + +* `relation_name`: Name of the table to determine the size for. + +== Examples + +Create a `users` table: + +[source,sql] +---- +CREATE TABLE users ( + username TEXT, + email TEXT +); +INSERT INTO users (username, email) VALUES + ('john_doe', 'john.doe@example.com'), + ('jane_smith', 'jane.smith@example.com'), + ('alice_smith', 'alice.smith@example.com'), + ('bob_jones', 'bob.jones@example.com'), + ('susan_wilson', 'susan.wilson@example.com'), + ('michael_jackson', 'michael.jackson@example.com'), + ('lisa_johnson', 'lisa.johnson@example.com'), + ('david_smith', 'david.smith@example.com'); +---- + +Use the `pg_total_relation_size()` function to determine the size of the `users` table (in bytes): + +[source,sql] +---- +SELECT pg_total_relation_size('users'); +---- + +The query returns: + +[source,sql] +---- + pg_total_relation_size +------------------------ + 556 +---- diff --git a/modules/reference/pages/sql/sql-functions/other-functions/pg-typeof.adoc b/modules/reference/pages/sql/sql-functions/other-functions/pg-typeof.adoc new file mode 100644 index 000000000..6fbac0532 --- /dev/null +++ b/modules/reference/pages/sql/sql-functions/other-functions/pg-typeof.adoc @@ -0,0 +1,104 @@ += pg_typeof() +:description: The pg_typeof() function retrieves the data type of any given value. +:page-topic-type: reference + +The link:https://www.postgresql.org/docs/current/functions-info.html#FUNCTIONS-INFO-CATALOG[`pg_typeof()`^] is a system catalog information function that retrieves the data type of any given value. It returns a string literal corresponding to the expression type. + +== Syntax + +[source,sql] +---- +SELECT pg_typeof(`any`); +---- + +== Parameters + +* `any`: Represents any value used to determine the data type. + +== Examples + +=== Numeric + +This example shows the function usage with a numeric value: + +[source,sql] +---- +SELECT pg_typeof(100) as "data type"; +---- + +[source,sql] +---- + data type +----------- + integer +---- + +=== String + +This example uses a string value as an input: + +[source,sql] +---- +SELECT pg_typeof('event'::TEXT) as "data type"; +---- + +[source,sql] +---- + data type +----------- + text +---- + +=== Interval + +This example uses an interval input: + +[source,sql] +---- +SELECT pg_typeof(INTERVAL '1 day') as "data type"; +---- + +[source,sql] +---- + data type +----------- + interval +---- + +=== Table + +This section shows how to create a sample table and then uses `pg_typeof()` to retrieve the data types of information stored in the table: + +[source,sql] +---- +CREATE TABLE timestamp_example ( + id int, + event_time timestamp, + description text +); + +INSERT INTO timestamp_example (event_time, description) +VALUES + ('2023-10-20 12:30:00', 'Event 1'), + (NULL, 'Event 2'); +---- + +Use the `pg_typeof()` function to determine the data types of the `event_time` and `description` columns for each row: + +[source,sql] +---- +SELECT + pg_typeof(event_time) AS event_time_type, + pg_typeof(description) AS description_type +FROM timestamp_example; +---- + +The query returns: + +[source,sql] +---- + event_time_type | description_type +-----------------------------+------------------ + timestamp without time zone | text + timestamp without time zone | text +---- diff --git a/modules/reference/pages/sql/sql-functions/other-functions/shobj-description.adoc b/modules/reference/pages/sql/sql-functions/other-functions/shobj-description.adoc new file mode 100644 index 000000000..a50fc93f6 --- /dev/null +++ b/modules/reference/pages/sql/sql-functions/other-functions/shobj-description.adoc @@ -0,0 +1,21 @@ += shobj_description() +:description: The shobj_description() function retrieves the comment associated with a shared database object. +:page-topic-type: reference + +The link:https://www.postgresql.org/docs/current/functions-info.html#FUNCTIONS-INFO-CATALOG[`shobj_description()`^] is a comment information function that retrieves the comment associated with a shared database object. + +== Syntax + +[source,text] +---- +shobj_description (object_oid, catalog_name) → NULL +---- + +== Parameters + +* link:https://www.postgresql.org/docs/current/datatype-oid.html[`object_oid`^]: Specifies the object identifier (OID) of the shared object to retrieve the comment for. +* link:https://www.postgresql.org/docs/current/catalogs.html[`catalog_name`^]: Specifies the name of the system catalog that contains the shared object. + +== Restrictions + +* This function always returns `NULL` if no parameters are specified. diff --git a/modules/reference/pages/sql/sql-functions/string-functions/concat.adoc b/modules/reference/pages/sql/sql-functions/string-functions/concat.adoc new file mode 100644 index 000000000..5c0acbddf --- /dev/null +++ b/modules/reference/pages/sql/sql-functions/string-functions/concat.adoc @@ -0,0 +1,118 @@ += CONCAT +:description: Use the CONCAT() function to concatenate one or more input values into a single result. +:page-topic-type: reference + +Use the `CONCAT()` function to concatenate one or more input values into a single result. It supports all data types in Redpanda SQL, except `TIMESTAMPTZ`. The output is returned as a concatenation of the input values. + +*Special cases:* Returns `NULL` if there are no input rows or `NULL` values. + +== Examples + +=== Basic `CONCAT()` function + +This example uses the `CONCAT()` function to concatenate three values = into a single result: + +[source,sql] +---- +SELECT CONCAT ('Oxla', '.', 'com') AS "Website"; +---- + +The query returns: + +[source,sql] +---- ++------------+ +| Website | ++------------+ +| Oxla.com | ++------------+ +---- + +=== `CONCAT()` function using column + +This example uses a *payment* table that stores customer payment data: + +[source,sql] +---- +CREATE TABLE payment ( + paymentid int, + custFirstName text, + custLastName text, + product text, + ordertotal float +); +INSERT INTO payment + (paymentid, custFirstName, custLastName, product, ordertotal) +VALUES + (9557451,'Alex','Drue','Latte',2.10), + (9557421,'Lana','Rey','Latte',2.10), + (9557411,'Tom','Hanks','Americano',1.85), + (9557351,'Maya','Taylor','Cappuccino',2.45), + (9557321,'Smith','Jay','Cappuccino',2.45), + (9557311,'Will','Ritchie','Americano',1.85); +---- + +[source,sql] +---- +SELECT * FROM payment; +---- + +This query displays the table: + +[source,sql] +---- ++------------+----------------+----------------+--------------+---------------+ +| paymentid | custFirstName | custLastName | product | ordertotal | ++------------+----------------+----------------+--------------+---------------+ +| 9557451 | Alex | Drue | Latte | 2.10 | +| 9557421 | Lana | Rey | Latte | 2.10 | +| 9557411 | Tom | Hanks | Americano | 1.85 | +| 9557351 | Maya | Taylor | Cappuccino | 2.45 | +| 9557321 | Smith | Jay | Cappuccino | 2.45 | +| 9557311 | Will | Ritchie | Americano | 1.85 | ++------------+----------------+----------------+--------------+---------------+ +---- + +This query concatenates values in the `custFirstName` and `custLastName` columns of the *payment* table: + +[source,sql] +---- +SELECT CONCAT (custFirstName, ' ', custLastName) AS "Customer Name" +FROM payment; +---- + +This displays an output where spaces separate the first and last names. + +[source,sql] +---- ++-----------------+ +| Customer Name | ++-----------------+ +| Tom Hanks | +| Lana Rey | +| Alex Drue | +| Will Ritchie | +| Smith Jay | +| Maya Taylor | ++-----------------+ +---- + +=== CONCAT() function with NULL + +This example shows how to use the `CONCAT()` function to concatenate a string with a `NULL` value: + +[source,sql] +---- +SELECT CONCAT('Talent Source ',NULL) AS "concat"; +---- + +The result shows that the `CONCAT` function will skip the `NULL` value: + +[source,sql] +---- ++------------------+ +| concat | ++------------------+ +| Talent Source | ++------------------+ +---- diff --git a/modules/reference/pages/sql/sql-functions/string-functions/ends-with.adoc b/modules/reference/pages/sql/sql-functions/string-functions/ends-with.adoc new file mode 100644 index 000000000..214bc46e9 --- /dev/null +++ b/modules/reference/pages/sql/sql-functions/string-functions/ends-with.adoc @@ -0,0 +1,154 @@ += ENDS_WITH +:description: The ENDS_WITH() function determines whether the first argument ends with a specified string in the second argument or not. +:page-topic-type: reference + +The `ENDS_WITH()` function determines whether the first argument ends with a specified string in the second argument or not. + +[source,sql] +---- +ENDS_WITH(first_argument, 'second_argument') +---- + +* `first_argument`: The search reference. Can be a string or a column name. +* `second_argument`: The specified argument, which will have the search keywords. + +The input type will be `STRING`, and the return type is `BOOL`, shown as `true` or `false`. + +*Special case:* + +* Returns `NULL` for the `NULL` record. +* Returns `true` (including the `NULL` record) if the `second_argument` is not specified. + +== Examples + +=== `ENDS_WITH()` function using column + +Consider a table named *courses*: + +[source,sql] +---- +CREATE TABLE courses ( + course_id int, + course_name text, + credits text +); +INSERT INTO courses + (course_id, course_name, credits) +VALUES + (2111,'Basics of Plant Biotechnology',2), + (2102,'Biochemistry',3), + (1241,'Statistics',3), + (4142,'Microbial Biodiversity',2), + (3262,'Introduction to Plant Pathology',3), + (3233,'Enzyme Technology',2), + (1201,'Rural Sociology',2); +---- + +[source,sql] +---- +SELECT * FROM courses; +---- + +The query displays the table: + +[source,sql] +---- ++------------+----------------------------------+-----------+ +| course_id | course_name | credits | ++------------+----------------------------------+-----------+ +| 2111 | Basics of Plant Biotechnology | 2 | +| 2102 | Biochemistry | 3 | +| 1241 | Statistics | 3 | +| 4142 | Microbial Biodiversity | 2 | +| 3262 | Introduction to Plant Pathology | 3 | +| 3233 | Enzyme Technology | 2 | +| 1201 | Rural Sociology | 2 | ++------------+----------------------------------+-----------+ +---- + +This query checks which values of the *course_name* column end with "`ology`" in the preceding table: + +[source,sql] +---- +SELECT course_name, ENDS_WITH(course_name, 'ology') FROM courses; +---- + +This returns true to all the courses with the name ending with **ology.** Otherwise*,* `false`. + +[source,sql] +---- ++----------------------------------+-------------+ +| course_name | ends_with | ++----------------------------------+-------------+ +| Basics of Plant Biotechnology | true | +| Biochemistry | false | +| Statistics | false | +| Microbial Biodiversity | false | +| Introduction to Plant Pathology | true | +| Enzyme Technology | true | +| Rural Sociology | true | ++----------------------------------+-------------+ +---- + +=== `ENDS_WITH()` function with no specified argument + +The **patients_data** table has a `NULL` value in the *allergies* column: + +[source,sql] +---- +CREATE TABLE patients_data ( + record_number int, + patient_name text, + height_in_cm int, + weight_in_kg int, + allergies text +); +INSERT INTO patients_data + (record_number, patient_name, height_in_cm, weight_in_kg, allergies) +VALUES + (2009000908,'Vivienne Desjardin',168,49,''), + (2012000876,'Elizabeth Reinhard',163,55,''), + (2015000965,'James McCarthy',188,70,'penicillin'), + (2020000109,'Jose Ramirez',170,70,'sulfonamide'), + (2020000222,'Stefani Ricci',170,70,'peniccilin'); +---- + +[source,sql] +---- +SELECT * FROM patients_data; +---- + +[source,sql] +---- ++----------------+---------------------+---------------+--------------+-------------+ +| record_number | patient_name | height_in_cm | weight_in_kg | allergies | ++----------------+---------------------+---------------+--------------+-------------+ +| 2009000908 | Vivienne Desjardin | 168 | 49 | null | +| 2012000876 | Elizabeth Reinhard | 163 | 55 | null | +| 2015000965 | James McCarthy | 188 | 70 | penicillin | +| 2020000109 | Jose Ramirez | 170 | 70 | sulfonamide | +| 2020000222 | Stefani Ricci | 170 | 70 | peniccilin | ++----------------+---------------------+---------------+--------------+-------------+ +---- + +For example, run the `ENDS_WITH` function but with no specified `second_argument`: + +[source,sql] +---- +SELECT allergies, ENDS_WITH(allergies, '') FROM patients_data; +---- + +The result shows that `ENDS_WITH` returns true for all records (even the `NULL` one): + +[source,sql] +---- ++--------------+--------------+ +| allergies | ends_with | ++--------------+--------------+ +| null | true | +| null | true | +| penicillin | true | +| sulfonamide | true | +| peniccilin | true | ++--------------+--------------+ +---- diff --git a/modules/reference/pages/sql/sql-functions/string-functions/index.adoc b/modules/reference/pages/sql/sql-functions/string-functions/index.adoc new file mode 100644 index 000000000..83772e07c --- /dev/null +++ b/modules/reference/pages/sql/sql-functions/string-functions/index.adoc @@ -0,0 +1,37 @@ += Overview +:description: String functions analyze and manipulate string values. + +Use string functions to analyze and manipulate string values. Redpanda SQL supports these string related functions and operators: + +== String functions + +[width="100%",cols="20%,80%",options="header",] +|=== +|*Function* |*Description* +|xref:reference:sql/sql-functions/string-functions/length.adoc[LENGTH()] |Returns the number of characters in a string +|xref:reference:sql/sql-functions/string-functions/lower.adoc[LOWER()] |Makes string lowercase +|xref:reference:sql/sql-functions/string-functions/upper.adoc[UPPER()] |Makes string upper case +|xref:reference:sql/sql-functions/string-functions/starts-with.adoc[STARTS_WITH()] |Checks if a string starts with a specified substring +|xref:reference:sql/sql-functions/string-functions/ends-with.adoc[ENDS_WITH()] |Checks if a string ends with a specified substring +|xref:reference:sql/sql-functions/string-functions/concat.adoc[CONCAT()] |Adds two or more strings together +|xref:reference:sql/sql-functions/string-functions/substr.adoc[SUBSTR()] |Extracts a substring from a string +|xref:reference:sql/sql-functions/string-functions/strpos.adoc[STRPOS()] |Finds the position at which the substring starts within the string +|xref:reference:sql/sql-functions/string-functions/regex/regexp-match.adoc[REGEXP_MATCH()] |Matches a POSIX regular expression pattern to a string +|xref:reference:sql/sql-functions/string-functions/regex/regexp-replace.adoc[REGEXP_REPLACE()] |Substitutes new text for substrings that match POSIX regular expression patterns +|xref:reference:sql/sql-functions/string-functions/replace.adoc[REPLACE()] |Finds and replace occurences of a substring in a string +|xref:reference:sql/sql-functions/string-functions/position.adoc[POSITION()] |Returns the position of the first occurrence of a substring in a string +|=== + +== String operators + +[width="100%",cols="51%,49%",options="header",] +|=== +|*Operator* |*Description* +|text ~ text -> boolean |Returns `true` if the first argument matches the pattern of the second argument in case-sensitive match +|text ~* text -> boolean |Returns `true` if the first argument matches the pattern of the second argument in a case-insensitive match +|text !~ text -> boolean |Returns `true` if the first argument does not match the pattern of the second argument in case-sensitive match. +|text ~ text -> boolean |Returns `true` if the first argument matches the pattern of the second argument in case-sensitive match. +|text ~* text -> boolean |Returns `true` if the first argument matches the pattern of the second argument in a case-insensitive match. +|text !~ text -> boolean |Returns `true` if the first argument does not match the pattern of the second argument in case-sensitive match. +|text !~* text -> boolean |Returns `true` if the first argument does not match the pattern of the second argument in a case-insensitive match. +|=== diff --git a/modules/reference/pages/sql/sql-functions/string-functions/length.adoc b/modules/reference/pages/sql/sql-functions/string-functions/length.adoc new file mode 100644 index 000000000..6c6a48985 --- /dev/null +++ b/modules/reference/pages/sql/sql-functions/string-functions/length.adoc @@ -0,0 +1,105 @@ += LENGTH +:description: Use the LENGTH() function to find the length of a string (for example, the number of characters in a given string). +:page-topic-type: reference + +Use the `LENGTH()` function to find the length of a string(for example, the number of characters in a given string). It accepts a string as a parameter. The syntax of the length function is: + +[source,sql] +---- +LENGTH(string) +---- + +The input type is a string, and the return type is int, as it returns the number of characters. + +*Special cases:* + +* If a NULL value is passed in the function. (For example, `LENGTH(NULL)`, it returns `NULL`). +* If the parameter is an empty string `LENGTH(")`, it returns 0. +* If the parameter is a space character `LENGTH('')`, not empty or NULL, it returns 1 as it is not empty anymore. + +== Examples + +=== Basic `LENGTH()` function + +This example uses the `LENGTH()` function to find out the length of a string text: + +[source,sql] +---- +SELECT LENGTH ('Redpanda SQL Tutorial'); +---- + +The query returns: + +[source,sql] +---- ++------------+ +| length | ++------------+ +| 21 | ++------------+ +---- + +=== `LENGTH()` function using columns + +The following example uses the `LENGTH()` function on a `personal_details` table containing `id`, `first_name`, `last_name`, and `gender` columns for retail store employees. + +[source,sql] +---- +CREATE TABLE personal_details ( + id int, + first_name text, + last_name text, + gender text +); +INSERT INTO personal_details + (id, first_name, last_name, gender) +VALUES + (1,'Mark','Wheeler','M'), + (2,'Tom','Hanks','M'), + (3,'Jane','Hopper','F'), + (4,'Emily','Byers','F'), + (5,'Lucas','Sinclair','M'); +---- + +[source,sql] +---- +SELECT * FROM personal_details; +---- + +The query shows this table: + +[source,sql] +---- ++-----+-------------+-------------+----------+ +| id | first_name | last_name | gender | ++-----+-------------+-------------+----------+ +| 1 | Mark | Wheeler | M | +| 2 | Tom | Hanks | M | +| 3 | Jane | Hopper | F | +| 4 | Emily | Byers | F | +| 5 | Lucas | Sinclair | M | ++-----+-------------+-------------+----------+ +---- + +The query returns the last name and the length of the last name from the personal_details table, where the length of the last_name is greater than 5. + +[source,sql] +---- +SELECT last_name,length(last_name) +AS "Length of Last Name" +FROM personal_details +WHERE LENGTH(last_name) > 5; +---- + +The output displays all those items in the last_name column with a length of more than 5 characters: + +[source,sql] +---- ++---------------+-----------------------+ +| last_name | Length of Last Name | ++---------------+-----------------------+ +| Wheeler | 7 | +| Hopper | 6 | +| Sinclair | 8 | ++---------------+-----------------------+ +---- diff --git a/modules/reference/pages/sql/sql-functions/string-functions/lower.adoc b/modules/reference/pages/sql/sql-functions/string-functions/lower.adoc new file mode 100644 index 000000000..da123f7da --- /dev/null +++ b/modules/reference/pages/sql/sql-functions/string-functions/lower.adoc @@ -0,0 +1,105 @@ += LOWER +:description: The LOWER() function returns a given string, an expression, or values in a column in all lowercase letters. +:page-topic-type: reference + +The LOWER() function returns a given string, an expression, or values in a column in all lowercase letters. The syntax of the function is: + +[source,sql] +---- +LOWER(string) +---- + +It accepts input as a string and returns the text in the lowercase alphabet. + +*Special Cases:* If there are characters in the input which are not of type string, they remain unaffected by the LOWER()function. + +[NOTE] +==== +Unicode is supported so that the ß is equivalent to the string ss. +==== + +== Examples + +=== Basic `LOWER()` function + +This basic query shows how to convert the given string in all lowercase alphabets: + +[source,sql] +---- +SELECT LOWER('PostGreSQL'); +---- + +The query returns: + +[source,sql] +---- ++------------+ +| lower | ++------------+ +| postgresql | ++------------+ +---- + +=== `LOWER()` function using columns + +This example shows how the `LOWER()` function works with columns. The *personal_details* table contains columns *id*, *first_name*, *last_name*, and *gender* of retail store employees. + +[source,sql] +---- +CREATE TABLE personal_details ( + id int, + first_name text, + last_name text, + gender text +); +INSERT INTO personal_details + (id, first_name, last_name, gender) +VALUES + (1,'Mark','Wheeler','M'), + (2,'Tom','Hanks','M'), + (3,'Jane','Hopper','F'), + (4,'Emily','Byers','F'), + (5,'Lucas','Sinclair','M'); +---- + +[source,sql] +---- +SELECT * FROM personal_details; +---- + +This query shows the table: + +[source,sql] +---- ++-----+-------------+-------------+----------+ +| id | first_name | last_name | gender | ++-----+-------------+-------------+----------+ +| 1 | Mark | Wheeler | M | +| 2 | Tom | Hanks | M | +| 3 | Jane | Hopper | F | +| 4 | Emily | Byers | F | +| 5 | Lucas | Sinclair | M | ++-----+-------------+-------------+----------+ +---- + +Assume that the goal is to convert the first and last names of employees with *id* numbers 2, 4, and 5 to all lowercase letters: + +[source,sql] +---- +SELECT first_name,last_name,LOWER(first_name),LOWER(last_name) +FROM personal_details +where id in (2, 4, 5); +---- + +The output displays the first and last names of employees with the specified ids in lowercase letters: + +[source,sql] +---- ++------------+-------------+----------+----------+ +| first_name | last_name | lower | lower | ++------------+-------------+----------+----------+ +| Tom | Hanks | tom | hanks | +| Emily | Byers | emily | byers | +| Lucas | Sinclair | lucas | sinclair | ++------------+-------------+----------+----------+ +---- diff --git a/modules/reference/pages/sql/sql-functions/string-functions/position.adoc b/modules/reference/pages/sql/sql-functions/string-functions/position.adoc new file mode 100644 index 000000000..28053257e --- /dev/null +++ b/modules/reference/pages/sql/sql-functions/string-functions/position.adoc @@ -0,0 +1,90 @@ += POSITION +:description: The POSITION() function returns the position of the first occurrence of a substring in a string. +:page-topic-type: reference + +The `POSITION()` function returns the position of the first occurrence of a substring in a string. It works the same as xref:reference:sql/sql-functions/string-functions/strpos.adoc[STRPOS], but it has slightly different syntax. + +== Syntax + +The syntax for this function is: + +[source,sql] +---- +POSITION(substring IN string) +---- + +The position of the substring within the string starts from 1. If the substring is not found, it returns 0. + +== Examples + +=== Example 1 + +This query looks for the position of the substring `world` within the string `Hello, world!`. + +[source,sql] +---- +SELECT POSITION('world' IN 'Hello, world!'); +---- + +The result would be the starting position of the substring `world`, which is 7. + +[source,sql] +---- +position +---------- + 7 +---- + +=== Example 2 + +The query looks for the position of the substring `123` within the string `1a2b3c`. + +[source,sql] +---- +SELECT POSITION('123' IN '1a2b3c'); +---- + +`123` is found starting at position 1, the result would be 1. + +[source,sql] +---- +position +---------- + 7 +---- + +=== Example 3 + +The query tries to find the position of the substring `abc` within the string `xyz`. + +[source,sql] +---- +SELECT POSITION('abc' IN 'xyz'); +---- + +`abc` is not found in `xyz`, the result would be 0. + +[source,sql] +---- +position +---------- + 0 +---- + +=== Example 4 + +This query searches for the position of the substring `cde` within the string `cde`. + +[source,sql] +---- +SELECT POSITION('cde' IN 'cde'); +---- + +`cde` is the entire string, the result would be 1. + +[source,sql] +---- +position +---------- + 1 +---- diff --git a/modules/reference/pages/sql/sql-functions/string-functions/regex/index.adoc b/modules/reference/pages/sql/sql-functions/string-functions/regex/index.adoc new file mode 100644 index 000000000..ebdccff46 --- /dev/null +++ b/modules/reference/pages/sql/sql-functions/string-functions/regex/index.adoc @@ -0,0 +1,3 @@ += Regular Expressions +:description: Reference for regular expression functions in Redpanda SQL. +:page-layout: index diff --git a/modules/reference/pages/sql/sql-functions/string-functions/regex/posix-regular-expressions.adoc b/modules/reference/pages/sql/sql-functions/string-functions/regex/posix-regular-expressions.adoc new file mode 100644 index 000000000..9341fa2bd --- /dev/null +++ b/modules/reference/pages/sql/sql-functions/string-functions/regex/posix-regular-expressions.adoc @@ -0,0 +1,20 @@ += POSIX Regular Expressions +:description: POSIX (Portable Operating System Interface) defines a set of standard operating system interfaces based on the UNIX OS. +:page-topic-type: reference + +*POSIX* (Portable Operating System Interface) defines a set of standard operating system interfaces based on the UNIX OS. In POSIX Basic Regex Expression (BRE) syntax, most characters are treated as literals (for example, they match only themselves). However, some characters called *metacharacters* have special meaning. + +This table describes common POSIX BRE metacharacters: + +[width="100%",cols="38%,62%",options="header",] +|=== +|*Metacharacter* |*Description* +|`.` |Matches any single character. For example, `a.c` matches "`*abc*`", but `[a.c]` matches only "`*a*`", "`*.*`", or "`*c*`" +|`-` |Used to define a range. For example, `[a-c]` will match characters *a* to *c* (both inclusive) +|[] |Calculates and returns a value corresponding to the minimal metric in the same row from a set of values +|`^` |Calculates and returns the maximum value +|`$` |Calculates and returns a value corresponding to the maximum metric in the same row from a set of values +|`*` |Calculates and returns the average value +|`\{n}` |Counts the number of rows +|`{n,m}` |Calculates the boolean of all the boolean values in the aggregated group (returns `FALSE` if at least one of aggregated rows is `FALSE` ) +|=== diff --git a/modules/reference/pages/sql/sql-functions/string-functions/regex/regexp-match.adoc b/modules/reference/pages/sql/sql-functions/string-functions/regex/regexp-match.adoc new file mode 100644 index 000000000..f2158f9c2 --- /dev/null +++ b/modules/reference/pages/sql/sql-functions/string-functions/regex/regexp-match.adoc @@ -0,0 +1,138 @@ += REGEXP_MATCH() +:description: The REGEXP_MATCH() function matches a POSIX regular expression pattern to a string. +:page-topic-type: reference + +The `REGEXP_MATCH()` function matches a POSIX regular expression pattern to a string. It returns an array of `TEXT[]` type with substring(s) of matched groups within the first match. + +== Syntax + +The syntax for `REGEXP_MATCH()` function is: + +[source,sql] +---- +REGEXP_MATCH(source_string, pattern, [flags]) +---- + +== Parameters + +* `source_string`: String on which to perform the match. +* `pattern`: POSIX regular expression pattern to match. +* `flags`: Optional. Flags that change the matching behavior of `REGEXP_MATCH()`. + +The `flags` parameter is an optional string that controls how the function operates. Here is a list of flags that are supported by Redpanda SQL: + +* `i`: Use this flag for case-insensitive matching. +* `c`: `REGEXP_MATCH()` function is case-sensitive by default, using the `c` flag has the same effect as having no flags at all. + +[NOTE] +==== +If using multiple flags, the last one takes precedence. If using the `ci` flags, the regex will be case-insensitive, while using the `ic` flags it will be case-sensitive. +==== + +== Examples + +=== Basic Usage + +These examples demonstrate how to find the first occurrence of an email address in the input string: + +[source,sql] +---- +SELECT REGEXP_MATCH('Contact us at hello@example.com', '[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,}'); +---- + +[source,sql] +---- + regexp_match +---------------------- + {hello@example.com} +(1 row) +---- + +=== Match multiple groups + +The `REGEXP_MATCH()` function can capture multiple groups within a match, which extracts key parts from a string in a structured way. This example shows how to extract the protocol, domain and path from a given URL: + +[source,sql] +---- +SELECT REGEXP_MATCH('https://www.example.com/products/item123', '(https?)://([\w.-]+)/(.+)'); +---- + +[source,sql] +---- + regexp_match +------------------------------------------ + {https,www.example.com,products/item123} +(1 row) +---- + +=== Case-insensitive matching + +This example shows how to match a pattern regardless of case-sensitivity: + +[source,sql] +---- +SELECT REGEXP_MATCH('User.Name@Example.COM', '@([a-z0-9.-]+)$', 'i'); +---- + +[source,sql] +---- + regexp_match +--------------- + {Example.COM} +(1 row) +---- + +=== Match with patterns stored in a table + +This example shows how to take the source string and regex pattern directly from the table. First, create two sample tables: + +[source,sql] +---- +CREATE TABLE users ( + email TEXT NOT NULL +); + +CREATE TABLE patterns ( + id INT, + regex_pattern TEXT NOT NULL +); +---- + +Once that is done, insert values into those tables: + +[source,sql] +---- +INSERT INTO users (email) VALUES + ('user@example.com'), + ('admin@test.org'), + ('invalid-email@wrong'); + +INSERT INTO patterns (id, regex_pattern) VALUES + (0, '^[a-z0-9._%+-]+@[a-z0-9.-]+\.[a-z]{2,}$'); +---- + +Validate whether user emails in the `users` table are valid. If the regex doesn't match, a `NULL` value is returned. + +[source,sql] +---- +SELECT users.email, + patterns.regex_pattern, + REGEXP_MATCH(users.email, patterns.regex_pattern, 'i') AS is_valid +FROM users +JOIN patterns ON patterns.id = 0; +---- + +[source,sql] +---- + email | regex_pattern | is_valid +---------------------+-----------------------------------------+-------------------- + user@example.com | ^[a-z0-9._%+-]+@[a-z0-9.-]+\.[a-z]{2,}$ | {user@example.com} + admin@test.org | ^[a-z0-9._%+-]+@[a-z0-9.-]+\.[a-z]{2,}$ | {admin@test.org} + invalid-email@wrong | ^[a-z0-9._%+-]+@[a-z0-9.-]+\.[a-z]{2,}$ | +(3 rows) +---- + +=== Restrictions + +* The function returns `NULL` if it cannot match the regular expression pattern. +* `i` and `c` flags shouldn't be used with each other diff --git a/modules/reference/pages/sql/sql-functions/string-functions/regex/regexp-replace.adoc b/modules/reference/pages/sql/sql-functions/string-functions/regex/regexp-replace.adoc new file mode 100644 index 000000000..170428a90 --- /dev/null +++ b/modules/reference/pages/sql/sql-functions/string-functions/regex/regexp-replace.adoc @@ -0,0 +1,156 @@ += REGEXP_REPLACE() +:description: The REGEXP_REPLACE() function replaces all occurrences of a regular expression pattern in a string with a specified replacement string. +:page-topic-type: reference + +The `REGEXP_REPLACE()` function replaces all occurrences of a regular expression pattern in a string with a specified replacement string. + +== Syntax + +The syntax for `REGEXP_REPLACE()` function is: + +[source,sql] +---- +REGEXP_REPLACE(source_string, pattern, replacement, [flags]) +---- + +== Parameters + +* `source_string`: String on which to perform the replacement. +* `pattern`: POSIX regular expression pattern to match. +* `replacement`: Replacement string. +* `flags`: Optional. Flags that change the matching behavior of `REGEXP_REPLACE()`. + +The `flags` parameter is an optional string that controls how the function operates. Here is a list of flags supported in Redpanda SQL: + +* `g`: Global replacement. This flag ensures that all occurrences of the pattern are replaced. +* `i`: Use this flag for case-insensitive matching. +* `c`: `REGEXP_REPLACE()` function is case-sensitive by default, using the `c` flag has the same effect as using no flags. + +== Examples + +=== Basic function usage + +This example uses the `REGEXP_REPLACE()` function with a basic POSIX regular expression pattern: + +[source,sql] +---- +SELECT REGEXP_REPLACE('The SQL ENGINE supports various data types', 'T[^ ]*', 'The') AS "Replaced_String"; +---- + +The query returns: + +[source,sql] +---- + Replaced_String +----------------------------------------- + The SQL ENGINE supports various data types +---- + +The pattern used was *"`T[^ ]*`"*, which matches any substring that starts with a '`T`' character, followed by any number of non-space characters. The function replaces the matched substring with the specified replacement string *"`We`"*. + +=== Replace special characters + +This example demonstrates how to replace a non-alphanumeric character in a string with a tilde (~): + +[source,sql] +---- +SELECT REGEXP_REPLACE('Hello World!', '[^A-Za-z0-9 ]', '~') AS "Replaced_String"; +---- + +In this query, the second parameter is a regular expression *"[^A-Za-z0-9 ]"* that matches any characters that are not uppercase / lowercase letters, digits or spaces. Output: + +[source,sql] +---- + Replaced String +------------------- + Hello World~ +---- + +=== Flag usage + +==== Replace certain substrings with a single flag defined + +This example uses the `REGEXP_REPLACE()` function with a defined flag to replace certain substrings in a string. First, create a sample `quotes` table: + +[source,sql] +---- +CREATE TABLE quotes (quotes_text text); +INSERT INTO quotes (quotes_text) +VALUES ('Work hard and stay hungry. Lazy people get nowhere in life.'), + ('An excuse is a way for a LAZY person to feel better.'), + ('The word LUCKY is how a lazy person describes someone who works hard.'); + +SELECT quotes_text FROM quotes; +---- + +The query returns: + +[source,bash] +---- + quotes_text +----------------------------------------------------------------------- + Work hard and stay hungry. Lazy people get nowhere in life. + An excuse is a way for a LAZY person to feel better. + The word LUCKY is how a lazy person describes someone who works hard. +(3 rows) +---- + +Now, use the `REGEXP_REPLACE()` function with the `i` flag specified to replace all occurrences of the word `lazy` with `active` regardless of case sensitivity: + +[source,sql] +---- +SELECT quotes_text, REGEXP_REPLACE(quotes_text, 'lazy', 'active', 'i') AS "New quotes" FROM quotes; +---- + +In this case, all occurrences of the word `lazy` have been replaced with `active`: + +[source,bash] +---- + quotes_text | New quotes +-----------------------------------------------------------------------+------------------------------------------------------------------------- + Work hard and stay hungry. Lazy people get nowhere in life. | Work hard and stay hungry. active people get nowhere in life. + An excuse is a way for a LAZY person to feel better. | An excuse is a way for a active person to feel better. + The word LUCKY is how a lazy person describes someone who works hard. | The word LUCKY is how a active person describes someone who works hard. +(3 rows) +---- + +=== Specify one or more flags + +Without specifying the `g` flag, `REGEXP_REPLACE()` function replaces only the first occurrence of a substring: + +[source,sql] +---- +SELECT REGEXP_REPLACE('ab12c', '[0-9]', 'X'); +---- + +[source,sql] +---- + regexp_replace +---------------- + abX2c +---- + +In this case, only the first digit (`1`) was replaced with `X`. By adding the `g` flag, all occurrences are replaced with `X`: + +[source,sql] +---- +SELECT REGEXP_REPLACE('ab12c', '[0-9]', 'X', 'g'); +---- + +[source,sql] +---- + regexp_replace +---------------- + abXXc +---- + +[NOTE] +==== +If using multiple flags, the last one takes precedence. If using the `ci` flags, the regex will be case-insensitive, while using the `ic` flags it will be case-sensitive +==== + +== Restrictions + +* The function returns `NULL` if there are no input rows or `NULL` values. +* If the regular expression pattern isn't found in the string, the `REGEXP_REPLACE()` function returns the original string +* `i` and `c` flags shouldn't be used with each other diff --git a/modules/reference/pages/sql/sql-functions/string-functions/replace.adoc b/modules/reference/pages/sql/sql-functions/string-functions/replace.adoc new file mode 100644 index 000000000..e376a6db2 --- /dev/null +++ b/modules/reference/pages/sql/sql-functions/string-functions/replace.adoc @@ -0,0 +1,152 @@ += REPLACE() +:description: The REPLACE() function looks for and replaces a substring with a new one in a string. +:page-topic-type: reference + +The `REPLACE()` function looks for and replaces a substring with a new one in a string. This function is often used to update the outdated or spelling mistakes in data that require an amendment. + +[NOTE] +==== +Redpanda SQL also supports the xref:reference:sql/sql-functions/string-functions/regex/regexp-replace.adoc[`REGEXP_REPLACE()`] function. It searches and replaces a substring that matches with a POSIX regular expression +==== + +== Syntax + +The syntax for `REPLACE()` function is: + +[source,sql] +---- +REPLACE(string, old_substring, new_substring) +---- + +[WARNING] +==== +The `REPLACE()` function performs a case-sensitive replacement +==== + +=== Parameters + +The syntax requires these parameters: + +* `string`: String to replace. +* `old_substring`: Substring to replace. All occurrences in the string are replaced. +* `new_substring`: New substring that will replace the old one. + +== Examples + +=== Basic usage + +This example demonstrates a basic usage of the `REPLACE()` function. + +[source,sql] +---- +SELECT REPLACE ('NewDatabase', 'New', 'Redpanda'); +---- + +The `REPLACE()` function finds all occurrences of the '`New`' substring in the '`NewDatabase`' string and replaces it with the '`Redpanda`' substring, producing: + +[source,sql] +---- ++---------------------+ +| f | ++---------------------+ +| RedpandaDatabase | ++---------------------+ +---- + +=== Replace specified values in a table + +This example shows how to replace the values of a specific column in a table. First, create a new table named *extracurriculars* with *club* and *category* columns and insert the values into the respective columns. + +[source,sql] +---- +CREATE TABLE hobby ( + club text, + category text +); +INSERT INTO hobby + (club, category) +VALUES + ('Bridge','group'), + ('Painting','individual'), + ('Basketball','group'), + ('Volleyball','group'); +---- + +After that is completed, retrieve all values from the table using this query: + +[source,sql] +---- +SELECT * FROM hobby; +---- + +[source,sql] +---- ++------------+---------------+ +| club | category | ++------------+---------------+ +| Bridge | group | +| Painting | individual | +| Basketball | group | +| Volleyball | group | ++--------------+-------------+ +---- + +This query replaces the *'`group`'* values in the *category* column with *'`sports`'*: + +[source,sql] +---- +SELECT REPLACE(category, 'group', 'sports') from hobby; +---- + +[source,sql] +---- ++--------------+ +| f | ++--------------+ +| sports | +| individual | +| sports | +| sports | ++--------------+ +---- + +=== Remove a substring from a string + +This example shows how to remove a substring from a string using the `REPLACE()` function. In this case, the goal is to find all occurrences of the '`Friends`' substring in the '`Hello Friends`' string and remove it: + +[source,sql] +---- +SELECT REPLACE('Hello Friends', 'Friends', ''); +---- + +[source,sql] +---- ++-----------+ +| f | ++-----------+ +| Hello | ++-----------+ +---- + +=== Replace multiple patterns + +This example uses the `REPLACE()` function to replace multiple patterns of the given string: + +[source,sql] +---- +SELECT REPLACE(REPLACE(REPLACE(REPLACE('2*[9-5]/{4+8}', '[', '('), ']', ')'), '{', '('), '}', ')'); +---- + +The `REPLACE()` function is called multiple times to replace the corresponding string as specified: + +* *`[]`* into *`()`* +* *`{}`* into *`()`* + +[source,sql] +---- ++------------------+ +| f | ++------------------+ +| 2*(9-5)/(4+8) | ++------------------+ +---- diff --git a/modules/reference/pages/sql/sql-functions/string-functions/starts-with.adoc b/modules/reference/pages/sql/sql-functions/string-functions/starts-with.adoc new file mode 100644 index 000000000..7f32b0d24 --- /dev/null +++ b/modules/reference/pages/sql/sql-functions/string-functions/starts-with.adoc @@ -0,0 +1,155 @@ += STARTS_WITH +:description: The STARTS_WITH() function determines whether the first argument starts with a specified string in the second argument or not. +:page-topic-type: reference + +The `STARTS_WITH()` function determines whether the first argument starts with a specified string in the second argument or not. + +[source,sql] +---- +STARTS_WITH(first_argument, 'second_argument') +---- + +* `first_argument`: The search reference. Can be a string or a column name. +* `second_argument`: The specified argument, which will have the search keywords. + +The input type will be `STRING`, and the return type is `BOOL`, shown as `true` or `false`. + +Special case: + +* Returns `NULL` for the `NULL` record. +* Returns `true` (including the `NULL` record) if the `second_argument` is not specified. + +== Examples + +=== `STARTS_WITH()` function using column + +Consider a table with the title *petsData*. + +[source,sql] +---- +CREATE TABLE petsData ( + petid int, + petname text, + species text, + breed text, + sex text, + age int +); +INSERT INTO petsData + (petid, petname, species, breed, sex, age) +VALUES + (2021001,'Bartholomeow','cat','persian','m',2), + (2021004,'Jack','dog','boston terrier','m',1), + (2022001,'Jesse','hamster','dzungarian','m',1), + (2022010,'Bella','dog','dobberman','f',3), + (2022011,'June','cat','american shorthair','f',2); +---- + +[source,sql] +---- +SELECT * FROM petsData; +---- + +This query shows the table: + +[source,sql] +---- ++----------+--------------+----------+---------------------+------+-----+ +| petid | petname | species | breed | sex | age | ++----------+--------------+----------+---------------------+------+-----+ +| 2021001 | Bartholomeow | cat | persian | m | 2 | +| 2021004 | Jack | dog | boston terrier | m | 1 | +| 2022001 | Jesse | hamster | dzungarian | m | 1 | +| 2022010 | Bella | dog | dobberman | f | 3 | +| 2022011 | June | cat | american shorthair | f | 2 | ++----------+--------------+----------+---------------------+------+-----+ +---- + +From the table, this query retrieves the values of the *petname* column that start with "J": + +[source,sql] +---- +SELECT petname, STARTS_WITH(petname, 'J') FROM petsData; +---- + +This returns `true` to the pet with a pet starting with the letter J. Otherwise, `false`. + +[source,sql] +---- ++--------------+---------------+ +| petname | starts_with | ++---------------+--------------+ +| Bartholomeow | false | +| Jack | true | +| Jesse | true | +| Bella | false | +| June | true | ++---------------+--------------+ +---- + +=== `STARTS_WITH()` function with no specified argument + +The *petsData* table has a `NULL` value in the breed column. + +[source,sql] +---- +CREATE TABLE petsData ( + petid int, + petname text, + species text, + breed text, + sex text, + age int +); +INSERT INTO petsData + (petid, petname, species, breed, sex, age) +VALUES + (2021001,'Bartholomeow','cat','persian','m',2), + (2021004,'Jack','dog','boston terrier','m',1), + (2022001,'Jesse','hamster','dzungarian','m',1), + (2022010,'Bella','dog','dobberman','f',3), + (2022011,'June','cat','american shorthair','f',2), + (2022012,'Phoebe','gold fish','','f',1); +---- + +[source,sql] +---- +SELECT * FROM petsData; +---- + +[source,sql] +---- ++----------+--------------+------------+---------------------+------+------+ +| petid | petname | species | breed | sex | age | ++----------+--------------+------------+---------------------+------+------+ +| 2021001 | Bartholomeow | cat | persian | m | 2 | +| 2021004 | Jack | dog | boston terrier | m | 1 | +| 2022001 | Jesse | hamster | dzungarian | m | 1 | +| 2022010 | Bella | dog | dobberman | f | 3 | +| 2022011 | June | cat | american shorthair | f | 2 | +| 2022012 | Phoebe | gold fish | | f | 1 | ++----------+--------------+------------+---------------------+------+------+ +---- + +For example, run the `STARTS_WITH` function but with no specified `second_argument:` + +[source,sql] +---- +SELECT breed, STARTS_WITH(breed, '') FROM petsData; +---- + +This result shows that `STARTS_WITH` returns true for all records (even the `NULL` one): + +[source,sql] +---- ++---------------------+--------------+ +| breed | starts_with | ++---------------------+--------------+ +| persian | true | +| boston terrier | true | +| dzungarian | true | +| dobberman | true | +| american shorthair | true | +| null | true | ++---------------------+--------------+ +---- diff --git a/modules/reference/pages/sql/sql-functions/string-functions/strpos.adoc b/modules/reference/pages/sql/sql-functions/string-functions/strpos.adoc new file mode 100644 index 000000000..cdeba0eb0 --- /dev/null +++ b/modules/reference/pages/sql/sql-functions/string-functions/strpos.adoc @@ -0,0 +1,113 @@ += STRPOS +:description: Use the STRPOS() to return the position from where the substring (the second argument) is matched with the string (the first argument). +:page-topic-type: reference + +Use the `STRPOS()` function to return the position from where the substring (the second argument) is matched with the string (the first argument). + +[source,sql] +---- +STRPOS(string, substring) +---- + +The input and return must be of type `string`. + +*Special cases:* + +* Returns `NULL` if there are no input rows or `NULL` values. +* If the `substring` is not found in the string, then the `STRPOS()` function will return 0. + +== Examples + +=== Basic `STRPOS()` function + +This example shows how to find the *ut* (substring) position in the *computer* (string): + +[source,sql] +---- +SELECT STRPOS('computer', 'ut') AS "Position of ut"; +---- + +The result shows that *ut* is located at the fifth character of *computer*: + +[source,sql] +---- ++-----------------+ +| Position of ut | ++-----------------+ +| 5 | ++-----------------+ +---- + +=== STRPOS() function using column + +The *listofwords* table stores word data: + +[source,sql] +---- +CREATE TABLE listofwords ( + words text +); +INSERT INTO listofwords + (words) +VALUES + ('corral'), + ('traditionally'), + ('real'), + ('communal'), + ('challenge'), + ('fall'), + ('wall'), + ('gallop'), + ('albatross'); +---- + +[source,sql] +---- +SELECT * FROM listofwords; +---- + +The preceding query shows the table: + +[source,sql] +---- ++----------------+ +| words | ++----------------+ +| corral | +| traditionally | +| real | +| communal | +| challenge | +| fall | +| wall | +| gallop | +| albatross | ++----------------+ +---- + +The query returns the words and a position of a specific substring = '*al*' using the `STRPOS()` function: + +[source,sql] +---- +SELECT words, STRPOS(words, 'al') AS "Position of al" +FROM listofwords; +---- + +The result displays the *al* position of different words: + +[source,sql] +---- ++----------------+------------------+ +| words | Position of al | ++----------------+------------------+ +| corral | 5 | +| traditionally | 10 | +| real | 3 | +| communal | 7 | +| challenge | 3 | +| fall | 2 | +| wall | 2 | +| gallop | 2 | +| albatross | 1 | ++----------------+------------------+ +---- diff --git a/modules/reference/pages/sql/sql-functions/string-functions/substr.adoc b/modules/reference/pages/sql/sql-functions/string-functions/substr.adoc new file mode 100644 index 000000000..c6868b14d --- /dev/null +++ b/modules/reference/pages/sql/sql-functions/string-functions/substr.adoc @@ -0,0 +1,144 @@ += SUBSTR +:description: The SUBSTR() function extracts a specific number of characters from a string. +:page-topic-type: reference + +The `SUBSTR()` function extracts a specific number of characters from a string. + +== Syntax + +The syntax of the function is: + +*2 Arguments* + +[source,sql] +---- +substr( string, start_position) +---- + +*3 Arguments* + +[source,sql] +---- +substr( string, start_position, length ) +---- + +[TIP] +==== +Both syntaxes will have input and return of type `string`. +==== + +=== Start position + +Use the `start_position` as the starting position, specifying the part from where the substring is to be returned. It is written as an integer value. + +[width="100%",cols="36%,64%",options="header",] +|=== +|*Input* |*Return* +|`start_position < 0 ``start_position < string` |The `start_position` is a given character in the string. The count starts from the first character. +|`start_position > string` |Returns an empty substring. +|`start_position` = negative value |The count starts from the provided negative value, with subsequent characters yielded as it approaches 0. +|=== + +If the index is less than or equal to 0, no characters are returned. + +Once it exceeds 0, characters from the string are yielded, starting from the first one. | + +=== Length + +Use the `length` function to determine the number of characters to be extracted__.__ It can be one or more characters. + +[width="100%",cols="20%,80%",options="header",] +|=== +|*Input* |*Return* +|`length` = 0 |Returns an empty substring. +|`length` is not set |The function will start from the specified `start_position` and end at the last character of the `string`. +|`length` = negative value |Returns an error. +|=== + +== Examples + +=== `SUBSTR()` function with specified `start_position` & `length` + +In this example, the `start_position` is set to the first six characters and five characters are extracted: + +[source,sql] +---- +SELECT substr('Watermelon',6,5) AS "Fruit"; +---- + +The query returns: + +[source,sql] +---- +Fruit +------- + melon +---- + +=== `SUBSTR()` function with `length` = 0 + +This query extracts a string with `length` = 0: + +[source,sql] +---- +SELECT substr('Watermelon',6,0) AS "Fruit"; +---- + +This displays an empty output as there is no `length` specified: + +[source,sql] +---- +Fruit +------- +---- + +=== `SUBSTR()` function with `length` = negative value + +This example checks if the `length` is specified with a negative value: + +[source,sql] +---- +SELECT substr('Watermelon',6,-2) AS "Fruit"; +---- + +Instead of extracting the string from the last characters, it returns an error: + +[source,sql] +---- +ERROR: Length of substring cannot be negative +---- + +=== `SUBSTR()` function with `start_position` > `string` + +The string *Watermelon* has only ten characters. This example shows what happens when the specified `start_position` is larger than the string's characters: + +[source,sql] +---- +SELECT substr('Watermelon',20,2) AS "Fruit"; +---- + +This displays an empty output: + +[source,sql] +---- +Fruit +------- +---- + +=== `SUBSTR()` function with 2 arguments + +In this example, the `start_position` is set to the first six characters and five characters are extracted. + +[source,sql] +---- +SELECT substr('database', 6) AS "Result"; +---- + +This displays the substring from position 6: + +[source,sql] +---- +Result +-------- + ase +---- diff --git a/modules/reference/pages/sql/sql-functions/string-functions/substring.adoc b/modules/reference/pages/sql/sql-functions/string-functions/substring.adoc new file mode 100644 index 000000000..e437ed16c --- /dev/null +++ b/modules/reference/pages/sql/sql-functions/string-functions/substring.adoc @@ -0,0 +1,50 @@ += SUBSTRING +:description: SUBSTR is an alias for SUBSTRING. +:page-topic-type: reference + +[WARNING] +==== +SUBSTR is an alias for SUBSTRING. Learn more at xref:reference:sql/sql-functions/string-functions/substr.adoc[SUBSTR] documentation. +==== +The SUBSTRING() function lets you extract a part of a string and return that substring. + +== Syntax + +Here are the 2 basic syntaxes of the `SUBSTRING()` function in Redpanda SQL: + +*2 Arguments* + +[source,sql] +---- +SUBSTRING( string, start_position ) +---- + +*3 Arguments* + +[source,sql] +---- +SUBSTRING(string, start_position, length) +---- + +[TIP] +==== +Both syntaxes will have input and return of type `string`. +==== + +== Examples + +This example shows how to use the `SUBSTRING()` function to extract the first 7 characters from the string: + +[source,sql] +---- +SELECT SUBSTRING('RedpandaDocumentation', 1, 8); +---- + +This displays the substring from position 1: + +[source,sql] +---- +substring +----------- + Redpanda +---- diff --git a/modules/reference/pages/sql/sql-functions/string-functions/upper.adoc b/modules/reference/pages/sql/sql-functions/string-functions/upper.adoc new file mode 100644 index 000000000..94fe1db62 --- /dev/null +++ b/modules/reference/pages/sql/sql-functions/string-functions/upper.adoc @@ -0,0 +1,109 @@ += UPPER +:description: The UPPER() function returns a given string, an expression, or values in a column in all uppercase letters. +:page-topic-type: reference + +The `UPPER()` function returns a given string, an expression, or values in a column in all uppercase letters: + +[source,sql] +---- +UPPER(string) +---- + +It accepts input as a string and returns text in uppercase letters. + +*Special Case:* + +* If characters in the input are not of type string, they remain unaffected by the `UPPER()` function. +* Unicode is supported for the `UPPER()` function. + +== Examples + +=== Basic `UPPER()` function + +This basic query converts the given string to all uppercase letters: + +[source,sql] +---- +SELECT UPPER('PostGreSQL'); +---- + +The query returns: + +[source,sql] +---- ++-------------+ +| upper | ++-------------+ +| POSTGRESQL | ++-------------+ +---- + +=== UPPER() function using columns and CONCAT() function + +This example shows how the `UPPER()` function works with columns. A table named *personal_details* contains employee's *id*, *first_name*, *last_name*, and *gender* of a retail store: + +[source,sql] +---- +CREATE TABLE personal_details ( + id int, + first_name text, + last_name text, + gender text +); +INSERT INTO personal_details + (id, first_name, last_name, gender) +VALUES + (1,'Mark','Wheeler','M'), + (2,'Tom','Hanks','M'), + (3,'Jane','Hopper','F'), + (4,'Emily','Byers','F'), + (5,'Lucas','Sinclair','M'); +---- + +[source,sql] +---- +SELECT * FROM personal_details; +---- + +The query returns: + +[source,sql] +---- ++-----+-------------+-------------+----------+ +| id | first_name | last_name | gender | ++-----+-------------+-------------+----------+ +| 1 | Mark | Wheeler | M | +| 2 | Tom | Hanks | M | +| 3 | Jane | Hopper | F | +| 4 | Emily | Byers | F | +| 5 | Lucas | Sinclair | M | ++-----+-------------+-------------+----------+ +---- + +Assume that: + +. The goal is to convert employees' first and last names with *id* numbers 1, 3, and 5 to all uppercase letters. +. Then, combine them using the `CONCAT()` function into one *full_name* column in uppercase. ++ +Use this query: ++ +[source,sql] +---- +SELECT CONCAT (UPPER(first_name),' ', UPPER(last_name)) +as full_name +FROM personal_details +where id in (1, 3, 5); +---- ++ +The output displays the first and last names of employees with the specified ids in uppercase letters: ++ +[source,sql] +---- ++---------------------+ +| full_name | ++---------------------+ +| MARK WHEELER | +| JANE HOPPER | +| LUCAS SINCLAIR | ++---------------------+ +---- diff --git a/modules/reference/pages/sql/sql-functions/timestamp-functions/current-timestamp.adoc b/modules/reference/pages/sql/sql-functions/timestamp-functions/current-timestamp.adoc new file mode 100644 index 000000000..b2bbc88d9 --- /dev/null +++ b/modules/reference/pages/sql/sql-functions/timestamp-functions/current-timestamp.adoc @@ -0,0 +1,63 @@ += CURRENT_TIMESTAMP +:description: The CURRENT_TIMESTAMP() returns the current timestamp value representing the date and time the query was executed. +:page-topic-type: reference + +The `CURRENT_TIMESTAMP()` returns the current timestamp value representing the date and time the query was executed. + +[NOTE] +==== +Note that the time returned by this function is the time when the query was executed. +==== + +== Syntax + +[source,sql] +---- +CURRENT_TIMESTAMP +CURRENT_TIMESTAMP() +CURRENT_TIMESTAMP(precision) +---- + +== Arguments + +* `precision`: Optional. An integer literal from 0 to 6 that controls how many fractional-second digits to include. When omitted, the function returns full microsecond precision (6 digits). + +== Examples + +=== Default precision + +This example returns the current date and time with full microsecond precision: + +[source,sql] +---- +SELECT CURRENT_TIMESTAMP AS "Current Time"; +---- + +The final result will display the current date and time in the timezone in which it was issued: + +[source,sql] +---- +----------------------------- + Current Time +----------------------------- + 2022-08-31 16:56:06.464016 +----------------------------- +---- + +=== Truncated precision + +This example returns the current timestamp with no fractional seconds: + +[source,sql] +---- +SELECT CURRENT_TIMESTAMP(0) AS "Current Time"; +---- + +[source,sql] +---- +--------------------- + Current Time +--------------------- + 2022-08-31 16:56:06 +--------------------- +---- diff --git a/modules/reference/pages/sql/sql-functions/timestamp-functions/date-trunc.adoc b/modules/reference/pages/sql/sql-functions/timestamp-functions/date-trunc.adoc new file mode 100644 index 000000000..4facdc7a0 --- /dev/null +++ b/modules/reference/pages/sql/sql-functions/timestamp-functions/date-trunc.adoc @@ -0,0 +1,160 @@ += DATE_TRUNC +:description: The DATE_TRUNC() function truncates a timestamp, timestamp with time zone or interval value to the specified precision, effectively rounding down the +:page-topic-type: reference + +The `DATE_TRUNC()` function truncates a timestamp, timestamp with time zone or interval value to the specified precision, effectively rounding down the value to the start of the given time unit. The return type matches the input type. + +== Syntax + +The syntax for using the `DATE_TRUNC()` function is: + +.Without time_zone +[source,sql] +---- +DATE_TRUNC(field, source) +---- + +.With time_zone +[source,sql] +---- +DATE_TRUNC(field, source, time_zone) +---- + +== Parameters + +* `field`: The unit of time used to truncate the `source` value. Type: `text`. Case-insensitive. +* `source`: The value to truncate. Must be of type `INTERVAL`, `TIMESTAMP`, or `TIMESTAMP WITH TIME ZONE`. +* `time_zone`: Optional. Time zone for the operation. Type: `text`. Used only with the second syntax form. + +== Fields + +Here is a list of supported values available to specify the fields param in `DATE_TRUNC()` syntax. + +* `microseconds` +* `milliseconds` +* `second` +* `minute` +* `hour` +* `day` +* `week` +* `month` +* `quarter` +* `year` +* `decade` +* `century` +* `millennium` + +[NOTE] +==== +Some fields like `microseconds` and `milliseconds` are supported only for interval types. +==== + +== Examples + +=== Truncate to year + +This example truncates the timestamp to the year level. + +[source,sql] +---- +select DATE_TRUNC('year', '1911-12-02 19:40:00'::timestamp); +---- + +The timestamp **"1911-12-02 19:40:00"** has been truncated to 1911, with the month and day set to January 1st. + +[source,sql] +---- + date_trunc +---------------------------- + 1911-01-01 00:00:00.000000 +---- + +=== Truncate to day + +This query truncates the timestamp *"`1911-12-02 19:40:00`"* to the day level. + +[source,sql] +---- +select DATE_TRUNC('day', '1911-12-02 19:40:00'::timestamp); +---- + +The timestamp has been truncated to the same day, year, month, and day components. + +[source,sql] +---- + date_trunc +---------------------------- + 1911-12-02 00:00:00.000000 +---- + +=== Truncate to week + +This query truncates the timestamp *"`1911-12-02 19:40:00`"* to the week level. + +[source,sql] +---- +select DATE_TRUNC('week', '1911-12-02 19:40:00'::timestamp); +---- + +The timestamp has been truncated to the start of the week containing the date, which is Monday, November 27, 1911, at 00:00:00. + +[source,sql] +---- + date_trunc +---------------------------- + 1911-11-27 00:00:00.000000 +---- + +=== Truncate to quarter + +This query truncates the timestamp *"`1911-12-02 19:40:00`"* to the quarter level. + +[source,sql] +---- +select DATE_TRUNC('quarter', '1911-12-02 19:40:00'::timestamp); +---- + +The timestamp is truncated to the start of the quarter. The month and day are set to the first month and first day of the quarter, with time components reset to zero. + +[source,sql] +---- + date_trunc +---------------------------- + 1911-10-01 00:00:00.000000 +---- + +=== Truncate to hour + +This query truncates the interval *"`15 hours 10 minutes`"* to the hour precision. + +[source,sql] +---- +select DATE_TRUNC('hour', '15 hour 10 minutes'::interval); +---- + +The minutes and seconds components are set to zero, resulting in an interval of exactly 15 hours. + +[source,sql] +---- + date_trunc +----------------- + 15:00:00.000000 +---- + +=== Truncate to quarter (interval) + +This query truncates the interval *"`16 years 4 months`"* to the quarter-year level. + +[source,sql] +---- +select DATE_TRUNC('quarter', '16 years 4 months'::interval); +---- + +The interval is truncated to the nearest quarter-year unit. The months components is adjusted to the start of the quarter. Since each quarter consists of 3 months, 4 months is truncated down to 3 months, resulting in: + +[source,sql] +---- + date_trunc +----------------- + 16 years 3 mons +---- diff --git a/modules/reference/pages/sql/sql-functions/timestamp-functions/extract.adoc b/modules/reference/pages/sql/sql-functions/timestamp-functions/extract.adoc new file mode 100644 index 000000000..6bcead654 --- /dev/null +++ b/modules/reference/pages/sql/sql-functions/timestamp-functions/extract.adoc @@ -0,0 +1,94 @@ += EXTRACT +:description: The EXTRACT() function retrieves a specified part (field) from a given date/time or interval value. +:page-topic-type: reference + +The `EXTRACT()` function retrieves a specified part (field) from a given date/time or interval value. It is commonly used to obtain components such as year, month, day, and hour from timestamps or dates. + +== Syntax + +[source,sql] +---- +EXTRACT (field FROM source) +---- + +== Parameters + +* `field`: String or identifier specifying the part of the date / time to extract. +* `source`: Date / time value from which to extract the specifed field. + +This table shows the supported input and corresponding return types for the `EXTRACT()` function: + +[width="100%",cols="24%,55%,21%",options="header",] +|=== +|Input Type: `source` |Supported `field` values |Return Type +|`TIMESTAMP` |`YEAR`, `MONTH`, `DAY`, `HOUR`, `MINUTE`, `SECOND` |`DOUBLE PRECISION` +|`TIMESTAMPTZ` |`YEAR`, `MONTH`, `DAY`, `HOUR`, `MINUTE`, `SECOND` |`DOUBLE PRECISION` +|`DATE` |`YEAR`, `MONTH`, `DAY` |`INTEGER` +|=== + +[NOTE] +==== +The SECOND field returns a fractional value as DOUBLE PRECISION to include fractional seconds, not an integer type +==== + +== Examples + +=== EXTRACT() with timestamp - year + +This example shows how to use the `EXTRACT()` function to extract a given timestamp's year: + +[source,sql] +---- +SELECT EXTRACT(YEAR FROM TIMESTAMP '2025-12-31 13:30:15.123456'); +---- + +The query returns: + +[source,sql] +---- ++----------+ +| extract | ++----------+ +| 2025 | ++----------+ +---- + +=== EXTRACT() with timestamp - month + +This example uses the `EXTRACT()` function to extract a given timestamp's month: + +[source,sql] +---- +SELECT EXTRACT(MONTH FROM TIMESTAMP '2025-12-31 13:30:15.123456'); +---- + +The query returns the month's part of the given timestamp: + +[source,sql] +---- ++----------+ +| extract | ++----------+ +| 12 | ++----------+ +---- + +=== EXTRACT() with timestamp - seconds (including fractional seconds) + +This example uses the `EXTRACT()` function to extract a given timestamp's seconds, including fractional seconds: + +[source,sql] +---- +SELECT EXTRACT(SECOND FROM TIMESTAMP '2025-12-31 13:30:15.123456'); +---- + +The query returns the seconds' part of the given timestamp: + +[source,sql] +---- ++----------+ +| extract | ++----------+ +| 15.123456| ++----------+ +---- diff --git a/modules/reference/pages/sql/sql-functions/timestamp-functions/format-timestamp.adoc b/modules/reference/pages/sql/sql-functions/timestamp-functions/format-timestamp.adoc new file mode 100644 index 000000000..ea033e15f --- /dev/null +++ b/modules/reference/pages/sql/sql-functions/timestamp-functions/format-timestamp.adoc @@ -0,0 +1,92 @@ += FORMAT_TIMESTAMP +:description: The FORMAT_TIMESTAMP() function returns a given timestamp value in a specified format. +:page-topic-type: reference + +The `FORMAT_TIMESTAMP()` function returns a given timestamp value in a specified format. Its syntax is: + +[source,sql] +---- +FORMAT_TIMESTAMP(timestamp, format_string) +---- + +This function requires two arguments: + +* `timestamp`: A string representing the timestamp value that needs to be converted to a specified format. +* `format_string`: A string specifying the format to be converted into. + +Its return type is a timestamp value with a timezone. + +== Basic `FORMAT_TIMESTAMP()` function + +This example shows how to use the `FORMAT_TIMESTAMP()` function to convert a given timestamp into a timestamp format as specified in the function arguments. + +[source,sql] +---- +SELECT FORMAT_TIMESTAMP( 2 '2022-05-30 5:30:04', 3 'YYYY-MM-DD HH:MI:SS' 4); +---- + +Details of the format specified: + +* `YYYY` is the four-digit year 2022 +* `MM` is the month: 05 +* `DD` is the day: 30 +* `HH` is the hour: 5 +* `MI` is the minute: 30 +* `SS` is the second: 04 + +[NOTE] +==== +The format specified in the string can be used in any combination. +==== +The query returns: + +[source,sql] +---- ++-----------------------------+ +| format_timestamp | ++-----------------------------+ +| 2022-05-30 05:30:04+05 | ++-----------------------------+ +---- + +== `FORMAT_TIMESTAMP()` function using multiple spaces + +This example shows how the `FORMAT_TIMESTAMP()` function handles multiple spaces in the input string. When given multiple spaces, it omits the spaces and only returns the correct timestamp value: + +[source,sql] +---- +SELECT 2 FORMAT_TIMESTAMP('2008 Dec','YYYY MON'); +---- + +This returns the output: + +[source,sql] +---- ++-----------------------------+ +| format_timestamp | ++-----------------------------+ +| 2008-12-01 00:00:00+05 | ++-----------------------------+ +---- + +== `FORMAT_TIMESTAMP()` function if the input value of the year is less than 4 digits + +`FORMAT_TIMESTAMP()` will adjust the year to the nearest year value if the input argument has less than the required number of digits (for example, less than 4). This example shows how it works: + +[source,sql] +---- +SELECT 2 FORMAT_TIMESTAMP('07 25 09 10:40', 'MM DD YY HH:MI'); +---- + +This returns the output: + +[source,sql] +---- ++-----------------------------+ +| format_timestamp | ++-----------------------------+ +| 2009-07-25 10:40:00+06 | ++-----------------------------+ +---- + +In this example, the two-digit year `09` has been changed to the nearest four-digit year (for example, `2009`). Similarly, `70` will become `1970`, and `10` will become `2010`. diff --git a/modules/reference/pages/sql/sql-functions/timestamp-functions/index.adoc b/modules/reference/pages/sql/sql-functions/timestamp-functions/index.adoc new file mode 100644 index 000000000..87723a73b --- /dev/null +++ b/modules/reference/pages/sql/sql-functions/timestamp-functions/index.adoc @@ -0,0 +1,22 @@ += Overview +:description: Timestamp functions return a date-time value based on a specified timestamp/interval. + +Timestamp functions return a date-time value based on a specified timestamp/interval. Redpanda SQL supports the timestamp functions: + +[width="100%",cols="<38%,<62%",options="header",] +|=== +|*Functions* |*Description* +|xref:reference:sql/sql-functions/timestamp-functions/current-timestamp.adoc[CURRENT_TIMESTAMP()] |Returns the current date and time as a timestamp data type. +|xref:reference:sql/sql-functions/timestamp-functions/format-timestamp.adoc[FORMAT_TIMESTAMP()] |Modifies the current timestamp into a different format. +|xref:reference:sql/sql-functions/timestamp-functions/unix-seconds.adoc[UNIX_SECONDS()] |Converts a given timestamp to a UNIX timestamp in seconds. +|xref:reference:sql/sql-functions/timestamp-functions/unix-millis.adoc[UNIX_MILLIS()] |Converts a given timestamp to a UNIX timestamp in milliseconds. +|xref:reference:sql/sql-functions/timestamp-functions/unix-micros.adoc[UNIX_MICROS()] |Converts a given timestamp to a UNIX timestamp in microseconds. +|xref:reference:sql/sql-functions/timestamp-functions/timestamp-seconds.adoc[TIMESTAMP_SECONDS()] |Converts a UNIX timestamp in seconds to a timestamp. +|xref:reference:sql/sql-functions/timestamp-functions/timestamp-millis.adoc[TIMESTAMP_MILLIS()] |Converts a UNIX timestamp in milliseconds to a timestamp. +|xref:reference:sql/sql-functions/timestamp-functions/timestamp-micros.adoc[TIMESTAMP_MICROS()] |Converts a UNIX timestamp in microseconds to a timestamp. +|xref:reference:sql/sql-functions/timestamp-functions/timestamp-trunc.adoc[TIMESTAMP_TRUNC()] |Truncates a given timestamp to the nearest time part. Supported time parts are YEAR, MONTH, DAY, HOUR, MINUTE, and SECOND +|xref:reference:sql/sql-functions/timestamp-functions/extract.adoc[EXTRACT()] |Extracts some part of a specified timestamp or interval. +|xref:reference:sql/sql-functions/timestamp-functions/to-timestamp.adoc[TO_TIMESTAMP()] |Converts a string into a timestamp based on the provided format. +|xref:reference:sql/sql-functions/timestamp-functions/date-trunc.adoc[DATE_TRUNC()] |Truncates intervals or timestamps/time zones to a specified field. +|xref:reference:sql/sql-functions/timestamp-functions/to-char.adoc[TO_CHAR() from Timestamp] |Formats a timestamp into a string using a given format. +|=== diff --git a/modules/reference/pages/sql/sql-functions/timestamp-functions/timestamp-micros.adoc b/modules/reference/pages/sql/sql-functions/timestamp-functions/timestamp-micros.adoc new file mode 100644 index 000000000..33863845b --- /dev/null +++ b/modules/reference/pages/sql/sql-functions/timestamp-functions/timestamp-micros.adoc @@ -0,0 +1,93 @@ += TIMESTAMP_MICROS +:description: The TIMESTAMP_MICROS() function converts a given UNIX timestamp value in microseconds since 1970-01-01 00:00:00 UTC into a timestamp. +:page-topic-type: reference + +The `TIMESTAMP_MICROS()` function converts a given UNIX timestamp value in microseconds since 1970-01-01 00:00:00 UTC into a timestamp. Its syntax is: + +[source,sql] +---- +SELECT TIMESTAMP_MICROS(BIGINT) +---- + +Its input type is a `BIGINT` expression representing a UNIX timestamp in microseconds and the return data type is a timestamp. + +== Examples + +=== Basic `TIMESTAMP_MICROS()` function + +This example shows how to use the `TIMESTAMP_MICROS()` function to convert a given UNIX timestamp in microseconds into a timestamp without a timezone: + +[source,sql] +---- +SELECT TIMESTAMP_MICROS(2280419000000000) AS timestamp_microsvalues; +---- + +The query returns: + +[source,sql] +---- ++-----------------------------+ +| timestamp_microsvalues | ++-----------------------------+ +| 2042-04-06 17:43:20 | ++-----------------------------+ +---- + +=== `TIMESTAMP_MICROS()` function using columns + +Suppose a table named **timemicro_example** has these UNIX time values in microseconds in the *unix_timestamp* column: + +[source,sql] +---- +CREATE TABLE timemicro_example ( + unix_timestamp long +); + +INSERT INTO timemicro_example VALUES +('1350417000000000'), +('2130215000000000'), +('1110115000000000'), +('2310112000000000'); +---- + +[source,sql] +---- +SELECT * FROM timemicro_example; +---- + +This query shows the table: + +[source,sql] +---- ++--------------------+ +| unix_timestamp | ++--------------------+ +| 1350417000000000 | +| 2130215000000000 | +| 1110115000000000 | +| 2310112000000000 | ++--------------------+ +---- + +To convert all UNIX timestamp values in microseconds to timestamp values, run the query: + +[source,sql] +---- +SELECT unix_timestamp, TIMESTAMP_MICROS(unix_timestamp) +AS timestamp_value +FROM timemicro_example; +---- + +The output displays all the entries in the table in UNIX timestamp format (in microseconds) in the *unix_timestamp* column and in the timestamp format in the column *timestamp_value* without timezone: + +[source,sql] +---- ++-------------------------+-----------------------+ +| unix_timestamp | timestamp_value | ++-------------------------+-----------------------+ +|1350417000000000 | 2012-10-16 19:50:00 | +|2130215000000000 | 2037-07-03 06:23:20 | +|1110115000000000 | 2005-03-06 13:16:40 | +|2310112000000000 | 2043-03-16 09:46:40 | ++-------------------------+-----------------------+ +---- diff --git a/modules/reference/pages/sql/sql-functions/timestamp-functions/timestamp-millis.adoc b/modules/reference/pages/sql/sql-functions/timestamp-functions/timestamp-millis.adoc new file mode 100644 index 000000000..475e65c19 --- /dev/null +++ b/modules/reference/pages/sql/sql-functions/timestamp-functions/timestamp-millis.adoc @@ -0,0 +1,90 @@ += TIMESTAMP_MILLIS +:description: The TIMESTAMP_MILLIS() function converts a given UNIX timestamp value in milliseconds since 1970-01-01 00:00:00 UTC into a timestamp. +:page-topic-type: reference + +The `TIMESTAMP_MILLIS()` function converts a given UNIX timestamp value in milliseconds since 1970-01-01 00:00:00 UTC into a timestamp. Its syntax is: + +[source,sql] +---- +SELECT TIMESTAMP_MILLIS(BIGINT) +---- + +Its input type is a `BIGINT` expression which represents a UNIX timestamp in milliseconds and the return data type is a timestamp. + +== Examples + +=== Basic `TIMESTAMP_MILLIS()` function + +This example shows how to use the `TIMESTAMP_MILLIS()` function to convert a given UNIX timestamp in milliseconds into a timestamp without a timezone. + +[source,sql] +---- +SELECT TIMESTAMP_MILLIS(1671975000000) AS timestamp_millisvalues; +---- + +The query returns: + +[source,sql] +---- ++-----------------------------+ +| timestamp_millisvalues | ++-----------------------------+ +| 2022-12-25 13:30:00 | ++-----------------------------+ +---- + +=== `TIMESTAMP_MILLIS()` function using columns + +Suppose a table named **unix_example** has these UNIX time values in milliseconds in the *unix_timestamp* column: + +[source,sql] +---- +CREATE TABLE unix_example ( + unix_timestamp long +); + +INSERT INTO unix_example VALUES +('171472000000'), +('1671975000000'), +('153276000000'); +---- + +[source,sql] +---- +SELECT * FROM unix_example; +---- + +This query shows the table: + +[source,sql] +---- ++----------------+ +| unix_timestamp | ++----------------+ +| 171472000000 | +| 1671975000000 | +| 153276000000 | ++----------------+ +---- + +To convert all UNIX timestamp values in milliseconds to timestamp values, run the query: + +[source,sql] +---- +SELECT unix_timestamp, TIMESTAMP_MILLIS(unix_timestamp) +AS timestamp_value +FROM unix_example; +---- + +The output displays all the entries in the table in UNIX timestamp format (in milliseconds) in the **unix_timestamp** column and in the timestamp format in the column** timestamp_value** without timezone. + +[source,sql] +---- ++-------------------------+-----------------------+ +| unix_timestamp | timestamp_value | ++-------------------------+-----------------------+ +|171472000000 | 1975-06-08 15:06:40 | +|1671975000000 | 2022-12-25 13:30:00 | +|153276000000 | 1974-11-10 00:40:00 | ++-------------------------+-----------------------+ +---- diff --git a/modules/reference/pages/sql/sql-functions/timestamp-functions/timestamp-seconds.adoc b/modules/reference/pages/sql/sql-functions/timestamp-functions/timestamp-seconds.adoc new file mode 100644 index 000000000..be2a01ae2 --- /dev/null +++ b/modules/reference/pages/sql/sql-functions/timestamp-functions/timestamp-seconds.adoc @@ -0,0 +1,90 @@ += TIMESTAMP_SECONDS +:description: The TIMESTAMP_SECONDS() function converts a given UNIX timestamp value in seconds from 1970-01-01 00:00:00 UTC into a timestamp. +:page-topic-type: reference + +The `TIMESTAMP_SECONDS()` function converts a given UNIX timestamp value in seconds from 1970-01-01 00:00:00 UTC into a timestamp. Its syntax is: + +[source,sql] +---- +SELECT TIMESTAMP_SECONDS(Int64) +---- + +Its input type is an `int64` expression representing a UNIX timestamp in seconds, and the return data type is a timestamp. + +== Examples + +=== Basic `TIMESTAMP_SECONDS()` function + +This example shows how to use the `TIMESTAMP_SECONDS()` function to convert a given UNIX timestamp in seconds into a timestamp: + +[source,sql] +---- +SELECT TIMESTAMP_SECONDS(1671975000) AS timestamp_secondsvalue; +---- + +The query returns: + +[source,sql] +---- ++-----------------------------+ +| timestamp_secondsvalue | ++-----------------------------+ +| 2022-12-25 13:30:00 | ++-----------------------------+ +---- + +=== `TIMESTAMP_SECONDS()` function using columns + +Suppose a table named **unix_time** contains these UNIX time values in seconds: + +[source,sql] +---- +CREATE TABLE unix_time ( + unix_time int +); + +INSERT INTO unix_time VALUES +('982384720'), +('1671975000'), +('171472000'); +---- + +[source,sql] +---- +SELECT * FROM unix_time; +---- + +The query shows the table: + +[source,sql] +---- ++-------------+ +| unix_time | ++-------------+ +| 982384720 | +| 1671975000 | +| 171472000 | ++-------------+ +---- + +To convert all UNIX timestamp values in seconds to timestamp values, run the query: + +[source,sql] +---- +SELECT unix_time, TIMESTAMP_SECONDS(unix_time) +AS timestamp_value +FROM unix_time ; +---- + +The output displays all the entries in the table in UNIX timestamp format (in seconds) in the *unix_time* column, and in the timestamp format without timezone in the column *timestamp_value*. + +[source,sql] +---- ++-------------------------+-----------------------+ +| unix_time | timestamp_value | ++-------------------------+-----------------------+ +| 982384720 | 2001-02-17 04:38:40 | +| 1671975000 | 2022-12-25 13:30:00 | +| 171472000 | 1975-06-08 15:06:40 | ++-------------------------+-----------------------+ +---- diff --git a/modules/reference/pages/sql/sql-functions/timestamp-functions/timestamp-trunc.adoc b/modules/reference/pages/sql/sql-functions/timestamp-functions/timestamp-trunc.adoc new file mode 100644 index 000000000..8021f6f8e --- /dev/null +++ b/modules/reference/pages/sql/sql-functions/timestamp-functions/timestamp-trunc.adoc @@ -0,0 +1,83 @@ += TIMESTAMP_TRUNC +:description: The TIMESTAMP_TRUNC() function rounds a timestamp to a specific day_time granularity, resulting in a truncated timestamp. +:page-topic-type: reference + +The `TIMESTAMP_TRUNC()` function rounds a timestamp to a specific `day_time` granularity, resulting in a truncated timestamp. + +== Syntax + +[source,sql] +---- +SELECT TIMESTAMP_TRUNC(TIMESTAMP 'YYYY-MM-DD hour:min:sec', day_time); +---- + +`day_time` accepts these time values: + +* `SECOND` +* `MINUTE` +* `HOUR` +* `DAY` +* `MONTH` +* `YEAR` + +== Examples + +=== `TIMESTAMP_TRUNC()` - hour + +This example shows how to round the hour to the closest value: + +[source,sql] +---- +SELECT TIMESTAMP_TRUNC(TIMESTAMP '2017-09-18 14:43:39.02322', HOUR) ; +---- + +The final result will display the current date and time in the timezone in which the query was issued: + +[source,sql] +---- ++-----------------------------+ +| f | ++-----------------------------+ +| 2017-09-18 14:00:00.00000 | ++-----------------------------+ +---- + +=== `TIMESTAMP_TRUNC()` - minute + +This example truncates the specified timestamp into the nearest value: + +[source,sql] +---- +SELECT TIMESTAMP_TRUNC(TIMESTAMP '2005-03-18 14:13:13', MINUTE) ; +---- + +The result is the truncated timestamp: + +[source,sql] +---- ++-----------------------------+ +| f | ++-----------------------------+ +| 2005-03-18 14:13:00.00000 | ++-----------------------------+ +---- + +=== Basic `TIMESTAMP_TRUNC()` function - year + +Run this query to round the date to the closest value: + +[source,sql] +---- +SELECT TIMESTAMP_TRUNC(TIMESTAMP '2023-03-04', YEAR); +---- + +The function will truncate the year and returns: + +[source,sql] +---- ++-----------------------------+ +| f | ++-----------------------------+ +| 2023-01-01 00:00:00.00000 | ++-----------------------------+ +---- diff --git a/modules/reference/pages/sql/sql-functions/timestamp-functions/to-char.adoc b/modules/reference/pages/sql/sql-functions/timestamp-functions/to-char.adoc new file mode 100644 index 000000000..289ef28d8 --- /dev/null +++ b/modules/reference/pages/sql/sql-functions/timestamp-functions/to-char.adoc @@ -0,0 +1,165 @@ += TO_CHAR +:description: The TO_CHAR function formats various data types, including date/time, integer, float point and numeric into a formatted string. +:page-topic-type: reference + +The `TO_CHAR` function formats various data types, including `date/time`, `integer`, `float point` and `numeric` into a formatted string. + +== Syntax + +The syntax for this function is: + +.Timestamp +[source,sql] +---- +TO_CHAR(timestamp, format_string) +---- + +.Interval +[source,sql] +---- +TO_CHAR(interval, format_string) +---- + +== Arguments + +* `timestamp`: `TIMESTAMP` or `TIMESTAMP WITH TIMEZONE` value to format. +* `format`: Format of the output string. + +NOTE: If the format string is `NULL`, `TO_CHAR` returns an empty string (`''`) instead of `NULL`. This behavior is compatible with PostgreSQL. + +== Supported formats + +The string format supports these template patterns (case-insensitive): + +[width="100%",cols="49%,51%",options="header",] +|=== +|*Pattern* |*Description* +|`YYYY` |Year (1-9999) +|`MM` |Month number (01–12) +|`DD` |Day of month (01–31) +|`HH` |Hour of day (1–12) +|`HH12` |Hour of day (1–12) +|`HH24` |Hour of day (0–23) +|`MI` |Minute (0–59) +|`SS` |Second (0–59) +|`MS` |Millisecond (0–999) +|`US` |Microsecond (0–999999) +|`AM`, `am`, `PM` or `pm` |Meridiem indicator without periods +|`A.M.`, `a.m.`, `P.M.` or `p.m.` |Meridiem indicator with periods +|=== + +=== General restrictions + +* All text inside double quote `"\{text}"` will not be considered a pattern +* The quote character (`"`) will not appear in the result string +* Any text that is not a template pattern is copied verbatim (for example, preserved in the result string) + +=== Interval overflow restrictions + +Interval overflow occurs when an operation involving interval values exceeds the maximum limits of the interval data type, resulting in an error or unexpected behavior. This can happen when adding, subtracting or multiplying interval values that lead to a representation that goes beyond the allowable range for any of its components (for example, years, months, days, hours, minutes and seconds). When executing the `TO_CHAR` function for intervals, it is important to be aware of these overflow restrictions: + +[cols="<,^,^",options="header",] +|=== +|Conversion |Source Component |Target Component +|Days to Months |Days |Months +|Hours to Days |Hours |Days +|Seconds to Days |Seconds |Days +|=== + +All in all, for intervals the date overflow doesn't apply (units smaller than an hour can only overflow into hours, but not into days and so on), any excess units will not carry over to the next larger unit. + +== Examples + +=== Intervals + +This query converts an interval and displays it in a specified string format: + +.Month_to_Year +[source,sql] +---- +SELECT TO_CHAR('25 months'::INTERVAL,'"YEAR:" YYYY "MONTH:" MM') AS FORMATTED_INTERVAL; +---- + +.Hour_to_Day +[source,sql] +---- +SELECT TO_CHAR('13 days' + '49 hours'::INTERVAL, '"Day:" DD "Hour:" HH') AS FORMATTED_INTERVAL; +---- + +.Second_to_Minute +[source,sql] +---- +SELECT TO_CHAR('65 seconds'::INTERVAL, '"MINUTE": MI "SECOND": SS') AS FORMATTED_INTERVAL; +---- +Outputs: + +.Month_to_Year +[source,sql] +---- + FORMATTED_INTERVAL +--------------------------------------- +YEAR: 0002 MONTH: 01 +---- + +.Hour_to_Day +[source,sql] +---- + FORMATTED_INTERVAL +--------------------------------------- +Day: 13 Hour: 01 +---- + +.Second_to_Minute +[source,sql] +---- + FORMATTED_INTERVAL +--------------------------------------- +MINUTE: 01 SECOND: 05 +---- + +=== Timestamps + +This query retrieves the current timestamp and displays it in a specified string format: + +.Timestamp +[source,sql] +---- +SELECT TO_CHAR(CURRENT_TIMESTAMP(), '"YEAR:" YYYY "MONTH:" MM "DAY:" DD') AS FORMATTED_TIMESTAMP; +---- + +.Timestamp_with_Microseconds +[source,sql] +---- +SELECT TO_CHAR(CURRENT_TIMESTAMP(), 'YYYY-MM-DD HH24:MI:SS.US') AS FORMATTED_TIMESTAMP; +---- + +.Timestamp_with_Meridiem +[source,sql] +---- +SELECT TO_CHAR(CURRENT_TIMESTAMP(), 'YYYY-MM-DD HH12:MI:SS a.m.') AS FORMATTED_TIMESTAMP; +---- +Outputs: + +.Timestamp +[source,sql] +---- + FORMATTED_TIMESTAMP +--------------------------------------- +YEAR:2025 MONTH:01 DAY:01 +---- + +.Timestamp_with_Microseconds +[source,sql] +---- + FORMATTED_TIMESTAMP +--------------------------------------- +2025-01-01 08:08:03.001200 +---- + +.Timestamp_with_Meridiem +[source,sql] +---- + FORMATTED_TIMESTAMP +--------------------------------------- +2025-01-01 08:08:03 p.m. +---- diff --git a/modules/reference/pages/sql/sql-functions/timestamp-functions/to-timestamp.adoc b/modules/reference/pages/sql/sql-functions/timestamp-functions/to-timestamp.adoc new file mode 100644 index 000000000..0f0258e71 --- /dev/null +++ b/modules/reference/pages/sql/sql-functions/timestamp-functions/to-timestamp.adoc @@ -0,0 +1,197 @@ += TO_TIMESTAMP +:description: The TO_TIMESTAMP() function converts a string into a timestamp based on the provided format. +:page-topic-type: reference + +The `TO_TIMESTAMP()` function converts a string into a timestamp based on the provided format. It returns a `TIMESTAMP WITH TIME ZONE` type. + +== Syntax + +The syntax for using the `TO_TIMESTAMP()` function is: + +[source,sql] +---- +SELECT TO_TIMESTAMP('source', 'format'); +---- + +* `source`: The date/time value to convert. Type is `TIMESTAMP` (`YYYY-MM-DD HH:MM:SS`). +* `format`: The format of the input string. + +NOTE: If the source string is `NULL`, `TO_TIMESTAMP` returns `NULL` instead of raising an error. This behavior is compatible with PostgreSQL. + +== Format + +Format string support these template patterns (can be lowercase): + +[cols="1,2,3",options="header"] +|=== +|Pattern |Description |Detail + +|`YYYY` +|Year (1–9999) +a|- The lowest possible value is 1 AD. + + 0001 is 1. + + 1 is 1. + +|`MM` +|Month number (1–12) +a|- Up to 2 digits. + + 01 is 1. + + 1 is 1. + +|`DD` +|Day of month (1–31) +a|- Up to 2 digits. + + 01 is 1. + + 1 is 1. + +|`HH` +|Hour of day (1–12) +a|- Up to 2 digits. + + 01 is 1. + + 1 is 1. + +|`HH12` +|Hour of day (1–12) +a|- Up to 2 digits. + + 01 is 1. + + 1 is 1. + +|`HH24` +|Hour of day (0–23) +a|- Up to 2 digits. + + 01 is 1. + + 1 is 1. + +|`MI` +|Minute (0–59) +a|- Up to 2 digits. + + 01 is 1. + + 1 is 1. + +|`SS` +|Second (0–59) +a|- Up to 2 digits. + + 01 is 1. + + 1 is 1. + +|`MS` +|Millisecond (0–999) +a|- Up to 3 digits. + + 001 is 1 millisecond. + + 1 is 100 milliseconds. + +|`US` +|Microsecond (0–999999) +a|- Up to 6 digits. + + 000001 is 1 microsecond. + + 1 is 100000 microseconds. + +|`AM`, `am`, `PM` or `pm` +|Meridiem indicator +|Without periods. + +|`A.M.`, `a.m.`, `P.M.` or `p.m.` +|Meridiem indicator +|With periods. +|=== + +== Examples + +=== Timestamp into YYYY-MM-DD HH24:MI + +The `TO_TIMESTAMP()` function converts the provided string into a timestamp with the format `YYYY-MM-DD HH24:MI`. + +[source,sql] +---- +select TO_TIMESTAMP('2020-03-04 14:30', 'YYYY-MM-DD HH24:MI'); +---- + +The output is a timestamp with a timezone. + +[source,sql] +---- + to_timestamp +------------------------------- + 2020-03-04 14:30:00.000000+00 +---- + +=== Timestamp into MM-DD HH12:MI + +The `TO_TIMESTAMP()` function converts the provided string into a timestamp with the format `MM-DD HH12:MI`. + +[source,sql] +---- +select TO_TIMESTAMP('3-04 02:30', 'MM-DD HH12:MI'); +---- + +The output is a timestamp with a timezone. + +[source,sql] +---- + to_timestamp +---------------------------- + 1-03-04 02:30:00.000000+00 +---- + +=== Timestamp into YYYY-MM HH12:MI(AM/PM) + +The `TO_TIMESTAMP()` function converts the provided string into a timestamp with the format `YYYY-MM HH12:MI` with meridiem indicator (AM/PM). + +*Request 1* + +[source,sql] +---- +select TO_TIMESTAMP('2020-02 12:30AM', 'YYYY-MM HH12:MIPM'); +---- + +*Request 2* + +[source,sql] +---- +select TO_TIMESTAMP('2020-02 12:30AM', 'YYYY-MM HH:MIAM'); +---- + +The output of both requests is the same. It changes the time into a 12-hour format, resulting in *12:30* being adjusted to *00:30*. + +[source,sql] +---- + to_timestamp +------------------------------- + 2020-02-01 00:30:00.000000+00 +---- + +=== Timestamp into YYYY-MM-DD HH24:MI:SS.MS.US + +The `TO_TIMESTAMP()` function converts the provided string into a timestamp with `YYYY-MM-DD HH24:MI:SS.MS.US` format. + +[source,sql] +---- +select TO_TIMESTAMP('1960-01-31 15:12:02.020.001230', 'YYYY-MM-DD HH24:MI:SS.MS.US'); +---- + +The output is a timestamp with milliseconds and microseconds. + +[source,sql] +---- + to_timestamp +------------------------------- + 1960-01-31 15:12:02.021230+00 +---- + +=== Timestamp into YYYY-MM-DD HH24:MI:SS.MS + +The `TO_TIMESTAMP()` function converts the provided string into a timestamp with `YYYY-MM-DD HH24:MI:SS.MS` format. + +[source,sql] +---- +select TO_TIMESTAMP('1960-01-31 15:12:02.02', 'YYYY-MM-DD HH24:MI:SS.MS'); +---- + +The output is a timestamp with milliseconds. + +[source,sql] +---- + to_timestamp +------------------------------- + 1960-01-31 15:12:02.020000+00 +---- diff --git a/modules/reference/pages/sql/sql-functions/timestamp-functions/unix-micros.adoc b/modules/reference/pages/sql/sql-functions/timestamp-functions/unix-micros.adoc new file mode 100644 index 000000000..97c0b13c7 --- /dev/null +++ b/modules/reference/pages/sql/sql-functions/timestamp-functions/unix-micros.adoc @@ -0,0 +1,90 @@ += UNIX_MICROS +:description: The UNIX_MICROS() function returns a given timestamp into a UNIX timestamp in microseconds, from 1970-01-01 00:00:00-00 (can be negative). +:page-topic-type: reference + +The `UNIX_MICROS()` function returns a given timestamp into a UNIX timestamp in microseconds, from 1970-01-01 00:00:00-00 (can be negative): + +[source,sql] +---- +SELECT UNIX_MICRO(TIMESTAMP) +---- + +Its input type is a TIMESTAMP expression, and the return data type is `int64` representing time in microseconds. + +== Examples + +=== Basic `UNIX_MICROS()` function + +This example shows how to use the `UNIX_MICROS()` function to convert a given timestamp into a UNIX timestamp in microseconds: + +[source,sql] +---- +SELECT UNIX_MICRO(TIMESTAMP "2022-12-25 13:30:00+00") AS unix_microsvalues; +---- + +The query returns: + +[source,sql] +---- ++-----------------------------+ +| unix_microsvalues | ++-----------------------------+ +| 1671975000000000.000000 | ++-----------------------------+ +---- + +=== `UNIX_MICROS()` function using columns + +Suppose a table named *time_example* has these timestamp values: + +[source,sql] +---- +CREATE TABLE time_example ( + time_stamp timestamp +); + +INSERT INTO time_example VALUES +('2022-12-25 13:30:00'), +('2021-10-02 06:30:00'), +('2020-09-25 07:25:00'); +---- + +[source,sql] +---- +SELECT * FROM time_example; +---- + +This query shows the table: + +[source,sql] +---- ++-------------------------+ +| time_example | ++-------------------------+ +| 2022-12-25 13:30:00 | +| 2021-10-02 06:30:00 | +| 2020-09-25 07:25:00 | ++-------------------------+ +---- + +To convert all timestamp values into UNIX timestamp values in microseconds, run the query: + +[source,sql] +---- +SELECT time_stamp, UNIX_MICROS(time_stamp) +AS time_micros +FROM time_example; +---- + +The output displays all the timestamp entries in the *time_stamp* column and the converted UNIX timestamps in microseconds in the column *time_micros*. + +[source,sql] +---- ++-------------------------+--------------------------+ +| time_stamp | time_micros | ++-------------------------+--------------------------+ +| 2022-12-25 13:30:00 | 1671975000000000.000000 | +| 2021-10-02 06:30:00 | 1633156200000000.000000 | +| 2020-09-25 07:25:00 | 1601018700000000.000000 | ++-------------------------+--------------------------+ +---- diff --git a/modules/reference/pages/sql/sql-functions/timestamp-functions/unix-millis.adoc b/modules/reference/pages/sql/sql-functions/timestamp-functions/unix-millis.adoc new file mode 100644 index 000000000..cfc9205ef --- /dev/null +++ b/modules/reference/pages/sql/sql-functions/timestamp-functions/unix-millis.adoc @@ -0,0 +1,88 @@ += UNIX_MILLIS +:description: The UNIX_MILLIS() function returns a given timestamp to a UNIX timestamp in milliseconds from 1970-01-01 00:00:00-00 (can be negative). +:page-topic-type: reference + +The `UNIX_MILLIS()` function returns a given timestamp to a UNIX timestamp in milliseconds from 1970-01-01 00:00:00-00 (can be negative). Its syntax is: + +[source,sql] +---- +SELECT UNIX_MILLIS(TIMESTAMP) +---- + +Its input type is a TIMESTAMP expression, and the return data type is `BIGINT` representing time in milliseconds. + +== Examples + +=== Basic `UNIX_MILLIS()` function + +This example shows how to use the `UNIX_MILLIS()` function to convert a given timestamp into a UNIX timestamp in milliseconds: + +[source,sql] +---- +SELECT UNIX_MILLIS(TIMESTAMP "1996-5-02 7:15:00+00") AS unix_millisvalues; +---- + +The query returns: + +[source,sql] +---- ++-----------------------------+ +| unix_millisvalues | ++-----------------------------+ +| 831021300000.000000 | ++-----------------------------+ +---- + +=== `UNIX_MILLIS()` function using columns + +Suppose a table named **time_example** has these timestamp values in the *time_stamp* column: + +[source,sql] +---- +CREATE TABLE time_example ( + time_stamp timestamp +); + +INSERT INTO time_example VALUES +('2004-07-23 11:30:00+00'), +('2011-02-12 04:45:00+00'), +('1975-08-03 07:50:00+00'); +---- + +[source,sql] +---- +SELECT * FROM time_example; +---- + +This query shows the table: + +[source,sql] +---- ++-------------------------+ +| time_example | ++-------------------------+ +| 2004-07-23 11:30:00 | +| 2011-02-12 04:45:00 | +| 1975-08-03 07:50:00 | ++-------------------------+ +---- + +To convert all timestamp values into UNIX timestamp values in milliseconds, run the query: + +[source,sql] +---- +SELECT time_stamp, UNIX_MILLIS(time_stamp) AS time_millis FROM time_example; +---- + +The output displays all the timestamp entries of the table in the **time_stamp** column and the converted UNIX milliseconds timestamp entries in the column *time_millis*. + +[source,sql] +---- ++-------------------------+-----------------------+ +| time_stamp | time_millis | ++-------------------------+-----------------------+ +| 2004-07-23 11:30:00 | 1090582200000.000000 | +| 2011-02-12 04:45:00 | 1297485900000.000000 | +| 1975-08-03 07:50:00 | 176284200000.000000 | ++-------------------------+-----------------------+ +---- diff --git a/modules/reference/pages/sql/sql-functions/timestamp-functions/unix-seconds.adoc b/modules/reference/pages/sql/sql-functions/timestamp-functions/unix-seconds.adoc new file mode 100644 index 000000000..bfcaf6f3b --- /dev/null +++ b/modules/reference/pages/sql/sql-functions/timestamp-functions/unix-seconds.adoc @@ -0,0 +1,93 @@ += UNIX_SECONDS +:description: The UNIX_SECONDS() function returns a given timestamp to a UNIX timestamp in seconds, from 1970-01-01 00:00:00-00. +:page-topic-type: reference + +The `UNIX_SECONDS()` function returns a given timestamp to a UNIX timestamp in seconds, from 1970-01-01 00:00:00-00. Its syntax is: + +[source,sql] +---- +SELECT UNIX_SECONDS(TIMESTAMP) +---- + +Its input type is a TIMESTAMP expression, and the return data type is `BIGINT` representing time in seconds. + +== Examples + +=== Basic `UNIX_SECONDS()` function + +This example shows how to use the `UNIX_SECONDS()` function to convert a given timestamp into a UNIX timestamp in seconds: + +[source,sql] +---- +SELECT UNIX_SECONDS(TIMESTAMP "2008-12-25 15:30:00+00") AS unix_secondsvalues; +---- + +The query returns: + +[source,sql] +---- ++-----------------------------+ +| unix_secondsvalues | ++-----------------------------+ +| 1230219000.000000 | ++-----------------------------+ +---- + +=== `UNIX_SECONDS()` function using columns + +Suppose a table named **time_example** has these timestamp values in the *time_stampvalues* column: + +[source,sql] +---- +CREATE TABLE time_example ( + time_stampvalues timestamp +); + +INSERT INTO time_example VALUES +('2022-12-25 13:30:00'), +('2020-09-25 07:25:00'), +('2008-12-25 15:30:00'), +('2021-10-02 06:30:00'); +---- + +[source,sql] +---- +SELECT * FROM time_example; +---- + +The query returns the table: + +[source,sql] +---- ++-------------------------+ +| time_stampvalues | ++-------------------------+ +| 2022-12-25 13:30:00 | +| 2020-09-25 07:25:00 | +| 2008-12-25 15:30:00 | +| 2021-10-02 06:30:00 | ++-------------------------+ +---- + +. To convert all timestamp values into UNIX timestamp values in seconds, run the query: ++ +[source,sql] +---- +SELECT time_stampvalues, UNIX_SECONDS(time_stampvalues) +AS time_secondsvalues +FROM time_example; +---- + +. The output displays all the timestamp entries of the table in the *time_stampvalues* column and the converted UNIX seconds timestamp entries in the column *time_secondsvalues*. ++ +[source,sql] +---- ++-------------------------+-----------------------+ +| time_stampvalues | time_secondsvalues | ++-------------------------+-----------------------+ +| 2022-12-25 13:30:00 | 1671975000.000000 | +| 2020-09-25 07:25:00 | 1601018700.000000 | +| 2008-12-25 15:30:00 | 1230219000.000000 | +| 2021-10-02 06:30:00 | 1633156200.000000 | ++-------------------------+-----------------------+ +---- diff --git a/modules/reference/pages/sql/sql-functions/window-functions/avg.adoc b/modules/reference/pages/sql/sql-functions/window-functions/avg.adoc new file mode 100644 index 000000000..d58e2fc13 --- /dev/null +++ b/modules/reference/pages/sql/sql-functions/window-functions/avg.adoc @@ -0,0 +1,140 @@ += AVG() +:description: The AVG() window function calculates the average (arithmetic mean) of a set of numeric values within a window. +:page-topic-type: reference + +The `AVG()` window function calculates the average (arithmetic mean) of a set of numeric values within a window. This function computes averages over a set of rows that are related to the current row, such as rows within a partition of ordered set. + +== Syntax + +The syntax for this function is: + +[source,sql] +---- +AVG(expression) OVER ( + [PARTITION BY partition_expression] + ORDER BY sort_expression + [ROWS | RANGE frame_specification] +) +---- + +== Parameters + +* `expression`: Column or expression that the function operates on (must be of numeric type). +* `ROWS or RANGE`: Optional. Specifies which rows to include in the calculation relative to the current row. + +== Examples + +The examples here use a simplified version of the `film` table from the Pagila database, containing only the `title`, `length` and `rating` columns. The complete schema for the `film` table can be found on the link:https://www.postgresql.org/ftp/projects/pgFoundry/dbsamples/pagila/pagila/[Pagila^] database website. + +[source,sql] +---- +DROP TABLE IF EXISTS film; +CREATE TABLE film ( + title text NOT NULL, + length int, + rating int +); +INSERT INTO film(title, length, rating) VALUES + ('ATTRACTION NEWTON', 83, 5), + ('CHRISTMAS MOONSHINE', 150, 7), + ('DANGEROUS UPTOWN', 121, 4), + ('KILL BROTHERHOOD', 54, 3), + ('HALLOWEEN NUTS', 47, 5), + ('HOURS RAGE', 122, 7), + ('PIANIST OUTFIELD', 136, 7), + ('PICKUP DRIVING', 77, 3), + ('INDEPENDENCE HOTEL', 157, 7), + ('PRIVATE DROP', 106, 4), + ('SAINTS BRIDE', 125, 3), + ('FOREVER CANDIDATE', 131, 7), + ('MILLION ACE', 142, 5), + ('SLEEPY JAPANESE', 137, 4), + ('WRATH MILE', 176, 7), + ('YOUTH KICK', 179, 7), + ('CLOCKWORK PARADISE', 143, 5); +---- + +=== Rolling average by rating + +This query uses the `AVG()` function to calculate the rolling average of `length` as rows are ordered by `rating`: + +[source,sql] +---- +SELECT + rating, + length, + AVG(length) OVER (ORDER BY rating) AS RollingAverageLength +FROM film +WHERE length IS NOT NULL +ORDER BY rating; +---- + +This query produces the output: + +[source,sql] +---- + rating | length | rollingaveragelength +--------+--------+---------------------- + 3 | 77 | 85.33333333333333 + 3 | 125 | 85.33333333333333 + 3 | 54 | 85.33333333333333 + 4 | 121 | 103.33333333333333 + 4 | 106 | 103.33333333333333 + 4 | 137 | 103.33333333333333 + 5 | 83 | 103.5 + 5 | 142 | 103.5 + 5 | 47 | 103.5 + 5 | 143 | 103.5 + 7 | 157 | 122.70588235294117 + 7 | 179 | 122.70588235294117 + 7 | 176 | 122.70588235294117 + 7 | 131 | 122.70588235294117 + 7 | 136 | 122.70588235294117 + 7 | 122 | 122.70588235294117 + 7 | 150 | 122.70588235294117 +(17 rows) +---- + +=== Time series: rolling average length over last 3 ratings + +This example demonstrates a time series-style rolling average using a window frame of the current row and the two preceding rows, ordered by rating. This simulates a moving average over a sliding window of 3 rows: + +[source,sql] +---- +SELECT + rating, + length, + AVG(length) OVER ( + ORDER BY rating + ROWS BETWEEN 2 PRECEDING AND CURRENT ROW + ) AS rolling_avg_length_3 +FROM film +WHERE length IS NOT NULL +ORDER BY rating; +---- + +This query calculates the average length over the current rating and the two previous ratings (based on ordering by rating) smoothing the fluctuations by averaging over a fixed-size window: + +[source,sql] +---- + rating | length | rolling_avg_length_3 +--------+--------+---------------------- + 3 | 77 | 65.5 + 3 | 125 | 85.33333333333333 + 3 | 54 | 54 + 4 | 121 | 107.66666666666667 + 4 | 106 | 117.33333333333333 + 4 | 137 | 121.33333333333333 + 5 | 83 | 91 + 5 | 142 | 90.66666666666667 + 5 | 47 | 109 + 5 | 143 | 128.66666666666666 + 7 | 157 | 127.33333333333333 + 7 | 179 | 159.33333333333334 + 7 | 176 | 170.66666666666666 + 7 | 131 | 162 + 7 | 136 | 147.66666666666666 + 7 | 122 | 129.66666666666666 + 7 | 150 | 136 +(17 rows) +---- diff --git a/modules/reference/pages/sql/sql-functions/window-functions/bool-and.adoc b/modules/reference/pages/sql/sql-functions/window-functions/bool-and.adoc new file mode 100644 index 000000000..d1ad4a937 --- /dev/null +++ b/modules/reference/pages/sql/sql-functions/window-functions/bool-and.adoc @@ -0,0 +1,92 @@ += BOOL_AND() +:description: The BOOL_AND() window function evaluates whether all values within a specified window of rows are TRUE. +:page-topic-type: reference + +The `BOOL_AND()` window function evaluates whether all values within a specified window of rows are `TRUE`. + +== Syntax + +The syntax for this function is: + +[source,sql] +---- +BOOL_AND (expression) OVER ( + [PARTITION BY partition_expression] + ORDER BY sort_expression +) +---- + +== Parameters + +* `expression`: Column or expression that the function operates on. It should evaluate to a boolean value (`TRUE` or `FALSE`). + +== Examples + +This example uses a simplified version of the `film` table from the Pagila database, containing only the `title`, `length` and `rating` columns. The complete schema for the `film` table can be found on the link:https://www.postgresql.org/ftp/projects/pgFoundry/dbsamples/pagila/pagila/[Pagila^] database website. + +[source,sql] +---- +DROP TABLE IF EXISTS film; +CREATE TABLE film ( + title text NOT NULL, + length int, + rating text +); +INSERT INTO film(title, length, rating) VALUES + ('ATTRACTION NEWTON', 83, 'PG-13'), + ('CHRISTMAS MOONSHINE', 150, 'NC-17'), + ('DANGEROUS UPTOWN', 121, 'PG'), + ('KILL BROTHERHOOD', 54, 'G'), + ('HALLOWEEN NUTS', 47, 'PG-13'), + ('HOURS RAGE', 122, 'NC-17'), + ('PIANIST OUTFIELD', 136, 'NC-17'), + ('PICKUP DRIVING', 77, 'G'), + ('INDEPENDENCE HOTEL', 157, 'NC-17'), + ('PRIVATE DROP', 106, 'PG'), + ('SAINTS BRIDE', 125, 'G'), + ('FOREVER CANDIDATE', 131, 'NC-17'), + ('MILLION ACE', 142, 'PG-13'), + ('SLEEPY JAPANESE', 137, 'PG'), + ('WRATH MILE', 176, 'NC-17'), + ('YOUTH KICK', 179, 'NC-17'), + ('CLOCKWORK PARADISE', 143, 'PG-13'); +---- + +This query uses the `BOOL_AND()` function to evaluate if all films in each rating category have a length greater than 100: + +[source,sql] +---- +SELECT + title, + length, + rating, + BOOL_AND(length > 100) OVER (PARTITION BY rating) as ALLlongFilmsByRating +FROM film +ORDER BY rating; +---- + +The query returns: + +[source,sql] +---- + title | length | rating | alllongfilmsbyrating +---------------------+--------+--------+---------------------- + KILL BROTHERHOOD | 54 | G | f + PICKUP DRIVING | 77 | G | f + SAINTS BRIDE | 125 | G | f + CHRISTMAS MOONSHINE | 150 | NC-17 | t + HOURS RAGE | 122 | NC-17 | t + PIANIST OUTFIELD | 136 | NC-17 | t + INDEPENDENCE HOTEL | 157 | NC-17 | t + FOREVER CANDIDATE | 131 | NC-17 | t + WRATH MILE | 176 | NC-17 | t + YOUTH KICK | 179 | NC-17 | t + DANGEROUS UPTOWN | 121 | PG | t + PRIVATE DROP | 106 | PG | t + SLEEPY JAPANESE | 137 | PG | t + ATTRACTION NEWTON | 83 | PG-13 | f + HALLOWEEN NUTS | 47 | PG-13 | f + MILLION ACE | 142 | PG-13 | f + CLOCKWORK PARADISE | 143 | PG-13 | f +(17 rows) +---- diff --git a/modules/reference/pages/sql/sql-functions/window-functions/bool-or.adoc b/modules/reference/pages/sql/sql-functions/window-functions/bool-or.adoc new file mode 100644 index 000000000..2deee927c --- /dev/null +++ b/modules/reference/pages/sql/sql-functions/window-functions/bool-or.adoc @@ -0,0 +1,92 @@ += BOOL_OR() +:description: The BOOL_OR() window function evaluates whether at least one value within a specified window of rows is TRUE. +:page-topic-type: reference + +The `BOOL_OR()` window function evaluates whether at least one value within a specified window of rows is `TRUE`. + +== Syntax + +The syntax for this function is: + +[source,sql] +---- +BOOL_OR (expression) OVER ( + [PARTITION BY partition_expression] + ORDER BY sort_expression +) +---- + +== Parameters + +* `expression`: Column or expression that the function operates on. It should evaluate to a boolean value (`TRUE` or `FALSE`). + +== Examples + +This example uses a simplified version of the `film` table from the Pagila database, containing only the `title`, `length` and `rating` columns. The complete schema for the `film` table can be found on the link:https://www.postgresql.org/ftp/projects/pgFoundry/dbsamples/pagila/pagila/[Pagila^] database website. + +[source,sql] +---- +DROP TABLE IF EXISTS film; +CREATE TABLE film ( + title text NOT NULL, + length int, + rating text +); +INSERT INTO film(title, length, rating) VALUES + ('ATTRACTION NEWTON', 83, 'PG-13'), + ('CHRISTMAS MOONSHINE', 150, 'NC-17'), + ('DANGEROUS UPTOWN', 121, 'PG'), + ('KILL BROTHERHOOD', 54, 'G'), + ('HALLOWEEN NUTS', 47, 'PG-13'), + ('HOURS RAGE', 122, 'NC-17'), + ('PIANIST OUTFIELD', 136, 'NC-17'), + ('PICKUP DRIVING', 77, 'G'), + ('INDEPENDENCE HOTEL', 157, 'NC-17'), + ('PRIVATE DROP', 106, 'PG'), + ('SAINTS BRIDE', 125, 'G'), + ('FOREVER CANDIDATE', 131, 'NC-17'), + ('MILLION ACE', 142, 'PG-13'), + ('SLEEPY JAPANESE', 137, 'PG'), + ('WRATH MILE', 176, 'NC-17'), + ('YOUTH KICK', 179, 'NC-17'), + ('CLOCKWORK PARADISE', 143, 'PG-13'); +---- + +This query uses the `BOOL_OR()` function to evaluate whether at least one film in each rating category have a length greater than 150: + +[source,sql] +---- +SELECT + title, + length, + rating, + BOOL_OR(length > 150) OVER (PARTITION BY rating) as ALLleastOneLongFilmsByRating +FROM film +ORDER BY rating; +---- + +The query returns: + +[source,sql] +---- + title | length | rating | allleastonelongfilmsbyrating +---------------------+--------+--------+------------------------------ + KILL BROTHERHOOD | 54 | G | f + PICKUP DRIVING | 77 | G | f + SAINTS BRIDE | 125 | G | f + CHRISTMAS MOONSHINE | 150 | NC-17 | t + HOURS RAGE | 122 | NC-17 | t + PIANIST OUTFIELD | 136 | NC-17 | t + INDEPENDENCE HOTEL | 157 | NC-17 | t + FOREVER CANDIDATE | 131 | NC-17 | t + WRATH MILE | 176 | NC-17 | t + YOUTH KICK | 179 | NC-17 | t + DANGEROUS UPTOWN | 121 | PG | f + PRIVATE DROP | 106 | PG | f + SLEEPY JAPANESE | 137 | PG | f + ATTRACTION NEWTON | 83 | PG-13 | f + HALLOWEEN NUTS | 47 | PG-13 | f + MILLION ACE | 142 | PG-13 | f + CLOCKWORK PARADISE | 143 | PG-13 | f +(17 rows) +---- diff --git a/modules/reference/pages/sql/sql-functions/window-functions/count.adoc b/modules/reference/pages/sql/sql-functions/window-functions/count.adoc new file mode 100644 index 000000000..d4c0b9747 --- /dev/null +++ b/modules/reference/pages/sql/sql-functions/window-functions/count.adoc @@ -0,0 +1,156 @@ += COUNT() +:description: The COUNT() window function retrieves the number of records that meet a specific criteria. +:page-topic-type: reference + +The `COUNT()` window function retrieves the number of records that meet a specific criteria. When using it with the `RANGE` clause, it performs counts within a defined range based on the values of the current row. This function can be used with all xref:reference:sql/sql-data-types/index.adoc[data types supported by Redpanda SQL]. + +== Syntax + +There are two available variants of that function: + +* `COUNT(*)`: Counts all rows in the target table, regardless of whether they contain NULL values or not. +* `COUNT(expression)`: Counts the number of non-NULL values in a specific column or expression. + +The syntax for this function is: + +[source,sql] +---- +COUNT(expression) OVER ( + [PARTITION BY partition_expression] + ORDER BY sort_expression + [ROWS | RANGE BETWEEN start_value AND end_value] +) +---- + +The `COUNT()` window function always return `BIGINT` as an output, which represents the total number of rows in a table irrespective of the input types. + +== Parameters + +* `expression`: Column or expression. +* `PARTITION BY`: Optional. Divides the result set into partitions, each processed independently. If omitted, the entire result set is treated as a single partition. +* `ROWS | RANGE BETWEEN`: Range-based window frame relative to the current row. + +== Examples + +The following examples use a `winsales` table that stores the details of some sales transactions: + +[source,sql] +---- +CREATE TABLE winsales( + salesid int, + dateid date, + sellerid int, + buyerid text, + qty int, + qty_shipped int); +INSERT INTO winsales VALUES + (30001, '8/2/2003', 3, 'b', 10, 10), + (10001, '12/24/2003', 1, 'c', 10, 10), + (10005, '12/24/2003', 1, 'a', 30, null), + (40001, '1/9/2004', 4, 'a', 40, null), + (10006, '1/18/2004', 1, 'c', 10, null), + (20001, '2/12/2004', 2, 'b', 20, 20), + (40005, '2/12/2004', 4, 'a', 10, 10), + (20002, '2/16/2004', 2, 'c', 20, 20), + (30003, '4/18/2004', 3, 'b', 15, null), + (30004, '4/18/2004', 3, 'b', 20, null), + (30007, '9/7/2004', 3, 'c', 30, null); +---- + +=== COUNT(*) + +This example executes the variant of this function that counts all rows in the target table: + +[source,sql] +---- +SELECT salesid, qty + COUNT(*) OVER (ORDER BY salesid rows unbounded preceding) AS count +FROM winsales +ORDER BY salesid; +---- + +The output displays the sales ID, quantity and the count of all rows from the start of the data window: + +[source,sql] +---- + salesid | qty | count +---------+-----+------- + 10001 | 10 | 1 + 10005 | 30 | 2 + 10006 | 10 | 3 + 20001 | 20 | 4 + 20002 | 20 | 5 + 30001 | 10 | 6 + 30003 | 15 | 7 + 30004 | 20 | 8 + 30007 | 30 | 9 + 40001 | 40 | 10 + 40005 | 10 | 11 +(11 rows) +---- + +=== Count(expression) + +This example executes the variant of this function that counts the number of non-NULL values in a specific expression: + +[source,sql] +---- +SELECT salesid, qty, qty_shipped, + COUNT(qty_shipped) OVER (ORDER BY salesid rows unbounded preceding) AS count +FROM winsales +ORDER BY salesid; +---- + +The query returns: + +[source,sql] +---- + salesid | qty | qty_shipped | count +---------+-----+-------------+------- + 10001 | 10 | 10 | 1 + 10005 | 30 | | 1 + 10006 | 10 | | 1 + 20001 | 20 | 20 | 2 + 20002 | 20 | 20 | 3 + 30001 | 10 | 10 | 4 + 30003 | 15 | | 4 + 30004 | 20 | | 4 + 30007 | 30 | | 4 + 40001 | 40 | | 4 + 40005 | 10 | 10 | 5 +(11 rows) +---- + +=== Time series: COUNT(*) with RANGE for last 90 days + +This example demonstrates counting the number of sales within a 90-day window prior to each sale, based on `dateid`: + +[source,sql] +---- +SELECT salesid, dateid, qty, + COUNT(*) OVER ( + ORDER BY dateid + RANGE BETWEEN INTERVAL '90 days' PRECEDING AND CURRENT ROW + ) AS sales_count_90d +FROM winsales +ORDER BY dateid; +---- + +This query counts the number of sales transactions within a 90-day window before each `dateid`, including the current sale: + +[source,sql] +---- + salesid | dateid | qty | sales_count_90d +---------+------------+-----+----------------- + 30001 | 2003-08-02 | 10 | 1 + 10001 | 2003-12-24 | 10 | 2 + 10005 | 2003-12-24 | 30 | 2 + 40001 | 2004-01-09 | 40 | 3 + 10006 | 2004-01-18 | 10 | 4 + 20001 | 2004-02-12 | 20 | 6 + 40005 | 2004-02-12 | 10 | 6 + 20002 | 2004-02-16 | 20 | 7 + 30003 | 2004-04-18 | 15 | 5 + 30004 | 2004-04-18 | 20 | 5 + 30007 | 2004-09-07 | 30 | 1 +---- diff --git a/modules/reference/pages/sql/sql-functions/window-functions/cume-dist.adoc b/modules/reference/pages/sql/sql-functions/window-functions/cume-dist.adoc new file mode 100644 index 000000000..d4d3a08fa --- /dev/null +++ b/modules/reference/pages/sql/sql-functions/window-functions/cume-dist.adoc @@ -0,0 +1,90 @@ += CUME_DIST() +:description: The CUME_DIST() function is a window function used to calculate the cumulative distribution of a value within a set of values. +:page-topic-type: reference + +The `CUME_DIST()` function is a window function used to calculate the cumulative distribution of a value within a set of values. This function returns a value between 0 and 1, representing a relative position of a row within a partition or result set. + +== Syntax + +The syntax for this function is: + +[source,sql] +---- +CUME_DIST() OVER ( + [PARTITION BY partition_expression, ... ] + ORDER BY sort_expression [ASC | DESC], ... +---- + +== Parameters + +* `()`: This function does not take any arguments, but the parentheses are required. + +== Examples + +The following example uses a simplified version of the `film` table from the link:https://www.postgresql.org/ftp/projects/pgFoundry/dbsamples/pagila/pagila/[Pagila database^], containing only the `title`, `length` and `rating` columns. + +[source,sql] +---- +DROP TABLE IF EXISTS film; +CREATE TABLE film ( + title text NOT NULL, + length int, + rating text +); + +INSERT INTO film(title, length, rating) VALUES + ('ATTRACTION NEWTON', 83, 'PG-13'), + ('CHRISTMAS MOONSHINE', 150, 'NC-17'), + ('DANGEROUS UPTOWN', 121, 'PG'), + ('KILL BROTHERHOOD', 54, 'G'), + ('HALLOWEEN NUTS', 47, 'PG-13'), + ('HOURS RAGE', 122, 'NC-17'), + ('PIANIST OUTFIELD', 136, 'NC-17'), + ('PICKUP DRIVING', 77, 'G'), + ('INDEPENDENCE HOTEL', 157, 'NC-17'), + ('PRIVATE DROP', 106, 'PG'), + ('SAINTS BRIDE', 125, 'G'), + ('FOREVER CANDIDATE', 131, 'NC-17'), + ('MILLION ACE', 142, 'PG-13'), + ('SLEEPY JAPANESE', 137, 'PG'), + ('WRATH MILE', 176, 'NC-17'), + ('YOUTH KICK', 179, 'NC-17'), + ('CLOCKWORK PARADISE', 143, 'PG-13'); +---- + +The following query uses the `CUME_DIST()` function to calculate the cumulative distribution of film lengths: + +[source,sql] +---- +SELECT + title, + length, + CUME_DIST() OVER (ORDER BY length) AS cume_dist +FROM film; +---- + +The query returns: + +[source,sql] +---- + title | length | cume_dist +---------------------+--------+---------------------- + HALLOWEEN NUTS | 47 | 0.058823529411764705 + KILL BROTHERHOOD | 54 | 0.11764705882352941 + PICKUP DRIVING | 77 | 0.17647058823529413 + ATTRACTION NEWTON | 83 | 0.23529411764705882 + PRIVATE DROP | 106 | 0.29411764705882354 + DANGEROUS UPTOWN | 121 | 0.35294117647058826 + HOURS RAGE | 122 | 0.4117647058823529 + SAINTS BRIDE | 125 | 0.47058823529411764 + FOREVER CANDIDATE | 131 | 0.5294117647058824 + PIANIST OUTFIELD | 136 | 0.5882352941176471 + SLEEPY JAPANESE | 137 | 0.6470588235294118 + MILLION ACE | 142 | 0.7058823529411765 + CLOCKWORK PARADISE | 143 | 0.7647058823529411 + CHRISTMAS MOONSHINE | 150 | 0.8235294117647058 + INDEPENDENCE HOTEL | 157 | 0.8823529411764706 + WRATH MILE | 176 | 0.9411764705882353 + YOUTH KICK | 179 | 1 +(17 rows) +---- diff --git a/modules/reference/pages/sql/sql-functions/window-functions/dense-rank.adoc b/modules/reference/pages/sql/sql-functions/window-functions/dense-rank.adoc new file mode 100644 index 000000000..e51d53e12 --- /dev/null +++ b/modules/reference/pages/sql/sql-functions/window-functions/dense-rank.adoc @@ -0,0 +1,122 @@ += DENSE_RANK() +:description: The DENSE_RANK() window function assigns a rank for each value within a specified group, based on the ORDER BY expression in the OVER clause. +:page-topic-type: reference + +The `DENSE_RANK()` window function assigns a rank for each value within a specified group, based on the `ORDER BY` expression in the `OVER` clause. Unlike the `RANK()` function, which can leave gaps in the ranking sequence when there are ties, `DENSE_RANK()` provides consecutive rank values without any gaps. This function can be used with all xref:reference:sql/sql-data-types/index.adoc[data types supported by Redpanda SQL]. + +== Syntax + +The syntax for this function is: + +[source,sql] +---- +DENSE_RANK() OVER ( + [PARTITION BY partition_expression] + ORDER BY sort_expression +) +---- + +The output type for this function is a `BIGINT` and it indicates the rank of values in a table, regardless of the input types. If the `ORDER BY` expression is omitted, all ranks default to 1. If an optional `PARTITION BY` expression is included, the rankings are reset for each group of rows. The rows with equal values for the ranking criteria receive the same rank. + +[NOTE] +==== +Unlike `RANK()` function, there is no gap in the sequence of ranked values (if two rows are ranked 1, the next rank is 2) +==== + +== Parameters + +* `()`: This function does not take any arguments, but the parentheses are required. +* `PARTITION BY`: Optional. Divides the result set into partitions, each processed independently. If omitted, the entire result set is treated as a single partition. +* `ORDER BY`: Order of rows in each partition to which the function is applied. + +== Examples + +The following examples use a `winsales` table that stores information about some sales transactions: + +[source,sql] +---- +CREATE TABLE winsales( + salesid int, + dateid date, + sellerid int, + buyerid text, + qty int, + qty_shipped int); +INSERT INTO winsales VALUES + (30001, '8/2/2003', 3, 'b', 10, 10), + (10001, '12/24/2003', 1, 'c', 10, 10), + (10005, '12/24/2003', 1, 'a', 30, null), + (40001, '1/9/2004', 4, 'a', 40, null), + (10006, '1/18/2004', 1, 'c', 10, null), + (20001, '2/12/2004', 2, 'b', 20, 20), + (40005, '2/12/2004', 4, 'a', 10, 10), + (20002, '2/16/2004', 2, 'c', 20, 20), + (30003, '4/18/2004', 3, 'b', 15, null), + (30004, '4/18/2004', 3, 'b', 20, null), + (30007, '9/7/2004', 3, 'c', 30, null); +---- + +=== DENSE_RANK() with ORDER BY + +This example executes the `DENSE_RANK()` function with `ORDER BY` keyword and calculates the descending dense rank of all rows based on the quantity sold: + +[source,sql] +---- +SELECT salesid, qty + Dense_RANK() OVER (ORDER BY qty DESC) AS d_rnk + RANK() OVER (ORDER BY qty DESC) AS rnk +FROM winsales +ORDER BY 2,1; +---- + +Output that includes the sales ID along with the quantity sold and both dense and regular ranks: + +[source,sql] +---- + salesid | qty | d_rnk | rnk +---------+-----+-------+----- + 10001 | 10 | 5 | 8 + 10006 | 10 | 5 | 8 + 30001 | 10 | 5 | 8 + 40005 | 10 | 5 | 8 + 30003 | 15 | 4 | 7 + 20001 | 20 | 3 | 4 + 20002 | 20 | 3 | 4 + 30004 | 20 | 3 | 4 + 10005 | 30 | 2 | 2 + 30007 | 30 | 2 | 2 + 40001 | 40 | 1 | 1 +(11 rows) +---- + +=== DENSE_RANK() with ORDER BY and PARTITION_BY + +This example executes the `DENSE_RANK()` function with `ORDER BY` keyword and `PARTITION BY` clause, partitions the table by seller ID, orders each partition by the quantity, and assigns a dense rank to each row: + +[source,sql] +---- +SELECT salesid, sellerid, qty + DENSE_RANK() OVER (PARTITION BY sellerid ORDER BY qty DESC) AS d_rnk +FROM winsales +ORDER BY 2,3,1; +---- + +The query returns: + +[source,sql] +---- + salesid | sellerid | qty | d_rnk +---------+----------+-----+------- + 10001 | 1 | 10 | 2 + 10006 | 1 | 10 | 2 + 10005 | 1 | 30 | 1 + 20001 | 2 | 20 | 1 + 20002 | 2 | 20 | 1 + 30001 | 3 | 10 | 4 + 30003 | 3 | 15 | 3 + 30004 | 3 | 20 | 2 + 30007 | 3 | 30 | 1 + 40005 | 4 | 10 | 2 + 40001 | 4 | 40 | 1 +(11 rows) +---- diff --git a/modules/reference/pages/sql/sql-functions/window-functions/first-value.adoc b/modules/reference/pages/sql/sql-functions/window-functions/first-value.adoc new file mode 100644 index 000000000..be5be6694 --- /dev/null +++ b/modules/reference/pages/sql/sql-functions/window-functions/first-value.adoc @@ -0,0 +1,99 @@ += FIRST_VALUE() +:description: The FIRST_VALUE() is a window function that retrieves the first value in an ordered set of values within a specified partition. +:page-topic-type: reference + +The `FIRST_VALUE()` is a window function that retrieves the first value in an ordered set of values within a specified partition. + +== Syntax + +The syntax for this function is: + +[source,sql] +---- +FIRST_VALUE(expression) OVER ( + [PARTITION BY partition_expression] + ORDER BY sort_expression + RANGE BETWEEN start_value AND end_value +) +---- + +== Parameters + +* `expression`: Target column or expression. +* `PARTITION BY`: Optional. Divides the result set into partitions, each processed independently. If omitted, the entire result set is treated as a single partition. +* `ORDER BY`: Order of rows in each partition to which the function is applied. +* `RANGE BETWEEN`: Range-based window frame relative to the current row. + +== Examples + +The following example uses a simplified version of the `film` table from the Pagila database, containing only the `title`, `length` and `rating` columns. The complete schema for the `film` table can be found on the link:https://www.postgresql.org/ftp/projects/pgFoundry/dbsamples/pagila/pagila/[Pagila^] database website. + +[source,sql] +---- +DROP TABLE IF EXISTS film; +CREATE TABLE film ( + title text NOT NULL, + length int, + rating text +); +INSERT INTO film(title, length, rating) VALUES + ('ATTRACTION NEWTON', 83, 'PG-13'), + ('CHRISTMAS MOONSHINE', 150, 'NC-17'), + ('DANGEROUS UPTOWN', 121, 'PG'), + ('KILL BROTHERHOOD', 54, 'G'), + ('HALLOWEEN NUTS', 47, 'PG-13'), + ('HOURS RAGE', 122, 'NC-17'), + ('PIANIST OUTFIELD', 136, 'NC-17'), + ('PICKUP DRIVING', 77, 'G'), + ('INDEPENDENCE HOTEL', 157, 'NC-17'), + ('PRIVATE DROP', 106, 'PG'), + ('SAINTS BRIDE', 125, 'G'), + ('FOREVER CANDIDATE', 131, 'NC-17'), + ('MILLION ACE', 142, 'PG-13'), + ('SLEEPY JAPANESE', 137, 'PG'), + ('WRATH MILE', 176, 'NC-17'), + ('YOUTH KICK', 179, 'NC-17'), + ('CLOCKWORK PARADISE', 143, 'PG-13'); +---- + +The following query uses the `FIRST_VALUE()` function to retrieve the title of the film with the shortest duration, partitioning results by rating and ordering by length. + +[source,sql] +---- +SELECT + title, + length, + rating, + FIRST_VALUE(title) OVER ( + PARTITION BY rating + ORDER BY + length ASC ROWS BETWEEN UNBOUNDED PRECEDING + AND UNBOUNDED FOLLOWING + ) AS shortest_film_in_rating +FROM film; +---- + +The query returns: + +[source,bash] +---- +| title | length | rating | shortest_film_in_rating | +|---------------------|------------|------------|-----------------------------| +| KILL BROTHERHOOD | 54 | G | KILL BROTHERHOOD | +| PICKUP DRIVING | 77 | G | KILL BROTHERHOOD | +| SAINTS BRIDE | 125 | G | KILL BROTHERHOOD | +| HOURS RAGE | 122 | NC-17 | HOURS RAGE | +| FOREVER CANDIDATE | 131 | NC-17 | HOURS RAGE | +| PIANIST OUTFIELD | 136 | NC-17 | HOURS RAGE | +| CHRISTMAS MOONSHINE | 150 | NC-17 | HOURS RAGE | +| INDEPENDENCE HOTEL | 157 | NC-17 | HOURS RAGE | +| WRATH MILE | 176 | NC-17 | HOURS RAGE | +| YOUTH KICK | 179 | NC-17 | HOURS RAGE | +| PRIVATE DROP | 106 | PG | PRIVATE DROP | +| DANGEROUS UPTOWN | 121 | PG | PRIVATE DROP | +| SLEEPY JAPANESE | 137 | PG | PRIVATE DROP | +| HALLOWEEN NUTS | 47 | PG-13 | HALLOWEEN NUTS | +| ATTRACTION NEWTON | 83 | PG-13 | HALLOWEEN NUTS | +| MILLION ACE | 142 | PG-13 | HALLOWEEN NUTS | +| CLOCKWORK PARADISE | 143 | PG-13 | HALLOWEEN NUTS | +---- diff --git a/modules/reference/pages/sql/sql-functions/window-functions/index.adoc b/modules/reference/pages/sql/sql-functions/window-functions/index.adoc new file mode 100644 index 000000000..eb6e98879 --- /dev/null +++ b/modules/reference/pages/sql/sql-functions/window-functions/index.adoc @@ -0,0 +1,67 @@ += Overview +:description: Window functions is a group of SQL functions, that operate on a partition or "window" of a result set, returning values for every row within that wind + +Window functions is a group of SQL functions, that operate on a partition or "`window`" of a result set, returning values for every row within that window. Redpanda SQL supports the following window functions and clauses: + +== Window functions + +[width="100%",cols="40%,60%",options="header",] +|=== +|Function Name |Description +|xref:reference:sql/sql-functions/window-functions/count.adoc[COUNT] |Counts all the rows or those specified by the given expression +|xref:reference:sql/sql-functions/window-functions/avg.adoc[AVG] |Calculates the average (arithmetic mean) of a set of numeric values within a window +|xref:reference:sql/sql-functions/window-functions/sum.adoc[SUM] |Calculates and returns the sum of values from the input column or expression values +|xref:reference:sql/sql-functions/window-functions/min.adoc[MIN] |Computes the minimum value of an expression across a set of rows +|xref:reference:sql/sql-functions/window-functions/max.adoc[MAX] |Computes the maximum value of an expression across a set of rows +|xref:reference:sql/sql-functions/window-functions/bool-and.adoc[BOOL_AND] |Evaluates whether all values within a specified window of rows are true +|xref:reference:sql/sql-functions/window-functions/bool-or.adoc[BOOL_OR] |Evaluates whether at least one value within a specified window of rows is true +|=== + +== Ranking functions + +[width="100%",cols="<40%,<60%",options="header",] +|=== +|*Function Name* |*Description* +|xref:reference:sql/sql-functions/window-functions/row-number.adoc[ROW_NUMBER] |Returns the current row index within its partition (beginning with 1) +|xref:reference:sql/sql-functions/window-functions/rank.adoc[RANK] |Calculates and returns the rank of a value within a specified group of values +|xref:reference:sql/sql-functions/window-functions/dense-rank.adoc[DENSE_RANK] |Calculates the percent rank of a value within a group and returns the result +|xref:reference:sql/sql-functions/window-functions/ntile.adoc[NTILE] |Divides an ordered data set into a specified number of approximately equal groups +|=== + +== Distribution functions + +[width="100%",cols="<40%,<60%",options="header",] +|=== +|*Function Name* |*Description* +|xref:reference:sql/sql-functions/window-functions/cume-dist.adoc[CUME_DIST] |Calculates the cumulative distribution of a value within a set of values +|xref:reference:sql/sql-functions/window-functions/percent-rank.adoc[PERCENT_RANK] |Calculates and returns the percent rank of a value within a specified group of values +|=== + +== Value functions + +[width="100%",cols="<40%,<60%",options="header",] +|=== +|*Function Name* |*Description* +|xref:reference:sql/sql-functions/window-functions/first-value.adoc[FIRST_VALUE] |Returns the first value in an ordered set of values within a specified partition +|xref:reference:sql/sql-functions/window-functions/last-value.adoc[LAST_VALUE] |Returns the last value in an ordered set of values within a specified partition +|xref:reference:sql/sql-functions/window-functions/nth-value.adoc[NTH_VALUE] |Returns a value from the nth row in an ordered partition of a result set +|xref:reference:sql/sql-functions/window-functions/lag.adoc[LAG] |Returns the values for a row located at a defined offset, either above or below the current row within the partition +|xref:reference:sql/sql-functions/window-functions/lead.adoc[LEAD] |Returns the values for a row located at a defined offset, either above or below the current row within the partition +|=== + +== Window clause + +[width="100%",cols="<35%,<65%",options="header",] +|=== +|*Clause Name* |*Description* +|xref:reference:sql/sql-clauses/over-window.adoc[OVER] |Defines the window specification and is mandatory for window functions +|xref:reference:sql/sql-clauses/over-window.adoc[WINDOW] |Optional clause that defines one or more named window specifications +|=== + +== Important notes + +There are a few essential things to remember when using window functions in Redpanda SQL: + +* Verify that you can effectively use window functions alongside the `PARTITION BY`, `ORDER BY` and `FRAME` clauses as part of your window specification +* Ensure the window specification chaining is supported by executing the following command: `SELECT SUM(i0) OVER w2 FROM tb1 WINDOW w1 AS (PARTITION BY i1), w2 AS (w1 ROWS CURRENT ROW)` +* The `FRAME` clause of the window specification is restricted to the `ROWS` clause and does not include frame exclusion diff --git a/modules/reference/pages/sql/sql-functions/window-functions/lag.adoc b/modules/reference/pages/sql/sql-functions/window-functions/lag.adoc new file mode 100644 index 000000000..6806cada7 --- /dev/null +++ b/modules/reference/pages/sql/sql-functions/window-functions/lag.adoc @@ -0,0 +1,143 @@ += LAG() +:description: The LAG() window function returns the values from specific rows based on the offset argument (previous to the current row in the partition). +:page-topic-type: reference + +The `LAG()` window function returns the values from specific rows based on the offset argument (previous to the current row in the partition). It can be used with all xref:reference:sql/sql-data-types/index.adoc[data types supported by Redpanda SQL] + +== Syntax + +The syntax for this function is: + +[source,sql] +---- +LAG (expression, offset, default) +OVER ( + [PARTITION BY partition_expression, ... ] + ORDER BY sort_expression [ASC | DESC], ... +) +---- + +The function returns a value of the same data type as the input. If no row meets the offset criteria, the function returns a default value, which must be of a type compatible with the expression. + +== Parameters + +* `expression`: Column to reference. +* `offset`: Optional. Number of rows behind the current row. Defaults to `1`. +* `default`: Optional. Value to return if the `offset` is out of range. Defaults to `NULL`. + +== Examples + +The following examples use the `winsales` table that stores details about some sales transactions: + +[source,sql] +---- +CREATE TABLE winsales( + salesid int, + dateid date, + sellerid int, + buyerid text, + qty int, + qty_shipped int); +INSERT INTO winsales VALUES + (30001, '8/2/2003', 3, 'b', 10, 10), + (10001, '12/24/2003', 1, 'c', 10, 10), + (10005, '12/24/2003', 1, 'a', 30, null), + (40001, '1/9/2004', 4, 'a', 40, null), + (10006, '1/18/2004', 1, 'c', 10, null), + (20001, '2/12/2004', 2, 'b', 20, 20), + (40005, '2/12/2004', 4, 'a', 10, 10), + (20002, '2/16/2004', 2, 'c', 20, 20), + (30003, '4/18/2004', 3, 'b', 15, null), + (30004, '4/18/2004', 3, 'b', 20, null), + (30007, '9/7/2004', 3, 'c', 30, null); +---- + +=== LAG(expression, offset) + +This example executes the `LAG()` function with expression and offset parameters' values specified: + +[source,sql] +---- +SELECT buyerid, dateid, qty + LAG(qty,1) OVER (ORDER BY buyerid, dateid) AS prev_qty +FROM winsales WHERE buyerid = 'c' +ORDER BY buyerid, dateid; +---- + +The query returns the buyer ID, date ID, quantity and previous quantity for all rows with buyer ID equal to `c`: + +[source,sql] +---- + buyerid | dateid | qty | prev_qty +---------+------------+-----+---------- + c | 2003-12-24 | 10 | + c | 2004-01-18 | 10 | 10 + c | 2004-02-16 | 20 | 10 + c | 2004-09-07 | 30 | 20 +(4 rows) +---- + +=== LAG(expression, offset, default) + +This example executes the `LAG()` function with expression, offset and default parameters' values specified: + +[source,sql] +---- +SELECT buyerid, dateid, qty + LAG(buyerid,1,'unknown') OVER (ORDER BY dateid) AS prev_buyerid +FROM winsales +ORDER BY dateid; +---- + +The query returns the buyer ID, date ID, quantity and previous buyer ID for all rows: + +[source,sql] +---- + buyerid | dateid | qty | prev_buyerid +---------+------------+-----+-------------- + b | 2003-08-02 | 10 | unknown + c | 2003-12-24 | 10 | b + a | 2003-12-24 | 30 | c + a | 2004-01-09 | 40 | a + c | 2004-01-18 | 10 | a + b | 2004-02-12 | 20 | c + a | 2004-02-12 | 10 | b + c | 2004-02-16 | 20 | a + b | 2004-04-18 | 15 | c + b | 2004-04-18 | 20 | b + c | 2004-09-07 | 30 | b +(11 rows) +---- + +=== Time series: LAG() to compare daily sales quantities + +This example uses LAG() to compare each day's sales quantity (`qty`) with the previous day's quantity, ordered by `dateid`: + +[source,sql] +---- +SELECT dateid, qty, + LAG(qty) OVER (ORDER BY dateid) AS prev_day_qty, + qty - LAG(qty) OVER (ORDER BY dateid) AS qty_change +FROM winsales +ORDER BY dateid; +---- + +The query returns: + +[source,sql] +---- + dateid | qty | prev_day_qty | qty_change +------------+-----+--------------+------------ + 2003-08-02 | 10 | | + 2003-12-24 | 10 | 10 | 0 + 2003-12-24 | 30 | 10 | 20 + 2004-01-09 | 40 | 30 | 10 + 2004-01-18 | 10 | 40 | -30 + 2004-02-12 | 20 | 10 | 10 + 2004-02-12 | 10 | 20 | -10 + 2004-02-16 | 20 | 10 | 10 + 2004-04-18 | 15 | 20 | -5 + 2004-04-18 | 20 | 15 | 5 + 2004-09-07 | 30 | 20 | 10 +(11 rows) +---- diff --git a/modules/reference/pages/sql/sql-functions/window-functions/last-value.adoc b/modules/reference/pages/sql/sql-functions/window-functions/last-value.adoc new file mode 100644 index 000000000..4ac2e4999 --- /dev/null +++ b/modules/reference/pages/sql/sql-functions/window-functions/last-value.adoc @@ -0,0 +1,98 @@ += LAST_VALUE() +:description: The LAST_VALUE() is a window function that retrieves the last value in an ordered set of values within a specified partition. +:page-topic-type: reference + +The `LAST_VALUE()` is a window function that retrieves the last value in an ordered set of values within a specified partition. + +== Syntax + +The syntax for this function is: + +[source,sql] +---- +LAST_VALUE(expression) OVER ( + [PARTITION BY partition_expression] + ORDER BY sort_expression + RANGE BETWEEN start_value AND end_value +) +---- + +== Parameters + +* `expression`: Column or expression that returns a single value. Represents the value to retrieve from the first row of the sorted partition. +* `PARTITION BY`: Optional. Divides the result set into partitions, each processed independently. If omitted, the entire result set is treated as a single partition. +* `ORDER BY`: Order of rows in each partition to which the function is applied. +* `RANGE BETWEEN`: Range-based window frame relative to the current row. + +== Examples + +The following example uses a simplified version of the `film` table from the Pagila database, containing only the `title`, `length` and `rating` columns. The complete schema for the `film` table can be found on the link:https://www.postgresql.org/ftp/projects/pgFoundry/dbsamples/pagila/pagila/[Pagila^] website. + +[source,sql] +---- +DROP TABLE IF EXISTS film; +CREATE TABLE film ( + title text NOT NULL, + length int, + rating text +); +INSERT INTO film(title, length, rating) VALUES + ('ATTRACTION NEWTON', 83, 'PG-13'), + ('CHRISTMAS MOONSHINE', 150, 'NC-17'), + ('DANGEROUS UPTOWN', 121, 'PG'), + ('KILL BROTHERHOOD', 54, 'G'), + ('HALLOWEEN NUTS', 47, 'PG-13'), + ('HOURS RAGE', 122, 'NC-17'), + ('PIANIST OUTFIELD', 136, 'NC-17'), + ('PICKUP DRIVING', 77, 'G'), + ('INDEPENDENCE HOTEL', 157, 'NC-17'), + ('PRIVATE DROP', 106, 'PG'), + ('SAINTS BRIDE', 125, 'G'), + ('FOREVER CANDIDATE', 131, 'NC-17'), + ('MILLION ACE', 142, 'PG-13'), + ('SLEEPY JAPANESE', 137, 'PG'), + ('WRATH MILE', 176, 'NC-17'), + ('YOUTH KICK', 179, 'NC-17'), + ('CLOCKWORK PARADISE', 143, 'PG-13'); +---- + +The following query uses the `LAST_VALUE()` function to retrieve the title of the film with the longest duration, partitioning results by rating and ordering by length. + +[source,sql] +---- +SELECT + title, + length, + rating, + LAST_VALUE(title) OVER ( + PARTITION BY rating + ORDER BY + length ASC ROWS BETWEEN UNBOUNDED PRECEDING AND UNBOUNDED FOLLOWING + ) AS longest_film_in_rating +FROM film; +---- + +The query returns: + +[source,bash] +---- +| title | length | rating | longest_film_in_rating | +|---------------------|--------|--------|------------------------| +| KILL BROTHERHOOD | 54 | G | SAINTS BRIDE | +| PICKUP DRIVING | 77 | G | SAINTS BRIDE | +| SAINTS BRIDE | 125 | G | SAINTS BRIDE | +| HOURS RAGE | 122 | NC-17 | YOUTH KICK | +| FOREVER CANDIDATE | 131 | NC-17 | YOUTH KICK | +| PIANIST OUTFIELD | 136 | NC-17 | YOUTH KICK | +| CHRISTMAS MOONSHINE | 150 | NC-17 | YOUTH KICK | +| INDEPENDENCE HOTEL | 157 | NC-17 | YOUTH KICK | +| WRATH MILE | 176 | NC-17 | YOUTH KICK | +| YOUTH KICK | 179 | NC-17 | YOUTH KICK | +| PRIVATE DROP | 106 | PG | SLEEPY JAPANESE | +| DANGEROUS UPTOWN | 121 | PG | SLEEPY JAPANESE | +| SLEEPY JAPANESE | 137 | PG | SLEEPY JAPANESE | +| HALLOWEEN NUTS | 47 | PG-13 | CLOCKWORK PARADISE | +| ATTRACTION NEWTON | 83 | PG-13 | CLOCKWORK PARADISE | +| MILLION ACE | 142 | PG-13 | CLOCKWORK PARADISE | +| CLOCKWORK PARADISE | 143 | PG-13 | CLOCKWORK PARADISE | +---- diff --git a/modules/reference/pages/sql/sql-functions/window-functions/lead.adoc b/modules/reference/pages/sql/sql-functions/window-functions/lead.adoc new file mode 100644 index 000000000..c5c390680 --- /dev/null +++ b/modules/reference/pages/sql/sql-functions/window-functions/lead.adoc @@ -0,0 +1,143 @@ += LEAD() +:description: The LEAD() window function takes a column and an integer offset as arguments and returns the value of the cell in that column that is located at the s +:page-topic-type: reference + +The `LEAD()` window function takes a column and an integer offset as arguments, and returns the value of the cell in the column located at the specified number of rows after the current row. It can be used with all xref:reference:sql/sql-data-types/index.adoc[data types supported by Redpanda SQL] + +== Syntax + +The syntax for this function is: + +[source,sql] +---- +LEAD (expression, offset, default) +OVER ( + [PARTITION BY partition_expression, ... ] + ORDER BY sort_expression [ASC | DESC], ... +) +---- + +The function returns a value of the same type as the input. If no row meets the offset criteria, the function returns the specified default value, which must be of a type compatible with the input. + +== Parameters + +* `expression`: Column to reference. +* `offset`: Optional. Number of rows ahead of the current row. Defaults to `1`. +* `default`: Optional. Value to return if the `offset` is out of range. Defaults to `NULL`. + +== Examples + +The following examples use the `winsales` table that stores details about some sales transactions: + +[source,sql] +---- +CREATE TABLE winsales( + salesid int, + dateid date, + sellerid int, + buyerid text, + qty int, + qty_shipped int); +INSERT INTO winsales VALUES + (30001, '8/2/2003', 3, 'b', 10, 10), + (10001, '12/24/2003', 1, 'c', 10, 10), + (10005, '12/24/2003', 1, 'a', 30, null), + (40001, '1/9/2004', 4, 'a', 40, null), + (10006, '1/18/2004', 1, 'c', 10, null), + (20001, '2/12/2004', 2, 'b', 20, 20), + (40005, '2/12/2004', 4, 'a', 10, 10), + (20002, '2/16/2004', 2, 'c', 20, 20), + (30003, '4/18/2004', 3, 'b', 15, null), + (30004, '4/18/2004', 3, 'b', 20, null), + (30007, '9/7/2004', 3, 'c', 30, null); +---- + +=== Lead(expression, offset) + +This example executes the `LEAD()` function with expression and offset parameters' values specified: + +[source,sql] +---- +SELECT buyerid, dateid, qty + LEAD(qty,1) OVER (ORDER BY buyerid, dateid) AS next_qty +FROM winsales WHERE buyerid = 'c' +ORDER BY buyerid, dateid; +---- + +This query returns the buyer ID, date ID, quantity and previous quantity for all rows with buyer ID equal to `c`: + +[source,sql] +---- + buyerid | dateid | qty | next_qty +---------+------------+-----+---------- + c | 2003-12-24 | 10 | 10 + c | 2004-01-18 | 10 | 20 + c | 2004-02-16 | 20 | 30 + c | 2004-09-07 | 30 | +(4 rows) +---- + +=== Expression, offset and default specified + +This example executes the `LEAD()` function with expression, offset and default parameters' values specified: + +[source,sql] +---- +SELECT buyerid, dateid, qty + LEAD(buyerid,1,'unknown') OVER (ORDER BY dateid) AS next_buyerid +FROM winsales +ORDER BY dateid; +---- + +The query returns the buyer ID, date ID, quantity and following buyer ID for all rows: + +[source,sql] +---- + buyerid | dateid | qty | next_buyerid +---------+------------+-----+-------------- + b | 2003-08-02 | 10 | c + c | 2003-12-24 | 10 | a + a | 2003-12-24 | 30 | a + a | 2004-01-09 | 40 | c + c | 2004-01-18 | 10 | b + b | 2004-02-12 | 20 | a + a | 2004-02-12 | 10 | c + c | 2004-02-16 | 20 | b + b | 2004-04-18 | 15 | b + b | 2004-04-18 | 20 | c + c | 2004-09-07 | 30 | unknown +(11 rows) +---- + +=== Time series: LEAD() to compare next day's sales quantity + +This example uses LEAD() to compare each day's sales quantity (`qty`) with the next day's quantity, ordered by `dateid`: + +[source,sql] +---- +SELECT dateid, qty, + LEAD(qty) OVER (ORDER BY dateid) AS next_day_qty, + LEAD(qty) OVER (ORDER BY dateid) - qty AS qty_change +FROM winsales +ORDER BY dateid; +---- + +The query returns: + +[source,sql] +---- + dateid | qty | next_day_qty | qty_change +------------+-----+--------------+------------ + 2003-08-02 | 10 | 10 | 0 + 2003-12-24 | 10 | 30 | 20 + 2003-12-24 | 30 | 40 | 10 + 2004-01-09 | 40 | 10 | -30 + 2004-01-18 | 10 | 20 | 10 + 2004-02-12 | 20 | 10 | -10 + 2004-02-12 | 10 | 20 | 10 + 2004-02-16 | 20 | 15 | -5 + 2004-04-18 | 15 | 20 | 5 + 2004-04-18 | 20 | 30 | 10 + 2004-09-07 | 30 | | +(11 rows) +---- diff --git a/modules/reference/pages/sql/sql-functions/window-functions/max.adoc b/modules/reference/pages/sql/sql-functions/window-functions/max.adoc new file mode 100644 index 000000000..ce1027816 --- /dev/null +++ b/modules/reference/pages/sql/sql-functions/window-functions/max.adoc @@ -0,0 +1,96 @@ += MAX() +:description: The MAX() window function computes the maximum value of an expression across a set of rows defined by a window specification. +:page-topic-type: reference + +The `MAX()` window function computes the maximum value of an expression across a set of rows defined by a window specification. + +== Syntax + +The syntax for this function is: + +[source,sql] +---- +MAX ([ALL] expression) OVER ( + [PARTITION BY partition_expression] + ORDER BY sort_expression + RANGE BETWEEN start_value AND end_value +) +---- + +== Parameters + +* `ALL`: Retains all duplicate values from the expression. + +== Examples + +The following example uses a simplified version of the `film` table from the Pagila database, containing only the `title`, `length` and `rating` columns. The complete schema for the `film` table can be found on the link:https://www.postgresql.org/ftp/projects/pgFoundry/dbsamples/pagila/pagila/[Pagila^] database website. + +[source,sql] +---- +DROP TABLE IF EXISTS film; +CREATE TABLE film ( + title text NOT NULL, + length int, + rating text +); +INSERT INTO film(title, length, rating) VALUES + ('ATTRACTION NEWTON', 83, 'PG-13'), + ('CHRISTMAS MOONSHINE', 150, 'NC-17'), + ('DANGEROUS UPTOWN', 121, 'PG'), + ('KILL BROTHERHOOD', 54, 'G'), + ('HALLOWEEN NUTS', 47, 'PG-13'), + ('HOURS RAGE', 122, 'NC-17'), + ('PIANIST OUTFIELD', 136, 'NC-17'), + ('PICKUP DRIVING', 77, 'G'), + ('INDEPENDENCE HOTEL', 157, 'NC-17'), + ('PRIVATE DROP', 106, 'PG'), + ('SAINTS BRIDE', 125, 'G'), + ('FOREVER CANDIDATE', 131, 'NC-17'), + ('MILLION ACE', 142, 'PG-13'), + ('SLEEPY JAPANESE', 137, 'PG'), + ('WRATH MILE', 176, 'NC-17'), + ('YOUTH KICK', 179, 'NC-17'), + ('CLOCKWORK PARADISE', 143, 'PG-13'); +---- + +The query uses the `MAX()` function to find the maximum length of films for each rating category and also calculates a running maximum length as you move through the films ordered by length. The `RunningMaxLength` column updates as it encounters longer films. + +[source,sql] +---- +SELECT + title, + length, + rating, + MAX(length) OVER ( PARTITION BY rating ) AS MaxLengthByRating, + MAX(length) OVER ( +ORDER BY + length ROWS BETWEEN unbounded preceding AND CURRENT ROW ) AS RunningMaxLength +FROM film +ORDER BY length; +---- + +The query returns: + +[source,sql] +---- + title | length | rating | maxlengthbyrating | runningmaxlength +---------------------+--------+--------+-------------------+------------------ + HALLOWEEN NUTS | 47 | PG-13 | 143 | 47 + KILL BROTHERHOOD | 54 | G | 125 | 54 + PICKUP DRIVING | 77 | G | 125 | 77 + ATTRACTION NEWTON | 83 | PG-13 | 143 | 83 + PRIVATE DROP | 106 | PG | 137 | 106 + DANGEROUS UPTOWN | 121 | PG | 137 | 121 + HOURS RAGE | 122 | NC-17 | 179 | 122 + SAINTS BRIDE | 125 | G | 125 | 125 + FOREVER CANDIDATE | 131 | NC-17 | 179 | 131 + PIANIST OUTFIELD | 136 | NC-17 | 179 | 136 + SLEEPY JAPANESE | 137 | PG | 137 | 137 + MILLION ACE | 142 | PG-13 | 143 | 142 + CLOCKWORK PARADISE | 143 | PG-13 | 143 | 143 + CHRISTMAS MOONSHINE | 150 | NC-17 | 179 | 150 + INDEPENDENCE HOTEL | 157 | NC-17 | 179 | 157 + WRATH MILE | 176 | NC-17 | 179 | 176 + YOUTH KICK | 179 | NC-17 | 179 | 179 +(17 rows) +---- diff --git a/modules/reference/pages/sql/sql-functions/window-functions/min.adoc b/modules/reference/pages/sql/sql-functions/window-functions/min.adoc new file mode 100644 index 000000000..f00b81c64 --- /dev/null +++ b/modules/reference/pages/sql/sql-functions/window-functions/min.adoc @@ -0,0 +1,96 @@ += MIN() +:description: The MIN() window function computes the minimum value of an expression across a set of rows defined by a window specification. +:page-topic-type: reference + +The `MIN()` window function computes the minimum value of an expression across a set of rows defined by a window specification. + +== Syntax + +The syntax for this function is: + +[source,sql] +---- +MIN ([ALL] expression) OVER ( + [PARTITION BY partition_expression] + ORDER BY sort_expression + RANGE BETWEEN start_value AND end_value +) +---- + +== Parameters + +* `ALL`: Retains all duplicate values from the expression. + +== Examples + +The following example uses a simplified version of the `film` table from the Pagila database, containing only the `title`, `length` and `rating` columns. The complete schema for the `film` table can be found on the link:https://www.postgresql.org/ftp/projects/pgFoundry/dbsamples/pagila/pagila/[Pagila^] database website. + +[source,sql] +---- +DROP TABLE IF EXISTS film; +CREATE TABLE film ( + title text NOT NULL, + length int, + rating text +); +INSERT INTO film(title, length, rating) VALUES + ('ATTRACTION NEWTON', 83, 'PG-13'), + ('CHRISTMAS MOONSHINE', 150, 'NC-17'), + ('DANGEROUS UPTOWN', 121, 'PG'), + ('KILL BROTHERHOOD', 54, 'G'), + ('HALLOWEEN NUTS', 47, 'PG-13'), + ('HOURS RAGE', 122, 'NC-17'), + ('PIANIST OUTFIELD', 136, 'NC-17'), + ('PICKUP DRIVING', 77, 'G'), + ('INDEPENDENCE HOTEL', 157, 'NC-17'), + ('PRIVATE DROP', 106, 'PG'), + ('SAINTS BRIDE', 125, 'G'), + ('FOREVER CANDIDATE', 131, 'NC-17'), + ('MILLION ACE', 142, 'PG-13'), + ('SLEEPY JAPANESE', 137, 'PG'), + ('WRATH MILE', 176, 'NC-17'), + ('YOUTH KICK', 179, 'NC-17'), + ('CLOCKWORK PARADISE', 143, 'PG-13'); +---- + +The following query uses the `MIN()` to find the minimum length of films for each rating category and also calculates a running minimum length of films ordered by their length. + +[source,sql] +---- +SELECT + title, + length, + rating, + MIN(length) OVER ( PARTITION BY rating ) AS MinLengthByRating, + MIN(length) OVER ( +ORDER BY + length ROWS BETWEEN unbounded preceding AND CURRENT ROW ) AS RunningMinLength +FROM film +ORDER BY length; +---- + +The query returns: + +[source,sql] +---- + title | length | rating | minlengthbyrating | runningminlength +---------------------+--------+--------+-------------------+------------------ + HALLOWEEN NUTS | 47 | PG-13 | 47 | 47 + KILL BROTHERHOOD | 54 | G | 54 | 47 + PICKUP DRIVING | 77 | G | 54 | 47 + ATTRACTION NEWTON | 83 | PG-13 | 47 | 47 + PRIVATE DROP | 106 | PG | 106 | 47 + DANGEROUS UPTOWN | 121 | PG | 106 | 47 + HOURS RAGE | 122 | NC-17 | 122 | 47 + SAINTS BRIDE | 125 | G | 54 | 47 + FOREVER CANDIDATE | 131 | NC-17 | 122 | 47 + PIANIST OUTFIELD | 136 | NC-17 | 122 | 47 + SLEEPY JAPANESE | 137 | PG | 106 | 47 + MILLION ACE | 142 | PG-13 | 47 | 47 + CLOCKWORK PARADISE | 143 | PG-13 | 47 | 47 + CHRISTMAS MOONSHINE | 150 | NC-17 | 122 | 47 + INDEPENDENCE HOTEL | 157 | NC-17 | 122 | 47 + WRATH MILE | 176 | NC-17 | 122 | 47 + YOUTH KICK | 179 | NC-17 | 122 | 47 +(17 rows) +---- diff --git a/modules/reference/pages/sql/sql-functions/window-functions/nth-value.adoc b/modules/reference/pages/sql/sql-functions/window-functions/nth-value.adoc new file mode 100644 index 000000000..5080298aa --- /dev/null +++ b/modules/reference/pages/sql/sql-functions/window-functions/nth-value.adoc @@ -0,0 +1,100 @@ += NTH_VALUE() +:description: The NTH_VALUE() is a window function that accesses the value from the nth row within a specified window frame. +:page-topic-type: reference + +The `NTH_VALUE()` is a window function that accesses the value from the nth row within a specified window frame. + +== Syntax + +The syntax for this function is: + +[source,sql] +---- +NTH_VALUE (value, n) OVER ( + [PARTITION BY partition_expression] + ORDER BY sort_expression + RANGE BETWEEN start_value AND end_value +) +---- + +== Parameters + +* `value`: Column or expression for which you want to retrieve the value. +* `n`: Positive integer (greater than zero) that determines the row number within the window frame from which to retrieve the value. +* `PARTITION BY`: Optional. Divides the result set into partitions, each processed independently. If omitted, the entire result set is treated as a single partition. +* `ORDER BY`: Optional. Specifies the order of rows in each partition to which the function is applied. +* `RANGE BETWEEN`: Optional. Defines a range-based window frame relative to the current row. + +== Examples + +The following example uses a simplified version of the `film` table from the Pagila database, containing only the `title`, `length` and `rating` columns. The complete schema for the `film` table can be found on the link:https://www.postgresql.org/ftp/projects/pgFoundry/dbsamples/pagila/pagila/[Pagila^] database website. + +[source,sql] +---- +DROP TABLE IF EXISTS film; +CREATE TABLE film ( + title text NOT NULL, + length int, + rating text +); + +INSERT INTO film(title, length, rating) VALUES + ('ATTRACTION NEWTON', 83, 'PG-13'), + ('CHRISTMAS MOONSHINE', 150, 'NC-17'), + ('DANGEROUS UPTOWN', 121, 'PG'), + ('KILL BROTHERHOOD', 54, 'G'), + ('HALLOWEEN NUTS', 47, 'PG-13'), + ('HOURS RAGE', 122, 'NC-17'), + ('PIANIST OUTFIELD', 136, 'NC-17'), + ('PICKUP DRIVING', 77, 'G'), + ('INDEPENDENCE HOTEL', 157, 'NC-17'), + ('PRIVATE DROP', 106, 'PG'), + ('SAINTS BRIDE', 125, 'G'), + ('FOREVER CANDIDATE', 131, 'NC-17'), + ('MILLION ACE', 142, 'PG-13'), + ('SLEEPY JAPANESE', 137, 'PG'), + ('WRATH MILE', 176, 'NC-17'), + ('YOUTH KICK', 179, 'NC-17'), + ('CLOCKWORK PARADISE', 143, 'PG-13'); +---- + +This query uses the `NTH_VALUE()` function to retrieve the title of the film with the second shortest duration, partitioning results by rating and ordering by length: + +[source,sql] +---- +SELECT + title, + length, + rating, + NTH_VALUE(title, 2) OVER ( + PARTITION BY rating + ORDER BY + length ASC + ) AS second_shortest_film_in_rating +FROM film; +---- + +This query shows the following table: + +[source,bash] +---- +| title |length |rating | second_shortest_film_in_rating | +|---------------------|--------|--------|----------------------------------| +| KILL BROTHERHOOD | 54 | G | NULL | +| PICKUP DRIVING | 77 | G | PICKUP DRIVING | +| SAINTS BRIDE | 125 | G | PICKUP DRIVING | +| HOURS RAGE | 122 | NC-17 | NULL | +| FOREVER CANDIDATE | 131 | NC-17 | FOREVER CANDIDATE | +| PIANIST OUTFIELD | 136 | NC-17 | FOREVER CANDIDATE | +| CHRISTMAS MOONSHINE | 150 | NC-17 | FOREVER CANDIDATE | +| INDEPENDENCE HOTEL | 157 | NC-17 | FOREVER CANDIDATE | +| WRATH MILE | 176 | NC-17 | FOREVER CANDIDATE | +| YOUTH KICK | 179 | NC-17 | FOREVER CANDIDATE | +| PRIVATE DROP | 106 | PG | NULL | +| DANGEROUS UPTOWN | 121 | PG | DANGEROUS UPTOWN | +| SLEEPY JAPANESE | 137 | PG | DANGEROUS UPTOWN | +| HALLOWEEN NUTS | 47 | PG-13 | NULL | +| ATTRACTION NEWTON | 83 | PG-13 | ATTRACTION NEWTON | +| MILLION ACE | 142 | PG-13 | ATTRACTION NEWTON | +| CLOCKWORK PARADISE | 143 | PG-13 | ATTRACTION NEWTON | +---- diff --git a/modules/reference/pages/sql/sql-functions/window-functions/ntile.adoc b/modules/reference/pages/sql/sql-functions/window-functions/ntile.adoc new file mode 100644 index 000000000..adf4a91dd --- /dev/null +++ b/modules/reference/pages/sql/sql-functions/window-functions/ntile.adoc @@ -0,0 +1,95 @@ += NTILE() +:description: The NTILE() function is a window function used to divide an ordered data set into a specified number of approximately equal groups or buckets. +:page-topic-type: reference + +The `NTILE()` function is a window function used to divide an ordered data set into a specified number of approximately equal groups or buckets. This function assigns each group a bucket number starting form one. + +== Syntax + +The syntax for this function is: + +[source,sql] +---- +NTILE(buckets) OVER ( + PARTITION BY partition_expression, ... ] + [ORDER BY sort_expression [ASC | DESC], ...] +) +---- + +== Parameters + +* `bucket`: Positive integer or an expression that evaluates to a positive integer for each partition. It specifies the number of groups into which the data should be divided. + +== Restrictions + +* `buckets`: Must be a positive integer. Redpanda SQL truncates non-integer constants to an integer. + +== Examples + +The following example uses a simplified version of the `film` table from the link:https://www.postgresql.org/ftp/projects/pgFoundry/dbsamples/pagila/pagila/[Pagila database^], containing only the `title`, `length` and `rating` columns. + +[source,sql] +---- +DROP TABLE IF EXISTS film; +CREATE TABLE film ( + title text NOT NULL, + length int, + rating text +); + +INSERT INTO film(title, length, rating) VALUES + ('ATTRACTION NEWTON', 83, 'PG-13'), + ('CHRISTMAS MOONSHINE', 150, 'NC-17'), + ('DANGEROUS UPTOWN', 121, 'PG'), + ('KILL BROTHERHOOD', 54, 'G'), + ('HALLOWEEN NUTS', 47, 'PG-13'), + ('HOURS RAGE', 122, 'NC-17'), + ('PIANIST OUTFIELD', 136, 'NC-17'), + ('PICKUP DRIVING', 77, 'G'), + ('INDEPENDENCE HOTEL', 157, 'NC-17'), + ('PRIVATE DROP', 106, 'PG'), + ('SAINTS BRIDE', 125, 'G'), + ('FOREVER CANDIDATE', 131, 'NC-17'), + ('MILLION ACE', 142, 'PG-13'), + ('SLEEPY JAPANESE', 137, 'PG'), + ('WRATH MILE', 176, 'NC-17'), + ('YOUTH KICK', 179, 'NC-17'), + ('CLOCKWORK PARADISE', 143, 'PG-13'); +---- + +The following query uses the `NTILE()` function to divide the films into four quartiles based on their length: + +[source,sql] +---- +SELECT + title, + length, + NTILE(4) OVER (ORDER BY length) AS quartile +FROM film; +---- + +The query returns: + +[source,sql] +---- + title | length | quartile +---------------------+--------+---------- + HALLOWEEN NUTS | 47 | 1 + KILL BROTHERHOOD | 54 | 1 + PICKUP DRIVING | 77 | 1 + ATTRACTION NEWTON | 83 | 1 + PRIVATE DROP | 106 | 1 + DANGEROUS UPTOWN | 121 | 2 + HOURS RAGE | 122 | 2 + SAINTS BRIDE | 125 | 2 + FOREVER CANDIDATE | 131 | 2 + PIANIST OUTFIELD | 136 | 3 + SLEEPY JAPANESE | 137 | 3 + MILLION ACE | 142 | 3 + CLOCKWORK PARADISE | 143 | 3 + CHRISTMAS MOONSHINE | 150 | 4 + INDEPENDENCE HOTEL | 157 | 4 + WRATH MILE | 176 | 4 + YOUTH KICK | 179 | 4 +(17 rows) +---- diff --git a/modules/reference/pages/sql/sql-functions/window-functions/percent-rank.adoc b/modules/reference/pages/sql/sql-functions/window-functions/percent-rank.adoc new file mode 100644 index 000000000..1867d4827 --- /dev/null +++ b/modules/reference/pages/sql/sql-functions/window-functions/percent-rank.adoc @@ -0,0 +1,128 @@ += PERCENT_RANK() +:description: PERCENT_RANK() window function determines the relative rank of a value in a group of values, based on the ORDER BY expression in the OVER clause. +:page-topic-type: reference + +`PERCENT_RANK()` window function determines the relative rank of a value in a group of values, based on the `ORDER BY` expression in the `OVER` clause. It can be used with all xref:reference:sql/sql-data-types/index.adoc[data types supported by Redpanda SQL]. + +== Syntax + +The syntax for this function is: + +[source,sql] +---- +PERCENT_RANK() OVER ( + [PARTITION BY partition_expression] + ORDER BY sort_expression +) +---- + +The `PERCENT_RANK()` is calculated as: + +[source,bash] +---- +(r - 1) / (n - 1) +---- + +Where `r` is the rank of the current row and `n` is the total number of rows in the window or partition. + +Rows with equal values for the ranking criteria receive the same relative rank. The output data type for this function is `DOUBLE PRECISION`. The output will indicate the rank of values in a table, regardless of the input types. + +* If the optional `PARTITION BY` expression is present, the rankings are reset for each group of rows +* If the `ORDER BY` expression is omitted then all relative ranks are equal to 0 + +== Parameters + +* `()`: This function does not take any arguments, but the parentheses are required. +* `PARTITION BY`: Optional. Divides the result set into partitions, each processed independently. If omitted, the entire result set is treated as a single partition. +* `ORDER BY`: Order of rows in each partition to which the function is applied. + +== Examples + +The following examples use the `winsales` table that stores details about some sales transactions: + +[source,sql] +---- +CREATE TABLE winsales( + salesid int, + dateid date, + sellerid int, + buyerid text, + qty int, + qty_shipped int); +INSERT INTO winsales VALUES + (30001, '8/2/2003', 3, 'b', 10, 10), + (10001, '12/24/2003', 1, 'c', 10, 10), + (10005, '12/24/2003', 1, 'a', 30, null), + (40001, '1/9/2004', 4, 'a', 40, null), + (10006, '1/18/2004', 1, 'c', 10, null), + (20001, '2/12/2004', 2, 'b', 20, 20), + (40005, '2/12/2004', 4, 'a', 10, 10), + (20002, '2/16/2004', 2, 'c', 20, 20), + (30003, '4/18/2004', 3, 'b', 15, null), + (30004, '4/18/2004', 3, 'b', 20, null), + (30007, '9/7/2004', 3, 'c', 30, null); +---- + +=== PERCENT_RANK() with ORDER BY + +This example executes the `PERCENT_RANK()` function with `ORDER BY` keyword and calculates the descending percent rank of all rows based on the quantity sold: + +[source,sql] +---- +SELECT salesid, qty + PERCENT_RANK() OVER (ORDER BY qty DESC) AS p_rnk + RANK() OVER (ORDER BY qty DESC) AS rnk +FROM winsales +ORDER BY 2,1; +---- + +Output that includes the sales ID along with the quantity sold and both percent and regular ranks: + +[source,sql] +---- + salesid | qty | p_rnk | rnk +---------+-----+-------+----- + 10001 | 10 | 0.7 | 8 + 10006 | 10 | 0.7 | 8 + 30001 | 10 | 0.7 | 8 + 40005 | 10 | 0.7 | 8 + 30003 | 15 | 0.6 | 7 + 20001 | 20 | 0.3 | 4 + 20002 | 20 | 0.3 | 4 + 30004 | 20 | 0.3 | 4 + 10005 | 30 | 0.1 | 2 + 30007 | 30 | 0.1 | 2 + 40001 | 40 | 0 | 1 +---- + +=== PERCENT_RANK() with ORDER BY and PARTITION BY + +This example executes the `PERCENT_RANK()` function with `ORDER BY` keyword and `PARTITION BY` clause, partitions the table by seller ID, orders each partition by the quantity, and assigns a percent rank to each row: + +[source,sql] +---- +SELECT salesid, sellerid, qty + PERCENT_RANK() OVER (PARTITION BY sellerid ORDER BY qty DESC) AS p_rnk +FROM winsales +ORDER BY 2,3,1; +---- + +The query returns: + +[source,sql] +---- + salesid | sellerid | qty | p_rnk +---------+----------+-----+-------------------- + 10001 | 1 | 10 | 0.5 + 10006 | 1 | 10 | 0.5 + 10005 | 1 | 30 | 0 + 20001 | 2 | 20 | 0 + 20002 | 2 | 20 | 0 + 30001 | 3 | 10 | 1 + 30003 | 3 | 15 | 0.6666666666666666 + 30004 | 3 | 20 | 0.3333333333333333 + 30007 | 3 | 30 | 0 + 40005 | 4 | 10 | 1 + 40001 | 4 | 40 | 0 +(11 rows) +---- diff --git a/modules/reference/pages/sql/sql-functions/window-functions/rank.adoc b/modules/reference/pages/sql/sql-functions/window-functions/rank.adoc new file mode 100644 index 000000000..f2c29f7e8 --- /dev/null +++ b/modules/reference/pages/sql/sql-functions/window-functions/rank.adoc @@ -0,0 +1,117 @@ += RANK() +:description: The RANK() window function determines the rank of a value in a group of values, based on the ORDER BY expression in the OVER clause. +:page-topic-type: reference + +The `RANK()` window function determines the rank of a value in a group of values, based on the `ORDER BY` expression in the `OVER` clause. It can be used with all xref:reference:sql/sql-data-types/index.adoc[data types supported by Redpanda SQL]. + +== Syntax + +The syntax for this function is: + +[source,sql] +---- +RANK() OVER ( + [PARTITION BY partition_expression, ... ] + ORDER BY sort_expression [ASC | DESC], ... +) +---- + +Rows with equal values for the ranking criteria receive the same rank. The output type for this function is `BIGINT` and it indicates the rank of values in a table, regardles of the input types. + +* If the optional `PARTITION BY` expression is present, the rankings are reset for each group of rows +* If the `ORDER BY` expression is omitted then all ranks are equal to 1 + +== Parameters + +* `()`: This function does not take any arguments, but the parentheses are required. + +== Examples + +The following examples use the `winsales` table that stores details about some sales transactions: + +[source,sql] +---- +CREATE TABLE winsales( + salesid int, + dateid date, + sellerid int, + buyerid text, + qty int, + qty_shipped int); +INSERT INTO winsales VALUES + (30001, '8/2/2003', 3, 'b', 10, 10), + (10001, '12/24/2003', 1, 'c', 10, 10), + (10005, '12/24/2003', 1, 'a', 30, null), + (40001, '1/9/2004', 4, 'a', 40, null), + (10006, '1/18/2004', 1, 'c', 10, null), + (20001, '2/12/2004', 2, 'b', 20, 20), + (40005, '2/12/2004', 4, 'a', 10, 10), + (20002, '2/16/2004', 2, 'c', 20, 20), + (30003, '4/18/2004', 3, 'b', 15, null), + (30004, '4/18/2004', 3, 'b', 20, null), + (30007, '9/7/2004', 3, 'c', 30, null); +---- + +=== RANK() with ORDER BY + +This example executes the `RANK()` function with `ORDER BY` keyword and calculates the rank of all rows based on the quantity sold: + +[source,sql] +---- +SELECT salesid, qty + RANK() OVER (ORDER BY qty) +FROM winsales +ORDER BY 2,1; +---- + +Output that includes the sales ID along with the quantity sold and regular ranks: + +[source,sql] +---- + salesid | qty | rank +---------+-----+------ + 10001 | 10 | 1 + 10006 | 10 | 1 + 30001 | 10 | 1 + 40005 | 10 | 1 + 30003 | 15 | 5 + 20001 | 20 | 6 + 20002 | 20 | 6 + 30004 | 20 | 6 + 10005 | 30 | 9 + 30007 | 30 | 9 + 40001 | 40 | 11 +(11 rows) +---- + +=== RANK() with ORDER BY and PARTITION BY + +This example executes the `RANK()` function with `ORDER BY` keyword and `PARTITION BY` clause, partitions the table by seller ID, orders each partition by the quantity, and assigns a rank to each row: + +[source,sql] +---- +SELECT salesid, sellerid, qty + RANK() OVER (PARTITION BY sellerid ORDER BY qty) +FROM winsales +ORDER BY 2,3,1; +---- + +The query returns: + +[source,sql] +---- + salesid | sellerid | qty | rank +---------+----------+-----+------ + 10001 | 1 | 10 | 1 + 10006 | 1 | 10 | 1 + 10005 | 1 | 30 | 3 + 20001 | 2 | 20 | 1 + 20002 | 2 | 20 | 1 + 30001 | 3 | 10 | 1 + 30003 | 3 | 15 | 2 + 30004 | 3 | 20 | 3 + 30007 | 3 | 30 | 4 + 40005 | 4 | 10 | 1 + 40001 | 4 | 40 | 2 +(11 rows) +---- diff --git a/modules/reference/pages/sql/sql-functions/window-functions/row-number.adoc b/modules/reference/pages/sql/sql-functions/window-functions/row-number.adoc new file mode 100644 index 000000000..f0ec6c158 --- /dev/null +++ b/modules/reference/pages/sql/sql-functions/window-functions/row-number.adoc @@ -0,0 +1,146 @@ += ROW_NUMBER +:description: The ROW_NUMBER() window function returns the number of the current row within its partition (counting from 1), based on the ORDER BY expression in the +:page-topic-type: reference + +The `ROW_NUMBER()` window function returns the number of the current row within its partition (counting from 1), based on the `ORDER BY` expression in the `OVER` clause. It can be used with all xref:reference:sql/sql-data-types/index.adoc[data types] supported by Redpanda SQL. + +== Syntax + +The syntax for this function is: + +[source,sql] +---- +ROW_NUMBER() OVER ( + [PARTITION BY partition_expression, ... ] + ORDER BY sort_expression [ASC | DESC], ... +) +---- + +The function returns a value of type `BIGINT`. Rows with equal values for the `ORDER BY` expression receive different row numbers nondeterministically. + +== Parameters + +* `()`: This function does not take any arguments, but the parentheses are required. + +== Examples + +The following examples use the `winsales` table that stores details about some sales transactions: + +[source,sql] +---- +CREATE TABLE winsales( + salesid int, + dateid date, + sellerid int, + buyerid text, + qty int, + qty_shipped int); +INSERT INTO winsales VALUES + (30001, '8/2/2003', 3, 'b', 10, 10), + (10001, '12/24/2003', 1, 'c', 10, 10), + (10005, '12/24/2003', 1, 'a', 30, null), + (40001, '1/9/2004', 4, 'a', 40, null), + (10006, '1/18/2004', 1, 'c', 10, null), + (20001, '2/12/2004', 2, 'b', 20, 20), + (40005, '2/12/2004', 4, 'a', 10, 10), + (20002, '2/16/2004', 2, 'c', 20, 20), + (30003, '4/18/2004', 3, 'b', 15, null), + (30004, '4/18/2004', 3, 'b', 20, null), + (30007, '9/7/2004', 3, 'c', 30, null); +---- + +=== ROW_NUMBER() with ORDER BY + +This example executes the `ROW_NUMBER()` function with `ORDER BY` keyword, assigns a row number to each row, and orders the table by the row number (the results are sorted after the window function results are applied): + +[source,sql] +---- +SELECT salesid, qty + ROW_NUMBER() OVER (ORDER BY salesid) +FROM winsales +ORDER BY 3; +---- + +The query returns: + +[source,sql] +---- + salesid | qty | row_number +---------+-----+------------ + 10001 | 10 | 1 + 10005 | 30 | 2 + 10006 | 10 | 3 + 20001 | 20 | 4 + 20002 | 20 | 5 + 30001 | 10 | 6 + 30003 | 15 | 7 + 30004 | 20 | 8 + 30007 | 30 | 9 + 40001 | 40 | 10 + 40005 | 10 | 11 +(11 rows) +---- + +=== ROW_NUMBER() with ORDER BY and PARTITION BY + +This example executes the `ROW_NUMBER()` function with `ORDER BY` keyword and `PARTITION BY` clause, partitions the table by seller ID, assigns a row number to each row, and orders the table by the sales ID and row number (the results are sorted after the window function results are applied): + +[source,sql] +---- +SELECT salesid, sellerid, qty + ROW_NUMBER() OVER (PARTITION BY sellerid ORDER BY salesid) +FROM winsales +ORDER BY 1; +---- + +The query returns: + +[source,sql] +---- + salesid | sellerid | qty | row_number +---------+----------+-----+------------ + 10001 | 1 | 10 | 1 + 10005 | 1 | 30 | 2 + 10006 | 1 | 10 | 3 + 20001 | 2 | 20 | 1 + 20002 | 2 | 20 | 2 + 30001 | 3 | 10 | 1 + 30003 | 3 | 15 | 2 + 30004 | 3 | 20 | 3 + 30007 | 3 | 30 | 4 + 40001 | 4 | 40 | 1 + 40005 | 4 | 10 | 2 +(11 rows) +---- + +=== Time series: assigning sequential row numbers by date + +This example assigns a sequential row number to each sale ordered by `dateid`: + +[source,sql] +---- +SELECT dateid, salesid, qty, + ROW_NUMBER() OVER (ORDER BY dateid, salesid) AS time_series_position +FROM winsales +ORDER BY dateid, salesid; +---- + +The query returns: + +[source,sql] +---- + dateid | salesid | qty | time_series_position +------------+---------+-----+---------------------- + 2003-08-02 | 30001 | 10 | 1 + 2003-12-24 | 10001 | 10 | 2 + 2003-12-24 | 10005 | 30 | 3 + 2004-01-09 | 40001 | 40 | 4 + 2004-01-18 | 10006 | 10 | 5 + 2004-02-12 | 20001 | 20 | 6 + 2004-02-12 | 40005 | 10 | 7 + 2004-02-16 | 20002 | 20 | 8 + 2004-04-18 | 30003 | 15 | 9 + 2004-04-18 | 30004 | 20 | 10 + 2004-09-07 | 30007 | 30 | 11 +(11 rows) +---- diff --git a/modules/reference/pages/sql/sql-functions/window-functions/sum.adoc b/modules/reference/pages/sql/sql-functions/window-functions/sum.adoc new file mode 100644 index 000000000..2d1f82138 --- /dev/null +++ b/modules/reference/pages/sql/sql-functions/window-functions/sum.adoc @@ -0,0 +1,191 @@ += SUM() +:description: The SUM() window function returns the sum of the input column or expression values. +:page-topic-type: reference + +The `SUM()` window function returns the sum of the input column or expression values. It can be used with a `RANGE` clause, that defines a logical frame of rows based on the values of the current row, rather than a fixed number of rows. + +== Syntax + +The syntax for this function is: + +[source,sql] +---- +SUM(expression) OVER ( + [PARTITION BY partition_expression] + ORDER BY sort_expression + [ROWS | RANGE BETWEEN start_value AND end_value] +) +---- + +The expression's argument types supported by the `SUM` window function are `INTEGER`, `BIGINT`, `REAL` and `DOUBLE PRECISION`. The return types of the `SUM` function are: `BIGINT` for integer and `DOUBLE PRECISION` for floating-point arguments. + +[NOTE] +==== +The `SUM()` window function works with numeric values and ignores NULL ones +==== + +== Parameters + +* `expression`: Column or expression to sum. +* `PARTITION BY`: Optional. Divides the result set into partitions, each processed independently. If omitted, the entire result set is treated as a single partition. +* `ROWS | RANGE BETWEEN`: Range-based window frame relative to the current row. + +== Examples + +The following examples use the `winsales` table that stores details of some sales transactions: + +[source,sql] +---- +CREATE TABLE winsales( + salesid int, + dateid date, + sellerid int, + buyerid text, + qty int, + qty_shipped int); +INSERT INTO winsales VALUES + (30001, '8/2/2003', 3, 'b', 10, 10), + (10001, '12/24/2003', 1, 'c', 10, 10), + (10005, '12/24/2003', 1, 'a', 30, null), + (40001, '1/9/2004', 4, 'a', 40, null), + (10006, '1/18/2004', 1, 'c', 10, null), + (20001, '2/12/2004', 2, 'b', 20, 20), + (40005, '2/12/2004', 4, 'a', 10, 10), + (20002, '2/16/2004', 2, 'c', 20, 20), + (30003, '4/18/2004', 3, 'b', 15, null), + (30004, '4/18/2004', 3, 'b', 20, null), + (30007, '9/7/2004', 3, 'c', 30, null); +---- + +=== SUM() with ORDER BY + +This example executes the `SUM()` window function with `ORDER BY` keyword: + +[source,sql] +---- +SELECT salesid, dateid, sellerid, qty + SUM(qty) OVER (ORDER BY dateid, salesid ROWS UNBOUNDED PRECEDING) +FROM winsales +ORDER BY 2,1; +---- + +The output from this query includes the sales ID, date ID, seller ID, quantity and quantity sum: + +[source,sql] +---- + salesid | dateid | sellerid | qty | sum +---------+------------+----------+-----+----- + 30001 | 2003-08-02 | 3 | 10 | 10 + 10001 | 2003-12-24 | 1 | 10 | 20 + 10005 | 2003-12-24 | 1 | 30 | 50 + 40001 | 2004-01-09 | 4 | 40 | 90 + 10006 | 2004-01-18 | 1 | 10 | 100 + 20001 | 2004-02-12 | 2 | 20 | 120 + 40005 | 2004-02-12 | 4 | 10 | 130 + 20002 | 2004-02-16 | 2 | 20 | 150 + 30003 | 2004-04-18 | 3 | 15 | 165 + 30004 | 2004-04-18 | 3 | 20 | 185 + 30007 | 2004-09-07 | 3 | 30 | 215 +(11 rows) +---- + +=== SUM() with ORDER BY and ROWS frame + +This example calculates the running total of `qty` ordered by dateid and salesid using a `ROWS UNBOUNDED PRECEDING` frame, which sums all rows from the start up to the current row: + +[source,sql] +---- +SELECT salesid, dateid, sellerid, qty, + SUM(qty) OVER (ORDER BY dateid, salesid ROWS UNBOUNDED PRECEDING) AS running_qty_sum +FROM winsales +ORDER BY dateid, salesid; +---- + +The query returns: + +[source,sql] +---- + salesid | dateid | qty | running_qty_sum +---------+------------+-----+----------------- + 30001 | 2003-08-02 | 10 | 10 + 10001 | 2003-12-24 | 10 | 20 + 10005 | 2003-12-24 | 30 | 50 + 40001 | 2004-01-09 | 40 | 90 + 10006 | 2004-01-18 | 10 | 100 + 20001 | 2004-02-12 | 20 | 120 + 40005 | 2004-02-12 | 10 | 130 + 20002 | 2004-02-16 | 20 | 150 + 30003 | 2004-04-18 | 15 | 165 + 30004 | 2004-04-18 | 20 | 185 + 30007 | 2004-09-07 | 30 | 215 +(11 rows) +---- + +The `running_qty_sum` column shows the cumulative sum of `qty` ordered by `dateid` and `salesid`. For each row, it sums all `qty` values from the first row up to the current row in that order. + +=== SUM() with ORDER BY and PARTITION BY + +This example executes the `SUM()` function with `ORDER BY` keyword and `PARTITION BY` clause: + +[source,sql] +---- +SELECT salesid, dateid, sellerid, qty + SUM(qty) OVER (PARTITION BY sellerid ORDER BY dateid, sellerid ROWS UNBOUNDED PRECEDING) +FROM winsales +ORDER BY 3,2,1; +---- + +The query returns: + +[source,sql] +---- + salesid | dateid | sellerid | qty | sum +---------+------------+----------+-----+----- + 10001 | 2003-12-24 | 1 | 10 | 10 + 10005 | 2003-12-24 | 1 | 30 | 40 + 10006 | 2004-01-18 | 1 | 10 | 50 + 20001 | 2004-02-12 | 2 | 20 | 20 + 20002 | 2004-02-16 | 2 | 20 | 40 + 30001 | 2003-08-02 | 3 | 10 | 10 + 30003 | 2004-04-18 | 3 | 15 | 25 + 30004 | 2004-04-18 | 3 | 20 | 45 + 30007 | 2004-09-07 | 3 | 30 | 75 + 40001 | 2004-01-09 | 4 | 40 | 40 + 40005 | 2004-02-12 | 4 | 10 | 50 +(11 rows) +---- + +=== Time series: SUM() with RANGE BETWEEN for last 30 days + +This example demonstrates a common time series use case: calculating the rolling sum of sales quantity over the last 30 days for each row, using the RANGE BETWEEN INTERVAL '`30 days`' PRECEDING AND CURRENT ROW frame: + +[source,sql] +---- +SELECT salesid, dateid, qty, + SUM(qty) OVER ( + ORDER BY dateid + RANGE BETWEEN INTERVAL '30 days' PRECEDING AND CURRENT ROW + ) AS rolling_30d_qty_sum +FROM winsales +ORDER BY dateid; +---- + +The output from this query sums the `qty` of all sales within the 30-day window ending at the current row's `dateid`: + +[source,sql] +---- + salesid | dateid | qty | rolling_30d_qty_sum +---------+------------+-----+--------------------- + 30001 | 2003-08-02 | 10 | 10 + 10001 | 2003-12-24 | 10 | 40 + 10005 | 2003-12-24 | 30 | 40 + 40001 | 2004-01-09 | 40 | 80 + 10006 | 2004-01-18 | 10 | 90 + 20001 | 2004-02-12 | 20 | 40 + 40005 | 2004-02-12 | 10 | 40 + 20002 | 2004-02-16 | 20 | 60 + 30003 | 2004-04-18 | 15 | 35 + 30004 | 2004-04-18 | 20 | 35 + 30007 | 2004-09-07 | 30 | 30 +(11 rows) +---- diff --git a/modules/reference/pages/sql/sql-operators/bitwise-shift-left.adoc b/modules/reference/pages/sql/sql-operators/bitwise-shift-left.adoc new file mode 100644 index 000000000..5557db819 --- /dev/null +++ b/modules/reference/pages/sql/sql-operators/bitwise-shift-left.adoc @@ -0,0 +1,97 @@ += BITWISE SHIFT LEFT +:description: Bitwise shift operators in Redpanda SQL manipulate the bits of integer value by shifting them left or right. +:page-topic-type: reference + +Bitwise shift operators in Redpanda SQL manipulate the bits of integer value by shifting them left or right. These operations are fundamental in low-level data processing and optimization. + +The bitwise *left shift (`<<`)* operator shifts the bits of an integer to the left by the specified shift amount. For *integers*, this operation is equivalent to multiplying the integer value by 2 raised to the power of the shift amount. During this operation, high-order bits that are shifted out are permanently lost without the ability to be preserved, while zeros are shifted in from the right to fill the vacant positions. Because the left shifts operation (<<) on signed integers is *arithmetic*, meaning it shifts all bits to the left and fills the vacant rightmost bits with zeros on the right, the behavior is the same as a logical shift in this case. However, the overall length of the bit string is preserved, with zeros padding on the right to maintain the length. + +== Syntax + +The syntax for this function is: + +[source,sql] +---- +value << shift_amount +---- + +== Parameters + +* `value`: Integer expression. +* `shift_amount`: A *non-negative* integer specifying how many bit positions to shift. + +== Restrictions + +Bitwise shift operators in Redpanda SQL require the shift amount to be a *non-negative* integer. Redpanda SQL treats negative shift counts as valid by applying modulo arithmetic based on the bit width, so shifting `1 << -3` in a 32-bit integer is equivalent to shifting `1 << 29`, producing predictable results without errors or undefined behavior. + +When performing bitwise left shift operations (<<) on 32-bit integer values in Redpanda SQL, the shift count is taken *modulo* 32. This means: * Shifting by a number of bits greater than or equal to 32 wraps around * For example, `1 << 35` is equivalent to `1 << 3` because `35`latexmath:[modulo]`32 = 3` + +[WARNING] +==== +If you shift by a value larger than or equal to 32, the actual shift is the remainder after dividing by 32, which may lead to unexpected results if not carefully considered. +==== + +== Examples + +This section uses a simplified version of the `film` table from the Pagila database, containing only the `title`, `rating` and `privilegs` columns. The complete schema for the `film` table can be found on the link:https://www.postgresql.org/ftp/projects/pgFoundry/dbsamples/pagila/pagila/[Pagila^] database website. + +[source,sql] +---- +DROP TABLE IF EXISTS film; +CREATE TABLE film ( + title TEXT NOT NULL, + rating TEXT, + privileges INT NOT NULL +); +INSERT INTO film(title, rating, privileges) VALUES + ('ATTRACTION NEWTON', 'PG-13', 1), -- Free users + ('CHRISTMAS MOONSHINE', 'NC-17', 2), -- Premium users + ('DANGEROUS UPTOWN', 'PG', 3), -- Free + Premium users (bits 0 and 1) + ('KILL BROTHERHOOD', 'G', 4), -- Admin-only content + ('HALLOWEEN NUTS', 'PG-13', 1), + ('HOURS RAGE', 'NC-17', 2), + ('PIANIST OUTFIELD', 'NC-17', 3), + ('PICKUP DRIVING', 'G', 4), + ('INDEPENDENCE HOTEL', 'NC-17', 1), + ('PRIVATE DROP', 'PG', 2), + ('SAINTS BRIDE', 'G', 3), + ('FOREVER CANDIDATE', 'NC-17', 4), + ('MILLION ACE', 'PG-13', 1), + ('SLEEPY JAPANESE', 'PG', 2), + ('WRATH MILE', 'NC-17', 3), + ('YOUTH KICK', 'NC-17', 4), + ('CLOCKWORK PARADISE', 'PG-13', 1); +---- + +[NOTE] +==== +* Privilege 1 (binary 0001): Free users can watch. +* Privilege 2 (binary 0010): Premium users can watch. +* Privilege 3 (binary 0011): Both free and premium users can watch. +* Privilege 4 (binary 0100): Admin-only content. +==== +The following query uses the integer `Left shift (<<)` operation, shifting the privileges value left by 1 for the movie '`ATTRACTION NEWTON`': + +[source,sql] +---- +UPDATE film +SET privileges = privileges << 1 +WHERE title = 'ATTRACTION NEWTON'; +---- + +After running the update, you can verify the change with: + +[source,sql] +---- +SELECT title, privileges FROM film WHERE title = 'ATTRACTION NEWTON'; +---- + +Expected output: + +[source,sql] +---- + title | privileges +-------------------+------------ + ATTRACTION NEWTON | 2 +(1 row) +---- diff --git a/modules/reference/pages/sql/sql-operators/bitwise-shift-right.adoc b/modules/reference/pages/sql/sql-operators/bitwise-shift-right.adoc new file mode 100644 index 000000000..e3fc67643 --- /dev/null +++ b/modules/reference/pages/sql/sql-operators/bitwise-shift-right.adoc @@ -0,0 +1,98 @@ += BITWISE SHIFT RIGHT +:description: Bitwise shift operators in Redpanda SQL manipulate the bits of integer value by shifting them left or right. +:page-topic-type: reference + +Bitwise shift operators in Redpanda SQL manipulate the bits of integer value by shifting them left or right. These operations are fundamental in low-level data processing and optimization. + +The bitwise *right shift (`>>`)* operator shifts the bits of an integer to the right by the specified number of positions. For *integers*, this operation is equivalent to dividing the integer value by 2 raised to the power of the shift amount, discarding any remainder. Unlike a logical shift, the right shift in Redpanda SQL is an *arithmetic* shift, meaning that the vacant leftmost bits are filled with the original sign bits (the most significant bit) rather than zeros. This preserves the sign of the integer after the shift, ensuring correct behavior for signed values. During the shift, low-order bits that move beyond the size limit are permanently lost. However, the overall length of the bit string is preserved, with zeros padding on the left side to maintain the length. + +== Syntax + +The syntax for this function is: + +[source,sql] +---- +value >> shift_amount +---- + +== Parameters + +* `value`: Integer expression. +* `shift_amount`: A *non-negative* integer specifying how many bit positions to shift. + +== Restrictions + +Bitwise shift operators in Redpanda SQL require the shift amount to be a *non-negative* integer. Redpanda SQL treats negative shift counts as valid by applying modulo arithmetic based on the bit width, so shifting `1 >> -3` in a 32-bit integer is equivalent to shifting `1 >> 29`, producing predictable results without errors or undefined behavior. + +When performing bitwise right shift operations (>>) on 32-bit integer values in Redpanda SQL, the shift count is taken *modulo* 32, just as with left shifts. This means: * Shifting by a number of bits greater than or equal to 32 wraps around * For example, `1 >> 35` is equivalent to `1 >> 3` because `35`latexmath:[modulo]`32 = 3` + +[WARNING] +==== +If you shift by a value larger than or equal to 32, the actual shift is the remainder after dividing by 32, +which may lead to unexpected results if not carefully considered. +==== + +== Examples + +This section uses a simplified version of the `film` table from the Pagila database, containing only the `title`, `rating` and `privilegs` columns. The complete schema for the `film` table can be found on the link:https://www.postgresql.org/ftp/projects/pgFoundry/dbsamples/pagila/pagila/[Pagila^] database website. + +[source,sql] +---- +DROP TABLE IF EXISTS film; +CREATE TABLE film ( + title TEXT NOT NULL, + rating TEXT, + privileges INT NOT NULL +); +INSERT INTO film(title, rating, privileges) VALUES + ('ATTRACTION NEWTON', 'PG-13', 1), -- Free users + ('CHRISTMAS MOONSHINE', 'NC-17', 2), -- Premium users + ('DANGEROUS UPTOWN', 'PG', 3), -- Free + Premium users (bits 0 and 1) + ('KILL BROTHERHOOD', 'G', 4), -- Admin-only content + ('HALLOWEEN NUTS', 'PG-13', 1), + ('HOURS RAGE', 'NC-17', 2), + ('PIANIST OUTFIELD', 'NC-17', 3), + ('PICKUP DRIVING', 'G', 4), + ('INDEPENDENCE HOTEL', 'NC-17', 1), + ('PRIVATE DROP', 'PG', 2), + ('SAINTS BRIDE', 'G', 3), + ('FOREVER CANDIDATE', 'NC-17', 4), + ('MILLION ACE', 'PG-13', 1), + ('SLEEPY JAPANESE', 'PG', 2), + ('WRATH MILE', 'NC-17', 3), + ('YOUTH KICK', 'NC-17', 4), + ('CLOCKWORK PARADISE', 'PG-13', 1); +---- + +[NOTE] +==== +* Privilege 1 (binary 0001): Free users can watch. +* Privilege 2 (binary 0010): Premium users can watch. +* Privilege 3 (binary 0011): Both free and premium users can watch. +* Privilege 4 (binary 0100): Admin-only content. +==== +The following query uses the integer `right shift (>>)` operation, shifting the privileges value right by 1 for the movie '`DANGEROUS UPTOWN`': + +[source,sql] +---- +UPDATE film +SET privileges = privileges >> 1 +WHERE title = 'DANGEROUS UPTOWN'; +---- + +After running the update, you can verify the change with: + +[source,sql] +---- +SELECT title, privileges FROM film WHERE title = 'DANGEROUS UPTOWN'; +---- + +Expected output: + +[source,sql] +---- + title | privileges +-------------------+------------ + DANGEROUS UPTOWN | 1 +(1 row) +---- diff --git a/modules/reference/pages/sql/sql-operators/index.adoc b/modules/reference/pages/sql/sql-operators/index.adoc new file mode 100644 index 000000000..72bb32bd9 --- /dev/null +++ b/modules/reference/pages/sql/sql-operators/index.adoc @@ -0,0 +1,113 @@ += Operators +:description: Operators in Redpanda SQL are special symbols used in expressions to compare, combine, or manipulate values. +:page-topic-type: reference + +Operators in Redpanda SQL are special symbols used in expressions to compare, combine, or manipulate values. + +== Comparison operators + +[cols="1,1,3,2",options="header"] +|=== +|Operator |Name |Description |Example +|`=` |Equal to |The value of one item is equal to another item's value. |`cust_name = 'Mike'` +|`<>` or `!=` |Not equal to |The value of one item is not equal to the other item's value. |`subj_score != 'FAILED'` +|`>` |Greater than |The value of one item is greater than another item's value. |`stock_value > 10` +|`<` |Less than |The value of one item is less than another item's value. |`stock_value < 20` +|`>=` |Greater than or equal to |The value of one item is greater than or equal to the other item's value. |`prod_price >= 3000` +|`+<=+` |Less than or equal to |The value of one item is less than or equal to the other item's value. |`prod_price +<=+ 9000` +|=== + +== Logical operators + +[cols="1,1,3,2",options="header"] +|=== +|Operator |Name |Description |Example +|`AND` |Logical AND |Returns true if both conditions are true. |`a > 1 AND b < 10` +|`OR` |Logical OR |Returns true if at least one condition is true. |`a = 1 OR a = 2` +|`NOT` |Logical NOT |Reverses the result of a condition. |`NOT true = false` +|=== + +== Null and boolean test operators + +[cols="1,3,2",options="header"] +|=== +|Operator |Description |Example +|`IS NULL` |Returns true if the value is NULL. |`WHERE salary IS NULL` +|`IS NOT NULL` |Returns true if the value is not NULL. |`WHERE salary IS NOT NULL` +|`IS DISTINCT FROM` |Returns true if the two values are not equal, treating NULL as a comparable value. |`a IS DISTINCT FROM b` +|`IS NOT DISTINCT FROM` |Returns true if the two values are equal, treating NULL as a comparable value. |`a IS NOT DISTINCT FROM b` +|`IS TRUE` |Returns true if the boolean value is true. |`WHERE active IS TRUE` +|`IS NOT TRUE` |Returns true if the boolean value is not true (false or NULL). |`WHERE active IS NOT TRUE` +|`IS FALSE` |Returns true if the boolean value is false. |`WHERE active IS FALSE` +|`IS NOT FALSE` |Returns true if the boolean value is not false (true or NULL). |`WHERE active IS NOT FALSE` +|=== + +== Arithmetic operators + +[cols="1,1,3,2",options="header"] +|=== +|Operator |Name |Description |Example +|`+` |Addition |Adds two values, or acts as unary positive. |`salary + bonus` +|`-` |Subtraction |Subtracts one value from another, or negates a value. |`price - discount` +|`*` |Multiplication |Multiplies two values. |`quantity * price` +|`/` |Division |Divides one value by another. |`total / count` +|`%` |Modulus |Returns the remainder of a division. |`10 % 3` +|=== + +== Mathematical operators + +[cols="1,1,3,2",options="header"] +|=== +|Operator |Name |Description |Example +|`@` |Absolute value |Returns the absolute value of a number. |`@ -5` +|`^` |Exponentiation |Raises a number to a power. |`2 ^ 3` +|`\|/` |Square root |Returns the square root of a number. |`\|/ 25` +|`\|\|/` |Cube root |Returns the cube root of a number. |`\|\|/ 27` +|=== + +== Bitwise operators + +[cols="1,1,3,2",options="header"] +|=== +|Operator |Name |Description |Example +|`&` |Bitwise AND |Performs a bitwise AND on two integers. |`5 & 3` +|`\|` |Bitwise OR |Performs a bitwise OR on two integers. |`5 \| 3` +|`#` |Bitwise XOR |Performs a bitwise exclusive OR on two integers. |`5 # 3` +|`~` |Bitwise NOT |Performs a bitwise NOT (complement) on an integer. |`~ 5` +|xref:reference:sql/sql-operators/bitwise-shift-left.adoc[`<<`] |Bitwise shift left |Shifts the bits of an integer to the left. |`1 << 4` +|xref:reference:sql/sql-operators/bitwise-shift-right.adoc[`>>`] |Bitwise shift right |Shifts the bits of an integer to the right. |`16 >> 2` +|=== + +== String and pattern matching operators + +[cols="1,1,3,2",options="header"] +|=== +|Operator |Name |Description |Example +|`\|\|` |Concatenation |Concatenates two strings, or appends elements to an array. |`'foo' \|\| 'bar'` +|`~~` |LIKE |Returns true if the string matches the pattern. |`name ~~ 'J%'` +|`!~~` |NOT LIKE |Returns true if the string does not match the pattern. |`name !~~ 'J%'` +|`~~*` |ILIKE |Case-insensitive LIKE pattern matching. |`name ~~* 'j%'` +|`!~~*` |NOT ILIKE |Case-insensitive NOT LIKE pattern matching. |`name !~~* 'j%'` +|`~` |Regex match |Returns true if the string matches the regular expression. |`name ~ '^J'` +|`~*` |Regex match (case-insensitive) |Case-insensitive regular expression match. |`name ~* '^j'` +|`!~` |Regex not match |Returns true if the string does not match the regular expression. |`name !~ '^J'` +|`!~*` |Regex not match (case-insensitive) |Case-insensitive regular expression non-match. |`name !~* '^j'` +|=== + +== JSON operators + +[cols="1,1,3,2",options="header"] +|=== +|Operator |Name |Description |Example +|`->` |JSON extract |Extracts a JSON object field or array element, returning JSON. |`data -> 'key'` +|`->>` |JSON extract text |Extracts a JSON object field or array element as text. |`data ->> 'key'` +|=== + +== Array operators + +[cols="1,1,3,2",options="header"] +|=== +|Operator |Name |Description |Example +|`= ANY` |Any element equals |Returns true if any element in the array matches the value. |`3 = ANY(ARRAY[1,2,3])` +|`= ALL` |All elements equal |Returns true if all elements in the array match the value. |`1 = ALL(ARRAY[1,1,1])` +|=== diff --git a/modules/reference/pages/sql/sql-statements/alter-redpanda-catalog.adoc b/modules/reference/pages/sql/sql-statements/alter-redpanda-catalog.adoc new file mode 100644 index 000000000..10645e4cd --- /dev/null +++ b/modules/reference/pages/sql/sql-statements/alter-redpanda-catalog.adoc @@ -0,0 +1,27 @@ += ALTER REDPANDA CATALOG +:description: The ALTER REDPANDA CATALOG statement modifies connection properties of an existing Redpanda catalog. +:page-topic-type: reference + +The `ALTER REDPANDA CATALOG` statement modifies connection properties of an existing Redpanda catalog. + +== Syntax + +[source,sql] +---- +ALTER REDPANDA CATALOG [IF EXISTS] catalog_name +WITH (option = 'value' [, ...]); +---- + +* `catalog_name`: Name of the catalog to modify. +* `IF EXISTS`: Optional. Prevents an error if the catalog does not exist. +* `option = 'value'`: One or more connection options to update. See xref:reference:sql/sql-statements/create-redpanda-catalog.adoc[CREATE REDPANDA CATALOG] for the full list of options. + +== Examples + +Update the broker address for an existing catalog: + +[source,sql] +---- +ALTER REDPANDA CATALOG my_catalog +WITH (initial_brokers = 'new-broker:9092'); +---- diff --git a/modules/reference/pages/sql/sql-statements/alter-storage.adoc b/modules/reference/pages/sql/sql-statements/alter-storage.adoc new file mode 100644 index 000000000..108464989 --- /dev/null +++ b/modules/reference/pages/sql/sql-statements/alter-storage.adoc @@ -0,0 +1,27 @@ += ALTER STORAGE +:description: The ALTER STORAGE statement modifies credentials or configuration of an existing storage connection. +:page-topic-type: reference + +The `ALTER STORAGE` statement modifies the credentials or configuration of an existing storage connection. + +== Syntax + +[source,sql] +---- +ALTER STORAGE [IF EXISTS] storage_name +WITH (option = 'value' [, ...]); +---- + +* `storage_name`: Name of the storage connection to modify. +* `IF EXISTS`: Optional. Prevents an error if the storage connection does not exist. +* `option = 'value'`: One or more options to update. See xref:reference:sql/sql-statements/create-storage.adoc[CREATE STORAGE] for the full list of options. + +== Examples + +Update the region for an existing storage connection: + +[source,sql] +---- +ALTER STORAGE my_s3_storage +WITH (region = 'eu-west-1'); +---- diff --git a/modules/reference/pages/sql/sql-statements/alter-table.adoc b/modules/reference/pages/sql/sql-statements/alter-table.adoc new file mode 100644 index 000000000..49cf42215 --- /dev/null +++ b/modules/reference/pages/sql/sql-statements/alter-table.adoc @@ -0,0 +1,28 @@ += ALTER TABLE +:description: The ALTER TABLE statement modifies options of a catalog table mapped to a Redpanda topic. +:page-topic-type: reference + +The `ALTER TABLE` statement modifies the options of a catalog table mapped to a Redpanda topic. + +== Syntax + +[source,sql] +---- +ALTER TABLE [IF EXISTS] catalog_name=>table_name +WITH (option = 'value' [, ...]); +---- + +* `catalog_name`: Name of the Redpanda catalog containing the table. +* `table_name`: Name of the table to modify. +* `IF EXISTS`: Optional. Prevents an error if the table does not exist. +* `option = 'value'`: One or more table options to update. See xref:reference:sql/sql-statements/create-table.adoc[CREATE TABLE] for the full list of options. + +== Examples + +Update the error handling policy for an existing catalog table: + +[source,sql] +---- +ALTER TABLE my_catalog=>sensor_readings +WITH (error_handling_policy = 'FILL_NULL'); +---- diff --git a/modules/reference/pages/sql/sql-statements/copy-to.adoc b/modules/reference/pages/sql/sql-statements/copy-to.adoc new file mode 100644 index 000000000..36ba8c99e --- /dev/null +++ b/modules/reference/pages/sql/sql-statements/copy-to.adoc @@ -0,0 +1,202 @@ += COPY TO +:description: The COPY TO statement exports tables, specific columns, or query results to CSV files or to a client. +:page-topic-type: reference + +The `COPY TO` statement exports tables, specific columns, or query results to a CSV file or to the standard output. + +[WARNING] +==== +By default, the `COPY TO` command overwrites the destination file if it already exists. Ensure that the directory where you save the file has the necessary write permissions. +==== + +== Syntax + +[source,sql] +---- +COPY { table_name [ ( column_name [, ...] ) ] | ( query ) } +TO { 'file_path' | STDOUT } +[ WITH ( option [, ...] ) ]; +---- + +* `table_name`: Table containing the data to export. +* `column_name`: Optional. Specific columns to export. If omitted, all columns are exported. +* `query`: A `SELECT` statement to export specific results. +* `file_path`: Path to the output file. Use `STDOUT` to send the data to the standard output stream. +* `option`: One or more options. See <>. + +[#options] +== Options + +* `FORMAT`: Output format. Only `CSV` is supported. +* `DELIMITER`: Single-character string used to separate fields. Default is `,`. +* `HEADER`: Whether to include a header row with column names. Accepts `ON`, `TRUE`, `1`, `OFF`, `FALSE`, or `0`. Default is `OFF`. +* `NULL`: String used to represent `NULL` values. Default is the empty string. +* Cloud storage credentials: Use `AWS_CRED`, `AZURE_CRED`, or `GCS_CRED` to authenticate when exporting to cloud storage. See <>. + +== Examples + +The following examples use a table mapped to a Redpanda topic through a catalog. For information on setting up catalogs and tables, see xref:reference:sql/sql-statements/create-table.adoc[CREATE TABLE]. + +=== Export all columns from a table + +Copy all columns in a table to a CSV file: + +[source,sql] +---- +COPY my_catalog=>employee_salary TO '/path/to/exportsalary.csv'; +---- + +=== Export specific columns from a table + +Specify the column names to export only those columns: + +[source,sql] +---- +COPY my_catalog=>employee_salary (empid, empname, empsalary) TO '/path/to/exportsalary.csv'; +---- + +=== Export results of a SELECT statement + +Export only the rows that match a `WHERE` condition: + +[source,sql] +---- +COPY (SELECT * FROM my_catalog=>employee_salary WHERE empdept = 'Marketing') TO '/path/to/exportsalary.csv'; +---- + +=== Export with a custom delimiter + +Specify the delimiter using the `DELIMITER` option. Common delimiters include comma (`,`), semicolon (`;`), pipe (`|`), and dash (`-`). + +[source,sql] +---- +COPY my_catalog=>customer TO '/path/to/customerexport.csv' WITH (DELIMITER ';'); +---- + +The exported CSV uses the specified delimiter: + +[source,text] +---- +cust_id;cust_name +11001;Maya +11003;Ricky +11009;Sean +---- + +=== Export with column headers + +Include column names as a header row using `HEADER ON` (or `HEADER TRUE`, or `HEADER 1`): + +[source,sql] +---- +COPY my_catalog=>personal_details TO '/path/to/personalinfo.csv' WITH (HEADER ON); +---- + +The exported file includes a header row: + +[source,text] +---- +id,first_name,last_name,gender +1,'Mark','Wheeler','M' +2,'Tom','Hanks','M' +3,'Jane','Hopper','F' +---- + +To omit the header (the default), use `HEADER OFF` (or `HEADER FALSE`, or `HEADER 0`). + +=== Export with a NULL replacement string + +Specify a string to replace `NULL` values in the exported file: + +[source,sql] +---- +COPY my_catalog=>example_table TO '/path/to/exampleexport.csv' WITH (NULL 'unknown'); +---- + +In the exported file, `NULL` values are represented as `unknown`. + +=== Export to standard output + +Send the data directly to the client instead of writing to a server-side file: + +[source,sql] +---- +COPY my_catalog=>book_inventory TO STDOUT; +---- + +The query returns: + +[source,sql] +---- +"To Kill a Mockingbird",5 +1984,8 +"The Great Gatsby",3 +"Moby Dick",2 +"War and Peace",4 +---- + +[NOTE] +==== +* Only CSV format is supported with `STDOUT`. +* The default delimiter for CSV format is a comma (`,`). +==== + +[#cloud-storage] +=== Export to cloud storage + +To export data to cloud storage, use the `COPY TO` command with the appropriate credentials option for your provider. + +==== AWS S3 + +* `AWS_REGION`: AWS region associated with the storage service. +* `AWS_KEY_ID`: Key identifier for authentication. +* `AWS_PRIVATE_KEY`: Access key for authentication. +* `ENDPOINT`: URL endpoint for the storage service. + +[source,sql] +---- +COPY my_catalog=>film TO 's3://your-bucket/file_name' +WITH (AWS_CRED(AWS_REGION 'us-west-1', AWS_KEY_ID 'key_id', AWS_PRIVATE_KEY 'access_key', ENDPOINT 's3.us-west-1.amazonaws.com'), + FORMAT CSV, HEADER ON, NULL 'unknown'); +---- + +==== Google Cloud Storage + +Pass the path to your credentials JSON file: + +[source,sql] +---- +COPY my_catalog=>film TO 'gs://your-bucket/file_name' WITH (GCS_CRED('/path/to/credentials.json')); +---- + +If you cannot use a path to the credentials file, pass its contents as a string: + +[source,sql] +---- +COPY my_catalog=>project TO 'gs://your-bucket/project_file' WITH (GCS_CRED('')); +---- + +You can also use `AWS_CRED` with GCS by setting the endpoint: + +[source,sql] +---- +COPY my_catalog=>project TO 'gs://your-bucket/project_file' +WITH (AWS_CRED(AWS_REGION 'region1', AWS_KEY_ID 'key_id', AWS_PRIVATE_KEY 'access_key', ENDPOINT 'https://storage.googleapis.com')); +---- + +[TIP] +==== +For Google Cloud Storage, use HMAC keys for authentication. See the link:https://cloud.google.com/storage/docs/authentication/hmackeys[HMAC keys - Cloud Storage^] page for details. +==== + +==== Azure Blob Storage + +* `TENANT_ID`: Tenant identifier representing your organization's identity in Azure. +* `CLIENT_ID`: Client identifier used for authentication. +* `CLIENT_SECRET`: Secret identifier used as a password for authentication. + +[source,sql] +---- +COPY my_catalog=>taxi_data TO 'wasbs://container-name/your_blob' +WITH (AZURE_CRED(TENANT_ID 'your_tenant_id' CLIENT_ID 'your_client_id', CLIENT_SECRET 'your_client_secret')); +---- diff --git a/modules/reference/pages/sql/sql-statements/create-redpanda-catalog.adoc b/modules/reference/pages/sql/sql-statements/create-redpanda-catalog.adoc new file mode 100644 index 000000000..605e16821 --- /dev/null +++ b/modules/reference/pages/sql/sql-statements/create-redpanda-catalog.adoc @@ -0,0 +1,93 @@ += CREATE REDPANDA CATALOG +:description: The CREATE REDPANDA CATALOG statement creates a named connection to a Redpanda cluster, enabling you to map topics as SQL tables. +:page-topic-type: reference + +The `CREATE REDPANDA CATALOG` statement creates a named connection to a Redpanda cluster. After creating a catalog, you can map Redpanda topics as queryable SQL tables using xref:reference:sql/sql-statements/create-table.adoc[CREATE TABLE]. + +== Syntax + +[source,sql] +---- +CREATE REDPANDA CATALOG [IF NOT EXISTS] catalog_name +WITH (option = 'value' [, ...]); +---- + +* `catalog_name`: Name for the new catalog connection. +* `IF NOT EXISTS`: Optional. Prevents an error if a catalog with the same name already exists. + +== Options + +[cols="<30%,<15%,<10%,<45%",options="header"] +|=== +|Option |Type |Required |Description + +|`initial_brokers` +|STRING +|Yes +|Bootstrap broker address(es) for the Redpanda cluster. + +|`schema_registry_url` +|STRING +|Yes +|URL of the Schema Registry endpoint. + +|`sasl_mechanism` +|STRING +|No +|SASL authentication mechanism (for example, `SCRAM-SHA-256`). + +|`sasl_username` +|STRING +|No +|SASL username. + +|`sasl_password` +|STRING +|No +|SASL password. + +|`tls_enabled` +|STRING +|No +|Enable TLS for broker connections (`true` or `false`). + +|`schema_registry_username` +|STRING +|No +|Schema Registry authentication username. + +|`schema_registry_password` +|STRING +|No +|Schema Registry authentication password. +|=== + +== Examples + +=== Create a basic catalog + +[source,sql] +---- +CREATE REDPANDA CATALOG my_catalog +WITH ( + initial_brokers = 'broker1:9092', + schema_registry_url = 'http://schema-registry:8081' +); +---- + +=== Create a catalog with authentication + +[source,sql] +---- +CREATE REDPANDA CATALOG my_secure_catalog +WITH ( + initial_brokers = 'broker1:9092', + schema_registry_url = 'https://schema-registry:8081', + sasl_mechanism = 'SCRAM-SHA-256', + sasl_username = 'admin', + sasl_password = 'secret', + tls_enabled = 'true', + schema_registry_username = 'sr_user', + schema_registry_password = 'sr_pass' +); +---- diff --git a/modules/reference/pages/sql/sql-statements/create-storage.adoc b/modules/reference/pages/sql/sql-statements/create-storage.adoc new file mode 100644 index 000000000..b5660288a --- /dev/null +++ b/modules/reference/pages/sql/sql-statements/create-storage.adoc @@ -0,0 +1,53 @@ += CREATE STORAGE +:description: The CREATE STORAGE statement creates a connection to external object storage for use with Redpanda catalogs. +:page-topic-type: reference + +The `CREATE STORAGE` statement creates a named connection to external object storage such as Amazon S3. + +== Syntax + +[source,sql] +---- +CREATE STORAGE [IF NOT EXISTS] storage_name +TYPE = S3 +WITH (option = 'value' [, ...]); +---- + +* `storage_name`: Name for the new storage connection. +* `TYPE`: Storage type. Redpanda SQL currently supports only `S3`. +* `IF NOT EXISTS`: Optional. Prevents an error if a storage connection with the same name already exists. + +== Options + +[cols="<30%,<15%,<10%,<45%",options="header"] +|=== +|Option |Type |Required |Description + +|`region` +|STRING +|Yes +|Cloud region for the storage bucket (for example, `us-west-2`). + +|`access_key_id` +|STRING +|Yes +|AWS access key ID. + +|`secret_access_key` +|STRING +|Yes +|AWS secret access key. +|=== + +== Examples + +[source,sql] +---- +CREATE STORAGE my_s3_storage +TYPE = S3 +WITH ( + region = 'us-west-2', + access_key_id = 'AKIAIOSFODNN7EXAMPLE', + secret_access_key = 'wJalrXUtnFEMI/K7MDENG/bPxRfiCYEXAMPLEKEY' +); +---- diff --git a/modules/reference/pages/sql/sql-statements/create-table.adoc b/modules/reference/pages/sql/sql-statements/create-table.adoc new file mode 100644 index 000000000..bcd8bdf6a --- /dev/null +++ b/modules/reference/pages/sql/sql-statements/create-table.adoc @@ -0,0 +1,105 @@ += CREATE TABLE +:description: The CREATE TABLE statement maps a Redpanda topic to a SQL table through a catalog, making topic data queryable with SQL. +:page-topic-type: reference + +The `CREATE TABLE` statement maps a Redpanda topic to a SQL table through a catalog. After creating the table, you can query topic data using standard SQL. + +NOTE: You must first xref:reference:sql/sql-statements/create-redpanda-catalog.adoc[create a Redpanda catalog connection] before creating tables. `CREATE TABLE` in Redpanda SQL maps Redpanda topics to SQL tables — it does not create standalone tables with user-defined schemas. + +== Syntax + +[source,sql] +---- +CREATE TABLE [IF NOT EXISTS] catalog_name=>table_name +WITH (option = 'value' [, ...]); +---- + +* `catalog_name`: Name of an existing Redpanda catalog. +* `table_name`: Name for the new table. +* `IF NOT EXISTS`: Optional. Prevents an error if a table with the same name already exists in the catalog. + +== Options + +[cols="<30%,<15%,<10%,<45%",options="header"] +|=== +|Option |Type |Required |Description + +|`topic` +|STRING +|Yes +|Name of the Redpanda topic to map to this table. + +|`schema_subject` +|STRING +|No +|Schema Registry subject name to use for deserializing topic data. + +|`schema_lookup_policy` +|STRING +|No +|How to resolve the schema version. Only `LATEST` is supported. + +|`error_handling_policy` +|STRING +|No +a|How to handle records that fail deserialization. + +* `FAIL` (default): Raises an error. +* `FILL_NULL`: Replaces failed fields with NULL. +* `DROP_RECORD`: Skips the record. + +|`struct_mapping_policy` +|STRING +|No +a|How to map nested structures to SQL columns. + +* `JSON` (default): Stores nested data as JSON. +* `FLATTEN`: Expands nested fields into top-level columns. +* `COMPOUND`: Maps to ROW types. +* `VARIANT`: Stores as a variant type. + +|`output_schema_message_full_name` +|STRING +|No +|Full Protobuf message name. Required when the schema contains multiple message definitions. +|=== + +== Examples + +=== Create a basic table + +Map the `transactions` topic to a table through `my_catalog`: + +[source,sql] +---- +CREATE TABLE my_catalog=>transactions +WITH (topic = 'transactions'); +---- + +=== Specify a Schema Registry subject + +Map a topic and specify the Schema Registry subject: + +[source,sql] +---- +CREATE TABLE my_catalog=>user_events +WITH ( + topic = 'user-events', + schema_subject = 'user-events-value', + schema_lookup_policy = 'LATEST' +); +---- + +=== Create a table with error handling + +Map a topic and skip records that fail deserialization: + +[source,sql] +---- +CREATE TABLE IF NOT EXISTS my_catalog=>sensor_readings +WITH ( + topic = 'sensor-data', + schema_subject = 'sensor-data-value', + error_handling_policy = 'DROP_RECORD' +); +---- diff --git a/modules/reference/pages/sql/sql-statements/describe.adoc b/modules/reference/pages/sql/sql-statements/describe.adoc new file mode 100644 index 000000000..3b36ddbf3 --- /dev/null +++ b/modules/reference/pages/sql/sql-statements/describe.adoc @@ -0,0 +1,128 @@ += DESCRIBE +:description: The DESCRIBE statement displays columns of a table or lists tables in a database. +:page-topic-type: reference + +The `DESCRIBE` statement displays columns of a table or lists tables in a database. + +== Syntax + +[source,sql] +---- +DESCRIBE DATABASE; +DESCRIBE TABLE table_name; +DESCRIBE TABLE catalog_name=>table_name; +DESCRIBE REDPANDA CATALOG catalog_name; +---- + +* `table_name`: Name of the table to describe. +* `catalog_name=>table_name`: Describes a table that is mapped to a Redpanda topic through a catalog. +* `catalog_name`: Name of a Redpanda catalog. Lists the tables and topic mappings in that catalog. + +[NOTE] +==== +This statement is available to all users with the `USAGE` privilege on the schema where the table is located. +==== + +== Examples + +=== Describe a table + +To show the columns of the `part` table, run the query: + +[source,sql] +---- +DESCRIBE TABLE part; +---- + +The query returns: + +[source,sql] +---- ++----------------+------------+-------------+-------+----------+ +| database_name | table_name | name | type | nullable | ++----------------+------------+-------------+-------+----------+ +| public | part | p_partkey | INT | f | +| public | part | p_name | TEXT | f | +| public | part | p_mfgr | TEXT | f | +| public | part | p_category | TEXT | f | +| public | part | p_brand | TEXT | f | +| public | part | p_color | TEXT | f | +| public | part | p_type | TEXT | f | +| public | part | p_size | INT | f | +| public | part | p_container | TEXT | f | ++----------------+------------+-------------+-------+----------+ +---- + +[TIP] +==== +Tables in this example use the `public` schema, the default in Redpanda SQL. For information on displaying tables from other schemas, see xref:reference:sql/schema.adoc[Schema]. +==== + +=== Describe a database + +To list all tables in the database, run: + +[source,sql] +---- +DESCRIBE DATABASE; +---- + +The query returns: + +[source,sql] +---- ++-----------------------------+ +| name | ++-----------------------------+ +| supplier_scale_1_no_index | +| features | +| orders | +| features2 | +| featurestable | +| featurestable1 | +| featurestable10 | ++-----------------------------+ +---- + +=== Describe a catalog table + +To view column details for a table mapped through a Redpanda catalog, run: + +[source,sql] +---- +DESCRIBE TABLE my_catalog=>transactions; +---- + +The query returns: + +[source,sql] +---- ++----------------+--------------+-------------+-------+----------+ +| database_name | table_name | name | type | nullable | ++----------------+--------------+-------------+-------+----------+ +| my_catalog | transactions | tx_id | INT | f | +| my_catalog | transactions | amount | REAL | f | +| my_catalog | transactions | created_at | TEXT | f | ++----------------+--------------+-------------+-------+----------+ +---- + +=== Describe a Redpanda catalog + +To list the tables and topic mappings in a Redpanda catalog, run: + +[source,sql] +---- +DESCRIBE REDPANDA CATALOG my_catalog; +---- + +The query returns: + +[source,sql] +---- ++----------------+--------------+ +| table_name | topic_name | ++----------------+--------------+ +| transactions | transactions | +| user_events | user-events | ++----------------+--------------+ +---- diff --git a/modules/reference/pages/sql/sql-statements/drop-redpanda-catalog.adoc b/modules/reference/pages/sql/sql-statements/drop-redpanda-catalog.adoc new file mode 100644 index 000000000..93bf456b9 --- /dev/null +++ b/modules/reference/pages/sql/sql-statements/drop-redpanda-catalog.adoc @@ -0,0 +1,32 @@ += DROP REDPANDA CATALOG +:description: The DROP REDPANDA CATALOG statement removes a Redpanda catalog connection. +:page-topic-type: reference + +The `DROP REDPANDA CATALOG` statement removes a Redpanda catalog connection. You must drop all tables within a catalog before dropping the catalog itself. + +== Syntax + +[source,sql] +---- +DROP REDPANDA CATALOG [IF EXISTS] catalog_name; +---- + +* `catalog_name`: Name of the catalog to remove. +* `IF EXISTS`: Optional. Prevents an error if the catalog does not exist. + +== Examples + +First, drop all tables in the catalog: + +[source,sql] +---- +DROP TABLE my_catalog=>user_events; +DROP TABLE my_catalog=>transactions; +---- + +Then drop the catalog: + +[source,sql] +---- +DROP REDPANDA CATALOG my_catalog; +---- diff --git a/modules/reference/pages/sql/sql-statements/drop-storage.adoc b/modules/reference/pages/sql/sql-statements/drop-storage.adoc new file mode 100644 index 000000000..6ba8eb644 --- /dev/null +++ b/modules/reference/pages/sql/sql-statements/drop-storage.adoc @@ -0,0 +1,22 @@ += DROP STORAGE +:description: The DROP STORAGE statement removes a named storage definition. +:page-topic-type: reference + +The `DROP STORAGE` statement removes a named storage definition. + +== Syntax + +[source,sql] +---- +DROP STORAGE [IF EXISTS] storage_name; +---- + +* `storage_name`: Name of the storage definition to remove. +* `IF EXISTS`: Optional. Prevents an error if the storage definition does not exist. + +== Examples + +[source,sql] +---- +DROP STORAGE my_s3_storage; +---- diff --git a/modules/reference/pages/sql/sql-statements/drop-table.adoc b/modules/reference/pages/sql/sql-statements/drop-table.adoc new file mode 100644 index 000000000..16477679d --- /dev/null +++ b/modules/reference/pages/sql/sql-statements/drop-table.adoc @@ -0,0 +1,28 @@ += DROP TABLE +:description: The DROP TABLE statement removes a table that was mapped to a Redpanda topic through a catalog. +:page-topic-type: reference + +The `DROP TABLE` statement removes a table that was mapped to a Redpanda topic through a catalog. This does not delete the underlying Redpanda topic. + +== Syntax + +[source,sql] +---- +DROP TABLE [IF EXISTS] catalog_name=>table_name; +---- + +* `catalog_name`: Name of the Redpanda catalog containing the table. +* `table_name`: Name of the table to remove. +* `IF EXISTS`: Optional. Prevents an error if the table does not exist. + +== Examples + +[source,sql] +---- +DROP TABLE my_catalog=>transactions; +---- + +[source,sql] +---- +DROP TABLE IF EXISTS my_catalog=>old_events; +---- diff --git a/modules/reference/pages/sql/sql-statements/index.adoc b/modules/reference/pages/sql/sql-statements/index.adoc new file mode 100644 index 000000000..e7dd6068d --- /dev/null +++ b/modules/reference/pages/sql/sql-statements/index.adoc @@ -0,0 +1,30 @@ += SQL statements +:description: SQL statements are the commands used to interact with Redpanda SQL. +:page-topic-type: reference + +SQL statements are commands used to interact with Redpanda SQL. These statements let you query data, manage catalog connections, and export results. + +NOTE: Redpanda SQL operates in read-only mode. Data mutation operations (`INSERT`, `UPDATE`, `DELETE`) and bulk import (`COPY FROM`) are not available. Data is ingested into Redpanda topics and made queryable through catalog mappings. + +The following table summarizes the statements supported by Redpanda SQL: + +[cols="<48%,<52%",options="header"] +|=== +|Statement |Description +|xref:reference:sql/sql-statements/alter-redpanda-catalog.adoc[ALTER REDPANDA CATALOG] |Modifies connection properties of an existing Redpanda catalog. +|xref:reference:sql/sql-statements/alter-storage.adoc[ALTER STORAGE] |Modifies credentials or configuration of an existing storage connection. +|xref:reference:sql/sql-statements/alter-table.adoc[ALTER TABLE] |Modifies options of a catalog table mapped to a Redpanda topic. +|xref:reference:sql/sql-statements/create-redpanda-catalog.adoc[CREATE REDPANDA CATALOG] |Creates a named connection to a Redpanda cluster. +|xref:reference:sql/sql-statements/create-storage.adoc[CREATE STORAGE] |Creates a connection to external object storage. +|xref:reference:sql/sql-statements/create-table.adoc[CREATE TABLE] |Maps a Redpanda topic to a SQL table through a catalog. +|xref:reference:sql/sql-statements/drop-table.adoc[DROP TABLE] |Removes a catalog table mapping. Does not delete the underlying Redpanda topic. +|xref:reference:sql/sql-statements/drop-redpanda-catalog.adoc[DROP REDPANDA CATALOG] |Removes a Redpanda catalog connection. +|xref:reference:sql/sql-statements/drop-storage.adoc[DROP STORAGE] |Removes a named storage definition. +|xref:reference:sql/sql-statements/select.adoc[SELECT] |Retrieves data from a table. +|xref:reference:sql/sql-statements/copy-to.adoc[COPY TO] |Exports query results or table data to CSV files. +|xref:reference:sql/sql-statements/set-show.adoc[SET/SHOW] |Configures or displays session-level settings such as time zone and search path. +|xref:reference:sql/sql-statements/show-execs.adoc[SHOW EXECS] |Displays currently running execution tasks across the cluster. +|xref:reference:sql/sql-statements/show-tables.adoc[SHOW TABLES] |Lists all tables within the current schema, database, or catalog. +|xref:reference:sql/sql-statements/show-nodes.adoc[SHOW NODES] |Displays the current state of nodes in a distributed cluster. +|xref:reference:sql/sql-statements/describe.adoc[DESCRIBE] |Shows detailed information about columns in a table, tables within a database, or catalog contents. +|=== diff --git a/modules/reference/pages/sql/sql-statements/keywords.adoc b/modules/reference/pages/sql/sql-statements/keywords.adoc new file mode 100644 index 000000000..688398b3d --- /dev/null +++ b/modules/reference/pages/sql/sql-statements/keywords.adoc @@ -0,0 +1,472 @@ += Keywords +:description: In Redpanda SQL, reserved and non-reserved keywords play an important role in SQL syntax and usage. +:page-topic-type: reference + +Redpanda SQL has reserved and non-reserved keywords. Reserved keywords cannot be used as identifiers (such as table or column names) unless explicitly quoted. Examples include `SELECT`, `INSERT`, and `UPDATE`. + +Non-reserved keywords have special meanings only in specific contexts and can be used as identifiers in others. For example, `DB` is non-reserved, so you can use it as a database name. + +The following table lists all available keywords: + +[cols="2,^1,^1,2",options="header"] +|=== +|Keyword |Reserved |Non-reserved |Notes + +|ABSOLUTE | |✓ | +|ACTION | |✓ | +|ADD | |✓ | +|AFTER | |✓ | +|AGGREGATE | |✓ | +|ALL |✓ | | +|ALLOCATE |✓ | | +|ALTER | |✓ | +|ANALYSE |✓ | | +|ANALYZE |✓ | | +|AND |✓ | | +|ANY |✓ | | +|ANY_VALUE | |✓ | +|ARE |✓ | | +|ARRAY |✓ | |Requires AS +|ARRAY_MAX_CARDINALITY | |✓ | +|AS |✓ | |Requires AS +|ASC |✓ | | +|ASENSITIVE | |✓ | +|ASSERTION | |✓ | +|ASSIGNMENT | |✓ | +|ASYMMETRIC |✓ | | +|AT | |✓ | +|ATOMIC | |✓ | +|AUTHORIZATION |✓ | |Can be function or type +|AVG | |✓ | +|BEFORE | |✓ | +|BEGIN | |✓ | +|BEGIN_FRAME | |✓ | +|BEGIN_PARTITION | |✓ | +|BETWEEN | |✓ |Cannot be function or type +|BIGINT | |✓ |Cannot be function or type +|BIT | |✓ |Cannot be function or type +|BIT_LENGTH |✓ | | +|BLOB | |✓ | +|BOOL | |✓ | +|BOOLEAN | |✓ |Cannot be function or type +|BOTH |✓ | | +|BY | |✓ | +|CACHE | |✓ | +|CALL |✓ | | +|CALLED |✓ | | +|CARDINALITY | |✓ | +|CASCADE |✓ | | +|CASCADED |✓ | | +|CASE |✓ | | +|CAST |✓ | | +|CATALOG | |✓ | +|CEILING | |✓ | +|CHAR | |✓ | +|CHAR_LENGTH | |✓ | +|CHARACTER | |✓ | +|CHARACTER_LENGTH | |✓ | +|CHECK |✓ | | +|CLASSIFIER | |✓ | +|CLOB | |✓ | +|CLOSE |✓ | | +|COALESCE |✓ | | +|COLLATE |✓ | | +|COLLATION |✓ | | +|COLLECT | |✓ | +|COLUMN |✓ | | +|COLUMNS | |✓ | +|COMMIT |✓ | | +|CONDITION |✓ | | +|CONNECT |✓ | | +|CONNECTION |✓ | | +|CONSTRAINT |✓ | | +|CONSTRAINTS | |✓ | +|CONTAINS | |✓ | +|CONTINUE |✓ | | +|CONTROL | |✓ | +|CONVERT | |✓ | +|COPY | |✓ | +|CORR | |✓ | +|CORRESPONDING |✓ | | +|COVAR_POP | |✓ | +|COVAR_SAMP | |✓ | +|CREATE |✓ | | +|CROSS |✓ | | +|CUBE |✓ | | +|CUME_DIST | |✓ | +|CURRENT |✓ | | +|CURRENT_USER |✓ | | +|CURRENT_ROLE |✓ | | +|CURSOR |✓ | | +|CYCLE |✓ | | +|DATABASE | |✓ | +|DATABASES | |✓ | +|DATALINK | |✓ | +|DATE | |✓ | +|DATETIME | |✓ | +|DAY | |✓ | +|DEALLOCATE |✓ | | +|DEC | |✓ | +|DECFLOAT | |✓ | +|DECIMAL | |✓ | +|DECLARE |✓ | | +|DEFAULT |✓ | | +|DEFERRABLE |✓ | | +|DEFERRED |✓ | | +|DEFINE | |✓ | +|DELETE |✓ | | +|DELTA | |✓ | +|DENSE_RANK | |✓ | +|DEREF | |✓ | +|DESC |✓ | | +|DESCRIBE |✓ | | +|DESCRIPTOR |✓ | | +|DETERMINISTIC |✓ | | +|DIAGNOSTICS |✓ | | +|DIRECT | |✓ | +|DISCONNECT |✓ | | +|DISTINCT |✓ | | +|DLNEWCOPY | |✓ | +|DLPREVIOUSCOPY | |✓ | +|DLURLCOMPLETE | |✓ | +|DLURLCOMPLETEONLY | |✓ | +|DLURLCOMPLETEWRITE | |✓ | +|DLURLPATH | |✓ | +|DLURLPATHONLY | |✓ | +|DLURLPATHWRITE | |✓ | +|DLURLSCHEME | |✓ | +|DLURLSERVER | |✓ | +|DLVALUE | |✓ | +|DO |✓ | | +|DOMAIN | |✓ | +|DOUBLE | |✓ | +|DROP |✓ | | +|DYNAMIC | |✓ | +|EACH |✓ | | +|ELEMENT | |✓ | +|ELSE |✓ | | +|EMPTY | |✓ | +|END |✓ | | +|END_FRAME | |✓ | +|END_PARTITION | |✓ | +|EQUALS | |✓ | +|ESCAPE |✓ | | +|EVERY |✓ | | +|EXCEPT |✓ | | +|EXCEPTION |✓ | | +|EXEC |✓ | | +|EXECUTE |✓ | | +|EXISTS |✓ | | +|EXP | |✓ | +|EXPLAIN |✓ | | +|EXTERNAL |✓ | | +|EXTRACT |✓ | | +|FALSE |✓ | | +|FETCH |✓ | | +|FILE | |✓ | +|FILTER |✓ | | +|FIRST |✓ | | +|FIRST_VALUE | |✓ | +|FLOAT | |✓ | +|FLOOR | |✓ | +|FOR |✓ | | +|FOREIGN |✓ | | +|FORMAT | |✓ | +|FOUND | |✓ | +|FRAME_ROW | |✓ | +|FREE | |✓ | +|FROM |✓ | | +|FULL |✓ | | +|FUNCTION |✓ | | +|FUSION | |✓ | +|GET | |✓ | +|GLOBAL |✓ | | +|GO | |✓ | +|GOTO | |✓ | +|GRANT |✓ | | +|GROUP |✓ | | +|GROUPING |✓ | | +|GROUPS | |✓ | +|HASH | |✓ | +|HAVING |✓ | | +|HINT | |✓ | +|HOLD | |✓ | +|HOUR | |✓ | +|IDENTITY |✓ | | +|IF |✓ | | +|ILIKE | |✓ | +|IMMEDIATE |✓ | | +|IMPORT | |✓ | +|IN |✓ | | +|INDEX |✓ | | +|INDICATOR |✓ | | +|INITIAL |✓ | | +|INITIALLY |✓ | | +|INNER |✓ | | +|INOUT |✓ | | +|INPUT |✓ | | +|INSENSITIVE |✓ | | +|INSERT |✓ | | +|INT | |✓ | +|INTEGER | |✓ | +|INTERSECT |✓ | | +|INTERSECTION | |✓ | +|INTERVAL |✓ | | +|INTO |✓ | | +|IS |✓ | | +|ISNULL | |✓ | +|ISOLATION |✓ | | +|JOIN |✓ | | +|JSON | |✓ | +|JSON_ARRAY | |✓ | +|JSON_ARRAYAGG | |✓ | +|JSON_EXISTS | |✓ | +|JSON_OBJECT | |✓ | +|JSON_OBJECTAGG | |✓ | +|JSON_QUERY | |✓ | +|JSON_TABLE | |✓ | +|JSON_TABLE_PRIMITIVE | |✓ | +|JSON_VALUE | |✓ | +|JSONB | |✓ | +|KEY | |✓ | +|LAG | |✓ | +|LANGUAGE |✓ | | +|LARGE | |✓ | +|LAST |✓ | | +|LAST_VALUE | |✓ | +|LATERAL |✓ | | +|LEAD | |✓ | +|LEADING |✓ | | +|LEFT |✓ | | +|LEVEL | |✓ | +|LIKE |✓ | | +|LIKE_REGEX | |✓ | +|LIMIT |✓ | | +|LISTAGG | |✓ | +|LN | |✓ | +|LOAD | |✓ | +|LOCAL |✓ | | +|LOCALTIME |✓ | | +|LOCALTIMESTAMP |✓ | | +|LONG | |✓ | +|MEASURES | |✓ | +|MEMBER | |✓ | +|MERGE |✓ | | +|METHOD | |✓ | +|MINUS |✓ | | +|MINUTE | |✓ | +|MODIFIES |✓ | | +|MODULE | |✓ | +|MONTH | |✓ | +|MULTISET | |✓ | +|NAMES | |✓ | +|NATIONAL | |✓ | +|NATURAL |✓ | | +|NCHAR | |✓ | +|NCLOB | |✓ | +|NEW |✓ | | +|NEXT | |✓ | +|NO |✓ | | +|NONE | |✓ | +|NOT |✓ | | +|NTILE | |✓ | +|NULL |✓ | | +|NULLIF |✓ | | +|NULLS |✓ | | +|NVARCHAR | |✓ | +|OCCURRENCES_REGEX | |✓ | +|OCTET_LENGTH | |✓ | +|OF |✓ | | +|OFF | |✓ | +|OFFSET |✓ | | +|OLD |✓ | | +|OMIT | |✓ | +|ON |✓ | | +|ONE | |✓ | +|ONLY |✓ | | +|OPEN |✓ | | +|OPTION |✓ | | +|OR |✓ | | +|ORDER |✓ | | +|OUT |✓ | | +|OUTER |✓ | | +|OUTPUT |✓ | | +|OVER |✓ | | +|OVERLAPS |✓ | | +|OVERLAY | |✓ | +|PAD | |✓ | +|PARAMETER |✓ | | +|PARAMETERS | |✓ | +|PARTIAL |✓ | | +|PARTITION |✓ | | +|PATTERN | |✓ | +|PER | |✓ | +|PERCENT | |✓ | +|PERCENT_RANK | |✓ | +|PERCENTILE_CONT | |✓ | +|PERCENTILE_DISC | |✓ | +|PERIOD |✓ | | +|PERMUTE | |✓ | +|PLACING | |✓ | +|PLAN | |✓ | +|PORTION | |✓ | +|PRECEDES | |✓ | +|PRECISION |✓ | | +|PREPARE |✓ | | +|PRESERVE |✓ | | +|PRIMARY |✓ | | +|PRIOR |✓ | | +|PRIVILEGES | |✓ | +|PROCEDURE |✓ | | +|PTF | |✓ | +|PUBLIC |✓ | | +|RANGE |✓ | | +|READ |✓ | | +|READS |✓ | | +|REAL | |✓ | +|RECURSIVE |✓ | | +|REF |✓ | | +|REFERENCES |✓ | | +|REFERENCING |✓ | | +|REGR_AVGX | |✓ | +|REGR_AVGY | |✓ | +|REGR_COUNT | |✓ | +|REGR_INTERCEPT | |✓ | +|REGR_R2 | |✓ | +|REGR_SLOPE | |✓ | +|REGR_SXX | |✓ | +|REGR_SXY | |✓ | +|REGR_SYY | |✓ | +|RELATIVE | |✓ | +|RELEASE |✓ | | +|RENAME |✓ | | +|RESTRICT |✓ | | +|RESULT |✓ | | +|RETURN |✓ | | +|RETURNS |✓ | | +|REVOKE |✓ | | +|RIGHT |✓ | | +|ROLLBACK |✓ | | +|ROLLUP |✓ | | +|ROW |✓ | | +|ROW_NUMBER | |✓ | +|ROWS |✓ | | +|RUNNING | |✓ | +|SAVEPOINT |✓ | | +|SCHEMA |✓ | | +|SCHEMAS | |✓ | +|SCOPE |✓ | | +|SCROLL |✓ | | +|SEARCH | |✓ | +|SECOND | |✓ | +|SECTION | |✓ | +|SEEK | |✓ | +|SELECT |✓ | | +|SENSITIVE |✓ | | +|SESSION |✓ | | +|SESSION_USER |✓ | | +|SET |✓ | | +|SHOW | |✓ | +|SIMILAR | |✓ | +|SIZE | |✓ | +|SKIP | |✓ | +|SMALLINT | |✓ | +|SOME |✓ | | +|SORTED | |✓ | +|SPACE | |✓ | +|SPATIAL | |✓ | +|SPECIFIC |✓ | | +|SPECIFICTYPE | |✓ | +|SQL |✓ | | +|SQLCODE | |✓ | +|SQLERROR | |✓ | +|SQLEXCEPTION | |✓ | +|SQLSTATE | |✓ | +|SQLWARNING | |✓ | +|START |✓ | | +|STATIC |✓ | | +|STDDEV_POP | |✓ | +|STDDEV_SAMP | |✓ | +|STRING | |✓ | +|SUBMULTISET | |✓ | +|SUBSET | |✓ | +|SUCCEEDS | |✓ | +|SYMMETRIC |✓ | | +|SYSTEM |✓ | | +|SYSTEM_TIME | |✓ | +|SYSTEM_USER |✓ | | +|TABLE |✓ | | +|TABLES | |✓ | +|TABLESAMPLE |✓ | | +|TEMPORARY |✓ | | +|TEXT | |✓ | +|THEN |✓ | | +|TIME | |✓ | +|TIMESTAMP | |✓ | +|TIMESTAMP_TRUNC | |✓ | +|TO |✓ | | +|TOP | |✓ | +|TRAILING |✓ | | +|TRANSACTION |✓ | | +|TRANSLATE |✓ | | +|TRANSLATE_REGEX | |✓ | +|TRANSLATION | |✓ | +|TREAT |✓ | | +|TRIGGER |✓ | | +|TRUE |✓ | | +|TRUNCATE |✓ | | +|UESCAPE |✓ | | +|UNION |✓ | | +|UNIQUE |✓ | | +|UNKNOWN |✓ | | +|UNLOAD | |✓ | +|UNMATCHED | |✓ | +|UNNEST | |✓ | +|UPDATE |✓ | | +|UPPER | |✓ | +|USAGE |✓ | | +|USER | |✓ | +|USING |✓ | | +|VALUES |✓ | | +|VAR_POP | |✓ | +|VAR_SAMP | |✓ | +|VARBINARY | |✓ | +|VARCHAR | |✓ | +|VARIADIC |✓ | | +|VARYING |✓ | | +|VERSIONING | |✓ | +|VIEW |✓ | | +|VIRTUAL | |✓ | +|WHEN |✓ | | +|WHENEVER |✓ | | +|WHERE |✓ | | +|WIDTH_BUCKET | |✓ | +|WINDOW |✓ | | +|WITH |✓ | | +|WITHIN |✓ | | +|WITHOUT |✓ | | +|WORK |✓ | | +|WRITE | |✓ | +|XML | |✓ | +|XMLAGG | |✓ | +|XMLATTRIBUTES | |✓ | +|XMLBINARY | |✓ | +|XMLCAST | |✓ | +|XMLCOMMENT | |✓ | +|XMLCONCAT | |✓ | +|XMLDOCUMENT | |✓ | +|XMLELEMENT | |✓ | +|XMLEXISTS | |✓ | +|XMLFOREST | |✓ | +|XMLITERATE | |✓ | +|XMLNAMESPACES | |✓ | +|XMLPARSE | |✓ | +|XMLPI | |✓ | +|XMLQUERY | |✓ | +|XMLSERIALIZE | |✓ | +|XMLTABLE | |✓ | +|XMLTEXT | |✓ | +|XMLVALIDATE | |✓ | +|YEAR | |✓ | +|ZONE | |✓ | +|=== diff --git a/modules/reference/pages/sql/sql-statements/select.adoc b/modules/reference/pages/sql/sql-statements/select.adoc new file mode 100644 index 000000000..f0e6183fd --- /dev/null +++ b/modules/reference/pages/sql/sql-statements/select.adoc @@ -0,0 +1,128 @@ += SELECT +:description: The SELECT statement retrieves data from one or more tables. +:page-topic-type: reference + +The `SELECT` statement retrieves data from one or more tables. Use `SELECT` to: + +* Retrieve specific columns from a table. +* Query data across multiple tables. +* Filter results based on specific criteria. + +== Syntax + +To retrieve data from a table, use this syntax: + +[source,sql] +---- +SELECT * FROM table_name; +---- + +To filter by specific columns, use: + +[source,sql] +---- +SELECT column1, column2, ... +FROM table_name; +---- + +Where: + +* `SELECT`: Specifies the data to retrieve. +* `*`: Returns all columns. +* `FROM`: Specifies the table to query. +* `table_name`: The name of the table. +* `column1, column2, ...`: The columns to retrieve. + +[NOTE] +==== +The `SELECT` statement is case-insensitive. `select` and `SELECT` produce the same result. +==== + +== Examples + +The following examples query a table named `student_data` that contains student records with `id`, `name`, and `domicile` columns. + +=== Query data from all columns + +. To display all the data from the `student_data` table, use this syntax: ++ +[source,sql] +---- +SELECT * FROM table_name; +---- + +. Run the following query: ++ +[source,sql] +---- +SELECT * FROM student_data; +---- + +. The query returns: ++ +[source,sql] +---- ++--------+----------+----------------+ +| id | name | domicile | ++--------+----------+----------------+ +| 119291 | Jordan | Los Angeles | +| 119292 | Mike | Melbourne | +| 119293 | Will | Sydney | ++--------+----------+----------------+ +---- + +=== Query data from specific columns + +. To get the list of students' names with their IDs, use this syntax: ++ +[source,sql] +---- +SELECT column_1, column_2 FROM table_name; +---- + +. Run: ++ +[source,sql] +---- +SELECT id, name FROM student_data; +---- + +. The query returns: ++ +[source,sql] +---- ++--------+----------+ +| id | name | ++--------+----------+ +| 119291 | Jordan | +| 119292 | Mike | +| 119293 | Will | ++--------+----------+ +---- + +=== Query data from a specific column with the condition + +. With a large amount of data, skimming for the desired data can take a long time. Apply conditions to the `SELECT` statement to narrow the results: ++ +[source,sql] +---- +SELECT column_1 FROM table_name WHERE condition; +---- + +. To find the student who lives in Sydney, run: ++ +[source,sql] +---- +SELECT name FROM student_data WHERE domicile='Sydney'; +---- + +. The query returns: ++ +[source,sql] +---- ++----------+ +| name | ++----------+ +| Will | ++----------+ +---- diff --git a/modules/reference/pages/sql/sql-statements/set-show.adoc b/modules/reference/pages/sql/sql-statements/set-show.adoc new file mode 100644 index 000000000..0dac3f447 --- /dev/null +++ b/modules/reference/pages/sql/sql-statements/set-show.adoc @@ -0,0 +1,156 @@ += SET and SHOW +:description: The SET statement configures session options. The SHOW statement displays their current values. +:page-topic-type: reference + +The `SET` statement configures session options. The `SHOW` statement displays their current values. + +== Syntax + +`SET` statement: + +[source,sql] +---- +SET

W?R-C7QYV$VC;nn55J!_TVfQf3Ao{d3_zZ2XK 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