Как удалить из нескольких таблиц в одном операторе DELETE в файле .cfc? - PullRequest
2 голосов
/ 31 мая 2019

У меня есть две таблицы.Таблица устройств и таблица местоположений.В таблице устройств может быть только одно устройство, но в таблице расположений может быть несколько устройств с несколькими местоположениями.

Если пользователь удаляет устройство, мне нужно также удалить местоположения с тем же идентификатором из таблицы местоположений.

Есть ли способ удалить из нескольких таблиц в операторе Delete (с использованием сервера sql) из страницы cfc?

Я пытался написать <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>

Ответы [ 3 ]

3 голосов
/ 01 июня 2019

(я подумал, что было бы полезно объединить все полезные комментарии под ответом вики-сообщества)

Можете ли вы УДАЛИТЬ из нескольких таблиц одним оператором?

Не с SQL Server, нет. Некоторые базы данных, такие как MySQL, поддерживают операторы удаления нескольких таблиц . SQL Server нет. Вам понадобится несколько операторов удаления. Однако, если цель состоит в том, чтобы просто удалить записи, это не имеет большого значения.

Что не так с текущим кодом?

  • Есть несколько причин, по которым текущий код не работает. Во-первых, первоначальный запрос delete выполняется только в том случае, если форма была отправлена. Второй блок кода ссылается на тот же запрос, но не проверяет, действительно ли он существует первым. Это приведет к неопределенной переменной, если форма еще не была отправлена.

  • Во-вторых, и, что более важно, операторы DELETE никогда не возвращают объект «запрос» на языке CF. Таким образом, даже если первый запрос выполнил , переменная запроса DeleteDevice никогда не будет существовать . Снова, приводя к неопределенной переменной переменной, когда код пытается использовать это здесь: <cfif DeleteDevice.RecordCount eq 1>.

    Хотя, если это не так, ... cfif не имеет большого значения для ИМО. Если существуют записи для идентификатора устройства, они будут удалены. Если нет, ничего не произойдет. Любая незначительная экономия при пропуске второго удаления перевешивается добавленной сложностью кода.

  • Наконец, хотя это и не вызывает серьезных ошибок, 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>

  • Несколько операторов в одном запросе

    Как показывает ответ Джеймса Мохлера , размещение обоих операторов SQL в одном теге cfquery является еще одним вариантом. Однако имейте в виду ограничения, которые Алекс упомянул . По соображениям безопасности несколько операторов можно отключить на уровне БД / драйвера. Таким образом, этот подход может работать не во всех средах.

    <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>
    

Soft vs Hard Deletes

Наконец, альтернатива упомянутый Шон - это "soft-delete". Вместо того, чтобы физически удалять записи, вы можете добавить столбец BIT в таблицу (ы). Затем UPDATE помечает этот столбец всякий раз, когда вам нужно пометить запись как удаленную. См. в этой теме , где обсуждаются плюсы и минусы программных удалений.

      UPDATE TableName
      SET    IsDeleted   = 1
      WHERE  TheIDColumn = 12345
0 голосов
/ 02 июня 2019

Я бы удалил из обеих таблиц в одном операторе 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>
0 голосов
/ 31 мая 2019

Можете ли вы сделать что-то вроде этого

<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>
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...