让我用简化的例子说明这个问题。假设我正在使用带有PostgreSQL关系数据库的python构建项目。在我的数据库中,我有两个表“parent”和“child”,它们是通过表“parent_child”相关的N到M.我希望能够以安全的方式检索有关特定父级拥有的特定子级的一些数据,以下查询允许我这样做(X,Y和Z是用户提供的文字):
SELECT child.age FROM parent, parent_child, child
WHERE child.id = parent_child.child_id AND parent_child.id = X
AND parent_child.parent_id = parent.id AND parent.id = Y
AND parent.password = Z;
假设用户出现并为X,Y或Z键入错误的值,查询将返回一个空集,可以检测到该消息并将消息传递给用户,表明存在错误。问题当然是我无法确定哪个值导致问题,因此无法向用户提供有关他们错误输入的具体信息?
最简单的解决方案是将查询分解为几个部分。首先,验证parent.id是否存在。
SELECT parent.id FROM parent WHERE parent.id = Y;
其次,检查密码是否正确。
SELECT parent.id FROM parent WHERE parent.id = Y and parent.password = Z;
第三,检查孩子是否存在。
SELECT child.id FROM child WHERE child.id = X;
第四,检查孩子是否由父母拥有并返回我们需要的信息。
SELECT child.age FROM child, parent_child WHERE parent_child.child_id = child.id AND parent_child.parent_id = Y AND parent_child.child_id = X;
这四个查询将允许我们检查有关用户提供的信息的具体信息,并在发生时报告特定问题。显然,在单个查询中,四个查询中存在大量额外开销,我发现四个查询的可读性低于单个查询。那么无论如何都要拥有两全其美的东西?单个查询和详细的错误消息?
答案 0 :(得分:3)
SELECT p.id, p2.z AS pw, pc.parent_id, CASE p2.z WHEN p.pw THEN c.age END AS age
FROM (VALUES (1)) AS p1(y)
LEFT JOIN
parent p
ON p.id = p1.y
LEFT JOIN
(VALUES ('pw1')) AS p2(z)
ON p2.z = p.pw
CROSS JOIN
(VALUES(1)) AS p3(x)
LEFT JOIN
child c
ON c.id = p3.x
LEFT JOIN
parent_child pc
ON pc.parent_id = p.id
AND pc.child_id = c.id
在适当的列中 NULLs
意味着适当的条件失败。
答案 1 :(得分:1)
嗯,这里的问题在于查询实际上并没有错误 - 每次都会为您提供正确的条件信息。因此,如果不单独检查每个查询,确实无法知道。
您可以检查一下是否没有行,然后运行其他查询以找出原因,这样可以减少开销。
答案 2 :(得分:0)
这四个查询将允许我们检查有关用户提供的信息的具体信息,并在发生时报告特定问题。
是的,这是标准程序(并且它存在是有原因的。假设您正在更新行:您可能已经用尽了各种服务器资源,例如事务日志,只是发现它失败了,并且将整个事情推回去。完全可以避免。在尝试下一个级别之前,请务必检查每个级别。在执行完全验证之前,切勿锁定或更新任何内容。除非您确定它会成功,否则不要尝试任何事情。在这种情况下,你没有更新,但标准允许您在最早的时刻以通常的方式隔离错误,并避免浪费资源(由于早期的失败而在以后的级别)。
显然,四个查询与单个查询相比会产生大量额外开销
我不明白你的算术。假设PK的每个查询对表的成本是50个资源单位,如果它不在数据缓存中,如果是,则为2个单位。假设PostgreSQL有一个数据缓存和一个多线程引擎,你的代码段是一个连续的序列(存储过程与否):
等于156个单位
更重要的是,在出错的情况下,成本(取决于错误的位置)是50或52或102个单位
而独立的第四个陈述费用为150单位
我发现四个查询的可读性低于单个查询。
如果您需要提高可读性,请在中间放置一些空格和注释。 (你的代码很难让其他人阅读;我会格式化它。)
单个查询和详细的错误消息?
嗯,你得到了详细的错误,不多也不少;您要求的是将错误隔离到代码中的特定点(或用户请求)。如果您正在编写存储过程以供通用,并返回错误代码,则需要我已识别的序列。
任何其他方法(我确信存在复杂和狡猾的方法)将是(a)更多的开销和(b)将不必要的复杂性引入简单的行人要求,因此难以维护。