SELECT

SELECT, TABLE, WITH — 从一个表或视图检索行

Synopsis

[ WITH [ RECURSIVE ] with_query [, ...] ]
SELECT [ ALL | UNIQUE | DISTINCT [ ON ( expression [, ...] ) ] ]
    [ * | expression [ [ AS ] output_name ] [, ...] ]
    [ FROM from_item [, ...] ]
    [ { USE | IGNORE | FORCE } { INDEX | KEY } [ FOR { JOIN | ORDER BY | GROUP BY } ] (index_list) ]
    [ WHERE condition ]
    [ START WITH start_expression ]
    [ CONNECT BY { PRIOR parent_expr = child_expr | child_expr = PRIOR parent_expr} ]
    [ GROUP BY grouping_element [, ...] [WITH ROLLUP] ]
    [ HAVING condition ]
    [ WINDOW window_name AS ( window_definition ) [, ...] ]
    [ { UNION | INTERSECT | EXCEPT | MINUS } [ ALL | DISTINCT ] select ]
    [ ORDER BY expression [ ASC | DESC | USING operator ] [ NULLS { FIRST | LAST } ] [, ...] ]
    [ LIMIT { count | ALL } ]
    [ OFFSET start [ ROW | ROWS ] ]
    [ FETCH { FIRST | NEXT } [ count ] { ROW | ROWS } { ONLY | WITH TIES } ]
    [ FOR { UPDATE | NO KEY UPDATE | SHARE | KEY SHARE } [ OF table_name [, ...] ] [ NOWAIT | SKIP LOCKED ] [...] ]

其中 from_item 可以是以下之一:

    [ ONLY ] table_name [ SAMPLE ( argument ) ] [ * ] [ [ AS ] alias [ ( column_alias [, ...] ) ] ]
    [ TABLESAMPLE sampling_method ( argument [, ...] ) [ REPEATABLE ( seed ) ] ]
    [ LATERAL ] ( select ) [ AS ] alias [ ( column_alias [, ...] ) ]
    with_query_name [ [ AS ] alias [ ( column_alias [, ...] ) ] ]
    [ LATERAL ] function_name ( [ argument [, ...] ] )
                [ WITH ORDINALITY ] [ [ AS ] alias [ ( column_alias [, ...] ) ] ]
    [ LATERAL ] function_name ( [ argument [, ...] ] ) [ AS ] alias ( column_definition [, ...] )
    [ LATERAL ] function_name ( [ argument [, ...] ] ) AS ( column_definition [, ...] )
    [ LATERAL ] ROWS FROM( function_name ( [ argument [, ...] ] ) [ AS ( column_definition [, ...] ) ] [, ...] )
                [ WITH ORDINALITY ] [ [ AS ] alias [ ( column_alias [, ...] ) ] ]
    [ ONLY ] table_name [ * ] [ [ AS ] alias [ ( column_alias [, ...] ) ] ] opt_conversion_clause
    ( select ) [ AS ] alias [ ( column_alias [, ...] ) ] opt_conversion_clause
    from_item join_type from_item { ON join_condition | USING ( join_column [, ...] ) }
    from_item NATURAL join_type from_item
    from_item CROSS JOIN from_item

并且 grouping_element 可以是以下之一:

    ( )
    expression
    ( expression [, ...] )
    ROLLUP ( { expression | ( expression [, ...] ) } [, ...] )
    CUBE ( { expression | ( expression [, ...] ) } [, ...] )
    GROUPING SETS ( grouping_element [, ...] )

并且 with_query 是:

    with_query_name [ ( column_name [, ...] ) ] AS [ [ NOT ] MATERIALIZED ] ( select | values | insert | update | delete )

TABLE [ ONLY ] table_name [ * ]

并且 opt_conversion_clause 是:

    pivot_clause [ * ] [ AS ] alias

并且 pivot_clause 是:

    PIVOT ( function_name (argument) [ AS ] alias
        FOR columnref IN ( column_definition [, ...] ) ) [ AS ] alias

描述

SELECT从零或更多表中检索行。 SELECT的通常处理如下:

  1. WITH列表中的所有查询都会被计算。这些查询实际 充当了在FROM列表中可以引用的临时表。在 FROM中被引用多次的WITH查 询只会被计算一次,除非另有说明,否则NOT MATERIALIZED。 (见下文的WITH Clause)。

  2. FROM列表中的所有元素都会被计算( FROM中的每一个元素都是一个真实表或者虚拟表)。 如果在FROM列表中指定了多于一个元素,它们会被 交叉连接在一起(见下文的 FROM Clause)。

  3. 如果指定了WHERE子句,所有不满足该条件的行都会 被从输出中消除(见下文的WHERE Clause)。

  4. 如果指定了 WHERE 子句,则不满足条件的所有行都将从输出中删除。(请参见下面的 WHERE Clause。)

  5. 通过在条件中使用 '(+)', 你可以像 Oracle 一样指定外连接. (See oracle plus below.)

  6. 如果指定了CONNECT BY子句,则可以按层次顺序选择行。(请参见下面的CONNECT BY Clause。) 对于由分层查询返回的每一行,LEVEL伪列对于根行返回1,对于根的子项返回2,依此类推。 SYS_CONNECT_BY_PATH仅在分层查询中有效。对于由CONNECT BY条件返回的每一行,SYS_CONNECT_BY_PATH(column, char)使用char分隔的column值。CONNECT_BY_ ROOT是一种仅在分层查询中有效的一元运算符。当使用此运算符对列进行限定时,它会返回使用根行数据的列值。CONNECT BY LEVEL < n 显示 n 级以下的所有递归结果。 CONNECT_BY_ISLEAF 是一个伪列,通常与递归查询(使用 CONNECT BY 子句)一起使用。它的作用是标识在递归查询的上下文中,当前行是否为叶子节点(没有子节点)。CONNECT_BY_ISLEAF 列返回一个布尔值,如果当前行没有子节点,则返回 1(表示是叶子节点),否则返回 0(表示不是叶子节点)。CONNECT BY rownum用于产生序列。

  7. 如果指定了GROUP BY子句或者如果有聚集函数,输出 会被组合成由在一个或者多个值上匹配的行构成的分组,并且在其上计算聚 集函数的结果。如果出现了HAVING子句,它会消除不 满足给定条件的分组(见下文的 GROUP BY Clause以及 HAVING Clause)。

  8. 对于每一个被选中的行或者行组,会使用SELECT 输出表达式计算实际的输出行(见下文的 SELECT List)。

  9. SELECT DISTINCT | UNIQUE从结果中消除重复的行。 SELECT DISTINCT ON消除在所有指定表达式上匹 配的行。SELECT ALL(默认)将返回所有候选行, 包括重复的行(见下文的DISTINCT Clause)。

  10. 通过使用操作符UNIONINTERSECTEXCEPT,多于 一个SELECT语句的输出可以被整合形成 一个结果集。UNION操作符返回位于一个或者两 个结果集中的全部行。INTERSECT操作符返回同时 位于两个结果集中的所有行。EXCEPT操作符返回 位于第一个结果集但不在第二个结果集中的行。在所有三种情况下, 重复行都会被消除(除非指定ALL)。可以增加噪 声词DISTINCT来显式地消除重复行。注意虽然 ALLSELECT自身的默认行为, 但这里DISTINCT是默认行为(见下文的 UNION ClauseINTERSECT Clause以及 EXCEPT Clause)。

  11. 如果指定了ORDER BY子句,被返回的行会以指定的 顺序排序。如果没有给定ORDER BY,系统会以能最 快产生行的顺序返回它们(见下文的 ORDER BY Clause)。

  12. 如果指定了LIMIT(或FETCH FIRST) 或者OFFSET子句,SELECT 语句只返回结果行的一个子集(见下文的LIMIT Clause)。

  13. 如果指定了FOR UPDATEFOR NO KEY UPDATEFOR SHARE 或者FOR KEY SHARESELECT语句会把被选中的行锁定而不让并发 更新访问它们(见下文的The Locking Clause)。

  14. START WITH子句和CONNECT BY子句作为一个 整体可以放在GROUP BY子句的后面。

你必须拥有在一个SELECT命令中使用的每一列上的 SELECT特权。FOR NO KEY UPDATEFOR UPDATEFOR SHARE或者FOR KEY SHARE 还要求(对这样选中的每一个表至少一列的)UPDATE 特权。

参数

WITH 子句

WITH子句允许你指定一个或者多个在主查询中可以 其名称引用的子查询。在主查询期间子查询实际扮演了临时表或者视图 的角色。每一个子查询都可以是一个SELECTTABLEVALUESINSERTUPDATE或者 DELETE语句。在WITH中书写 一个数据修改语句(INSERTUPDATE或者 DELETE)时,通常要包括一个 RETURNING子句。构成被主查询读取的临时表的是 RETURNING的输出,而不是该语句修改的 底层表。如果省略RETURNING,该语句仍会被执行,但是它 不会产生输出,因此它不能作为一个表从主查询引用。

对于每一个WITH查询,都必须指定一个名称(无需模 式限定)。可选地,可以指定一个列名列表。如果省略该列表,会从该子查 询中推导列名。

如果指定了RECURSIVE,则允许一个 SELECT子查询使用名称引用自身。 这样一个子查询的形式必须是

non_recursive_term UNION [ ALL | DISTINCT ] recursive_term

其中递归自引用必须出现在UNION的右手边。每个 查询中只允许一个递归自引用。不支持递归数据修改语句,但是 可以在一个数据查询语句中使用一个递归 SELECT查询的结果。例子可见 Section 8.8

RECURSIVE的另一个效果是 WITH查询不需要被排序:一个查询可以引用另一个 在列表中比它靠后的查询(不过,循环引用或者互递归没有实现)。 如果没有RECURSIVEWITH 查询只能引用在WITH列表中位置更前面的兄弟 WITH查询。

WITH子句中有多个查询时,RECURSIVE应只编写一次,紧跟在WITH之后。 它适用于WITH子句中的所有查询,尽管它对不使用递归或前向引用的查询没有影响。

主查询和 WITH 查询都(概念上)同时执行。 这意味着 WITH 中的修改数据语句的影响不能从查询的其他部分看到, 除非读取其 RETURNING 输出。 如果两个这样的修改数据语句尝试修改同一行,则结果是未指定的。

WITH 查询的一个关键特性是,它们通常只在主查询的每次执行中被评估一次,即使主查询多次引用它们。 特别是,修改数据语句保证只执行一次,无论主查询是否读取它们的所有或任何输出。

但是,WITH查询可以标记为NOT MATERIALIZED以移除此保证。 在这种情况下,WITH查询可以折叠到主查询中,就好像它是主查询的FROM子句中的简单的sub-SELECT。 如果主查询多次引用WITH查询,则会导致重复计算;但是,如果每次此类使用只需要WITH查询的总输出中的几行,NOT MATERIALIZED可以通过允许查询联合优化来节省开销。 NOT MATERIALIZED被忽略,如果它被附加到一个递归的WITH查询,或者不是边际效应无关的(也就是说,不是包含非易失性函数的普通的SELECT)。

默认情况下,如果查询在主查询的FROM子句中仅一次使用,则边际效应无关的WITH查询将折叠到主查询中。 这允许在语义不可见的情况下两个查询级别的联合优化。 但是,通过将WITH查询标记为MATERIALIZED,可以防止此类折叠。 这可能很有用,例如,如果WITH查询被用作优化围栏,以防止规划者选择错误计划。

更多信息请见Section 8.8

FROM 子句

FROM子句为SELECT 指定一个或者更多源表。如果指定了多个源表,结果将是所有源表的 笛卡尔积(交叉连接)。但是通常会增加限定条件(通过 WHERE)来把返回的行限制为该笛卡尔积的一个小子集。

FROM子句可以包含下列元素:

table_name

一个现有表或视图的名称(可以是模式限定的)。如果在表名前指定了 ONLY,则只会扫描该表。如果没有指定 ONLY,该表及其所有后代表(如果有)都会被扫描。可 选地,可以在表名后指定*来显式地指示包括后代表。

alias

一个包含别名的FROM项的替代名称。别名被用于 让书写简洁或者消除自连接中的混淆(其中同一个表会被扫描多 次)。当提供一个别名时,表或者函数的实际名称会被隐藏。例 如,给定FROM foo AS fSELECT的剩余部分就必须以 f而不是foo来引用这个 FROM项。如果写了一个别名,还可以写一个列别 名列表来为该表的一个或者多个列提供替代名称。

TABLESAMPLE sampling_method ( argument [, ...] ) [ REPEATABLE ( seed ) ]

table_name之后的 TABLESAMPLE子句表示应该用指定的 sampling_method 来检索表中行的子集。这种采样优先于任何其他过滤器(例如 WHERE子句)。标准 LightDB发布包括两种采样 方法:BERNOULLISYSTEM, 其他采样方法可以通过扩展安装在数据库中。

BERNOULLI以及SYSTEM采样方法都接受 一个参数,它表示要采样的表 的分数,表示为一个 0 到 100 之间的百分数。这个参数可以是任意的 实数值表达式(其他的采样方法可能接受更多或者不同的 参数)。这两种方法都返回一个随机选取的该表采样,其中包含了指定 百分数的表行。BERNOULLI方法扫描整个表并且 用指定的几率选择或者忽略行。SYSTEM方法会做 块层的采样,每个块都有指定的机会能被选中,被选中块中的所有行都 会被返回。在指定较小的采样百分数时,SYSTEM 方法要比BERNOULLI方法快很多,但是前者可能 由于聚簇效应返回随机性较差的表采样。

可选的REPEATABLE子句指定一个用于产生采样方法中随机数的种子数或表达式。种子值可以是任何非空浮点值。如果查询时表没有被更改,指定相同种子和argument值的两个查询将会选择该表相同的采样。但是不同的种子值通常将会产生不同的采样。如果没有给出REPEATABLE,则会基于一个系统产生的种子为每一个查询选择一个新的随机采样。注意有些扩展采样方法不接受REPEATABLE,并且将总是为每一次使用产生新的采样。

SAMPLE ( argument )

这是兼容Oracle语法的TABLESAMPLE版本,当然仅可在Oracle兼容模式下使用。 使用这种语法时,采样方法固定为BERNOULLI。 与TABLESAMPLE不同的是,REPEATABLE 子句在语法上是不允许的。这就意味着系统会为每一个查询选择一个新的随机采样。

select

一个子-SELECT可以出现在FROM子句中。 这就好像把它的输出创建为一个存在于该SELECT命令期间的临时表。 注意子-SELECT必须用圆括号包围,并且必须为它提供一个别名。 也可以在这里使用一个VALUES command命令。

with_query_name

可以通过写一个WITH查询的名称来引用它,就好像 该查询的名称是一个表名(实际上,该WITH查询会 为主查询隐藏任何具有相同名称的真实表。如果必要,你可以使用 带模式限定的方式以相同的名称来引用真实表)。可以像表一样, 以同样的方式提供一个别名。

function_name

函数调用可以出现在FROM子句中(对于返回结果集合的函数特别有用,但是可以使用任何函数)。 这就好像把该函数的输出创建为一个存在于该SELECT命令期间的临时表。 如果函数的结果类型是合成的(包括函数有多个OUT参数的情况),在隐式表中每个属性成为单独的列。

当为该函数调用增加可选的WITH ORDINALITY子句时,会在该函数的结果列之后附加一个 bigint类型的列。 这个列的编号函数的结果集的行,从1开始。 默认的,这个列被命名为ordinality

可以用和表一样的方式提供一个别名。 如果写了一个别名,还可以写一个列别名列表来为该函数的组合返回类型的一个或者多个属性提供替代名称,如果有的话包括ordinality列。

通过把多个函数调用包围在ROWS FROM( ... )中可以把它们 整合在单个FROM-子句项中。这样一个项的输出是把每一个 函数的第一行串接起来,然后是每个函数的第二行,以此类推。如果有些 函数产生的行比其他函数少,则在缺失数据的地方放上空值,这样被返回 的总行数总是和产生最多行的函数一样。

如果函数被定义为返回record数据类型,那么必须出现一个 别名或者关键词AS,后面跟上形为 ( column_name data_type [, ... ])的列定义列表。列定义列表必须匹配该函数返回的列的实际 数量和类型。

在使用ROWS FROM( ... )语法时,如果函数之一要求一个列 定义列表,最好把该列定义列表放在ROWS FROM( ... )中该 函数的调用之后。当且仅当正好只有一个函数并且没有 WITH ORDINALITY子句时,才能把列定义列表放在 ROWS FROM( ... )结构后面。

要把ORDINALITY和列定义列表一起使用,你必须使用 ROWS FROM( ... )语法,并且把列定义列表放在 ROWS FROM( ... )里面。

join_type

One of

  • [ INNER ] JOIN

  • LEFT [ OUTER ] JOIN

  • RIGHT [ OUTER ] JOIN

  • FULL [ OUTER ] JOIN

对于INNEROUTER连接类型,必须指定 一个连接条件,即 ON join_conditionUSING (join_column [, ...]),或者 NATURAL 之一(只能有一种)。其含义见下文。

一个JOIN子句联合两个FROM项( 为了方便我们称之为,尽管实际上它们可以是任何类型 的FROM项)。如有必要可以使用圆括号确定嵌套的顺序。 在没有圆括号时,JOIN会从左至右嵌套。在任何情 况下,JOIN的联合比分隔FROM-列表 项的逗号更强。所有 JOIN 选项都只是为了符号上的方便, 因为它们无法完成使用普通的FROMWHERE 所不能完成的事情。

LEFT OUTER JOIN返回被限制过的笛卡尔积 中的所有行(即所有通过了其连接条件的组合行),外加左手表中 没有相应的通过了连接条件的右手行的每一行的拷贝。通过在右手 列中插入空值,这种左手行会被扩展为连接表的完整行。注意在决 定哪些行匹配时,只考虑JOIN子句自身的条件。之后 才应用外条件。

相反,RIGHT OUTER JOIN返回所有连接行,外加每 一个没有匹配上的右手行(在左端用空值扩展)。这只是为了记号 上的方便,因为你可以通过交换左右表把它转换成一个LEFT OUTER JOIN

FULL OUTER JOIN返回所有连接行,外加每 一个没有匹配上的左手行(在右端用空值扩展),再外加每一个没有 匹配上的右手行(在左端用空值扩展)。

ON join_condition

join_condition 是一个会得到boolean类型值的表达式(类似于一个 WHERE子句),它说明一次连接中哪些行被认为 相匹配。

USING ( join_column [, ...] )

形式USING ( a, b, ... )的子句是 ON left_table.a = right_table.a AND left_table.b = right_table.b ...的简写。还有, USING表示每一对相等列中只有一个会被 包括在连接输出中。

NATURAL

NATURAL是一个USING列表的速记,该列表中提到两个表中具有匹配名称的所有的列。如果没有公共列名,则NATURAL等效于ON TRUE

CROSS JOIN

CROSS JOIN 等同于 INNER JOIN ON (TRUE),也就是说,不会根据条件删除任何行。它们会产生一个简单的笛卡尔积,与在 FROM 的顶层列出两个表得到的结果相同,但受到连接条件(如果有)的限制。

LATERAL

LATERAL关键词可以放在一个 子-SELECT FROM项前面。这允许该 子-SELECT引用FROM列表中在它之 前的FROM项的列(如果没有LATERAL,每一 个子-SELECT会被独立计算并且因此不能交叉引用 任何其他的FROM项)。

LATERAL也可以放在一个函数调用 FROM项前面,但是在这种情况下它只是一个噪声 词,因为在任何情况下函数表达式都可以引用在它之前的 FROM项。

LATERAL项可以出现在FROM列表 顶层,或者一个JOIN中。在后一种情况中,它也可以引 用其作为右手端的JOIN左手端上的任何项。

当一个FROM项包含LATERAL交叉引用 时,计算会如此进行:对提供被交叉引用列的FROM项的每 一行或者提供那些列的多个FROM项的每一个行集,使用该 行或者行集的那些列值计算LATERAL项。结果行会与 计算得到它们的行进行通常的连接。对来自哪些列的源表的每一行或者行 集都会重复这样的步骤。

列的源表必须以INNER或者LEFT的方式连接到 LATERAL项,否则就没有用于为 LATERAL项计算每一个行集的良定行集。尽管 X RIGHT JOIN LATERAL Y这样的结构在语法上是合法的, 但实际上不允许用于在Y中引用 X

opt_conversion_clause

  • PIVOT

PIVOT只支持单聚集函数,单列多组的情景。 它作为FROM子句的一部分,可以在 JOINNATURALLATERAL 连接中使用,完成行转列的功能。

PIVOT不支持以下五种情景:

  • 不支持XMLANY关键字

  • 不支持多个聚集函数

    PIVOT (
    SUM(sale_amount),
    COUNT(sale_amount)
    FOR customer_id
    IN (1, 2, 3, 4)
    );
    
  • 不支持多列分组

    PIVOT (
    SUM(sale_amount)
    FOR (customer_id, prod_category)
    IN (
    (1, ‘furniture’) AS furn1,
    (2, ‘furniture’) AS furn2,
    (1, ‘electronics’) AS elec1,
    (2, ‘electronics’) AS elec2
    )
    
  • 不支持IN( )

    select * from test123
    PIVOT (
    SUM(score) as tc
    FOR course
    IN ());
    
  • PIVOT子句中使用的聚集函数参数只支持字段类型,不支持数字类型和'*'

WHERE 子句

可选的WHERE子句的形式

WHERE condition

其中condition 是任一计算得到布尔类型结果的表达式。任何不满足 这个条件的行都会从输出中被消除。如果用一行的实际值替换其中的 变量引用后,该表达式返回真,则该行符合条件。

CONNECT BY 子句

可选的CONNECT BY子句的形式

    CONNECT BY { PRIOR parent_expr = child_expr |
    child_expr = PRIOR parent_expr}.

CONNECT BY 指定分层结构中父行和子行之间的关系。 在分层查询中,条件中的一个表达式必须使用 PRIOR 运算符限定,以引用父行。

START 子句

可选的START WITH子句的形式

    [ START WITH start_expression ].

START WITH 指定分层结构的根行。

GROUP BY 子句

可选的GROUP BY子句的形式

GROUP BY grouping_element [, ...] [WITH ROLLUP]

GROUP BY将会把所有被选择的行中共享相同分组表达式 值的那些行压缩成一个行。一个被用在 grouping_element中的 expression可以是输入列名、输出列 (SELECT列表项)的名称或序号或者由输入列 值构成的任意表达式。在出现歧义时,GROUP BY名称 将被解释为输入列名而不是输出列名。

如果任何GROUPING SETSROLLUP或者CUBE作为分组元素存在,则GROUP BY子句整体上定义了数个独立的分组集。 其效果等效于在子查询间构建一个UNION ALL,子查询带有分组集作为它们的GROUP BY子句。 在处理前可选的DISTINCT子句移除副本集合;它UNION ALLUNION DISTINCT的转换 处理分组集的进一步细节请见Section 8.2.4

聚集函数(如果使用)会在组成每一个分组的所有行上进行计算,从而为每 一个分组产生一个单独的值(如果有聚集函数但是没有 GROUP BY子句,则查询会被当成是由所有选中行构成 的一个单一分组)。传递给每一个聚集函数的行集合可以通过在聚集函数调 用附加一个FILTER子句来进一步过滤,详见 Section 5.2.7。当存在一个 FILTER子句时,只有那些匹配它的行才会被包括在该聚 集函数的输入中。

当存在GROUP BY子句或者任何聚集函数时, SELECT列表表达式不能引用非分组列(除非它 出现在聚集函数中或者它函数依赖于分组列),因为这样做会导致返回 非分组列的值时会有多种可能的值。如果分组列是包含非分组列的表的主键( 或者主键的子集),则存在函数依赖。oracle模式是特殊的, 当存在 GROUP BY子句时, 如果未分组的列在子查询中,且子查询中和该列相等的表达式在分组列中,这时未分组列允许出现在select列表中。例如:

    select a,d from  (select a + 1 as a ,b,c, a + 1 as d from test_agg)s
    group by d;
    

记住所有的聚集函数都是在HAVING子句或者 SELECT列表中的任何标量表达式之前被计算。 这意味着一个CASE表达式不能被用来跳过一个聚集表达式的 计算,见Section 5.2.15

当前,FOR NO KEY UPDATEFOR UPDATEFOR SHAREFOR KEY SHARE不能和 GROUP BY一起指定。

WITH ROLLUPmysql模式下生效。 WITH ROLLUP使用的基本价值是,在聚合查询中生成额外行来提供汇总数据。

HAVING 子句

可选的HAVING子句的形式

HAVING condition

其中conditionWHERE子句中指定的条件相同。

HAVING消除不满足该条件的分组行。 HAVINGWHERE不同: WHERE会在应用GROUP BY之前过滤个体行,而HAVING过滤由 GROUP BY创建的分组行。 condition中引用 的每一个列必须无歧义地引用一个分组列(除非该引用出现在一个聚集 函数中或者该非分组列函数依赖于分组列。

即使没有GROUP BY子句,HAVING 的存在也会把一个查询转变成一个分组查询。这和查询中包含聚集函数但没有 GROUP BY子句时的情况相同。所有被选择的行都被认为是一个 单一分组,并且SELECT列表和 HAVING子句只能引用聚集函数中的表列。如果该 HAVING条件为真,这样一个查询将会发出一个单一行; 否则不返回行。

当前,FOR NO KEY UPDATEFOR UPDATEFOR SHAREFOR KEY SHARE不能与 HAVING一起指定。

WINDOW 子句

可选的WINDOW子句的形式

WINDOW window_name AS ( window_definition ) [, ...]

其中window_name 是一个可以从OVER子句或者后续窗口定义中引用的名称。 window_definition

[ existing_window_name ]
[ PARTITION BY expression [, ...] ]
[ ORDER BY expression [ ASC | DESC | USING operator ] [ NULLS { FIRST | LAST } ] [, ...] ]
[ frame_clause ]

如果指定了一个existing_window_name, 它必须引用WINDOW列表中一个更早出现的项。新窗口将从 该项中复制它的划分子句以及排序子句(如果有)。在这种情况下,新窗口 不能指定它自己的PARTITION BY子句,并且它只能在被复制 窗口没有ORDER BY的情况下指定该子句。新窗口总是使用它 自己的帧子句,被复制的窗口不必指定一个帧子句。

PARTITION BY列表元素的解释以 GROUP BY子句元素的方式 进行,不过它们总是简单表达式并且绝不能是输出列的名称或编号。另一个区 别是这些表达式可以包含聚集函数调用,而这在常规GROUP BY 子句中是不被允许的。它们被允许的原因是窗口是出现在分组和聚集之后的。

类似地,ORDER BY列表元素的解释也以语句级 ORDER BY子句元素的方式进行, 不过该表达式总是被当做简单表达式并且绝不会是输出列的名称或编号。

可选的frame_clause为依赖帧的窗口函数 定义窗口帧(并非所有窗口函数都依赖于帧)。窗口帧是查询中 每一样(称为当前行)的相关行的集合。 frame_clause可以是

{ RANGE | ROWS | GROUPS } frame_start [ frame_exclusion ]
{ RANGE | ROWS | GROUPS } BETWEEN frame_start AND frame_end [ frame_exclusion ]

之一,其中frame_startframe_end可以是

UNBOUNDED PRECEDING
offset PRECEDING
CURRENT ROW
offset FOLLOWING
UNBOUNDED FOLLOWING

之一,并且frame_exclusion可以是

EXCLUDE CURRENT ROW
EXCLUDE GROUP
EXCLUDE TIES
EXCLUDE NO OTHERS

之一。如果省略frame_end,它会被默认为CURRENT ROW。限制是: frame_start不能是UNBOUNDED FOLLOWINGframe_end不能是UNBOUNDED PRECEDING, 并且frame_end的选择在上面of frame_start以及frame_end 选项的列表中不能早于 frame_start的选择 — 例如 RANGE BETWEEN CURRENT ROW AND offset PRECEDING是不被允许的。

默认的帧选项是RANGE UNBOUNDED PRECEDING,它和 RANGE BETWEEN UNBOUNDED PRECEDING AND CURRENT ROW相同。它把帧设置为从分区开始直到当前行的最后一个平级行(被该窗口的ORDER BY子句认为等价于当前行的行,如果没有ORDER BY则所有的行都是平级的)。通常, UNBOUNDED PRECEDING表示从分区第一行开始的帧,类似地 UNBOUNDED FOLLOWING表示以分区最后一行结束的帧,不论是处于RANGEROWS或者GROUPS模式中。在ROWS模式中, CURRENT ROW表示以当前行开始或者结束的帧。而在 RANGE或者GROUPS模式中它表示当前行在ORDER BY排序中的第一个 或者最后一个平级行开始或者结束的帧。 offset PRECEDINGoffset FOLLOWING选项的含义会随着帧模式而变化。在ROWS模式中,offset是一个整数,表示帧开始或者结束于当前行之前或者之后的那么多行处。在GROUPS模式中,offset是一个整数,表示真开始或者结束于当前行的平级组之前或者之后那么多个平级组处,其中平级组是一组根据窗口的ORDER BY子句等效的行。在RANGE模式中,offset选项的使用要求在窗口定义中正好有一个ORDER BY列。那么该帧包含的行的排序列值不超过offset且小于(对于PRECEDING)或者大于(对于FOLLOWING)当前行的排序列值。在这些情况中,offset表达式的数据类型取决于排序列的数据类型。对于数字排序列,它通常与排序列是相同类型,但对于datetime类型的排序列它是interval。在所有这些情况中,offset的值必须是非空和非负。此外,虽然offset并非必须是简单常量,但它不能包含变量、聚集函数或者窗口函数。

frame_exclusion选项允许从帧中排除当前行周围的行,即便根据帧的起始选项来说它们应该被包含在帧中。EXCLUDE CURRENT ROW把当前行从帧中排除。EXCLUDE GROUP把当前行和它在排序上的平级行从帧中排除。EXCLUDE TIES从帧中排除当前行的任何平级行,但是不排除当前行本身。EXCLUDE NO OTHERS只是明确地指定不排除当前行或其平级行的默认行为。

注意,如果ORDER BY排序无法把行唯一地排序,则ROWS模式可能产生不可预测的结果。RANGE以及GROUPS模式的目的是确保在ORDER BY顺序中平等的行被同样对待:一个给定平级组中的所有行将在一个帧中或者被从帧中排除。

WINDOW子句的目的是指定出现在查询的 SELECT listORDER BY子句中的 窗口函数的行为。这些函数可以在它们的 OVER子句中用名称引用WINDOW 子句项。不过,WINDOW子句项不是必须被引用。 如果在查询中没有用到它,它会被简单地忽略。可以使用根本没有任何 WINDOW子句的窗口函数,因为窗口函数调用可 以直接在其OVER子句中指定它的窗口定义。不过,当多 个窗口函数都需要相同的窗口定义时, WINDOW子句能够减少输入。

当前,FOR NO KEY UPDATEFOR UPDATEFOR SHAREFOR KEY SHARE不能和 WINDOW一起被指定。

窗口函数的详细描述在 Section 4.5Section 5.2.8以及 Section 8.2.5中。

SELECT 列表

SELECT列表(位于关键词 SELECTFROM之间)指定构成 SELECT语句输出行的表达式。这些表达式 可以(并且通常确实会)引用FROM子句中计算得到的列。

正如在表中一样,SELECT的每一个输出列都有一个名称。 在一个简单的SELECT中,这个名称只是被用来标记要显 示的列,但是当SELECT是一个大型查询的一个子查询时,大型查询 会把该名称看做子查询产生的虚表的列名。要指定用于输出列的名称,在该列的表达式 后面写上 AS output_name( 你可以省略AS,但只能在期望的输出名称不匹配任何 LightDB关键词(见Appendix C)时省略。为了避免和未来增加的关键词冲突, 推荐总是写上AS或者用双引号引用输出名称)。如果你不指定列名, LightDB会自动选择一个名称。如果列的表达式 是一个简单的列引用,那么被选择的名称就和该列的名称相同。在使用函数或者类型名称 的更复杂的情况中,系统可能会生成诸如 ?column?之类的名称。

然而,显示的列名将被转换成小写,即便特地使用 AS 来指定其别名,除非 使用双引号或反引号引用它们。但是如果在mysql模式下,即创建数据库时指定 lightdb_syntax_compatible_type mysql 同时设置lightdb_sql_mode包含 'uppercase_identifier'参数,那么可保证大小写不变。特别地,'uppercase_identifier'选项建议 在lightdb.conf中设置而不是在会话中设置,因为这个参数最好设置后就不做改动。缺少上述条件中的任 意一项都会使这项特性失效。为实现这个特性,达成与mysql一致的效果,查询时列名与存储文件内列名进行 匹配时将忽略大小写,比如查询时列名'fOo'将认为与磁盘中存储的'FoO'相等。

与mysql模式类似但略有不同的是,oracle模式下guc参数'lightdb_oracle_sql_mode'可以在会话中配置, 取值为'show_identifier_uppercase'或''。如果设置为'show_identifier_uppercase',则执行一个select语句返回的结果列名默认显示大写。用户只有通过使用双引号包含列名才能使得大小写不变。设置'lightdb_oracle_sql_mode'为'',那么该功能会被禁用并恢复默认模式。

一个输出列的名称可以被用来在ORDER BY以及 GROUP BY子句中引用该列的值,但是不能用于 WHEREHAVING子句(在其中 必须写出表达式)。

可以在输出列表中写*来取代表达式,它是被选中 行的所有列的一种简写方式。还可以写 table_name.*,它 是只来自那个表的所有列的简写形式。在这些情况中无法用 AS指定新的名称,输出行的名称将和表列的名称相同。

根据 SQL 标准,输出列表中的表达式应该在应用DISTINCTORDER BY或者LIMIT之前计算。在使用DISTINCT时显然必须这样做,否则就无法搞清到底在区分什么值。不过,在很多情况下如果先计算ORDER BYLIMIT再计算输出表达式会很方便,特别是如果输出列表中包含任何 volatile 函数或者代价昂贵的函数时尤其如此。通过这种行为,函数计算的顺序更加直观并且对于从未出现在输出中的行将不会进行计算。只要输出表达式没有被DISTINCTORDER BY或者GROUP BY引用,LightDB实际将在排序和限制行数之后计算输出表达式(作为一个反例,SELECT f(x) FROM tab ORDER BY 1显然必须在排序之前计算f(x))。包含有集合返回函数的输出表达式实际是在排序之后和限制行数之前被计算,这样LIMIT才能切断来自集合返回函数的输出。

DISTINCT 子句

如果指定了SELECT DISTINCT,所有重复的行会被从结果 集中移除(为每一组重复的行保留一行)。SELECT DISTINCTSELECT UNIQUE是同义的。SELECT UNIQUE 只能在oracle下使用。SELECT ALL则 指定相反的行为:所有行都会被保留,这也是默认情况。

SELECT DISTINCT ON ( expression [, ...] ) 只保留在给定表达式上计算相等的行集合中的第一行。 DISTINCT ON表达式使用和 ORDER BY相同的规则(见上文)解释。注意,除非用 ORDER BY来确保所期望的行出现在第一位,每一个集 合的第一行是不可预测的。例如:

SELECT DISTINCT ON (location) location, time, report
    FROM weather_reports
    ORDER BY location, time DESC;

为每个地点检索最近的天气报告。但是如果我们不使用 ORDER BY来强制对每个地点的时间值进行降序排序, 我们为每个地点得到的报告的时间可能是无法预测的。

DISTINCT ON表达式必须匹配最左边的 ORDER BY表达式。ORDER BY子句通常 将包含额外的表达式,这些额外的表达式用于决定在每一个 DISTINCT ON分组内行的优先级。

DISTINCT支持与CONNECT BY一同使用。例如:

select distinct test_area1.id, test_area1.name, test_area1.pid 
    from test_area1 join test_area2 
    on test_area1.id = test_area2.id 
    connect by level < 3 order by test_area1.id;

select unique test_area1.id, test_area1.name, test_area1.pid 
    from test_area1 join test_area2 
    on test_area1.id = test_area2.id 
    connect by level < 3 order by test_area1.id;    

当前,FOR NO KEY UPDATEFOR UPDATEFOR SHAREFOR KEY SHARE不能和 DISTINCT一起使用。

UNION 子句

UNION子句具有下面的形式:

select_statement UNION [ ALL | DISTINCT ] select_statement

select_statement 是任何没有ORDER BYLIMITFOR NO KEY UPDATEFOR UPDATEFOR SHAREFOR KEY SHARE子句的 SELECT语句(如果子表达式被包围在圆括号内, ORDER BYLIMIT可以被附着到其上。如果没有 圆括号,这些子句将被应用到UNION的结果而不是右手边 的表达式上)。

UNION操作符计算所涉及的 SELECT语句所返回的行的并集。如果一行 至少出现在两个结果集中的一个内,它就会在并集中。作为 UNION两个操作数的 SELECT语句必须产生相同数量的列并且 对应位置上的列必须具有兼容的数据类型。

UNION的结果不会包含重复行,除非指定了 ALL选项。ALL会阻止消除重复(因此, UNION ALL通常显著地快于UNION, 尽量使用ALL)。可以写DISTINCT来 显式地指定消除重复行的行为。

除非用圆括号指定计算顺序, 同一个SELECT语句中的多个 UNION操作符会从左至右计算。

当前,FOR NO KEY UPDATEFOR UPDATEFOR SHAREFOR KEY SHARE不能用于UNION结果或者 UNION的任何输入。

为了兼容ORACLE数据库的使用,LightDB UNION语句支持NULL类型和其他类型之间的类型转换。 如果其他类型无法转换成NULL类型,则报错。这个功能仅在ORACLE模式下生效。

select null cont from dual
union all
select null cont from dual
union all
select 2 cont from dual;

select null cont from dual
union all
select null cont from dual
union all
select sysdate cont from dual;

select 2.1 cont from dual
union all
select 3.1 cont from dual
union all
select 4.1 cont from dual
union all
select 5.1 cont from dual
union all
select '3.65' cont from dual;

INTERSECT 子句

INTERSECT子句具有下面的形式:

select_statement INTERSECT [ ALL | DISTINCT ] select_statement

select_statement 是任何没有ORDER BY, LIMITFOR NO KEY UPDATEFOR UPDATEFOR SHARE以及FOR KEY SHARE子句的 SELECT语句。

INTERSECT操作符计算所涉及的 SELECT语句返回的行的交集。如果 一行同时出现在两个结果集中,它就在交集中。

INTERSECT的结果不会包含重复行,除非指定了 ALL选项。如果有ALL,一个在左表中有 m次重复并且在右表中有n 次重复的行将会在结果中出现 min(m,n) 次。 DISTINCT可以写DISTINCT来 显式地指定消除重复行的行为。

除非用圆括号指定计算顺序, 同一个SELECT语句中的多个 INTERSECT操作符会从左至右计算。 INTERSECT的优先级比 UNION更高。也就是说, A UNION B INTERSECT C将被读成A UNION (B INTERSECT C)

当前,FOR NO KEY UPDATEFOR UPDATEFOR SHAREFOR KEY SHARE不能用于INTERSECT结果或者 INTERSECT的任何输入。

EXCEPT 子句

EXCEPT子句具有下面的形式:

select_statement EXCEPT [ ALL | DISTINCT ] select_statement

select_statement 是任何没有ORDER BYLIMITFOR NO KEY UPDATEFOR UPDATEFOR SHARE以及FOR KEY SHARE子句的 SELECT语句。

EXCEPT操作符计算位于左 SELECT语句的结果中但不在右 SELECT语句结果中的行集合。

EXCEPT的结果不会包含重复行,除非指定了 ALL选项。如果有ALL,一个在左表中有 m次重复并且在右表中有 n次重复的行将会在结果集中出现 max(m-n,0) 次。 DISTINCT可以写DISTINCT来 显式地指定消除重复行的行为。

除非用圆括号指定计算顺序, 同一个SELECT语句中的多个 EXCEPT操作符会从左至右计算。 EXCEPT的优先级与 UNION相同。

当前,FOR NO KEY UPDATEFOR UPDATEFOR SHAREFOR KEY SHARE不能用于EXCEPT结果或者 EXCEPT的任何输入。

ORDER BY 子句

可选的ORDER BY子句的形式如下:

ORDER BY expression [ ASC | DESC | USING operator ] [ NULLS { FIRST | LAST } ] [, ...]

ORDER BY子句导致结果行被按照指定的表达式排序。 如果两行按照最左边的表达式是相等的,则会根据下一个表达式比较它们, 依次类推。如果按照所有指定的表达式它们都是相等的,则它们被返回的 顺序取决于实现。

每一个expression 可以是输出列(SELECT列表项)的名称或 者序号,它也可以是由输入列值构成的任意表达式,或者同一层次查询select列表中的列 (存储过程参数、窗口函数、rownum、volatile函数除外)别名构成的任意表达式。

序号指的是输出列的顺序(从左至右)位置。这种特性可以为不具有唯一 名称的列定义一个顺序。这不是绝对必要的,因为总是可以使用 AS子句为输出列赋予一个名称。

也可以在ORDER BY子句中使用任意表达式,包括没 有出现在SELECT输出列表中的列。因此, 下面的语句是合法的:

SELECT name FROM distributors ORDER BY code;

这种特性的一个限制是一个应用在UNIONINTERSECTEXCEPT子句结果上的 ORDER BY只能指定输出列名称或序号,但不能指定表达式。

如果一个ORDER BY表达式是一个既匹配输出列名称又匹配 输入列名称的简单名称,ORDER BY将把它解读成输出列名 称。这与在同样情况下GROUP BY会做出的选择相反。这种 不一致是为了与 SQL 标准兼容。

可以为ORDER BY子句中的任何表达式之后增加关键词 ASC(上升)DESC(下降)。如果没有指定, ASC被假定为默认值。或者,可以在USING 子句中指定一个特定的排序操作符名称。一个排序操作符必须是某个 B-树操作符族的小于或者大于成员。ASC通常等价于 USING <DESC通常等价于 USING >(但是一种用户定义数据类型的创建者可以 准确地定义默认排序顺序是什么,并且它可能会对应于其他名称的操作符)。

如果指定NULLS LAST,空值会排在非空值之后;如果指定 NULLS FIRST,空值会排在非空值之前。如果都没有指定, 在指定或者隐含ASC时的默认行为是NULLS LAST, 而指定或者隐含DESC时的默认行为是 NULLS FIRST(因此,默认行为是空值大于非空值)。 当指定USING时,默认的空值顺序取决于该操作符是否为 小于或者大于操作符。

注意顺序选项只应用到它们所跟随的表达式上。例如 ORDER BY x, y DESCORDER BY x DESC, y DESC是不同的。

字符串数据会被根据引用到被排序列上的排序规则排序。根据需要可以通过在 expression中包括一个 COLLATE子句来覆盖,例如 ORDER BY mycolumn COLLATE "en_US"。更多信息请见 Section 5.2.10Section 22.2

LIMIT 子句

LIMIT子句由两个独立的子句构成:

LIMIT { count | ALL }
OFFSET start

参数count指定要返回 的最大行数,而start 指定在返回行之前要跳过的行数。在两者都被指定时,在开始计算要返回的 count行之前会跳过 start行。

如果count表达式计算 为 NULL,它会被当成LIMIT ALL,即没有限制。如果 start计算为 NULL,它会被当作OFFSET 0

SQL:2008 引入了一种不同的语法来达到相同的结果, LightDB也支持它:

OFFSET start { ROW | ROWS }
FETCH { FIRST | NEXT } [ count ] { ROW | ROWS } { ONLY | WITH TIES }

在这种语法中,标准要求startcount是一个文本常量、一个参数或者一个变量名。 而作为一种LightDB的扩展,还允许其他的表达式,但通常需要被封闭在圆括号中以避免歧义。 如果在一个FETCH子句中省略count,它的默认值为 1。 WITH TIES选项用于根据ORDER BY子句返回与结果集中最后一个位置相关的任何附加行;ORDER BY在这种情况下是强制性的,并且SKIP LOCKED是不被允许的。 ROWROWS以及FIRSTNEXT是噪声,它们不影响这些子句的效果。 根据标准,如果都存在,OFFSET子句必须出现在FETCH子句之前。 但是LightDB更宽松,它允许两种顺序。

在使用LIMIT时,用一个ORDER BY子句把 结果行约束到一个唯一顺序是个好办法。否则你讲得到该查询结果行的 一个不可预测的子集 — 你可能要求从第 10 到第 20 行,但是在 什么顺序下的第 10 到第 20 呢?除非指定ORDER BY,你 是不知道顺序的。

查询优化器在生成一个查询计划时会考虑LIMIT,因此 根据你使用的LIMITOFFSET,你很可能 得到不同的计划(得到不同的行序)。所以,使用不同的 LIMIT/OFFSET值来选择一个查询结果的 不同子集将会给出不一致的结果,除非你 用ORDER BY强制一种可预测的结果顺序。这不是一个 缺陷,它是 SQL 不承诺以任何特定顺序(除非使用 ORDER BY来约束顺序)给出一个查询结果这一事实造 成的必然后果。

如果没有一个ORDER BY来强制选择一个确定的子集, 重复执行同样的LIMIT查询甚至可能会返回一个表中行 的不同子集。同样,这也不是一种缺陷,再这样一种情况下也无法 保证结果的确定性。

锁定子句

FOR UPDATEFOR NO KEY UPDATEFOR SHAREFOR KEY SHARE锁定子句,它们影响SELECT 把行从表中取得时如何对它们加锁。

锁定子句的一般形式:

FOR lock_strength [ OF table_name [, ...] ] [ NOWAIT | SKIP LOCKED ]

其中lock_strength可以是

UPDATE
NO KEY UPDATE
SHARE
KEY SHARE

更多关于每一种行级锁模式的信息可见 Section 14.3.2

为了防止该操作等待其他事务提交,可使用NOWAIT或者SKIP LOCKED选项。 使用NOWAIT时,如果选中的行不能被立即锁定,该语句会报告错误而不是等待。 使用 SKIP LOCKED时,无法被立即锁定的任何选中行都会被跳过。 跳过已锁定行会提供数据的一个不一致的视图,因此这不适合于一般目的的工作,但是可以被用来避免多个用户访问一个类似队列的表时出现锁竞争。 注意NOWAITSKIP LOCKED只适合行级锁 — 所要求的ROW SHARE表级锁仍然会以常规的方式(见Chapter 14)取得。 如果想要不等待的表级锁,你可以先使用带NOWAITLOCK

如果在一个锁定子句中提到了特定的表,则只有来自于那些表的 行会被锁定,任何SELECT中用到的 其他表还是被简单地照常读取。一个没有表列表的锁定子句会影响 该语句中用到的所有表。如果一个锁定子句被应用到一个视图或者 子查询,它会影响在该视图或子查询中用到的所有表。不过,这些 子句不适用于主查询引用的WITH查询。如果你希望 在一个WITH查询中发生行锁定,应该在该 WITH查询内指定一个锁定子句。

如果有必要对不同的表指定不同的锁定行为,可以写多个锁定子句。 如果同一个表在多于一个锁定子句中被提到(或者被隐式的影响到), 那么会按照所指定的最强的锁定行为来处理它。类似地,如果在任何 影响一个表的子句中指定了NOWAIT,就会按照 NOWAIT的行为来处理该表。否则如果 SKIP LOCKED在任何影响该表的子句中被指定, 该表就会被按照SKIP LOCKED来处理。

如果被返回的行无法清晰地与表中的行保持一致,则不能使用锁定子句。 例如锁定子句不能与聚集一起使用。

当一个锁定子句出现在一个SELECT查询的顶层时, 被锁定的行正好就是该查询返回的行。在连接查询的情况下,被锁定 的行是那些对返回的连接行有贡献的行。此外,自该查询的快照起满足 查询条件的行将被锁定,如果它们在该快照后被更新并且不再满足 查询条件,它们将不会被返回。如果使用了LIMIT,只要 已经返回的行数满足了限制,锁定就会停止(但注意被 OFFSET跳过的行将被锁定)。类似地,如果在一个游标 的查询中使用锁定子句,只有被该游标实际取出或者跳过的行才将被 锁定。

当一个锁定子句出现在一个子-SELECT中时,被锁定 行是那些该子查询返回给外层查询的行。这些被锁定的行的数量可能比 从子查询自身的角度看到的要少,因为来自外层查询的条件可能会被用 来优化子查询的执行。例如:

SELECT * FROM (SELECT * FROM mytable FOR UPDATE) ss WHERE col1 = 5;

将只锁定具有col1 = 5的行(虽然在子查询中并没有写上 该条件)。

Caution

一个运行在READ COMMITTED事务隔离级别并且使用ORDER BY和锁定子句的SELECT命令有可能返回无序的行。 这是因为ORDER BY会被首先应用。该命令对结果排序,但是可能 接着在尝试获得一个或者多个行上的锁时阻塞。一旦SELECT解除 阻塞,某些排序列值可能已经被修改,从而导致那些行变成无序的(尽管它们根 据原始列值是有序的)。根据需要,可以通过在子查询中放置 FOR UPDATE/SHARE来解决之一问题,例如

SELECT * FROM (SELECT * FROM mytable FOR UPDATE) ss ORDER BY column1;

注意这将导致锁定mytable的所有行,而顶层的 FOR UPDATE只会锁定实际被返回的行。这可能会导致显著的 性能差异,特别是把ORDER BYLIMIT或者其他 限制组合使用时。因此只有在并发更新排序列并且要求严格的排序结果时才推 荐使用这种技术。

REPEATABLE READ或者SERIALIZABLE 事务隔离级别上这可能导致一个序列化失败(SQLSTATE'40001'),因此在这些隔离级别下不可能收到无序行。

TABLE 命令

命令

TABLE name

等价于

SELECT * FROM name

它可以被用作一个顶层命令,或者用在复杂查询中以节省空间。只有 WITHUNIONINTERSECTEXCEPTORDER BYLIMITOFFSETFETCH以及FOR锁定子句可以用于 TABLE。不能使用WHERE子句和任何形式 的聚集。

示例

mysql模式下显示列名不变:

CREATE DATABASE films_distributors WITH lightdb_syntax_compatible_type mysql;
\c films_distributors;
-- it not suggest to set 'uppercase_identifier' on session to enable that as
-- 'set lightdb_sql_mode to uppercase_identifier;'. users should specify this
-- option in lightdb.conf instead, which follows by the current value of
-- 'lightdb_sql_mode' split with ','. Excuting 'lt_ctl --reload' to reload this
-- rule and don't modifiy it as far as possible.

SELECT Title as tiTLe, Did DiD, name, DAte_pROd, films.KIND
    FROM films;

       tiTLe       | DiD |     name     | DAte_pROd  |   KIND
-------------------+-----+--------------+------------+----------
 The Third Man     | 101 | British Lion | 1949-12-23 | Drama
 The African Queen | 101 | British Lion | 1951-08-11 | Romantic
 ...

示例oracle模式下显示大写列名

-- 需确保处于oracle模式
show lightdb_dblevel_syntax_compatible_type;
 lightdb_dblevel_syntax_compatible_type
----------------------------------------
 Oracle
(1 row)
set lightdb_oracle_sql_mode to 'show_identifier_uppercase';
show lightdb_oracle_sql_mode;
  lightdb_oracle_sql_mode
---------------------------
 show_identifier_uppercase
(1 row)

create table a(foo varchar(10));
-- 下述内部查询不会转换成大写
select "foo" from (select foo as Foo from a) as temp;                    -- 显示foo
 foo
-----
(0 rows)
select foo from (select foo as Foo from a) as temp;                      -- 显示FOO
 FOO
-----
(0 rows)
select "foO" foo from (select foo as Foo from a) as temp;		         -- 失败, "foO"列不存在
ERROR:  column "foO" does not exist
LINE 1: select "foO" foo from (select foo as Foo from a) as temp;

把表films与表 distributors连接:

SELECT f.title, f.did, d.name, f.date_prod, f.kind
    FROM distributors d JOIN films f USING (did);

       title       | did |     name     | date_prod  |   kind
-------------------+-----+--------------+------------+----------
 The Third Man     | 101 | British Lion | 1949-12-23 | Drama
 The African Queen | 101 | British Lion | 1951-08-11 | Romantic
 ...

要对所有电影的len列求和并且用 kind对结果分组:

SELECT kind, sum(len) AS total FROM films GROUP BY kind;

   kind   | total
----------+-------
 Action   | 07:34
 Comedy   | 02:58
 Drama    | 14:28
 Musical  | 06:42
 Romantic | 04:38

要对所有电影的len列求和、对结果按照 kind分组并且显示总长小于 5 小时的分组:

SELECT kind, sum(len) AS total
    FROM films
    GROUP BY kind
    HAVING sum(len) < interval '5 hours';

   kind   | total
----------+-------
 Comedy   | 02:58
 Romantic | 04:38

下面两个例子都是根据第二列(name)的内容来排序结果:

SELECT * FROM distributors ORDER BY name;
SELECT * FROM distributors ORDER BY 2;

 did |       name
-----+------------------
 109 | 20th Century Fox
 110 | Bavaria Atelier
 101 | British Lion
 107 | Columbia
 102 | Jean Luc Godard
 113 | Luso films
 104 | Mosfilm
 103 | Paramount
 106 | Toho
 105 | United Artists
 111 | Walt Disney
 112 | Warner Bros.
 108 | Westward

接下来的例子展示了如何得到表distributorsactors的并集,把结果限制为那些在每个表中以 字母 W 开始的行。只想要可区分的行,因此省略了关键词 ALL

distributors:               actors:
 did |     name              id |     name
-----+--------------        ----+----------------
 108 | Westward               1 | Woody Allen
 111 | Walt Disney            2 | Warren Beatty
 112 | Warner Bros.           3 | Walter Matthau
 ...                         ...

SELECT distributors.name
    FROM distributors
    WHERE distributors.name LIKE 'W%'
UNION
SELECT actors.name
    FROM actors
    WHERE actors.name LIKE 'W%';

      name
----------------
 Walt Disney
 Walter Matthau
 Warner Bros.
 Warren Beatty
 Westward
 Woody Allen

这个例子展示了如何在FROM子句中使用函数, 分别使用和不使用列定义列表:

CREATE FUNCTION distributors(int) RETURNS SETOF distributors AS $$
    SELECT * FROM distributors WHERE did = $1;
$$ LANGUAGE SQL;

SELECT * FROM distributors(111);
 did |    name
-----+-------------
 111 | Walt Disney

CREATE FUNCTION distributors_2(int) RETURNS SETOF record AS $$
    SELECT * FROM distributors WHERE did = $1;
$$ LANGUAGE SQL;

SELECT * FROM distributors_2(111) AS (f1 int, f2 text);
 f1  |     f2
-----+-------------
 111 | Walt Disney

这里是带有增加的序数列的函数的例子:

SELECT * FROM unnest(ARRAY['a','b','c','d','e','f']) WITH ORDINALITY;
 unnest | ordinality
--------+----------
 a      |        1
 b      |        2
 c      |        3
 d      |        4
 e      |        5
 f      |        6
(6 rows)

这个例子展示了如何使用简单的WITH子句:

WITH t AS (
    SELECT random() as x FROM generate_series(1, 3)
  )
SELECT * FROM t
UNION ALL
SELECT * FROM t

         x          
--------------------
  0.534150459803641
  0.520092216785997
 0.0735620250925422
  0.534150459803641
  0.520092216785997
 0.0735620250925422

注意该WITH查询只被计算一次,这样我们得到的两个 集合具有相同的三个随机值。

这个例子使用WITH RECURSIVE从一个只显示 直接下属的表中寻找雇员 Mary 的所有下属(直接的或者间接的)以及他们的间接层数:

WITH RECURSIVE employee_recursive(distance, employee_name, manager_name) AS (
    SELECT 1, employee_name, manager_name
    FROM employee
    WHERE manager_name = 'Mary'
  UNION ALL
    SELECT er.distance + 1, e.employee_name, e.manager_name
    FROM employee_recursive er, employee e
    WHERE er.employee_name = e.manager_name
  )
SELECT distance, employee_name FROM employee_recursive;

注意这种递归查询的典型形式:一个初始条件,后面跟着 UNION,然后是查询的递归部分。要确保 查询的递归部分最终将不返回任何行,否则该查询将无限循环( 更多例子见Section 8.8)。

这个例子使用LATERALmanufacturers 表的每一行应用一个集合返回函数get_product_names()

SELECT m.name AS mname, pname
FROM manufacturers m, LATERAL get_product_names(m.id) pname;

当前没有任何产品的制造商不会出现在结果中,因为这是一个内连接。 如果我们希望把这类制造商的名称包括在结果中,我们可以:

SELECT m.name AS mname, pname
FROM manufacturers m LEFT JOIN LATERAL get_product_names(m.id) pname ON true;

此示例使用 CONNECT BY 子句定义员工和管理者之间的关系,使用 LEVEL 伪列显示父行和子行,并使用 START WITH 子句为分层结构指定一个根行。

SELECT last_name, employee_id, manager_id, LEVEL
FROM employees
START WITH employee_id = 100
CONNECT BY PRIOR employee_id = manager_id;

LAST_NAME                 EMPLOYEE_ID MANAGER_ID      LEVEL
------------------------- ----------- ---------- ----------
King                              100                     1
Cambrault                         148        100          2
Bates                             172        148          3
Bloom                             169        148          3
Fox                               170        148          3
Kumar                             173        148          3
Ozer                              168        148          3
Smith                             171        148          3
De Haan                           102        100          2
Hunold                            103        102          3
Austin                            105        103          4
Ernst                             104        103          4
Lorentz                           107        103          4
Pataballa                         106        103          4
Errazuriz                         147        100          2
Ande                              166        147          3
Banda                             167        147          3

    

以下示例返回从员工 KING 到 KING 的员工(以及他们的员工)的员工姓名路径:

SELECT SYS_CONNECT_BY_PATH(last_name, '/') "Path"
FROM employees
START WITH last_name = 'KING'
CONNECT BY PRIOR employee_id = manager_id;
        Path
-------------------------
    /KING
    /KING/JONES
    /KING/BLAKE
    /KING/CLARK
    /KING/BLAKE/ALLEN
    /KING/BLAKE/WARD
    /KING/BLAKE/MARTIN
    /KING/JONES/SCOTT
    /KING/BLAKE/TURNER
    /KING/BLAKE/JAMES
    /KING/JONES/FORD
    /KING/CLARK/MILLER
    /KING/JONES/FORD/SMITH
    /KING/JONES/SCOTT/ADAMS
(14 rows)
    

以下示例返回 110 部门中每个员工的姓氏、层次结构中该员工之上最高级别的经理、经理和员工之间的级别以及两者之间的路径:

SELECT last_name "Employee", CONNECT_BY_ROOT last_name "Manager",
LEVEL-1 "Pathlen", SYS_CONNECT_BY_PATH(last_name, '/') "Path"
FROM employees
WHERE LEVEL > 1 and department_id = 110
CONNECT BY PRIOR employee_id = manager_id
ORDER BY "Employee", "Manager", "Pathlen", "Path";

Employee        Manager            Pathlen Path
--------------- --------------- ---------- ------------------------------
Gietz           Higgins                  1 /Higgins/Gietz
Gietz           King                     3 /King/Kochhar/Higgins/Gietz
Gietz           Kochhar                  2 /Kochhar/Higgins/Gietz
Higgins         King                     2 /King/Kochhar/Higgins
Higgins         Kochhar                  1 /Kochhar/Higgins
    

以下示例显示 n 层级以下的所有递归结果。

select * from staff_table connect by level < 2;
staff_id |       name        | manager_id
----------+-------------------+------------
    1001 | Michael North     |
    1002 | Megan Berry       |       1001
    1003 | Sarah Berry       |       1001
    1004 | Zoe Black         |       1001
    1005 | Tim James         |       1001
    1006 | Bella Tucker      |       1002
    1007 | Ryan Metcalfe     |       1002
    1008 | Max Mills         |       1002
    1009 | Benjamin Glover   |       1002
    1010 | Carolyn Henderson |       1003
    1011 | Nicola Kelly      |       1003
    1012 | Alexandra Climo   |       1003
    1013 | Dominic King      |       1003
    1014 | Leonard Gray      |       1004
    1015 | Eric Rampling     |       1004
    1016 | Piers Paige       |       1007
    1017 | Ryan Henderson    |       1007
    1018 | Frank Tucker      |       1008
    1019 | Nathan Ferguson   |       1008
    1020 | Kevin Rampling    |       1008
    

以下示例显示员工是否是叶节点(没有下属)。

SELECT staff_id, name, manager_id,
SYS_CONNECT_BY_PATH (name,'/'), connect_by_isleaf
    FROM staff_table
    START WITH staff_id = '1001'
    CONNECT BY PRIOR staff_id = manager_id;
staff_id |       name        | manager_id |                     sysconnectpath                      | connect_by_isleaf
----------+-------------------+------------+---------------------------------------------------------+-------------------
    1001 | Michael North     |       1000 | /Michael North                                          |                 0
    1002 | Megan Berry       |       1001 | /Michael North/Megan Berry                              |                 0
    1003 | Sarah Berry       |       1001 | /Michael North/Sarah Berry                              |                 0
    1004 | Zoe Black         |       1001 | /Michael North/Zoe Black                                |                 0
    1005 | Tim James         |       1001 | /Michael North/Tim James                                |                 1
    1006 | Bella Tucker      |       1002 | /Michael North/Megan Berry/Bella Tucker                 |                 1
    1007 | Ryan Metcalfe     |       1002 | /Michael North/Megan Berry/Ryan Metcalfe                |                 0
    1008 | Max Mills         |       1002 | /Michael North/Megan Berry/Max Mills                    |                 0
    1009 | Benjamin Glover   |       1002 | /Michael North/Megan Berry/Benjamin Glover              |                 1
    1010 | Carolyn Henderson |       1003 | /Michael North/Sarah Berry/Carolyn Henderson            |                 1
    1011 | Nicola Kelly      |       1003 | /Michael North/Sarah Berry/Nicola Kelly                 |                 1
    1012 | Alexandra Climo   |       1003 | /Michael North/Sarah Berry/Alexandra Climo              |                 1
    1013 | Dominic King      |       1003 | /Michael North/Sarah Berry/Dominic King                 |                 1
    1014 | Leonard Gray      |       1004 | /Michael North/Zoe Black/Leonard Gray                   |                 1
    1015 | Eric Rampling     |       1004 | /Michael North/Zoe Black/Eric Rampling                  |                 1
    1016 | Piers Paige       |       1007 | /Michael North/Megan Berry/Ryan Metcalfe/Piers Paige    |                 1
    1017 | Ryan Henderson    |       1007 | /Michael North/Megan Berry/Ryan Metcalfe/Ryan Henderson |                 1
    1018 | Frank Tucker      |       1008 | /Michael North/Megan Berry/Max Mills/Frank Tucker       |                 1
    1019 | Nathan Ferguson   |       1008 | /Michael North/Megan Berry/Max Mills/Nathan Ferguson    |                 1
    1020 | Kevin Rampling    |       1008 | /Michael North/Megan Berry/Max Mills/Kevin Rampling     |                 1

    

以下示例用于生成序列:'1 2 3 4 5 6'。

select rownum from dual CONNECT BY rownum <= 6;
rownum 
--------
    1
    2
    3
    4
    5
    6
(6 rows)
    

这个例子说明了 MySQL 索引提示的用法。

    -- multiple index
    select * from lt_test_mysql_ddl use index(pk_lt_test_mysql_ddl,uk_lt_test_mysql_ddl);
    select * from lt_test_mysql_ddl force index(pk_lt_test_mysql_ddl,uk_lt_test_mysql_ddl);
    select * from lt_test_mysql_ddl ignore index(pk_lt_test_mysql_ddl,uk_lt_test_mysql_ddl);

    -- multiple table join
    select * from lt_test_mysql_ddl a use index for order by(primary) join b using(id);
    select * from lt_test_mysql_ddl a force index for order by(pk_lt_test_mysql_ddl) join b using(id);
    select * from lt_test_mysql_ddl a ignore index for order by(primary,pk_lt_test_mysql_ddl) join b using(id);
    select * from lt_test_mysql_ddl a ignore index for order by(pk_lt_test_mysql_ddl) join b using(id);
   

兼容性

当然,SELECT语句是兼容 SQL 标准的。 但是也有一些扩展和缺失的特性。

省略的FROM子句

LightDB允许我们省略 FROM子句。一种简单的使用是计算简单表达式 的结果:

SELECT 2+2;

 ?column?
----------
        4

某些其他SQL数据库需要引入一个假的 单行表放在该SELECTFROM子句中才能做到这一点。

请注意,如果没有指定 FROM 子句,查询就不能引用任何数据库表。例如,以下查询是无效的:

SELECT distributors.* WHERE distributors.name = 'Westward';

SELECT列表

SELECT之后的输出表达式列表可以为空, 这会产生一个零列的结果表。对 SQL 标准来说这不是合法的 语法。LightDB允许 它是为了与允许零列表保持一致。不过在使用 DISTINCT时不允许空列表。

省略AS关键词

在 SQL 标准中,只要新列名是一个合法的列名(就是说与任何保留关键词不同), 就可以省略输出列名之前的可选关键词ASLightDB要稍微严格些:只要新列名匹配 任何关键词(保留或者非保留)就需要AS。推荐的习惯是使用 AS或者带双引号的输出列名来防止与未来增加的关键词可能的冲突。

FROM项中,标准和 LightDB都允许省略非保留 关键词别名之前的AS。但是由于语法的歧义,这无法 用于输出列名。

ONLY和继承

在书写ONLY时,SQL 标准要求在表名周围加上圆括号,例如 SELECT * FROM ONLY (tab1), ONLY (tab2) WHERE ...LightDB 认为这些圆括号是可选的。

LightDB允许写一个拖尾的*来 显式指定包括子表的非-ONLY行为。而标准则不允许 这样。

(这些点同等地适用于所有支持ONLY选项的 SQL 命令)。

TABLESAMPLE子句限制

当前只在常规表上接受TABLESAMPLE子句。 根据 SQL 标准,应该可以把它应用于任何FROM项。 在Oracle兼容模式下,TABLESAMPLE子句不能与 SAMPLE子句同时在一个SQL中使用。

SAMPLE子句限制

SAMPLE子句只能在Oracle兼容模式下使用,且不能与 TABLESAMPLE子句同时在一个SQL中使用。

FROM中的函数调用

LightDB允许一个函数调用被直接写作 FROM列表的一个成员。在 SQL 标准中,有必要把这样一个函数 调用包裹在一个子-SELECT中。也就是说,语法 FROM func(...) alias 近似等价于 FROM LATERAL (SELECT func(...)) alias。 注意该LATERAL被认为是隐式的,这是因为标准对于 FROM中的一个UNNEST()项要求 LATERAL语义。LightDB会把 UNNEST()和其他集合返回函数同样对待。

GROUP BYORDER BY可用的名字空间

在 SQL-92 标准中,一个ORDER BY子句只能使用输出 列名或者序号,而一个GROUP BY子句只能使用基于输 入列名的表达式。LightDB扩展了 这两种子句以允许它们使用其他的选择(但如果有歧义时还是使用标准的 解释)。LightDB也允许两种子句 指定任意表达式。注意出现在一个表达式中的名称将总是被当做输入列名而 不是输出列名。

SQL:1999 及其后的标准使用了一种略微不同的定义,它并不完全向后兼容 SQL-92。不过,在大部分的情况下, LightDB会以与 SQL:1999 相同的 方式解释ORDER BYGROUP BY表达式。

函数依赖

只有当一个表的主键被包括在GROUP BY列表中时, LightDB才识别函数依赖(允许 从GROUP BY中省略列)。SQL 标准指定了应该要识别 的额外情况。

LIMITOFFSET

LIMITOFFSET子句是 LightDB-特有的语法,在 MySQL也被使用。SQL:2008 标准已经 引入了具有相同功能的子句OFFSET ... FETCH {FIRST|NEXT} ...(如上文 LIMIT Clause中所示)。这种语法 也被IBM DB2使用( Oracle编写的应用常常使用自动生成的 rownum列来实现这些子句的效果,这在 LightDB 中是没有的)。

FOR NO KEY UPDATEFOR UPDATEFOR SHAREFOR KEY SHARE

尽管 SQL 标准中出现了FOR UPDATE,但标准只允许它作为 DECLARE CURSOR的一个选项。 LightDB允许它出现在任何 SELECT查询以及子-SELECT中,但这是 一种扩展。FOR NO KEY UPDATEFOR SHARE 以及FOR KEY SHARE变体以及NOWAITSKIP LOCKED选项没有在标准中出现。

WITH中的数据修改语句

LightDB允许把INSERTUPDATE以及DELETE用作WITH 查询。这在 SQL 标准中是找不到的。

非标准子句

DISTINCT ON ( ... )是 SQL 标准的扩展。

ROWS FROM( ... )是 SQL 标准的扩展。

WITHMATERIALIZEDNOT MATERIALIZED 选项是SQL标准的扩展。

oracle (+)

可以在 where 条件中使用 '(+)' 来像 Oracle 一样指定外连接,案例如下:

select * from t1 a, t2 b where a.key1(+) = b.key1;
                             QUERY PLAN                             
--------------------------------------------------------------------
 Hash Right Join  (cost=60.85..86.01 rows=2260 width=48)
   Hash Cond: (a.key1 = b.key1)
   ->  Seq Scan on t1 a  (cost=0.00..22.00 rows=1200 width=40)
   ->  Hash  (cost=32.60..32.60 rows=2260 width=8)
         ->  Seq Scan on t2 b  (cost=0.00..32.60 rows=2260 width=8)
(5 rows)

下面描述 Oracle (+) 的一些限制。

  • (+) 操作符只能在 WHERE 子句中使用,并且只能作用域表或视图的字段。

  • (+) 操作符必须完全匹配,中间不能有空格。oracle 中间可以有空格。

    select * from t1 a, t2 b where a.key1( +) = b.key1;
    ERROR:  syntax error at or near ")"
    LINE 1: explain select * from t1 a, t2 b where a.key1( +) = b.key1;
                                                            ^
    
  • 如果两个表间有多个关联条件,那么每个条件都要使用 (+), 这样才能把所有的关联条件作为join on 的关联条件。 不然,没有使用 (+) 的条件会保留在where 中,然后会导致满足空值拒绝, 最终被优化为 inner join(比如 left join 中的右表被 where 条件约束为不能为null, 则等价与 inner join)。

    explain select * from t1 a, t2 b where a.key1(+)=b.key1 and a.key2=b.key2;
                                 QUERY PLAN                             
    --------------------------------------------------------------------
     Merge Join  (cost=317.01..352.19 rows=128 width=16)
       Merge Cond: ((a.key1 = b.key1) AND (a.key2 = b.key2))
       ->  Sort  (cost=158.51..164.16 rows=2260 width=8)
             Sort Key: a.key1, a.key2
             ->  Seq Scan on t1 a  (cost=0.00..32.60 rows=2260 width=8)
       ->  Sort  (cost=158.51..164.16 rows=2260 width=8)
             Sort Key: b.key1, b.key2
             ->  Seq Scan on t2 b  (cost=0.00..32.60 rows=2260 width=8)
    (8 rows)
    
  • (+) 操作符只能作用于字段,不能作用于表达式,但可以作用于表达式中的字段。

    select * from t1 a, t2 b where mod(a.key1,10)(+)=b.key1;
    ERROR:  syntax error at or near "(+)"
    LINE 1: select * from t1 a, t2 b where mod(a.key1,10)(+)=b.key1;
                                                         ^
    
    select * from t1 a, t2 b where mod(a.key1(+),10)=b.key1;
     key1 | key2 | key1 | key2 
    ------+------+------+------
    (0 rows)
    
  • 使用了 (+) 操作符的约束不能与其他约束通过or 组合。

    select * from t1 a, t2 b where a.key1(+)=b.key1 or a.key2(+)=b.key2;
    ERROR:  Operator "(+)" is not allowed used with "OR" together
    
  • 不能将任何用 (+) 操作符标记的列与子查询进行比较。

    select * from t1 a, t2 b where a.key1(+)=(select key1 from t3);
    ERROR:  Operator "(+)" can not be used in outer join with SubQuery.
    LINE 1: select * from t1 a, t2 b where a.key1(+)=(select key1 from t...
                                                    ^ 
    
  • 表不能彼此进行外关联。

    select * from t1 a, t2 b where a.key1(+)=b.key1 and b.key2(+)=a.key2;
    ERROR:  Relation can't outer join with each other. 
    
  • (+) 作用的字段所属的表需要在当前查询块,不能位于上层的查询块。

    select * from t1 a  where  exists (select * from t2 b where a.key1(+)=b.key1);
    ERROR:  Operator "(+)" can't specify on "a" which cannot be referenced from this part of the query.
    LINE 1: ...rom t1 a  where  exists (select * from t2 b where a.key1(+)=...
                                                                 ^
    
  • (+) 操作符不能用于嵌套的and/or 表达式,这与 Oracle 不同,Oracle 支持,示例如下:

    # Oracle 也不支持, 因为在展开后,这实际是个 or 表达式。
    select * from t1 a, t2 b where not (a.key1(+)= b.key1 and a.key2(+)=b.key2);
    ERROR:  Operator "(+)" can not be used in nested and/or expression.
    LINE 1: select * from t1 a, t2 b where not (a.key1(+)= b.key1 and a....
                                           ^
    
    # Oracle 支持 'not (a.key1(+)= b.key1 or a.key2(+)=b.key2)' 
    select * from t1 a, t2 b where not (a.key1(+)= b.key1 or a.key2(+)=b.key2);
    ERROR:  Operator "(+)" can not be used in nested and/or expression.
    LINE 1: select * from t1 a, t2 b where not (a.key1(+)= b.key1 or a.k...
                                           ^
    
  • (+) 操作符不能和 ansi join 一起用。

    select * from t1 a join t2 b on a.key1=b.key1, t3 c, t4 d where c.key1(+)=d.key1;
    ERROR:  Operator "(+)" and Join in FromClause can't be used together
    
  • 一个表不能同时right join 多个表. oracle 支持这种用法.

    select * from t1 a, t2 b, t3 c where a.key1(+)=b.key1+c.key1;
    ERROR:  "a" can't outer join with more than one relation
    HINT:  "b", "c" are outer join with "a".
    
    select * from t1 a, t2 b, t3 c where a.key1(+)=b.key1 and a.key1(+)=c.key1;
    ERROR:  "a" can't outer join with more than one relation.
    
  • (+) 操作符不支持指定full out join。

    select * from t1 a, t2 b ,t3 c where a.key1(+) =b.key1(+);
    ERROR:  Operator "(+)" can't be specified on more than one relation in one join condition
    HINT:  "a", "b"...are specified Operator "(+)" in one condition.
    

pivot

提供简单pivot使用的例子.

create table test123(name varchar(40),chinese int,math int, course varchar(40), score int);
insert into test123 values('lisi',88,99,'math',99);
insert into test123 values('lisi',88,99,'chinese',88);
insert into test123 values('zhangsan',90,100,'chinese',90);
insert into test123 values('zhangsan',90,100,'math',100);

select * from test123 pivot (sum(score) for course in('chinese','math'));
   name   | chinese | math | 'chinese' | 'math' 
----------+---------+------+-----------+--------
 lisi     |      88 |   99 |        88 |     99
 lisi     |      89 |  100 |           |     99
 lisi     |     100 |   70 |       100 |       
 zhangsan |      76 |   89 |        99 |       
 zhangsan |      90 |  100 |        90 |    100
 zhangsan |      95 |   85 |           |    100
(6 rows)

drop table test123;

selectfrom tablename uppercase(oracle 兼容)

支持通过不带双引号的表名查询通过使用双引号创建的表。只支持在oracle 模式下使用。

create table "TEST123"(name varchar(40),chinese int,math int, course varchar(40), score int);
insert into "TEST123" values('lisi',88,99,'math',99);
insert into "TEST123" values('lisi',88,99,'chinese',88);
insert into "TEST123" values('zhangsan',90,100,'chinese',90);
insert into "TEST123" values('zhangsan',90,100,'math',100);

select * from test123;
ERROR:  relation "test123" does not exist
LINE 1: select * from test123;
                      ^

set lightdb_oracle_sql_mode = 'selectfrom_tablename_uppercase';

select * from test123;
   name   | chinese | math | course  | score 
----------+---------+------+---------+-------
 lisi     |      88 |   99 | math    |    99
 lisi     |      88 |   99 | chinese |    88
 zhangsan |      90 |  100 | chinese |    90
 zhangsan |      90 |  100 | math    |   100
(4 rows)

drop table "TEST123";