是否有任何经验法则可以根据人类可读的描述构造 SQL 查询?
Posted
技术标签:
【中文标题】是否有任何经验法则可以根据人类可读的描述构造 SQL 查询?【英文标题】:Is there any rule of thumb to construct SQL query from a human-readable description? 【发布时间】:2016-03-01 01:23:54 【问题描述】:每当我们面前有任何查询描述时,我们都会尝试应用启发式和头脑风暴来构建查询。
是否有任何系统的分步或数学方法来根据给定的人类可读描述构造 SQL 查询?
例如,如何确定,SQL 查询是否需要连接而不是子查询,是否需要 group by,是否需要 IN 子句等......
例如,学习数字电子学的人都会知道卡诺图或 Quin McClausky 方法等方法。这些是简化数字逻辑的一些系统方法。
是否有类似这样的方法来手动分析sql查询以避免每次都头脑风暴?
【问题讨论】:
该描述的格式是什么?人类可读的文本? 您可以通过不同的方式做一些事情。所以子查询或连接之间的选择可以做同样的事情,但取决于数据库或数据可以有不同的性能。 问题是英文有歧义,SQL一定不能。查询的任何英文描述总是需要开发人员返回给用户进行澄清 我刚刚编辑了一个链接到我的系统答案中,该系统允许直接以“人类可读”的形式表达查询。根据我的回答,最好考虑查询的“含义”而不是“描述”。 @Nick.McDermaid 虽然自然语言尚不清楚,但我们最终必须将精确的正式表达映射到原始的非正式表达,因此即使是我们的正式表达的含义也必须存在一些歧义。 (虽然我们可以确定形式陈述之间的联系。)但它有助于直接用受限的自然语言进行推理,即谓词逻辑。 @Nick.McDermaid “数据模型”是什么意思?数据库架构?假设一个“谓词”将一行和一个情况映射到一个命题。作为一个“问题”,查询可以询问“从 查询谓词 中提出真正命题的行是什么?”两个不同的数据库,即具有不同基表谓词和因此不同行的数据库,可以通过针对我们的查询谓词的不同查询表达式给出相同的表作为答案。或者我们可以将查询视为询问“这些行对情况的说明是什么?”。那么结果可以是具有相同行命题连接的不同表格。 【参考方案1】:有没有系统的一步一步或数学的方法来构建 来自给定人类可读描述的 SQL 查询?
是的,有。
事实证明,自然语言表达式和逻辑表达式以及关系代数表达式和 SQL 表达式(后两者的混合体)以一种相当直接的方式对应。 (以下是没有重复的行和没有空值。)
每个表(基础或查询结果)都有一个关联的谓词--一种由列名参数化的自然语言填充-(命名-)空白语句模板。
[liker] likes [liked]
一个表格包含每一行,使用该行的列值填充(命名的)空白,形成一个真实的陈述,也就是 proposition。这是一个包含该谓词及其行的命题的表:
liker | liked
--------------
Bob | Dex /* Bob likes Dex */
Bob | Alice /* Bob likes Alice */
Alice | Carol /* Alice likes Carol */
用表中一行的值填充谓词的每个命题都是真的。并且用表中行 not 中的值填充谓词的每个命题都是错误的。这张表是这样写的:
/*
Alice likes Carol
AND NOT Alice likes Alice
AND NOT Alice likes Bob
AND NOT Alice likes Dex
AND NOT Alice likes Ed
...
AND Bob likes Alice
AND Bob likes Dex
AND NOT Bob likes Bob
AND NOT Bob likes Carol
AND NOT Bob likes Ed
...
AND NOT Carol likes Alice
...
AND NOT Dex likes Alice
...
AND NOT Ed likes Alice
...
*/
DBA 为每个基表提供谓词。表声明的 SQL 语法很像给定谓词的自然语言版本的传统逻辑简写。这是一个保存我们值的基表的声明:
/* (person, liked) rows where [liker] likes [liked] */
/* (person, liked) rows where Likes(liker, liked) */
CREATE TABLE Likes (
liker ...,
liked ...
);
一个 SQL 查询(子)表达式将参数表值转换为一个新表值,该值包含从一个新谓词构成一个真实语句的行。根据(子)表达式的关系/表运算符,新表谓词可以用参数表谓词表示。查询是一个 SQL 表达式,其谓词是我们想要的行表的谓词。
当我们给一个表&(可能是隐式的)别名A
进行连接时,运算符作用于一个值& 谓词,就像表的一样,但列从C,...
重命名为A.C,...
。那么
R , S
& R CROSS JOIN S
是 the predicate of R AND the predicate of S
所在的行
R INNER JOIN S ON condition
是 the predicate of R AND the predicate of S AND condition
所在的行
R LEFT JOIN S ON condition
是行,其中(对于 S-only 列 S1,...)
the predicate of R AND the predicate of S AND condition
OR
the predicate of R
AND NOT FOR SOME values for S1,... [the predicate of S AND condition]
AND S1 IS NULL AND ...
R WHERE condition
是 the predicate of R AND condition
所在的行
SELECT DISTINCT A.C AS D,... FROM R
(可能带有隐式 A.
和/或隐式 AS D
)是行,其中
FOR SOME values for A.*,... [A.C=D AND ... AND the predicate of R]
(这可能不太紧凑,但看起来更像 SQL。)
如果没有删除的列,the predicate of R
将 A.C,...
替换为 D,...
如果有被删除的列,FOR SOME values for
被删除的列[
the predicate of R
用A.C,...
替换为D,...
]
(X,...) IN (R)
表示
the predicate of R
与列 C,...
替换为 X,...
(X,...) IN R
示例:(person, like) 行的自然语言,其中 [person] 是 Bob,Bob 喜欢喜欢 [喜欢] 但不喜欢 Ed 的人:
/* (person, liked) rows where
FOR SOME value for x,
[person] likes [x]
and [x] likes [liked]
and [person] = 'Bob'
and not [x] likes 'Ed'
*/
使用速记谓词重写:
/* (person, liked) rows where
FOR SOME value for x,
Likes(person, x)
AND Likes(x, liked)
AND person = 'Bob'
AND NOT Likes(x, 'Ed')
*/
仅使用基表和别名表的速记谓词重写:
/* (person, liked) rows where
FOR SOME values for l1.*, l2.*,
person = l1.liker AND liked = l2.liked
AND Likes(l1.liker, l1.liked)
AND Likes(l2.liker, l2.liked)
AND l1.liked = l2.liker
AND person = 'Bob'
AND NOT (l1.liked, 'Ed') IN Likes
*/
用 SQL 重写:
SELECT DISTINCT l1.liker AS person, l2.liked AS liked
/* (l1.liker, l1.liked, l2.liker, l2.liked) rows where
Likes(l1.liker, l1.liked)
AND Likes(l2.liker, l2.liked)
AND l1.liked = l2.liker
AND l1.liker = 'Bob'
AND NOT (l1.liked, 'Ed') IN Likes
*/
FROM Likes l1
INNER JOIN Likes l2
ON l1.liked = l2.liker
WHERE l1.liker = 'Bob'
AND NOT (l1.liked, 'Ed') IN (SELECT * FROM Likes)
同样,
R UNION CORRESPONDING S
是 the predicate of R OR the predicate of R
所在的行
R UNION S
是 the predicate of R OR the predicate we get by replacing the columns of S by the columns of R in the predicate of R
所在的行
VALUES (X,...), ...
的列 C,...
是 C = X AND ... OR ...
的行
例子:
/* (person) rows where
FOR SOME value for liked, Likes(person, liked)
OR person = 'Bob'
*/
SELECT liker AS person
FROM Likes
UNION
VALUES ('Bob')
因此,如果我们根据给定的基表自然语言语句模板来表达我们想要的行,这些行可以判断为真或假(返回或不返回),那么我们可以转换为 SQL 查询,这些查询是逻辑速记和运算符的嵌套和/或表名和运算符。然后 DBMS 可以完全转换为表来计算使我们的谓词为真的行。
请参阅How to get matching data from another SQL table for two different columns: Inner Join and/or Union? 将其重新应用于 SQL。 (另一个自加入。) 有关自然语言短语的更多信息,请参阅Relational algebra for banking scenario。 (在关系代数环境中。) 请参阅Null in Relational Algebra 以了解关系查询的另一个介绍。
【讨论】:
虽然我不能完全同意你的观点,但这是一个非常有趣的观点。【参考方案2】:这是我在非分组查询中所做的:
我在FROM
子句中放入了我希望表中每行接收零个或一个输出行的表。通常,您想要“具有特定属性的所有客户”之类的内容。然后,客户表进入FROM
子句。
使用连接来添加列和过滤行。联接不应重复行。连接应该找到零或一行,永远不会更多。这使它非常直观,因为您可以说“连接添加列并过滤掉一些行”。
如果连接可以替换子查询,则应避免子查询。联接看起来更好、更通用并且通常更有效(由于常见的查询优化器弱点)。
WHERE
的使用方法和投影很简单。
【讨论】:
那么分组查询呢? 对于分组问题,我将首先构建非分组问题。然后,添加分组相当容易,因为分组列是您希望每个唯一组合有一行的那些。我认为困难的部分是通过 from 和 join 组合表格。其余的都很直观。以上是关于是否有任何经验法则可以根据人类可读的描述构造 SQL 查询?的主要内容,如果未能解决你的问题,请参考以下文章