有用的BigQuery * EXCEPT 语法

705 阅读2分钟

使用和制作jOOQ最酷的事情之一是,我们可以发现供应商对标准SQL语言的最佳扩展,并通过模拟在jOOQ中增加对这些条款的支持。

这些语法之一是BigQuery的* EXCEPT 语法。每个曾经写过临时SQL查询的人都会希望这种语法可以使用,偶尔也可以。为什么需要它呢?看看这个查询:

SELECT * FROM actor

其结果是:

|actor_id|first_name|last_name   |last_update            |
|--------|----------|------------|-----------------------|
|1       |PENELOPE  |GUINESS     |2006-02-15 04:34:33.000|
|2       |NICK      |WAHLBERG    |2006-02-15 04:34:33.000|
|3       |ED        |CHASE       |2006-02-15 04:34:33.000|
|4       |JENNIFER  |DAVIS       |2006-02-15 04:34:33.000|
|5       |JOHNNY    |LOLLOBRIGIDA|2006-02-15 04:34:33.000|
|6       |BETTE     |NICHOLSON   |2006-02-15 04:34:33.000|
|...

但是那个LAST_UPDATE 列是非常烦人的,特别是当我们想NATURAL JOIN 的时候。例如,这样就不行了:

SELECT actor_id, a.first_name, a.last_name, count(fa.film_id)
FROM actor AS a
NATURAL LEFT JOIN film_actor AS fa
GROUP BY actor_id

结果只是没有电影的演员,因为不小心,LAST_UPDATE 列被包括在NATURAL JOIN

|actor_id|first_name|last_name|count|
|--------|----------|---------|-----|
|58      |CHRISTIAN |AKROYD   |0    |
|8       |MATTHEW   |JOHANSSON|0    |
|116     |DAN       |STREEP   |0    |
|184     |HUMPHREY  |GARLAND  |0    |
|87      |SPENCER   |PECK     |0    |

这是NATURAL JOIN 的最大缺陷,使得它对于那些不是为NATURAL JOIN 的使用而完美设计的模式几乎毫无用处,但这是临时的SQL,如果能做到这一点就好了。

我们可以,如果我们有这样的* EXCEPT

SELECT 
  a.actor_id, 
  a.first_name, 
  a.last_name, 
  count(fa.film_id)
FROM (
  SELECT * EXCEPT (last_update) FROM actor
) AS a
NATURAL LEFT JOIN (
  SELECT * EXCEPT (last_update) FROM film_actor
) AS fa
GROUP BY 
  a.actor_id, 
  a.first_name, 
  a.last_name

不幸的是,这在PostgreSQL和其他方言中不起作用,但jOOQ可以模仿它。如果你向在线SQL翻译器提供了sakila数据库的表定义:

CREATE TABLE actor (
    actor_id integer NOT NULL,
    first_name character varying(45) NOT NULL,
    last_name character varying(45) NOT NULL,
    last_update timestamp without time zone DEFAULT now() NOT NULL
);
CREATE TABLE film_actor (
    actor_id smallint NOT NULL,
    film_id smallint NOT NULL,
    last_update timestamp without time zone DEFAULT now() NOT NULL
);

那么,它可以解决查询的EXCEPT 语法,并产生这样的结果:

SELECT
  a.actor_id,
  a.first_name,
  a.last_name,
  count(fa.film_id)
FROM (
  SELECT actor.actor_id, actor.first_name, actor.last_name
  FROM actor
) a
  NATURAL LEFT OUTER JOIN (
    SELECT film_actor.actor_id, film_actor.film_id
    FROM film_actor
  ) fa
GROUP BY
  a.actor_id,
  a.first_name,
  a.last_name

显然,我们可以省略NATURAL JOIN ,以达到相同的结果,但有时,在工具链中有另一个工具来编写一个漂亮的查询,这实在是太好了。用jOOQ,查询结果是这样的:

Actor a = ACTOR.as("a");
FilmActor fa = FILM_ACTOR.as("fa");

ctx.select(
        a.ACTOR_ID,
        a.FIRST_NAME,
        a.LAST_NAME,
        count(fa.FILM_ID))
   .from(
        select(asterisk().except(a.LAST_UPDATE)).from(a).asTable(a))
   .naturalLeftOuterJoin(
        select(asterisk().except(fa.LAST_UPDATE)).from(fa).asTable(fa))
   .groupBy(a.ACTOR_ID, a.FIRST_NAME, a.LAST_NAME)
   .fetch();