如果我可以删除其他第一个如何删除记录?

时间:2014-07-15 18:11:23

标签: sql-server transactions

我有以下内容:

文件可以包含其他文件(视频文件包含音频和字幕),一般情况下,内容文件不会单独保存,只会显示为主文件的一部分。但是在某些特殊情况下,我将一些内容文件分开保存。

我也有商店,所以如果是主文件,那么一个文件在商店中,如果我有一个辅助文件也保存在主文件中。

所以我有两个表:

  • 文件(IDfile,...);
  • FilesContainsFiles(IDContainerFile,IDContentFile);
  • 商店(IDStore,...)
  • StoresHaveFiles(IDStore,IDFile)

如果我想删除主文件,我只想从特定的sotre中删除,所以如果文件在其他商店中,我不想删除该文件。如果文件不在另一个商店,我想删除该文件,因为在数据库中包含这些信息是没有意义的。

另外,我想删除所有内容文件,只有当内容文件没有单独存储在主文件之外的其他商店中时。

为了确保我不能删除其他商店中的文件,我不会在级联上删除StoresHasFiles上的关系,所以这样我得到了一个外国人密钥错误。

所以我想按照这个步骤:

  • 尝试删除主文件。如果我能这样做,请尝试删除内容文件。

我想我需要一个交易才能做到这一点,但我不知道该怎么做。

我不知道是否可以使用我要删除的文件声明列表,如何迭代此列表以及如何尝试删除内容文件,以及是否因某些内容文件无法删除存储在另一个地方,尝试删除下一个内容文件。

也许还有其他方法可以解决这个问题?

感谢。

2 个答案:

答案 0 :(得分:2)

有几种不同的方法可以做到这一点。关于事务,了解语法和用法的最简单方法是在MSDN上阅读Begin Transaction

对于删除,我的方式是:

    -- Assumed inputs
    DECLARE @StoreId int;
    DECLARE @FileIdToDelete int;

    -- Query
    SET XACT_ABORT ON;  -- Forces whole transaction to roll back if 
                        --  there was an error at any point

    BEGIN TRAN;

    DECLARE @NumberOfStoresContainingFile int;

    -- Find out how many stores have this file
    SELECT  @NumberOfStoresContainingFile = COUNT(*)
    FROM    StoresHaveFiles shf
    WHERE   IDFile = @FileIdToDelete

    -- If it is more than one we know we dont want to delete so signal caller
    IF (@NumberOfStoresContainingFile > 1)
        RAISERROR(<addYourDetailsHere>);

    -- Remove any contained files of the main file we want to remove
    DELETE  f
    FROM    Files f
    WHERE   EXISTS (SELECT * 
                    FROM FilesContainsFiles fcf 
                    WHERE fcf.IDContainerFile = @FileIdToDelete AND fcf.IDContentFile = f.IDFile);

    -- Remove the main file entry itself
    DELETE
    FROM    Files
    WHERE   IDFile = @FileIdToDelete;

    -- Remove the JOIN table record between store and file
    DELETE  StoresHaveFiles
    WHERE   IDStore = @StoreId
        AND IDFile = @FileIdToDelete;

    -- This is an optimistic approach (it assumes the file belongs to the store you asked about
    --  if the file is only in one store).  This check is to ensure that the JOIN record was
    --  deleted.  If it is not, this means the file did not belong to the store and an error
    --  should be thrown.  To do this pessimistically, simply do this check before the deletes
    --  and after the count check.
    IF (@@rowcount = 0)
        RAISERROR(<addYourDetailsHere>);

    COMMIT;

答案 1 :(得分:1)

我会通过一系列检查来解决这个问题。

如果文件存在于指定的商店..否则什么都不做。 如果该文件存在于其他商店......否则只需从一个商店中删除。

例如:

IF OBJECT_ID('TEMPDB..#Files') IS NOT NULL DROP TABLE #Files
IF OBJECT_ID('TEMPDB..#FilesContainsFiles') IS NOT NULL DROP TABLE #FilesContainsFiles
IF OBJECT_ID('TEMPDB..#Stores') IS NOT NULL DROP TABLE #Stores
IF OBJECT_ID('TEMPDB..#StoresHaveFiles') IS NOT NULL DROP TABLE #StoresHaveFiles

CREATE TABLE #Files ([IDFile] INT IDENTITY(1,1), [FileName] VARCHAR(100))
CREATE TABLE #FilesContainsFiles ([IDContainerFile] INT, [IDContentFile] INT)
CREATE TABLE #Stores ([IDStore] INT IDENTITY(1,1), [StoreName] VARCHAR(100))
CREATE TABLE #StoresHaveFiles ([IDStore] INT, [IDFile] INT)

INSERT INTO #Stores (StoreName) VALUES ('Target'),('Walmart')
INSERT INTO #Files ([FileName]) VALUES ('true.blood.mkv'), ('game.of.thrones.mkv'), ('game.of.thrones-swe.sub')
INSERT INTO #FilesContainsFiles (IDContainerFile, IDContentFile) VALUES (2,3)
INSERT INTO #StoresHaveFiles (IDStore, IDFile) VALUES (1,1),(1,2),(2,2)

DECLARE @StoreToDeleteFrom VARCHAR(100), @FileToDelete VARCHAR(100)
--SET @StoreToDeleteFrom='Target'
SET @StoreToDeleteFrom='Walmart'
--SET @FileToDelete='true.blood.mkv'
SET @FileToDelete='game.of.thrones.mkv'

SELECT *
FROM #Files F 
LEFT OUTER JOIN #FilesContainsFiles FParent ON F.IDFile=FParent.IDContainerFile
LEFT OUTER JOIN #StoresHaveFiles SHF ON F.IDFile=SHF.IDFile
LEFT OUTER JOIN #Stores S ON SHF.IDStore=S.IDStore

-- IF THE FILE IS AT OUR STORE...
IF EXISTS (SELECT *
            FROM #Files F 
            LEFT OUTER JOIN #StoresHaveFiles SHF ON F.IDFile=SHF.IDFile
            LEFT OUTER JOIN #Stores S ON SHF.IDStore=S.IDStore
            WHERE S.StoreName=@StoreToDeleteFrom
            AND F.[FileName]=@FileToDelete)
BEGIN
    -- IF THE FILE IS NOT AT ANY OTHER STORE
    IF NOT EXISTS (SELECT *
                    FROM #Files F 
                    LEFT OUTER JOIN #StoresHaveFiles SHF ON F.IDFile=SHF.IDFile
                    LEFT OUTER JOIN #Stores S ON SHF.IDStore=S.IDStore
                    WHERE S.StoreName<>@StoreToDeleteFrom
                    AND F.[FileName]=@FileToDelete)
    BEGIN
        -- GO AHEAD AND DELETE FILE EVERYWHERE...
        PRINT 'DELETE FILE'

        DELETE SHF
        FROM #Stores S 
        JOIN #StoresHaveFiles SHF
           ON S.IDStore=SHF.IDStore
           JOIN #Files F 
              ON SHF.IDFile=F.IDFile
              AND F.[FileName]=@FileToDelete
        WHERE S.StoreName=@StoreToDeleteFrom

        DELETE FCF
        FROM #Files F 
        JOIN #FilesContainsFiles FCF
           ON F.IDFile=FCF.IDContentFile
        WHERE F.[FileName]=@FileToDelete

        DELETE FCF
        FROM #Files F 
        JOIN #FilesContainsFiles FCF
           ON F.IDFile=FCF.IDContainerFile
        WHERE F.[FileName]=@FileToDelete

        DELETE F 
        FROM #Files F
        WHERE [FileName]=@FileToDelete
    END
    ELSE
    BEGIN
        -- ONLY DELETE FROM STORE
        PRINT 'DELETE FILE FROM STORE ONLY'

        DELETE SHF
        FROM #Stores S 
        JOIN #StoresHaveFiles SHF
           ON S.IDStore=SHF.IDStore
           JOIN #Files F 
              ON SHF.IDFile=F.IDFile
              AND F.[FileName]=@FileToDelete
        WHERE S.StoreName=@StoreToDeleteFrom
    END
END
ELSE
BEGIN
    -- ITS NOT AT THAT STORE...
    PRINT 'NOTHING TO DELETE'
END


SELECT *
FROM #Files F 
LEFT OUTER JOIN #FilesContainsFiles FParent ON F.IDFile=FParent.IDContainerFile
LEFT OUTER JOIN #StoresHaveFiles SHF ON F.IDFile=SHF.IDFile
LEFT OUTER JOIN #Stores S ON SHF.IDStore=S.IDStore