【问题标题】:How to delete from multiple tables in one DELETE statement in a .cfc file?如何在 .cfc 文件的一个 DELETE 语句中从多个表中删除?
【发布时间】:2019-10-16 16:59:06
【问题描述】:

我有两张桌子。一个设备表和一个位置表。设备表中只能有一个设备,但位置表中可以有多个具有多个位置的设备。

如果用户删除设备,我还需要从位置表中删除具有相同 id 的位置。

有没有办法在 cfc 页面中的 Delete 语句(使用 sql server)中从多个表中删除?

当我进行初始删除时,我尝试写一个<cfif>,以检查是否有记录被删除,如果有,则运行下一个从表中删除设备的删除语句。

我也尝试过使用这种格式。

delete T1, T2
from T1 
inner join T2 on T1.device_id = T2.device_id
where T2.device_id = '111';

T1 = 设备表,T2 = 位置表

<cfif ARGUMENTS.submitButton eq 'btn_Delete'>
   <cfquery name="DeleteDevice" datasource="#session.dsn#" maxRows=1 >
        DELETE from #session.tq#device
        WHERE device_id = <cfqueryparam value="#formStruct.deviceId#" cfsqltype="CF_SQL_VARCHAR">
    </cfquery>
 </cfif>

<cfif DeleteDevice.RecordCount eq 1>
<cfquery name="DeleteLocation" datasource="#session.dsn#" maxRows=1 >
        DELETE from #session.tq#device_location_xref
        WHERE device_location_xref_recno=<cfqueryparam value="#formStruct.deviceId#" cfsqltype="CF_SQL_VARCHAR">
</cfquery>

【问题讨论】:

  • 您可以在数据库中为子表添加级联删除。另外,请注意#session.tq#。如果您不使用 或进行其他验证,您的查询很容易被利用。
  • 你是说不起作用的代码是&lt;cfif DeleteDevice.RecordCount eq 1&gt; 条件吗?如果是这样,请尝试将result=variablename 添加到您的初始&lt;cfquery name="DeleteDevice" datasource="#session.dsn#" maxRows=1 &gt; 语句中。然后使用该变量名检查RecordCount
  • 如果device_location_xrefdevice有外键约束,那么你必须先从第二个表中删除。无论如何,总是做第二次删除,然后是第一次。如果没有链接记录,则不会删除任何内容,您可以继续。另外,您的表格是否以session.tq...something 命名?这似乎会成为一场数据噩梦。
  • 我一直发现使用void 标记记录比实际删除记录要好得多。如果有人不小心删除了错误的记录,将 void 设置为 false 比从备份中恢复要容易得多。
  • (编辑)有没有办法在 Delete 语句中从多个表中删除 不可以。SQL Server 一次只允许从一个表中删除。您的 cfif 代码不起作用的原因是因为 DELETE 语句不返回“查询”对象 - 用 CF 的说法。要确定受影响的记录数,您需要使用 cfquery "result" 属性,如 Miguel-F 所述。说了这么多,为什么重要?正如 Shawn 所描述的,您可以先从 secondary 表中删除任何记录,然后再从主表中删除,然后就完成了。 (续)

标签: sql-server tsql coldfusion


【解决方案1】:

你能做这样的事吗

<cfquery>
DECLARE @device_id varchar(20) = <cfqueryparam value="#formStruct.deviceId#" cfsqltype="CF_SQL_VARCHAR">

DELETE
FROM T1
WHERE device_id = @device_id 

DELETE
FROM T2
WHERE device_id = @device_id 
</cfquery>

【讨论】:

  • @Ash - 请记住,它仍在执行 multiple sql 语句。所以通常的规则仍然适用。多个相关操作应包含在 transaction 中,以确保一切作为一个单元完成或失败。
  • 级联删除将是类似的,但我对将它们用于任何类型的删除非常谨慎。我宁愿保持控制并自己做:-)
  • @Ash - 它仍然引出了一个问题,想要一个查询的原因是什么?有多种方法可以解决它,但正确的方法取决于这里的推理(除了确保从两个表中删除记录之外)。
  • 请注意,可以将连接/数据源配置为每个cfquery 只允许一个语句。我在这里是@Ageax,会推荐cftransaction,然后执行多个cfquery 删除相关记录。它更容易阅读/维护/扩展,开销应该可以忽略不计。
  • 或者更好的是,将其发送到存储过程并让数据库处理删除数据库记录。当然,这会将代码放在两个不同的地方,但我坚信数据库不仅仅是存储东西的地方。让它做它应该做的工作。
【解决方案2】:

(我认为将所有有用的 cmets 整合到社区 wiki 答案下会很有帮助)

您可以在单个语句中从多个表中删除吗?

不使用 SQL Server,不。一些数据库,如 MySQL,支持multi-table delete statements。 SQL Server 没有。您将需要多个删除语句。但是,如果目标只是删除记录,那差别不大。

当前代码有什么问题?

  • 当前代码无法正常工作的原因有多种。首先,最初的delete 查询仅在表单提交时执行。第二个代码块引用了相同的查询,但不首先验证它是否确实存在。如果表单尚未提交,这将导致未定义变量错误。

  • 其次,更重要的是,DELETE 语句从不返回“查询”对象,用 CF 的话来说。所以即使第一个查询确实执行了,查询变量DeleteDevice永远不会存在。同样,当代码尝试在此处使用它时导致未定义变量错误:&lt;cfif DeleteDevice.RecordCount eq 1&gt;

    虽然除非有更多内容,...cfif 在 IMO 中几乎没有什么用处。如果 deviceID 存在记录,它们将被删除。如果没有,什么都不会发生。跳过第二次删除所节省的任何微不足道的节省都被增加的代码复杂性所抵消。

  • 最后,虽然它不会导致硬错误,但 maxRows=1 并没有按照您的想法进行操作。它对 DELETE 语句没有任何影响,并且不会阻止 DELETE 语句删除多条记录。 maxRows 属性仅适用于返回“查询”对象的语句,并用于限制该查询中包含的记录数。

那我该如何处理多个delete语句呢?

由于 SQL Server 不支持多表 DELETE 语句,因此无论如何您都需要两个语句。有几种方法可以构建 SQL,每种方法都有自己的优缺点。但是,在所有情况下,您应该使用transaction 对相关语句进行分组,并确保所有内容作为一个单元完成或失败

  • 多个 cfquery 的

    只需在事务中一个接一个地运行您的两个查询。虽然交换语句顺序,如Shawn suggested。由于 FK 约束的可能性,最好先从子/FK 表中删除,最后从主/PK 表中删除。

    <cftransaction> <cfquery datasource="#session.dsn#"> DELETE FROM device_location_xref WHERE ... </cfquery> <cfquery datasource="#session.dsn#"> DELETE FROM device WHERE ..... </cfquery> </cftransaction>

  • 单个 cfquery 中的多个语句

    正如JamesAMohler's answer 所展示的,将两个 SQL 语句放在一个 cfquery 标记中是另一种选择。但是,请记住the limitations Alex mentioned。出于安全原因,可以在 db/driver 级别禁用多个语句。所以这种方法可能不适用于所有环境。

    <cftransaction> <cfquery datasource="#session.dsn#"> DELETE FROM device_location_xref WHERE ... DELETE FROM device WHERE ..... </cfquery> </cftransaction>

  • SQL 存储过程

    另一个选项是Shawn suggested:将sql逻辑放入存储过程。一般来说,存储过程更适合复杂的sql操作,而且不像cfquery,可以返回多个结果集。

    CREATE PROCEDURE DeleteDevice
      @deviceID VARCHAR(50)
    AS
    BEGIN
        -- minimal error handling for demo only
        BEGIN TRAN
            DELETE FROM device_location_xref
            WHERE device_location_xref_recno = @deviceID
    
            DELETE from device
            WHERE device_id = @deviceID
    
        END TRAN
    
    END
    

    然后使用cfstoredproc 而不是cfquery 调用它。

    <cfstoredproc procedure="DeleteDevice" datasource="#ds#"> 
       <cfprocparam type="in" value="#formStruct.deviceId#" 
              cfsqltype="cf_sql_varchar"> 
    </cfstoredproc>
    

软删除与硬删除

最后,替代Shawn mentioned 是“软删除”。您可以向表中添加一个 BIT 列,而不是物理删除记录。然后 UPDATE 每当您需要将记录标记为已删除时,该列标志。请参阅this thread 以讨论软删除的优缺点。

      UPDATE TableName
      SET    IsDeleted   = 1
      WHERE  TheIDColumn = 12345

【讨论】:

  • 对使用存储过程的额外投票。更容易根据需要进行更新,尤其是在没有相关应用程序代码更改的情况下。
【解决方案3】:

我会在一个 cfquery 语句中从两个表中删除,因为第二次删除不依赖于第一次删除。

  <cfif comparenocase(ARGUMENTS.submitButton, 'btn_Delete') EQ 0> <!--- bit quicker to use compare --->
    <cfquery name="DeleteDevice" datasource="#session.dsn#"> 
    <!--- delete from first table --->
    DELETE FROM #session.tq#device
    WHERE device_id = <cfqueryparam value="#formStruct.deviceId#" cfsqltype="CF_SQL_VARCHAR">;

<!--- delete from second table --->    
    DELETE FROM #session.tq#device_location_xref
    WHERE device_location_xref_recno=<cfqueryparam value="#formStruct.deviceId#" cfsqltype="CF_SQL_VARCHAR">
    </cfquery>
    </cfif>

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2018-11-26
    • 1970-01-01
    • 2012-01-14
    • 2021-06-25
    • 2013-07-06
    • 1970-01-01
    相关资源
    最近更新 更多