【问题标题】:SQL join following foreign key: statically check that LHS is key-preservedSQL join following foreign key:静态检查 LHS 是否保留键
【发布时间】:2014-09-13 03:45:33
【问题描述】:

通常您会在两个表的外键之后连接它们,以便始终可以找到 RHS 表中的行。添加连接不会影响受查询影响的行数。例如

create table a (x int not null primary key)
create table b (x int not null primary key, y int not null)
alter table a add foreign key (x) references b (x)

现在,假设你在这两个表中设置了一些数据,你可以从a中获取一定数量的行:

select x from a

在外键之后向 b 添加连接不会改变这一点:

select a.x from a join b on a.x = b.x

但是,一般的连接并非如此,它可能会过滤掉一些行或(通过笛卡尔积)添加更多:

select a.x from a join b on a.x = b.x and b.y != 42 -- probably gives fewer rows

select a.x from a join b on a.x != b.y -- probably gives more rows

在阅读 SQL 代码时,没有明显的方法来判断 join 是否是保留键的类型,它可能会添加额外的列但不会改变返回的行数,或者是否有其他影响。随着时间的推移,我制定了一个我主要坚持的编码约定:

  • 如果是保留键的连接,请使用join
  • 如果要过滤行,请将过滤条件放在where 子句中
  • 如果想要更多行,有时cross join 笛卡尔积是最清晰的方法

这些通常只是样式问题,因为您通常可以将谓词放入 join 子句或 where 子句中,例如。

我的问题

有没有办法在编译查询时让数据库服务器静态检查这些保留键的连接?我知道查询优化器已经知道外键上的连接总是会在外键指向的表中准确找到一行。但为了人类读者的利益,我想在我的 SQL 代码中标记它。例如,假设新语法fkjoin 用于外键后的连接。那么下面的SQL片段会不会报错:

a fkjoin b on a.x = b.x -- OK

a fkjoin b on a.x = b.x and b.y = 42 -- "Error, join can fail due to extra predicate"

a fkjoin b on a.x = b.y -- "Error, no foreign key from a.x to b.y"

这对我在编写 SQL 时以及在稍后返回阅读它时是一个有用的检查。我理解并接受更改数据库中的外键会更改在此方案下合法的 SQL - 对我来说,这是一个理想的结果,因为如果必要的 FK 不再存在,那么查询的键保留语义是不存在的保证时间更长,我想了解一下。

可能会有一些外部 SQL 静态检查器工具来完成这项工作,并且可以使用特殊的注释语法而不是新的关键字。检查器工具需要访问数据库模式才能查看存在哪些外键,但它不需要实际执行查询。

有什么东西可以满足我的需求吗?我正在使用 MSSQL 2008 R2。 (微软 SQL Server 为迂腐)

【问题讨论】:

  • 如果我正确理解了这个问题,一致的命名约定可能有助于缓解部分或全部这些问题。如果CustomersOrdersCustomers.CustomerID = Orders.CustomerID 上加入,你可以一眼看出条件是有道理的,而加入Customers.CategoryID = Orders.ProductID 显然看起来不对。
  • 连接“成功”或“失败”到底是什么意思?因为你可以加入任何两个表。
  • @philipxy,抱歉我的问题不够清楚,我已经编辑了一下。
  • @Andriy M,是的,命名约定可以提供帮助。但是,除非你是一个不会出错的编程机器人,否则要跟踪事情并不容易——我犯了太多错误,不能仅靠我自己的细心。例如,如果您有来自customers 的查询,那么添加join orders on customers.customerid = orders.customerid不是通过这种措施进行的安全更改 - 因为它现在将排除没有订单的客户,并为有两个订单的客户。所以我不能在不影响查询语义的情况下添加或删除这样的连接。 (反之亦然。)

标签: sql-server join sql-server-2008-r2 foreign-keys static-code-analysis


【解决方案1】:

我知道您有兴趣指出特定列上的特定连接是否在 FK 上,或者是一个限制,或者可能是其他情况,或者不是上述情况。 (并且不清楚您所说的连接的“成功”或“失败”或它的相关性是什么意思。)而关注该信息,如下所述,就是错过关注更重要和基本的事情。

基表具有“含义”或“谓词(表达式)”,它是 DBA 给出的填空(命名)语句。语句的空白名称是表的列。填空以提出关于世界的真实命题的行放在表格中。填补空白以提出关于世界的虚假命题的行被排除在外。即一个表包含满足其语句的行。在不知道它的语句、观察世界并将适当的行放入表中的情况下,您无法将基表设置为某个值。你无法从基表中了解世界,除非知道它的陈述并将当前行命题视为真而将缺席行命题视为假。即你需要它的语句来使用数据库。

请注意,表声明的典型语法看起来像是其语句的简写:

-- employee [eid] is named [name] and lives at [address] in ...
EMPLOYEE(eid,name,address,...)

您可以通过在其他语句之间/周围放置逻辑运算符 AND、OR、AND NOT、EXISTS name、AND condition 等来生成更大的语句。如果您通过转换将语句转换为关系/SQL 表达式

  • 一个表对其名称的声明
  • JOIN
  • UNION
  • 而不是EXCEPT/MINUS
  • 存在 C,... [...]SELECT all columns but C,... FROM @987654329 @
  • AND 条件ON/WHERE condition
  • 暗示SUBSETOF
  • IFF 到=

然后你得到一个关系表达式,计算使语句为真的行。 (UNIONEXCEPT/MINUS 的参数需要相同的列。)因此,正如每个表都包含满足其语句的行一样,查询表达式也包含满足其语句的行。您无法从查询结果中了解世界,除非知道它的陈述并将其当前行命题为真,而将缺席行命题为假。即你需要它的语句来组成或解释一个查询。 (请注意,无论约束条件如何,这都是正确的。)

这是关系模型的基础:表表达式计算满足相应语句的行。(SQL有所不同,从字面上看是不合逻辑的。)

例如:如果表 T 包含使语句 T(...,T.Ci,...) 为 true 的行,而表 U 包含使语句 U(...,U. Cj,...) 为真,则表 T JOIN U 包含使语句 T(...,T.Ci,...) AND U(...,U.Cj,...) 为真的行。这就是JOIN 的语义,这对于使用数据库很重要。你总是可以连接,连接总是有意义的,它总是它的操作数意义的与。任何表是否碰巧对其他表有 FK 对推理更新或查询并不是特别有帮助。 (当您犯错误时,DBMS 会使用约束。)

约束表达式只对应于一个关于世界的命题,也就是永远为真的陈述,同时也对应于一个关于基表的命题。比如CUNIQUE NOT NULLU中,下面三个表达式是等价的:

  • FOREIGN KEY T (C) REFERENCES U (C)
  •     EXISTS columns other than C T(...,C,...)
    IMPLIES EXISTS columns other than C U(...,C,...)
  • (SELECT C FROM T) SUBSETOF (SELECT C FROM U)

这确实意味着SELECT C FROM T JOIN U ON T.C = U.C = SELECT C FROM U,即FK 上的连接返回相同数量的行。但那又怎样?连接的含义仍然是其参数的相同功能。

特定列集上的特定连接是否涉及外键与理解查询的含义无关。

【讨论】:

  • 注意关系运算符和逻辑运算符的对应关系是对的。用这些术语来说,我想要一种方法来查看连接条件是否是重言式。如果 a.x 是 b.x 的外键,那么在查询中添加 'join b on a.x = b.x' 对其返回的行集没有影响,就像添加了 'where 1+1 = 2' 一样。它们都是永远真实的条件。编程时,我想断言我添加的条件必须始终为真,并进行静态验证。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2021-05-14
  • 2021-01-18
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2021-07-23
  • 1970-01-01
相关资源
最近更新 更多