【问题标题】:Is this the correct way to structure this SQL query?这是构造此 SQL 查询的正确方法吗?
【发布时间】:2013-10-30 05:09:47
【问题描述】:

我目前正在一个网站上使用 PHP 和 SQL。有一个包含用户(帐户)、组织和关系表的数据库,用于将组织链接到帐户(多对多关系)

当我从数据库中删除一个帐户时,如果要删除的帐户是与某个组织相关联的唯一帐户,则 SQL 查询还应该删除该帐户所链接到的所有组织。

我对 SQL 比较陌生,并且已经构建了一个查询,该查询应该在上述条件下从组织表中删除一个组织。 这是我的查询:

'DELETE FROM TBL_ORGANISATIONS WHERE id = (
    SELECT org_id FROM TBL_AFFILIATIONS WHERE account_email = :email AND (
        SELECT COUNT(*) FROM TBL_AFFILIATIONS WHERE org_id IN (
            SELECT org_id FROM TBL_AFFILIATIONS WHERE account_email = :email
        )
    ) = 1
)'

这是构建此查询的正确方法,还是有更清晰/更有效的方法?正如我之前提到的,我对 SQL 还很陌生,还没有掌握所有 SQL 关键字的概念,这些关键字可用于构建诸如此类的查询(JOIN 等)

提前感谢大家提供的任何建议。

顺便说一句: 我正在使用 PDO,因此对于那些想知道的人来说:电子邮件。

【问题讨论】:

    标签: php mysql sql


    【解决方案1】:

    如果你有foreign key constraints,就像我认为你应该的那样,那么这个语句将失败,因为从属记录仍然指向要删除的组织记录。

    您可以使用ON DELETE CASCADE 删除该组织,就像 Mihai 在他(现已删除)的评论中建议的那样,但要这样做,您仍然需要检查是否只有一个附属机构与该组织相关联。

    在这种情况下,我宁愿先查询组织的 ID。无论如何,您可能会手头有这些,因为您会知道要删除的帐户的详细信息。然后首先删除帐户,然后根据需要删除组织,使用如下所示的语句:

    DELETE FROM TBL_ORGANISATIONS o
    WHERE 
      o.id = :ThatIdYouQueriedBefore AND
      NOT EXISTS (SELECT 'x' FROM TBL_AFFILIATIONS a.org_id = o.id);
    

    就我个人而言,我不是级联删除的忠实拥护者,因为一个看似很小的错误可能会花费您大量数据,但即使您确实使用它们,我也不认为这会使这种特殊情况变得更容易。

    【讨论】:

    • 感谢您的回复。到目前为止,我还没有使用外键,因为我一直使用 MyISAM 作为表引擎,但是根据您的建议,我已将引擎更改为 InnoDB 并添加了外键,因为它们似乎是在两个表之间创建关系的正确方法。现在使用外键,这似乎是一个很好的解决方案。谢谢
    【解决方案2】:

    我想我会做两个逻辑查询。如果您需要保证删除总是一起发生,请随意包装事务,如果没有,则回滚。

    首先,使用单个查询删除帐户和与组织从属关系的帐户(这里我假设您的帐户表名称)

    DELETE tbl_account,tbl_affiliations
    FROM tbl_account INNER JOIN tbl_affiliations
       ON tbl_account.account_email = tbl_affiliations.account_email /* I am assuming join condition here, perhaps there is an id to be used instead */
    WHERE tbl_account.acount_email = ?
    

    然后,删除所有孤立的组织:

    DELETE tbl_organizations
    FROM tbl_organizations LEFT JOIN tbl_affiliations
      ON tbl_organizations.org_id = tbl_affiliations.org_id
    WHERE tbl_affiliations.org_id IS NULL
    

    请注意,由于最后一个查询不依赖于任何帐户特定信息,如果您不需要与帐户删除同步进行组织删除,您还可以考虑运行异步进程来清理孤立组织。

    这种方法的好处是,您始终可以使用第一个查询以相同的方式删除用户帐户,因为无论是否有多个帐户与组织相关联,这种方法都有效。因此,您不需要任何额外的应用程序逻辑或 SQL SELECT 子查询来查找与组织关联的单个帐户的情况。

    【讨论】:

    • 感谢您的回复。由于我现在使用外键约束,我认为由于组织表和附属关系表之间的关系,这可能不起作用。但是,如果我没有添加外键,这似乎是一个很好的解决方案,所以谢谢。我会投票给你,但我不能,因为我还没有 15 声望
    • @BenGuest 好吧,你真的不应该在组织表和从属关系表之间使用外键约束。无论如何,我个人并不喜欢外键约束,因为 IMO 应该尽可能在应用程序层实现这种逻辑(从性能的角度来看是合理的)。话虽如此,您可以在查询期间关闭外键约束。
    • 我不确定我是否理解为什么使用它们不是一个好主意。如果帐户电子邮件地址发生更改(这是帐户表中的主键),拥有外键允许从属关系表自动更新。我想我可以在 PHP 的单独查询中做到这一点,但我可以让数据库引擎为我处理这个。您的意思是说,拥有外键可能会使查询以完成问题中的操作更难创建/对数据库中的其他数据更危险(即更有可能意外删除其他数据)?
    • @BenGuest 我想我的偏好总是在应用层处理应用逻辑。您会发现您通常可以编写一个查询来同时跨多个表更新行。这样,您就不会将逻辑分布在两个地方。可能对开发人员在接触应用程序后隐藏的逻辑。在应用程序逻辑中明确定义表之间的交互可以让任何可能使用代码的人清楚地了解行为。
    • @BenGuey 我关于外键关系在组织与从属关系的情况下是一个问题的注释源于您的用例,即您实际上具有要实现的条件行为。在某些情况下,您只想删除组织表上的行。外键无法像那样有条件地操作。
    【解决方案3】:

    当您尝试一次从两个表中删除数据时,您可以试试这个。

    DELETE from TBL_ORGANISATIONS TO JOIN TBL_AFFILIATIONS TA 
        ON(TO.id=TA.org_id) where TA.account_email=:email;
    

    在这里,我们尝试使用 TO(即 id)的主键和外键 TA(即 org_id)从两个表 TBL_ORGANISATIONS 和 TBL_AFFILIATIONS 中删除行,并使用 where 子句 where TA.account_email=:email 添加条件。

    【讨论】:

    • 好吧,我真的想从 3 个表中删除数据:从帐户表中删除 email = account_to_delete's_email 的行,从附属表中删除 account_email = account_to_delete's_email 的所有行,最后,从组织表中删除组织在从属关系表中没有任何引用的行
    猜你喜欢
    • 1970-01-01
    • 2011-07-26
    • 1970-01-01
    • 2013-07-13
    • 1970-01-01
    • 2010-11-08
    • 2021-02-20
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多