文章摘要
文章探讨了在处理复杂关系数据时,如何将平面关系数据库中的数据转换为适合不同任务需求的层次结构。由于不同页面或任务需要不同的数据层次结构,直接存储层次结构并不实际,因此需要通过手动转换来适应不同的视觉需求,这一过程被称为“对象-关系不匹配”。核心问题在于,复杂关系数据需要根据具体任务构建不同的视觉层次结构。
文章总结
SQL所需的结构
在互联网电影数据库(IMDB)的页面中,数据以层次结构呈现。例如,电影页面包含导演、类型列表、演员列表,而每个演员又包含他们在电影中扮演的角色列表。这种复杂的层次结构无法简单地放入一个扁平的关系表中。此外,不同页面的层次结构顺序也不同,例如一个页面是电影->演员,另一个页面是演员->电影。因此,数据库需要能够双向遍历关系。
我们通常将数据存储在关系数据库的扁平表中,然后在需要呈现用户界面时,将扁平数据转换为所需的层次结构。手动进行这种转换既繁琐又容易出错,这被称为“对象-关系不匹配”。根本问题在于,将复杂关系适应人类视觉通常需要构建某种视觉层次结构,而不同的任务需要不同的层次结构。
无论使用哪种数据库和编程语言,都需要处理这个问题。但在SQL中尤其痛苦,因为SQL并不是为生成层次数据而设计的。
以IMDB公共数据集为例,尝试重现电影页面的源数据(或至少是其中的一部分)。我们希望看到如下输出:
json
{
"title": "Baby Driver",
"director": ["Edgar Wright"],
"writer": ["Edgar Wright"],
"genres": ["Action", "Crime", "Drama"],
"actors": [
{"name": "Ansel Elgort", "characters": ["Baby"]},
{"name": "Jon Bernthal", "characters": ["Griff"]},
{"name": "Jon Hamm", "characters": ["Buddy"]},
{"name": "Eiza González", "characters": ["Darling"]},
{"name": "Micah Howard", "characters": ["Barista"]},
{"name": "Lily James", "characters": ["Debora"]},
{"name": "Morgan Brown", "characters": ["Street Preacher"]},
{"name": "Kevin Spacey", "characters": ["Doc"]},
{"name": "Morse Diggs", "characters": ["Morse Diggs"]},
{"name": "CJ Jones", "characters": ["Joseph"]}
]
}
通过SQL查询,我们可以逐步获取电影的标题、导演、编剧、演员及其角色等信息。然而,如果电影有多个导演或编剧,查询结果会变得复杂,甚至可能无法正确返回电影名称。因此,我们不得不发出多个查询来获取所需的数据。
尽管通过连接操作可以检索到所有数据,但需要在后端Web服务器中重新组装这些扁平输出为页面结构。此外,返回的数据中有一半是nconst列,这些列在输出中并不需要,只是为了作为键来重复数据库中的连接操作。
为了简化这一过程,我们发明了对象关系映射(ORM)来自动化这些操作。然而,ORM也存在一些问题:
- 大多数ORM最终会为所需的输出发送多个查询。
- 许多ORM通过延迟加载数据在不同的事务中,可能会破坏一致性。
- 使用ORM会限制你只能使用一种特定的编程语言。
如今,SQL实际上可以从查询中生成结构化数据。尽管很多人对此表示不满,但结构必须在某个地方发生,因为输出页面就是这样的,而在数据库外进行处理效果并不理想。
在构建用户界面时,查询语言的主要用途是将关系数据转换为结构化数据,以便客户端渲染。因此,如果查询语言能够生成结构化数据,那将是非常有益的。
例如,可以使用如下SQL查询来生成所需的层次结构数据:
sql
select jsonb_agg(result) from (
select
primaryTitle as title,
genres,
(
select jsonb_agg(actor) from (
select
(select primaryName from person where person.nconst = principal.nconst) as name,
(
select jsonb_agg(character)
from principal_character
where principal_character.tconst = principal.tconst
and principal_character.nconst = principal.nconst
) as characters
from principal
where principal.tconst = title.tconst
and category = 'actor'
order by ordering
limit 10
) as actor
) as actors,
(
select jsonb_agg(primaryName)
from principal, person
where principal.tconst = title.tconst
and person.nconst = principal.nconst
and category = 'director'
) as director,
(
select jsonb_agg(primaryName)
from principal, person
where principal.tconst = title.tconst
and person.nconst = principal.nconst
and category = 'writer'
) as writer
from title
where tconst = $1
) as result;
虽然这个查询并不完美,但它可以在一个查询中获取整个页面所需的所有数据,只需一次网络往返。无论你是直接使用这些功能,还是作为ORM的输出,这都是关系数据库主要用例的一个显著改进。
SQL并不是关系代数,关系代数也不是数学,它们都不是刻在石碑上的永恒真理。我们制造工具是为了满足我们的需求,而自20世纪70年代以来,我们的需求已经发生了巨大的变化。也许我们的工具可以随着新的需求而进化,我们也可以随之进化。
评论总结
评论内容总结:
现代SQL支持JSON,无需在后端重新组装数据
- 作者tucnak指出,现代SQL(如Postgres)支持JSON,可以直接生成非规范化的视图,避免在后端重新组装数据。
- 引用:“This is NOT the case with modern SQL, as it supports JSON.”
- 引用:“Time and time again, software engineers reinvent the data-access wheel out of ignorance to database capabilities.”
开发者倾向于创建新解决方案而非改进SQL
- 作者sakesun认为,当SQL变得复杂时,开发者更倾向于创建新解决方案,而不是改进SQL本身。
- 引用:“When something in SQL becomes cumbersome or difficult to work with, people often tend to create entirely new solutions rather than improving the existing ones.”
JSON在SQL中的使用越来越普遍
- 作者mg表示,SQLite的JSON操作符使其成为一个优秀的JSON文档存储工具,并希望MariaDB也能支持类似功能。
- 引用:“I am becoming more and more comfortable with storing everything in JSON.”
- 引用:“SQLite makes a really nice JSON document store.”
结构化与非结构化数据的定义存在争议
- 作者greggyb指出,文章中对“结构化”和“非结构化”数据的定义与常规理解不同,JSON应被视为“半结构化”数据。
- 引用:“Flexible serialization formats such as JSON or XML are ‘semi-structured’.”
嵌套数据存储的局限性
- 作者roenxi认为,存储嵌套数据的数据库系统在竞争中往往不如关系型数据库,因为它们不适合长期存储和优化。
- 引用:“The real counterargument here is that the DB systems that stored nested data mostly lose in competition with relational systems over time.”
GraphQL与SQL的结合
- 作者hbrundage提出,GraphQL与SQL的结合可以更好地处理数据关系,并分享了他们开发的混合查询语言。
- 引用:“We built a GraphQL / SQL hybrid expression language that does just this.”
SQL的嵌套关系表达不足
- 作者andyferris认为,SQL在处理嵌套关系时表现不佳,导致对象关系映射问题。
- 引用:“SQL poorly expresses nested relations and this has been the root cause of object-relation impedence.”
JSON的类型限制
- 作者ivanb指出,JSON的类型系统有限,如小数和UUID的处理不够高效。
- 引用:“One limitation of JSON is its limited set of types.”
SQL查询结果的层次化结构
- 作者phiresky认为,SQL查询结果应自然地返回层次化结构,而不是将所有列合并。
- 引用:“Mangling the columns together and removing groupings that naturally appear is just so unnecessary.”
ORM的存在与开发者对SQL的理解不足
- 作者mcdonje认为,ORM的存在部分原因是开发者对表格数据的理解不足。
- 引用:“I’ve often wondered why ORMs exist, and it turns out a bunch of devs don’t understand tabular data.”
总结:评论中主要讨论了现代SQL对JSON的支持、开发者对SQL的改进态度、JSON在SQL中的使用、结构化数据的定义、嵌套数据存储的局限性、GraphQL与SQL的结合、SQL的嵌套关系表达不足、JSON的类型限制、SQL查询结果的层次化结构以及ORM的存在原因。