【问题标题】:Is it possible to create detailed error messages from complex database queries?是否可以从复杂的数据库查询中创建详细的错误消息?
【发布时间】:2010-10-29 20:29:30
【问题描述】:

让我用一个简化的例子来说明这个问题。假设我正在使用带有 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;

这四个查询将允许我们检查有关用户提供的信息的具体内容,并在出现具体问题时报告具体问题。显然,四个查询与单个查询相比有很多额外的开销,我发现四个查询的可读性不如单个查询。那么有没有两全其美的呢?单个查询和详细的错误信息?

【问题讨论】:

  • 我知道这只是一个示例,但我真的希望您实际上并没有要求用户直接提供 ID。还有更多用户友好的选择。
  • @Matt:虽然这只是一个例子,但在构建它时,我想到了一个 REST 服务器。 REST 通常在 URI 中有 ID。虽然最终用户通常不会更改 REST URI 的 ID,但使用 API 的程序员可能会导致一些错误,并且有关问题的详细报告可能很有用。

标签: sql user-interface error-handling relational-database


【解决方案1】:
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 在适当的列中意味着适当的条件失败。

【讨论】:

  • 这种方法确实增加了对用户提供的数据可能导致的问题的解决,但这种增加的细节不如问题中发布的四个查询那么高。因此,虽然这是一个很好的答案,但我仍然很好奇是否有更好的答案。
  • @Nixuz:查看帖子更新。请注意,参数的值在VALUES 子句中。
  • 又是一个很好的答案,但做出了一些取舍。这个答案很好,因为它允许以与四个查询解决方案几乎相同的详细程度报告错误。但是,这个解决方案比以前的解决方案复杂得多,这让我怀疑性能是否会受到影响。同样,复杂性降低了可读性。最后,这个解决方案似乎不如其他解决方案安全,因为即使使用了错误的密码,仍然会报告孩子的年龄。尽管如此,我很欣赏这个答案所做的工作,并将在我的项目中使用它。谢谢。
  • @Nixuz:性能将是一样的,因为在每个步骤中都会进行相同的检查,唯一的区别是返回 NULL 值而不是过滤掉记录。至于可读性——嗯,是你想要一个查询,实际上很简单。
【解决方案2】:

好吧,这里的问题在于查询实际上并没有出错——它每次都会为您提供符合条件的正确信息。因此,如果不单独检查每个查询,确实没有办法知道。

您也许可以检查一下是否没有行,然后运行其他查询以找出原因,这样可以减少您的开销。

【讨论】:

    【解决方案3】:

    这四个查询将允许我们检查有关用户提供的信息的具体事项,并在出现具体问题时报告具体问题。

    是的,这是标准程序(它的存在是有原因的。假设您正在更新行:您会用尽各种服务器资源,例如事务日志,却发现它失败了,并且回滚整个事情。完全可以避免。在尝试下一个级别之前,请务必检查每个级别。在执行完整验证之前,切勿锁定或更新任何内容。除非您确信它会成功,否则切勿尝试任何事情。在这种情况下,您没有更新,但该标准允许您以通常的方式在最早的时刻隔离错误,并避免浪费资源(由于较早的故障而在以后的级别)。

    显然,四个查询与单个查询相比有很多额外的开销

    我不懂你的算术。假设通过 PK 对表的每次查询需要 50 个资源单位,如果它不在数据缓存中,则需要 2 个单位。假设 PostgreSQL 有一个数据缓存和一个多线程引擎,并且你的代码段是一个连续的序列(存储过程与否):

    • 第一条语句 = 50
    • 第二条语句(因为页面在缓存中)= 2
    • 第三条语句 = 50
    • 第四条语句(因为父母和孩子都在缓存中)= 2 + 2 + 50
    • 等于 156 个单位

    • 更重要的是,在出现错误的情况下,成本(取决于错误所在的位置)为 50 或 52 或 102 个单位

    • 而独立的第四条语句花费 150 个单位

    我发现四个查询的可读性不如一个。

    如果您需要提高可读性,请在中间添加一些空格和注释。 (无论如何,您的代码对其他人来说很难阅读;我会格式化它。)

    单个查询和详细的错误消息?

    嗯,你得到了详细的错误,不多不少;您要求的是将错误隔离到代码(或用户请求)中的特定点。如果您正在编写一个通用的存储过程,并返回一个错误代码,则需要我确定的序列。

    任何其他方法(我确信存在复杂和迂回的方法)将 (a) 更多开销,并且 (b) 将不必要的复杂性引入简单的行人需求,因此难以维护。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2012-04-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2016-08-14
      • 1970-01-01
      相关资源
      最近更新 更多