用jOOQ在其他RDBMS上使用SQL Server FOR XML和FOR JSON语法

SQL Server支持将扁平的表格式SQL结果集转化为分层结构,通过使用方便的 FOR XMLFOR JSON语法。这确实很方便,而且比标准的SQL/XML或SQL/JSON API不那么啰嗦--尽管标准的API更强大。 在这篇博文中,我想展示SQL Server语法的几个核心特性,以及它们在标准SQL中的对应关系。jOOQ 3.14将同时支持SQL Server的语法和标准语法,并且能够从一个翻译到另一个,这样你就可以在Db2、MariaDB、MySQL、Oracle、PostgreSQL上使用SQL Server语法。你可以在我们的网站上玩一玩当前的开发状态,这里。 像往常一样,使用Sakila数据库,这里有一个简单的例子作为引子:

-- SQL Server
SELECT a.first_name, a.last_name, f.title
FROM actor a
JOIN film_actor fa ON a.actor_id = fa.actor_id
JOIN film f ON fa.film_id = f.film_id
FOR XML RAW;

-- Db2, Oracle, PostgreSQL
SELECT xmlagg(xmlelement(
  NAME row,
  xmlattributes(
    t.first_name AS first_name,
    t.last_name AS last_name,
    t.title AS title
  )
))
FROM (
  -- Original query here
  SELECT a.first_name, a.last_name, f.title
  FROM actor a
  JOIN film_actor fa ON a.actor_id = fa.actor_id
  JOIN film f ON fa.film_id = f.film_id
) AS t

在这两种情况下产生的东西是:

<row first_name="PENELOPE" last_name="GUINESS" title="OKLAHOMA JUMANJI"/>
<row first_name="PENELOPE" last_name="GUINESS" title="RULES HUMAN"/>
<row first_name="PENELOPE" last_name="GUINESS" title="SPLASH GUMP"/>
<row first_name="PENELOPE" last_name="GUINESS" title="VERTIGO NORTHWEST"/>
<row first_name="PENELOPE" last_name="GUINESS" title="WESTWARD SEABISCUIT"/>
<row first_name="PENELOPE" last_name="GUINESS" title="WIZARD COLDBLOODED"/>
<row first_name="NICK" last_name="WAHLBERG" title="ADAPTATION HOLES"/>
<row first_name="NICK" last_name="WAHLBERG" title="APACHE DIVINE"/>

FOR XML和FOR JSON的概念

从上面的预告中可以看出,SQL Server的语法远没有那么冗长和简洁,而且它似乎产生了一个合理的默认行为,而Db2、Oracle、PostgreSQL(和SQL标准)的SQL/XML APIs则更加冗长,但也更加强大。例如,可以非常容易地将列a映射到属性x,将列b映射到一个嵌套的XML元素y。 这两种方法的优点都很明显。在一般情况下,SQL Server的方法更可用。但什么是一般情况呢?让我们总结一下SQL结果集和XML/JSON数据结构之间的几个主要相似之处:

**表是XML元素或JSON数组**表(即数据集)对于XML和JSON文档来说都不是一个陌生的概念。在XML中表示一组数据的最自然的方式是一组使用相同元素名称的元素,可以选择用包装元素来包装。例如:

<!-- With wrapper element -->
<films>
  <film title="OKLAHOMA JUMANJI"/>
  <film title="RULES HUMAN"/>
  <film title="SPLASH GUMP"/>
</films>

<!-- Without wrapper element -->
<film title="OKLAHOMA JUMANJI"/>
<film title="RULES HUMAN"/>
<film title="SPLASH GUMP"/>

嵌套数据时,是否添加包装元素的区别是最重要的。 对于JSON,表示表格的数据结构的明显选择是一个数组。例如:

[
  {"title": "OKLAHOMA JUMANJI"},
  {"title": "RULES HUMAN"},
  {"title": "SPLASH GUMP"}
]

**行是XML元素或JSON对象**正如我们上面已经看到的,一个SQL行在XML中用一个元素表示:

<film title="OKLAHOMA JUMANJI"/>

问题只是元素的名称应该是什么。它通常可以是以下任何一种:

  • 一个标准名称,如 "行"
  • 行的表的名称
  • 一个自定义的名字

在JSON中,它是一个对象:

{"title": "OKLAHOMA JUMANJI"}

不像在XML中,没有所谓的元素名称,所以行是 "匿名的"。行的类型由JSON对象所包含的表/数组来定义。**列值是XML元素或属性,或JSON属性**我们对如何在XML中表示SQL列值有更多选择。主要有两种选择:

  • 将值作为属性表示
  • 以元素的形式表示数值

标量值可以很容易地被表示为属性。如果一个值需要进一步嵌套(如数组、用户定义的类型等),那么元素是一个更好的选择。在大多数情况下,这个选择并不重要,所以我们可以同时选择:

<!-- Using attributes -->
<film film_id="635" title="OKLAHOMA JUMANJI"/>

<!-- Using elements from table and column names -->
<film>
  <film_id>635</film_id>
  <title>OKLAHOMA JUMANJI</title>
</film>

<!-- Using standard element names
<row>
  <value name="film_id" value="635"/>
  <value name="title" value="OKLAHOMA JUMANJI"/>
</row>

在XML中,还有一些其他合理的默认选项来表示一个列值。 另一方面,在JSON中,有两种主要的合理方法。在大多数情况下,会选择一个对象,其中列值由列名识别。但就像SQL记录是 "结构体 "和 "图元 "的混合体一样,我们也可以想象一种将列值映射到数组索引的表示方法:

// Using objects
{"film_id": 635, "title": "OKLAHOMA JUMANJI"}

// Using arrays
[635, "OKLAHOMA JUMANJI"]

GROUP BY和ORDER BY可以被看作是一种嵌套数据的方式到目前为止,所有数据都是以平面方式表示的,就像SQL表一样。当用一些包装元素或对象包装JSON数组的XML元素时,或者用更多的元素而不是属性来表示XML数据时,有一些嵌套,但数据仍然总是表格式的。 不过很多时候,我们希望以分层的形式消费数据。一个演员在电影中演出,所以我们想按演员来分组,而不是为每部电影重复演员的信息。一般来说,像GROUP BYORDER BY 这样的操作可以达到这个目的。GROUP BY 可以将所有数据汇总到每组的嵌套数据结构中(例如,汇总到字符串、数组、XML元素、JSON数组、JSON对象中)。ORDER BY 也是这样做的,"视觉上"--也许有点不那么正式。当我们看这组XML元素时,我们可以直观地看到它们是按行为者 "分组"(即排序)的:

<row first_name="PENELOPE" last_name="GUINESS" title="OKLAHOMA JUMANJI"/>
<row first_name="PENELOPE" last_name="GUINESS" title="RULES HUMAN"/>
<row first_name="PENELOPE" last_name="GUINESS" title="SPLASH GUMP"/>
<row first_name="PENELOPE" last_name="GUINESS" title="VERTIGO NORTHWEST"/>
<row first_name="PENELOPE" last_name="GUINESS" title="WESTWARD SEABISCUIT"/>
<row first_name="PENELOPE" last_name="GUINESS" title="WIZARD COLDBLOODED"/>
<row first_name="NICK" last_name="WAHLBERG" title="ADAPTATION HOLES"/>
<row first_name="NICK" last_name="WAHLBERG" title="APACHE DIVINE"/>

SQL Server至少以两种方式支持这种分组:

  • 通过惯例隐式地使用ORDER BY
  • 通过创建相关的子查询来明确表示

隐式方法可以将上述平面表示法转化为这样的内容:

<a first_name="PENELOPE" last_name="GUINESS">
    <f title="OKLAHOMA JUMANJI"/>
    <f title="RULES HUMAN"/>
    <f title="SPLASH GUMP"/>
    <f title="VERTIGO NORTHWEST"/>
    <f title="WESTWARD SEABISCUIT"/>
    <f title="WIZARD COLDBLOODED"/>
</a>
<a first_name="NICK" last_name="WAHLBERG">
    <f title="ADAPTATION HOLES"/>
    <f title="APACHE DIVINE"/>
</a>

... 其中 "a "和 "f "是查询中的表名(actor afilm f )。

FOR XML和FOR JSON是如何详细工作的?

在SQL Server中,有几个功能可以结合起来。完整的情况可以从文档中看到。在这篇博文中,我们会省略一些功能:

  • 转换算法RAW (平面结果,仅在XML中),AUTO (分层的,自动的结果),PATH (分层的,明确的结果)
  • 根 "名称,对应于一个XML封装元素,或一个JSON封装对象
  • 仅限XML:值是否应放在ELEMENTS 或属性中
  • 仅限JSON:INCLUDE_NULL_VALUES 指定NULL 值是显式的,还是隐式的(不在JSON对象中)。
  • 仅限JSON:WITHOUT_ARRAY_WRAPPER 指定JSON对象的集合是否应该以JSON数组的形式列出,或者以逗号分隔的对象列表(可以与其他查询结合)。

这并不完整,还有更多的标志和功能,但与其在理论上讨论它们,不如看看几个例子:FOR XML RAW产生带有属性值的平面结果

-- SQL Server
SELECT a.first_name, a.last_name, f.title
FROM actor a
JOIN film_actor fa ON a.actor_id = fa.actor_id
JOIN film f ON fa.film_id = f.film_id
ORDER BY 1, 2, 3
FOR XML RAW;

-- Standard SQL
SELECT xmlagg(xmlelement(
  NAME row,
  xmlattributes(
    t.first_name AS first_name,
    t.last_name AS last_name,
    t.title AS title
  )
))
FROM (
  SELECT a.first_name, a.last_name, f.title
  FROM actor a
  JOIN film_actor fa ON a.actor_id = fa.actor_id
  JOIN film f ON fa.film_id = f.film_id
  ORDER BY 1, 2, 3
) AS t

这将产生

<row first_name="NICK" last_name="WAHLBERG" title="SMILE EARRING"/>
<row first_name="NICK" last_name="WAHLBERG" title="WARDROBE PHANTOM"/>
<row first_name="PENELOPE" last_name="GUINESS" title="ACADEMY DINOSAUR"/>
<row first_name="PENELOPE" last_name="GUINESS" title="ANACONDA CONFESSIONS"/>

FOR XML RAW, ROOT产生带有属性值的平面结果,并有一个根元素来包裹所列元素

-- SQL Server
SELECT a.first_name, a.last_name, f.title
FROM actor a
JOIN film_actor fa ON a.actor_id = fa.actor_id
JOIN film f ON fa.film_id = f.film_id
ORDER BY 1, 2, 3
FOR XML RAW, ROOT('rows');

-- Standard SQL
SELECT xmlelement(
  NAME rows,
  xmlagg(xmlelement(
    NAME row,
    xmlattributes(
      t.first_name AS first_name,
      t.last_name AS last_name,
      t.title AS title
    )
  ))
)
FROM (
  SELECT a.first_name, a.last_name, f.title
  FROM actor a
  JOIN film_actor fa ON a.actor_id = fa.actor_id
  JOIN film f ON fa.film_id = f.film_id
  ORDER BY 1, 2, 3
) AS t

产生

<rows>
  <row first_name="NICK" last_name="WAHLBERG" title="SMILE EARRING"/>
  <row first_name="NICK" last_name="WAHLBERG" title="WARDROBE PHANTOM"/>
  <row first_name="PENELOPE" last_name="GUINESS" title="ACADEMY DINOSAUR"/>
  <row first_name="PENELOPE" last_name="GUINESS" title="ANACONDA CONFESSIONS"/>
</rows>

FOR XML RAW, ELEMENTS产生带有元素值的平面结果:

-- SQL Server
SELECT a.first_name, a.last_name, f.title
FROM actor a
JOIN film_actor fa ON a.actor_id = fa.actor_id
JOIN film f ON fa.film_id = f.film_id
ORDER BY 1, 2, 3
FOR XML RAW, ELEMENTS;

-- Standard SQL
SELECT xmlagg(xmlelement(
  NAME row,
  xmlelement(
    NAME first_name,
    first_name
  ),
  xmlelement(
    NAME last_name,
    last_name
  ),
  xmlelement(
    NAME title,
    title
  )
))
FROM (
  SELECT a.first_name, a.last_name, f.title
  FROM actor a
  JOIN film_actor fa ON a.actor_id = fa.actor_id
  JOIN film f ON fa.film_id = f.film_id
  ORDER BY 1, 2, 3
  FOR XML RAW, ELEMENTS
) AS t

这就产生了

<row>
    <first_name>NICK</first_name>
    <last_name>WAHLBERG</last_name>
    <title>SMILE EARRING</title>
</row>
<row>
    <first_name>NICK</first_name>
    <last_name>WAHLBERG</last_name>
    <title>WARDROBE PHANTOM</title>
</row>
<row>
    <first_name>PENELOPE</first_name>
    <last_name>GUINESS</last_name>
    <title>ACADEMY DINOSAUR</title>
</row>
<row>
    <first_name>PENELOPE</first_name>
    <last_name>GUINESS</last_name>
    <title>ANACONDA CONFESSIONS</title>
</row>

这也可以和ROOT ,为了简洁起见,我们省略了这一点。FOR XML/JSON AUTO这种方法从你的查询结构中完全自动得出结果。主要是:

  • SELECT 子句定义了XML或JSON数据的嵌套顺序。
  • FROM 子句定义了表名(通过别名),这些表名被翻译成XML元素或JSON对象属性名。
  • ORDER BY 子句产生 "分组",它被翻译成嵌套的XML元素或JSON对象。
-- SQL Server
SELECT a.first_name, a.last_name, f.title
FROM actor a
JOIN film_actor fa ON a.actor_id = fa.actor_id
JOIN film f ON fa.film_id = f.film_id
ORDER BY 1, 2, 3
FOR XML AUTO;

-- Standard SQL
SELECT xmlagg(e)
FROM (
  SELECT xmlelement(
    NAME a,
    xmlattributes(
      t.first_name AS first_name,
      t.last_name AS last_name
    ),
    xmlagg(xmlelement(
      NAME f,
      xmlattributes(t.title AS title)
    ))
  ) AS e
  FROM (
    SELECT a.first_name, a.last_name, f.title
    FROM actor a
    JOIN film_actor fa ON a.actor_id = fa.actor_id
    JOIN film f ON fa.film_id = f.film_id
    ORDER BY 1, 2, 3
  ) AS t
  GROUP BY
    first_name,
    last_name
) AS t

请注意这种模拟需要两步XMLAGGGROUP BY 。随着更多的表被连接和投射,它变得更加毛糙!我不会在这里添加更复杂的例子,但可以在网上试试!这产生了:

<a first_name="NICK" last_name="WAHLBERG">
    <f title="SMILE EARRING"/>
    <f title="WARDROBE PHANTOM"/>
</a>
<a first_name="PENELOPE" last_name="GUINESS">
    <f title="ACADEMY DINOSAUR"/>
    <f title="ANACONDA CONFESSIONS"/>
</a>

让我们用JSON再次尝试同样的事情:

-- SQL Server
SELECT a.first_name, a.last_name, f.title
FROM actor a
JOIN film_actor fa ON a.actor_id = fa.actor_id
JOIN film f ON fa.film_id = f.film_id
ORDER BY 1, 2, 3
FOR JSON AUTO;

-- Standard SQL
SELECT json_arrayagg(e)
FROM (
  SELECT JSON_OBJECT(
    KEY 'FIRST_NAME' VALUE first_name,
    KEY 'LAST_NAME' VALUE last_name,
    KEY 'F' VALUE JSON_ARRAYAGG(JSON_OBJECT(
      KEY 'TITLE' VALUE title
      ABSENT ON NULL
    ))
    ABSENT ON NULL
  ) e
  FROM (
    SELECT a.first_name, a.last_name, f.title
    FROM actor a
    JOIN film_actor fa ON a.actor_id = fa.actor_id
    JOIN film f ON fa.film_id = f.film_id
    ORDER BY 1, 2, 3
  ) t
  GROUP BY
    first_name,
    last_name
) t

结果是:

[
    {
        "first_name": "NICK",
        "last_name": "WAHLBERG",
        "f": [
            {
                "title": "SMILE EARRING"
            },
            {
                "title": "WARDROBE PHANTOM"
            }
        ]
    },
    {
        "first_name": "PENELOPE",
        "last_name": "GUINESS",
        "f": [
            {
                "title": "ACADEMY DINOSAUR"
            },
            {
                "title": "ANACONDA CONFESSIONS"
            }
        ]
    }
]

FOR XML/JSON AUTO, ROOT像以前一样,如果需要的话,我们可以用根XML元素或根JSON对象来包装它:

-- SQL Server
SELECT a.first_name, a.last_name, f.title
FROM actor a
JOIN film_actor fa ON a.actor_id = fa.actor_id
JOIN film f ON fa.film_id = f.film_id
ORDER BY 1, 2, 3
FOR XML AUTO, ROOT;

-- Standard SQL
SELECT xmlelement(
  NAME join,
  xmlagg(e)
)
FROM (
  SELECT xmlelement(
    NAME a,
    xmlattributes(
      t.first_name AS first_name,
      t.last_name AS last_name
    ),
    xmlagg(xmlelement(
      NAME f,
      xmlattributes(t.title AS title)
    ))
  ) e
  FROM (
    SELECT a.first_name, a.last_name, f.title
    FROM actor a
    JOIN film_actor fa ON a.actor_id = fa.actor_id
    JOIN film f ON fa.film_id = f.film_id
    ORDER BY 1, 2, 3
  ) t
  GROUP BY
    first_name,
    last_name
) t

这和之前做的事情一样,只是在另一个XMLELEMENT() 函数调用中包装了之前的根XMLAGG() 元素。这产生了

<root>
    <a first_name="NICK" last_name="WAHLBERG">
        <f title="SMILE EARRING"/>
        <f title="WARDROBE PHANTOM"/>
    </a>
    <a first_name="PENELOPE" last_name="GUINESS">
        <f title="ACADEMY DINOSAUR"/>
        <f title="ANACONDA CONFESSIONS"/>
    </a>
</root>

让我们再试试用JSON做同样的事情:

-- SQL Server
SELECT a.first_name, a.last_name, f.title
FROM actor a
JOIN film_actor fa ON a.actor_id = fa.actor_id
JOIN film f ON fa.film_id = f.film_id
ORDER BY 1, 2, 3
FOR JSON AUTO, ROOT;

-- Standard SQL
SELECT JSON_OBJECT(KEY 'a' VALUE json_arrayagg(e))
FROM (
  SELECT JSON_OBJECT(
    KEY 'FIRST_NAME' VALUE first_name,
    KEY 'LAST_NAME' VALUE last_name,
    KEY 'F' VALUE JSON_ARRAY_AGG(JSON_OBJECT(
      KEY 'TITLE' VALUE title
      ABSENT ON NULL
    ))
    ABSENT ON NULL
  ) e
  FROM (
    SELECT a.first_name, a.last_name, f.title
    FROM actor a
    JOIN film_actor fa ON a.actor_id = fa.actor_id
    JOIN film f ON fa.film_id = f.film_id
    ORDER BY 1, 2, 3
  ) t
  GROUP BY
    first_name,
    last_name
) t

结果是:

{
    "a": [
        {
            "first_name": "NICK",
            "last_name": "WAHLBERG",
            "f": [
                {
                    "title": "SMILE EARRING"
                },
                {
                    "title": "WARDROBE PHANTOM"
                }
            ]
        },
        {
            "first_name": "PENELOPE",
            "last_name": "GUINESS",
            "f": [
                {
                    "title": "ACADEMY DINOSAUR"
                },
                {
                    "title": "ANACONDA CONFESSIONS"
                }
            ]
        }
    ]
}

FOR XML AUTO, ELEMENTS像以前一样,我们可能决定不产生属性,而是产生元素(仅在XML中):

-- SQL Server
SELECT a.first_name, a.last_name, f.title
FROM actor a
JOIN film_actor fa ON a.actor_id = fa.actor_id
JOIN film f ON fa.film_id = f.film_id
ORDER BY 1, 2, 3
FOR XML AUTO, ELEMENTS;

-- Standard SQL
SELECT xmlagg(e)
FROM (
  SELECT xmlelement(
    NAME a,
    xmlelement(
      NAME first_name,
      first_name
    ),
    xmlelement(
      NAME last_name,
      last_name
    ),
    xmlagg(xmlelement(
      NAME f,
      xmlelement(
        NAME title,
        title
      )
    ))
  ) e
  FROM (
    SELECT a.first_name, a.last_name, f.title
    FROM actor a
    JOIN film_actor fa ON a.actor_id = fa.actor_id
    JOIN film f ON fa.film_id = f.film_id
    ORDER BY 1, 2, 3
  ) t
  GROUP BY
    first_name,
    last_name
) t

除了进行一组XMLELEMENT() 的调用,而不是XMLATTRIBUTES() 的调用之外,没有什么变化。这产生了

<a>
    <first_name>NICK</first_name>
    <last_name>WAHLBERG</last_name>
    <f>
        <title>SMILE EARRING</title>
    </f>
    <f>
        <title>WARDROBE PHANTOM</title>
    </f>
</a>
<a>
    <first_name>PENELOPE</first_name>
    <last_name>GUINESS</last_name>
    <f>
        <title>ACADEMY DINOSAUR</title>
    </f>
    <f>
        <title>ANACONDA CONFESSIONS</title>
    </f>
</a>

FOR XML/JSON PATH PATH 策略是我个人最喜欢的。它被用来更明确地创建嵌套的XML或JSON路径结构,并且在将投影分组时还允许额外的嵌套级别。这一点最好通过例子来说明。注意,我现在是如何为我的列使用别名的,而且别名看起来像一个使用'/' (斜线)的XPath表达式:

-- SQL Server
SELECT 
  a.first_name AS [author/first_name], 
  a.last_name AS [author/last_name], 
  f.title
FROM actor a
JOIN film_actor fa ON a.actor_id = fa.actor_id
JOIN film f ON fa.film_id = f.film_id
ORDER BY 1, 2, 3
FOR XML PATH;

-- Standard SQL
SELECT xmlagg(xmlelement(
  NAME row,
  xmlelement(
    NAME author,
    xmlelement(
      NAME first_name,
      "author/first_name"
    ),
    xmlelement(
      NAME last_name,
      "author/last_name"
    )
  ),
  xmlelement(
    NAME title,
    title
  )
))
FROM (
  SELECT 
    a.first_name AS "author/first_name", 
    a.last_name AS "author/last_name", 
    f.title
  FROM actor a
  JOIN film_actor fa ON a.actor_id = fa.actor_id
  JOIN film f ON fa.film_id = f.film_id
  ORDER BY 1, 2, 3
) t

检查一下,根据惯例,我们现在为作者相关的列在row/author 元素下得到了一个额外的嵌套层次:

<row>
    <author>
        <first_name>NICK</first_name>
        <last_name>WAHLBERG</last_name>
    </author>
    <title>SMILE EARRING</title>
</row>
<row>
    <author>
        <first_name>NICK</first_name>
        <last_name>WAHLBERG</last_name>
    </author>
    <title>WARDROBE PHANTOM</title>
</row>
<row>
    <author>
        <first_name>PENELOPE</first_name>
        <last_name>GUINESS</last_name>
    </author>
    <title>ACADEMY DINOSAUR</title>
</row>
<row>
    <author>
        <first_name>PENELOPE</first_name>
        <last_name>GUINESS</last_name>
    </author>
    <title>ANACONDA CONFESSIONS</title>
</row>

这真的很整洁!对于这种常见的使用情况,SQL Server的语法肯定要方便得多。 让我们再次尝试用JSON做同样的事情。我们唯一改变的是我们现在使用JSON-path-ish语法,使用点('.')而不是斜杠('/'):

-- SQL Server
SELECT 
  a.first_name AS [author.first_name], 
  a.last_name AS [author.last_name], 
  f.title
FROM actor a
JOIN film_actor fa ON a.actor_id = fa.actor_id
JOIN film f ON fa.film_id = f.film_id
ORDER BY 1, 2, 3
FOR JSON PATH;

-- Standard SQL
SELECT JSON_ARRAYAGG(JSON_OBJECT(
  KEY 'author' VALUE JSON_OBJECT(
    KEY 'first_name' VALUE author.first_name,
    KEY 'last_name' VALUE author.last_name
  ),
  KEY 'TITLE' VALUE title
  ABSENT ON NULL
))
FROM (
  SELECT 
    a.first_name AS "author.first_name", 
    a.last_name AS "author.last_name", 
    f.title
  FROM actor a
  JOIN film_actor fa ON a.actor_id = fa.actor_id
  JOIN film f ON fa.film_id = f.film_id
  ORDER BY 1, 2, 3
) t

结果是(同样是嵌套对象):

[
    {
        "author": {
            "first_name": "NICK",
            "last_name": "WAHLBERG"
        },
        "title": "SMILE EARRING"
    },
    {
        "author": {
            "first_name": "NICK",
            "last_name": "WAHLBERG"
        },
        "title": "WARDROBE PHANTOM"
    },
    {
        "author": {
            "first_name": "PENELOPE",
            "last_name": "GUINESS"
        },
        "title": "ACADEMY DINOSAUR"
    },
    {
        "author": {
            "first_name": "PENELOPE",
            "last_name": "GUINESS"
        },
        "title": "ANACONDA CONFESSIONS"
    }
]

对于更复杂的嵌套,包括集合的嵌套,在SQL Server中需要一个相关的子查询,同样使用FOR XMLFOR JSON 语法。

结论

XML和JSON是数据库外部和内部的流行文档格式。在大多数情况下,SQL Server有一些最方便的语法,而标准SQL支持更基本的,因此也更强大的结构。在标准SQL中,几乎任何种类的XML或JSON投影都是可能的,而且通过XMLTABLE()JSON_TABLE() ,这些文档也可以被转换回SQL表。在许多应用中,使用这些XML或JSON功能会导致更少的模板代码,因为许多应用不需要数据库和一些客户端之间的中间件,只需要在不同格式之间转换数据。 大多数ORM由于各种原因没有暴露这个功能,主要的原因是魔鬼在细节中。虽然XML和JSON都有很好的标准化,但实现方式差别很大。

  • SQL/XML标准主要由DB2、Oracle和PostgreSQL实现。许多方言都提供了一些XML功能,但没有像标准和前三者那样令人印象深刻。SQL Server有FOR XML ,它对标准的XML序列化非常强大,但对于边缘情况可能有点难以使用
  • SQL/JSON标准添加得很晚,而且DB2和Oracle又在很大程度上实现了它,但MariaDB和MySQL也在不断地实现它。PostgreSQL(以及随之而来的兼容方言,如CockroachDB)有他们自己的专有函数和API,这与标准不兼容。再有,SQL Server有FOR JSON ,对于标准的序列化来说效果很好,但对于边缘情况来说就有点不理想了

由于存在许多细微的差异,这些技术在客户中的采用率很低。jOOQ多年来一直在平整这些细微的差异,而没有隐藏核心功能。SQL/XML和SQL/JSON是jOOQ 3.14(将于2020年第二季度发布)的完美用例,它现在允许在jOOQ专业版和企业版中使用标准的SQL/XMLSQL/JSON语法,以及SQL ServerFOR XMLFOR JSON 语法。在jOOQ 3.14发布之前,你已经可以在我们的网站上使用当前功能: https://www.jooq.org/translate

猜你喜欢

转载自juejin.im/post/7126378395461484551