sql-server - 如何在 .cfc 文件的一个 DELETE 语句中从多个表中删除?
问题描述
我有两张桌子。一个设备表和一个位置表。设备表中只能有一个设备,但位置表中可以有多个具有多个位置的设备。
如果用户删除设备,我还需要从位置表中删除具有相同 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>
解决方案
(我认为在社区 wiki 答案下整合所有有用的评论会很有帮助)
您可以在单个语句中从多个表中删除吗?
不使用 SQL Server,不。一些数据库,如 MySQL,支持多表删除语句。SQL Server 没有。您将需要多个删除语句。但是,如果目标只是删除记录,那差别不大。
当前代码有什么问题?
当前代码不起作用有几个原因。首先,初始
delete
查询仅在表单提交时执行。第二个代码块引用了相同的查询,但不首先验证它是否确实存在。如果表单尚未提交,这将导致未定义的变量错误。其次,更重要
DELETE
的是,用 CF 的说法,语句从不返回“查询”对象。因此,即使第一个查询确实执行了,查询变量DeleteDevice
也永远不会存在。同样,当代码尝试在此处使用它时会导致未定义的变量错误:<cfif DeleteDevice.RecordCount eq 1>
.虽然除非有更多的东西,......这对
cfif
IMO没什么用。如果 deviceID 存在记录,它们将被删除。如果没有,什么都不会发生。跳过第二次删除所节省的任何可忽略不计的节省都被增加的代码复杂性所抵消。最后,虽然它不会导致硬错误,但并没有按照
maxRows=1
你的想法做。它对DELETE
语句没有任何影响,也不会阻止 DELETE 语句删除多条记录。该maxRows
属性仅适用于返回“查询”对象的语句,并用于限制该查询中包含的记录数。
那么如何处理多个删除语句呢?
由于 SQL Server 不支持多表DELETE
语句,因此无论如何您都需要两条语句。有几种方法可以构建 SQL,每种方法都有自己的优缺点。但是,在所有情况下,您都应该使用事务来对相关语句进行分组,并确保所有内容作为一个单元完成或失败
多个cfquery
只需在事务中一个接一个地运行您的两个查询。正如肖恩建议的那样,尽管交换语句顺序。由于 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 的回答所示,将两个 SQL 语句放在一个 cfquery 标记中是另一种选择。但是,请记住Alex 提到的限制。出于安全原因,可以在 db/driver 级别禁用多个语句。所以这种方法可能不适用于所有环境。
<cftransaction> <cfquery datasource="#session.dsn#"> DELETE FROM device_location_xref WHERE ... DELETE FROM device WHERE ..... </cfquery> </cftransaction>
SQL 存储过程
另一个选择是肖恩建议的:将 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 提到的另一种选择是“软删除”。您可以向表中添加一个 BIT 列,而不是物理删除记录。然后UPDATE
,每当您需要将记录标记为已删除时,该列就会标记。有关软删除的优缺点的讨论,请参阅此线程。
UPDATE TableName
SET IsDeleted = 1
WHERE TheIDColumn = 12345
推荐阅读
- google-bigquery - BigQuery:按日期分区的过度子句返回不同日期的相同值
- android - 使用firebase和flutter在应用程序中接收图像时出错
- concat - 选择公式中的 Concat - 雪花
- python - 使用 matplotlib 绘制感知器算法
- c# - 针对匿名对象的 System.Text.Json 序列化
- c# - WPF 打开新窗口并设置其属性
- python - 如何从 utf-8 中恢复符号
- javascript - Javascript附加对象不保证顺序
- javascript - 在 Jest 中测试包含 Promise 和 FileReader() 的函数
- c# - 通过 NGINX 在 Linux 上部署的 ASP.Net Core 2.2,POST 请求返回 HTTP 错误 400