Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

SQL: Implement CASE... WHEN... THEN... ELSE... END #41349

Merged
merged 12 commits into from
Apr 22, 2019
81 changes: 80 additions & 1 deletion docs/reference/sql/functions/conditional.asciidoc
Original file line number Diff line number Diff line change
@@ -1,10 +1,89 @@
[role="xpack"]
[testenv="basic"]
[[sql-functions-conditional]]
=== Conditional Functions
=== Conditional Functions And Expressions

Functions that return one of their arguments by evaluating in an if-else manner.

[[sql-functions-conditional-case]]
==== `CASE`

.Synopsis:
[source, sql]
----
CASE WHEN condition THEN result
[WHEN ...]
[ELSE defaultResult]
END
----

*Input*:

Multiple but at least one WHEN *condition* THEN *result* clause and optional ELSE *defaultResult* clause.
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This sentence sounds weird. Maybe get rid of "Multiple" and rephrase?
At least one _WHEN *condition* THEN *result*_ clause is required and the expression can optionally have an _ELSE *default_result*_ clause. Every *condition* must be boolean expression.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I would rephrase with something like "One or multiple WHEN ... are used and the ..."

Every *condition* should be boolean expression.

*Output*: one of the *result* expressions if the corresponding WHEN *condition* evaluates to `true`,
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

*Output*: one of the *result* expressions if the corresponding _WHEN *condition*_ evaluates to trueor the *default_result* if all _WHEN *condition*_ clauses evaluate tofalse. If the optional _ELSE *default_result*_ clause is missing and all _WHEN *condition*_ clauses evaluate to falsethennull is returned.

the *defaultResult* if all WHEN *condition* clauses evaluate to `false`. If the optional ELSE *defaultResult*
clause is missing and all WHEN *condition* clauses evaluate to `false` then `null` is returned.

.Description

The case expression is a generic conditional expression which simulates if/else statements of other programming languages
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The CASE expression....

If the condition’s result is true, the value of the result expression that follows the condition will be the returned
the subsequent when clauses will be skipped and not processed.



["source","sql",subs="attributes,callouts,macros"]
----
include-tagged::{sql-specs}/docs/docs.csv-spec[case]
----

["source","sql",subs="attributes,callouts,macros"]
----
include-tagged::{sql-specs}/docs/docs.csv-spec[caseReturnNull]
----

["source","sql",subs="attributes,callouts,macros"]
----
include-tagged::{sql-specs}/docs/docs.csv-spec[caseWithElse]
----


As a variant, a case expression can be expressed with a syntax similar to *switch-case* of other programming languages:
[source, sql]
----
CASE expression
WHEN value1 THEN result1
[WHEN value2 THEN result2]
[WHEN ...]
[ELSE defaultResult]
END
----

In this case it's transformed internally to:
[source, sql]
----
CASE WHEN expression = value1 THEN result1
[WHEN expression = value2 THEN result2]
[WHEN ...]
[ELSE defaultResult]
END
----

["source","sql",subs="attributes,callouts,macros"]
----
include-tagged::{sql-specs}/docs/docs.csv-spec[caseWithOperand]
----

["source","sql",subs="attributes,callouts,macros"]
----
include-tagged::{sql-specs}/docs/docs.csv-spec[caseWithOperandAndElse]
----

[NOTE]
All result expressions must be of compatible data types.
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can you give an example here?


[[sql-functions-conditional-coalesce]]
==== `COALESCE`

Expand Down
1 change: 1 addition & 0 deletions docs/reference/sql/functions/index.asciidoc
Original file line number Diff line number Diff line change
Expand Up @@ -127,6 +127,7 @@
** <<sql-functions-type-conversion-cast>>
** <<sql-functions-type-conversion-convert>>
* <<sql-functions-conditional>>
** <<sql-functions-conditional-case>>
** <<sql-functions-conditional-coalesce>>
** <<sql-functions-conditional-greatest>>
** <<sql-functions-conditional-ifnull>>
Expand Down
1 change: 1 addition & 0 deletions x-pack/plugin/sql/qa/src/main/resources/command.csv-spec
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@ STDDEV_POP |AGGREGATE
SUM_OF_SQUARES |AGGREGATE
VAR_POP |AGGREGATE
HISTOGRAM |GROUPING
CASE |CONDITIONAL
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Do you agree with listing CASE as a conditional function?
Case is quite special and doesn't follow at all the traditional functional format.

COALESCE |CONDITIONAL
GREATEST |CONDITIONAL
IFNULL |CONDITIONAL
Expand Down
159 changes: 159 additions & 0 deletions x-pack/plugin/sql/qa/src/main/resources/conditionals.csv-spec
Original file line number Diff line number Diff line change
@@ -0,0 +1,159 @@
caseField
SELECT CASE WHEN emp_no - 10000 < 10 THEN 'First 10' ELSE 'Second 10' END as "case" FROM test_emp WHERE emp_no >= 10005
astefan marked this conversation as resolved.
Show resolved Hide resolved
ORDER BY emp_no LIMIT 10;

case
-----------
First 10
First 10
First 10
First 10
First 10
Second 10
Second 10
Second 10
Second 10
Second 10
;

caseFieldNoElse
SELECT CASE WHEN emp_no - 10000 < 10 THEN 'First 10' END as "case" FROM test_emp WHERE emp_no >= 10005
astefan marked this conversation as resolved.
Show resolved Hide resolved
ORDER BY emp_no LIMIT 10;

case
-----------
First 10
First 10
First 10
First 10
First 10
null
null
null
null
null
;

caseWhere
SELECT last_name FROM test_emp WHERE CASE WHEN LENGTH(last_name) < 7 THEN 'ShortName' ELSE 'LongName' END = 'LongName'
ORDER BY emp_no LIMIT 10;

last_name
-----------
Facello
Bamford
Koblick
Maliniak
Preusig
Zielinski
Kalloufi
Piveteau
Bridgland
Nooteboom
;

caseWhereNoElse
SELECT last_name FROM test_emp WHERE CASE WHEN LENGTH(last_name) < 7 THEN 'ShortName' END IS NOT NULL
ORDER BY emp_no LIMIT 10;

last_name
-----------
Simmel
Peac
Sluis
Terkki
Genin
Peha
Erde
Famili
Pettey
Heyers
;

caseOrderBy
SELECT last_name FROM test_emp ORDER BY CASE WHEN languages >= 3 THEN 'first' ELSE 'second' END, emp_no LIMIT 10;
astefan marked this conversation as resolved.
Show resolved Hide resolved

last_name
-----------
Simmel
Bamford
Koblick
Preusig
Zielinski
Piveteau
Sluis
Bridgland
Genin
Nooteboom
;

caseOrderByNoElse
SELECT last_name FROM test_emp ORDER BY CASE WHEN languages >= 3 THEN 'first' END NULLS FIRST, emp_no LIMIT 10;

last_name
-------------
Facello
Maliniak
Kalloufi
Peac
Terkki
Cappelletti
Bouloucos
Peha
Haddadi
Warwick
;

caseGroupBy
schema::count:l|lang_skills:s
SELECT count(*) AS count, CASE WHEN NVL(languages, 0) <= 1 THEN 'zero-to-one' ELSE 'multilingual' END as lang_skills
FROM test_emp GROUP BY lang_skills ORDER BY lang_skills;

count | lang_skills
-------+--------------
75 | multilingual
25 | zero-to-one
;

caseGroupByNoElse
schema::count:l|lang_skills:s
SELECT count(*) AS count, CASE WHEN NVL(languages, 0) <= 1 THEN 'zero-to-one' END as lang_skills
FROM test_emp GROUP BY lang_skills ORDER BY lang_skills;

count | lang_skills
-------+-------------
75 | null
25 | zero-to-one
;

caseGroupByComplexNested
schema::count:l|lang_skills:s
SELECT count(*) AS count,
CASE WHEN NVL(languages, 0) = 0 THEN 'zero'
WHEN languages = 1 THEN 'one'
WHEN languages = 2 THEN 'bilingual'
WHEN languages = 3 THEN 'trilingual'
ELSE 'multilingual'
END as lang_skills FROM test_emp GROUP BY lang_skills ORDER BY 2;

count | lang_skills
-------+--------------
19 | bilingual
39 | multilingual
15 | one
17 | trilingual
10 | zero
;

caseGroupByAndHaving
schema::count:l|gender:s|languages:byte
SELECT count(*) AS count, gender, languages FROM test_emp
GROUP BY 2, 3 HAVING CASE WHEN count(*) > 10 THEN 'many' ELSE 'a few' END = 'many'
ORDER BY 2, 3;

count | gender | languages
----------+-------------+---------------
11 | M | 2
11 | M | 3
11 | M | 4
;
74 changes: 73 additions & 1 deletion x-pack/plugin/sql/qa/src/main/resources/docs/docs.csv-spec
Original file line number Diff line number Diff line change
Expand Up @@ -202,6 +202,7 @@ STDDEV_POP |AGGREGATE
SUM_OF_SQUARES |AGGREGATE
VAR_POP |AGGREGATE
HISTOGRAM |GROUPING
CASE |CONDITIONAL
COALESCE |CONDITIONAL
GREATEST |CONDITIONAL
IFNULL |CONDITIONAL
Expand Down Expand Up @@ -1987,10 +1988,81 @@ SELECT TRUNCATE(-345.153, 1) AS trimmed;

///////////////////////////////
//
// Null handling
// Conditional
//
///////////////////////////////

case
schema::case:s
// tag::case
SELECT CASE WHEN 1 > 2 THEN 'elastic'
WHEN 2 <= 3 THEN 'search'
END AS "case";

case
---------------
search
// end::case
;

caseReturnNull
schema::case:s
// tag::caseReturnNull
SELECT CASE WHEN 1 > 2 THEN 'elastic'
WHEN 2 > 10 THEN 'search'
END AS "case";

case
---------------
null
// end::caseReturnNull
;

caseWithElse
schema::case:s
// tag::caseWithElse
SELECT CASE WHEN 1 > 2 THEN 'elastic'
WHEN 2 > 10 THEN 'search'
ELSE 'default'
END AS "case";

case
---------------
default
// end::caseWithElse
;

caseWithOperand
schema::case:s
// tag::caseWithOperand
SELECT CASE 5
WHEN 1 THEN 'elastic'
WHEN 2 THEN 'search'
WHEN 5 THEN 'elasticsearch'
END AS "case";

case
---------------
elasticsearch
// end::caseWithOperand
;

caseWithOperandAndElse
schema::case:s
// tag::caseWithOperandAndElse
SELECT CASE 5
WHEN 1 THEN 'elastic'
WHEN 2 THEN 'search'
WHEN 3 THEN 'elasticsearch'
ELSE 'default'
END AS "case";

case
---------------
default
// end::caseWithOperandAndElse
;

coalesceReturnNonNull
// tag::coalesceReturnNonNull
SELECT COALESCE(null, 'elastic', 'search') AS "coalesce";
Expand Down
36 changes: 0 additions & 36 deletions x-pack/plugin/sql/qa/src/main/resources/null.csv-spec
Original file line number Diff line number Diff line change
Expand Up @@ -37,39 +37,3 @@ SELECT COALESCE(null, null, null) AS c;
c
null
;

coalesceFirstNotNull
SELECT COALESCE(123) AS c;

c
123
;


coalesceWithFirstNullOfString
SELECT COALESCE(null, 'first') AS c;

c:s
first
;

coalesceWithFirstNullOfNumber
SELECT COALESCE(null, 123) AS c;

c:i
123
;

coalesceMixed
SELECT COALESCE(null, 123, null, 321) AS c;

c:i
123
;

coalesceScalar
SELECT COALESCE(null, ABS(123) + 1) AS c;

c:i
124
;
Loading