Skip to content

Commit 2bc16a7

Browse files
Yanghanrui666Yanghanrui666
authored andcommitted
docs: add LNNVL function documentation
1 parent 6d755f6 commit 2bc16a7

4 files changed

Lines changed: 268 additions & 0 deletions

File tree

CN/modules/ROOT/nav.adoc

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -119,6 +119,7 @@
119119
**** xref:master/oracle_builtin_functions/stragg.adoc[stragg]
120120
**** xref:master/oracle_builtin_functions/dbtimezone_impl.adoc[dbtimezone]
121121
**** xref:master/oracle_builtin_functions/vsize.adoc[vsize]
122+
**** xref:master/oracle_builtin_functions/lnnvl.adoc[lnnvl]
122123
*** xref:master/gb18030.adoc[国标GB18030]
123124
* 参考指南
124125
** xref:master/tools_reference.adoc[工具参考]
Lines changed: 130 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,130 @@
1+
:sectnums:
2+
:sectnumlevels: 5
3+
4+
= LNNVL
5+
6+
== 功能概述
7+
8+
IvorySQL 提供兼容 Oracle 的内置函数 `LNNVL(condition)`。当条件结果为 `TRUE` 时返回
9+
`FALSE`;当条件结果为 `FALSE` 或 `UNKNOWN`(NULL)时返回 `TRUE`。
10+
11+
该行为等价于 SQL 谓词 `condition IS NOT TRUE`。与 `NOT condition` 不同:条件为 NULL
12+
时,`NOT` 的结果为 `UNKNOWN`,在 `WHERE` 子句中会丢失这些行;而 `LNNVL` 将未知条件
13+
视为满足条件。因此,`LNNVL` 适合用于筛选“不满足条件”的数据,同时保留条件结果为
14+
NULL 的行。
15+
16+
== 语法
17+
18+
```
19+
LNNVL(condition)
20+
```
21+
22+
[cols="2,6"]
23+
|====
24+
|*参数* |*说明*
25+
|condition |需要按 Oracle 兼容的空值语义取反的布尔表达式。
26+
|====
27+
28+
返回类型:`boolean`。
29+
30+
== 真值表
31+
32+
[cols="2,2,3"]
33+
|====
34+
|*条件结果* |*LNNVL 结果* |*WHERE 子句是否返回该行*
35+
|TRUE |FALSE |否
36+
|FALSE |TRUE |是
37+
|UNKNOWN |TRUE |是
38+
|====
39+
40+
`LNNVL(NULL)` 同样返回 `TRUE`,因为 `NULL IS NOT TRUE` 的结果为 `TRUE`。
41+
42+
== 示例
43+
44+
下面的查询展示了条件结果为 TRUE、FALSE 和 UNKNOWN 时的返回值:
45+
46+
```
47+
SELECT LNNVL(1 = 1) AS true_condition,
48+
LNNVL(1 = 2) AS false_condition,
49+
LNNVL(NULL::boolean) AS unknown_condition;
50+
```
51+
52+
```
53+
true_condition | false_condition | unknown_condition
54+
----------------+-----------------+-------------------
55+
f | t | t
56+
(1 row)
57+
```
58+
59+
假设需要查询所有不满足 `amount >= 60` 的数据。普通 `NOT` 会丢弃 `amount` 为 NULL
60+
的行,而 `LNNVL` 会保留这些行:
61+
62+
```
63+
CREATE TEMP TABLE lnnvl_orders (id int, amount numeric);
64+
INSERT INTO lnnvl_orders VALUES (1, 100), (2, NULL), (3, 50);
65+
66+
SELECT id FROM lnnvl_orders
67+
WHERE NOT (amount >= 60)
68+
ORDER BY id;
69+
70+
id
71+
----
72+
3
73+
(1 row)
74+
75+
SELECT id FROM lnnvl_orders
76+
WHERE LNNVL(amount >= 60)
77+
ORDER BY id;
78+
79+
id
80+
----
81+
2
82+
3
83+
(2 rows)
84+
85+
DROP TABLE lnnvl_orders;
86+
```
87+
88+
`LNNVL` 也可用于 `LIKE`、`BETWEEN`、`IN`、`EXISTS` 等条件:
89+
90+
```
91+
SELECT LNNVL('ab' LIKE 'a%') AS like_result,
92+
LNNVL(1 BETWEEN 0 AND 5) AS between_result,
93+
LNNVL(EXISTS (SELECT 1 FROM dual)) AS exists_result;
94+
```
95+
96+
```
97+
like_result | between_result | exists_result
98+
-------------+----------------+---------------
99+
f | f | f
100+
(1 row)
101+
```
102+
103+
== 兼容性说明
104+
105+
* 参数必须是布尔表达式。`LNNVL(1)` 会报错,因为该函数没有整数重载,也不支持从整数
106+
到布尔值的隐式转换。
107+
* 在 Oracle 兼容模式下,可以直接调用 `LNNVL(condition)`;在 PostgreSQL 模式下,
108+
需要使用带模式限定的形式 `sys.lnnvl(condition)`。
109+
* IvorySQL 接受任意布尔表达式作为参数,而 Oracle 只接受单个简单条件。若 SQL 还需要
110+
在 Oracle 上运行,应将复合条件拆分为多个 `LNNVL` 调用,并使用 `AND` 或 `OR` 组合:
111+
112+
```
113+
SELECT LNNVL(a > 1) OR LNNVL(b < 2);
114+
```
115+
116+
== 实现原理
117+
118+
函数在 `contrib/ivorysql_ora/src/builtin_functions/builtin_functions--1.0.sql` 中注册:
119+
120+
```sql
121+
CREATE FUNCTION sys.lnnvl(pg_catalog.bool)
122+
RETURNS pg_catalog.bool
123+
AS $$SELECT $1 IS NOT TRUE$$
124+
LANGUAGE sql
125+
CALLED ON NULL INPUT
126+
PARALLEL SAFE
127+
IMMUTABLE;
128+
```
129+
130+
直接使用 `IS NOT TRUE` 即可得到与 Oracle 一致的真值表,包括 NULL 情况。

EN/modules/ROOT/nav.adoc

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -119,6 +119,7 @@
119119
*** xref:master/oracle_builtin_functions/stragg.adoc[stragg]
120120
*** xref:master/oracle_builtin_functions/dbtimezone_impl_en.adoc[dbtimezone]
121121
*** xref:master/oracle_builtin_functions/vsize_en.adoc[vsize]
122+
*** xref:master/oracle_builtin_functions/lnnvl.adoc[lnnvl]
122123
** xref:master/gb18030.adoc[GB18030 Character Set]
123124
* Reference
124125
** xref:master/tools_reference.adoc[Tool Reference]
Lines changed: 136 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,136 @@
1+
:sectnums:
2+
:sectnumlevels: 5
3+
4+
= LNNVL
5+
6+
== Overview
7+
8+
IvorySQL provides the Oracle-compatible built-in function `LNNVL(condition)`.
9+
It returns `FALSE` when the condition evaluates to `TRUE`, and returns `TRUE`
10+
when the condition evaluates to `FALSE` or `UNKNOWN` (NULL).
11+
12+
This behavior is equivalent to the SQL predicate `condition IS NOT TRUE`.
13+
Unlike `NOT condition`, which evaluates to `UNKNOWN` when the condition is
14+
NULL, `LNNVL` treats an unknown condition as a matching condition. This makes
15+
it useful in `WHERE` clauses when rows that do not satisfy a condition must be
16+
returned without losing the rows whose condition is NULL.
17+
18+
== Syntax
19+
20+
```
21+
LNNVL(condition)
22+
```
23+
24+
[cols="2,6"]
25+
|====
26+
|*Parameter* |*Description*
27+
|condition |A boolean expression to negate with Oracle-compatible NULL semantics.
28+
|====
29+
30+
Return type: `boolean`.
31+
32+
== Truth table
33+
34+
[cols="2,2,3"]
35+
|====
36+
|*Condition result* |*LNNVL result* |*Returned by a WHERE clause*
37+
|TRUE |FALSE |No
38+
|FALSE |TRUE |Yes
39+
|UNKNOWN |TRUE |Yes
40+
|====
41+
42+
`LNNVL(NULL)` also returns `TRUE`, because `NULL IS NOT TRUE` evaluates to
43+
`TRUE`.
44+
45+
== Examples
46+
47+
The following query shows the three possible condition results:
48+
49+
```
50+
SELECT LNNVL(1 = 1) AS true_condition,
51+
LNNVL(1 = 2) AS false_condition,
52+
LNNVL(NULL::boolean) AS unknown_condition;
53+
```
54+
55+
```
56+
true_condition | false_condition | unknown_condition
57+
----------------+-----------------+-------------------
58+
f | t | t
59+
(1 row)
60+
```
61+
62+
Suppose a query must return all rows that do not satisfy `amount >= 60`.
63+
A plain `NOT` drops rows where `amount` is NULL, while `LNNVL` keeps them:
64+
65+
```
66+
CREATE TEMP TABLE lnnvl_orders (id int, amount numeric);
67+
INSERT INTO lnnvl_orders VALUES (1, 100), (2, NULL), (3, 50);
68+
69+
SELECT id FROM lnnvl_orders
70+
WHERE NOT (amount >= 60)
71+
ORDER BY id;
72+
73+
id
74+
----
75+
3
76+
(1 row)
77+
78+
SELECT id FROM lnnvl_orders
79+
WHERE LNNVL(amount >= 60)
80+
ORDER BY id;
81+
82+
id
83+
----
84+
2
85+
3
86+
(2 rows)
87+
88+
DROP TABLE lnnvl_orders;
89+
```
90+
91+
`LNNVL` can be used with conditions such as `LIKE`, `BETWEEN`, `IN`, and
92+
`EXISTS`:
93+
94+
```
95+
SELECT LNNVL('ab' LIKE 'a%') AS like_result,
96+
LNNVL(1 BETWEEN 0 AND 5) AS between_result,
97+
LNNVL(EXISTS (SELECT 1 FROM dual)) AS exists_result;
98+
```
99+
100+
```
101+
like_result | between_result | exists_result
102+
-------------+----------------+---------------
103+
f | f | f
104+
(1 row)
105+
```
106+
107+
== Compatibility notes
108+
109+
* The condition must be a boolean expression. `LNNVL(1)` raises an error
110+
because there is no integer overload or implicit cast from an integer.
111+
* In Oracle-compatible mode, call the function as `LNNVL(condition)`.
112+
In PostgreSQL mode, use the schema-qualified form `sys.lnnvl(condition)`.
113+
* IvorySQL accepts any boolean expression as the argument. Oracle accepts a
114+
single simple condition. For SQL that must also run on Oracle, write a
115+
compound condition as separate `LNNVL` calls combined with `AND` or `OR`:
116+
117+
```
118+
SELECT LNNVL(a > 1) OR LNNVL(b < 2);
119+
```
120+
121+
== Implementation
122+
123+
The function is registered in `contrib/ivorysql_ora/src/builtin_functions/builtin_functions--1.0.sql`:
124+
125+
```sql
126+
CREATE FUNCTION sys.lnnvl(pg_catalog.bool)
127+
RETURNS pg_catalog.bool
128+
AS $$SELECT $1 IS NOT TRUE$$
129+
LANGUAGE sql
130+
CALLED ON NULL INPUT
131+
PARALLEL SAFE
132+
IMMUTABLE;
133+
```
134+
135+
Using `IS NOT TRUE` directly gives the Oracle-compatible truth table, including
136+
the NULL case.

0 commit comments

Comments
 (0)