忽略重复键而不更改表SQL

时间:2017-03-08 10:12:49

标签: sql-server

问题(1):

是否可以创建一个插入一组代码的try-catch以及何时抛出错误

(例如:Violation of PRIMARY KEY constraint 'PK_CouponNumber'. Cannot insert duplicate key in object 'dbo.CouponNumber'.),

它会跳过该值并继续插入其余代码吗?

我当前的代码是这样的SP:

 DECLARE @Success    BIT
    SELECT @RetryCount = 1, @Success = 0
    WHILE @Success = 0
    BEGIN
       BEGIN TRY
          BEGIN TRANSACTION

          CREATE TABLE dbo.RandomGenerator(
              RowNumber INT PRIMARY KEY CLUSTERED,
              NextID INT )
          ;WITH x AS (
             SELECT TOP (1000000) rn = ROW_NUMBER() OVER (ORDER BY s1.[object_id])
             FROM sys.all_objects AS s1 
             CROSS JOIN sys.all_objects AS s2
             ORDER BY s1.[object_id] 
          )
          INSERT dbo.RandomGenerator(RowNumber, NextID)
          SELECT rn, ROW_NUMBER() OVER (ORDER BY NEWID()) + 1000000 FROM x;

          INSERT  INTO dbo.RandomNumbers( EventID, RandomID, UserID)
             SELECT TOP (@Number) @EventID, NextID, @UserID FROM RandomGenerator

          COMMIT TRANSACTION

          SELECT 'Success!'
          SELECT @Success = 1 -- To exit the loop
       END TRY

       BEGIN CATCH
          ROLLBACK TRANSACTION

          SELECT  ERROR_NUMBER() AS [Error Number],
          ERROR_MESSAGE() AS [ErrorMessage];     

          IF ERROR_NUMBER() = 2627
          BEGIN
              --Do Something...
          END 
       END CATCH
    END

它的作用就是生成并试着尝试直到成功。

我打算尝试插入所有数字而不重复,然后重复这个过程,直到插入数字的数量等于@Number。

在创建此问题时,我发现了一个相关问题,他们使用IGNORE_KEY_DUP。我想尝试一下,但我不能,因为我不能改变桌子......

问题(2):
有没有办法在不改变表的情况下使用IGNORE_KEY_UP?

预先谢谢!

修改

忘记提到我在插入过程中已尝试过NOT EXIST和NOT IN,但如果表中存在100,000个randomID,则特别慢。所以我把它作为我的选项删除了,我想尝试尝试直到成功插入比比较现有的1M randomID中的每个100,000新的randomID要快得多。

修改

显然我的老板只是允许在此期间更换桌子。所以这就是我所取得的成就

Duplicate key was ignored.
@newCouponCount727012       @generatedCount:227012      @notGeneratedCount:22988
Duplicate key was ignored.
@newCouponCount747842       @generatedCount:247842      @notGeneratedCount:2158
Duplicate key was ignored.
@newCouponCount749846       @generatedCount:249846      @notGeneratedCount:154
Duplicate key was ignored.
@newCouponCount749991       @generatedCount:249991      @notGeneratedCount:9
@newCouponCount750000       @generatedCount:250000      @notGeneratedCount:0

。与使用NOT EXISTS时的30分钟+相比,这运行时间为14秒。这就是我的意思,尝试尝试直到成功。我知道这与我的问题的标题相矛盾,所以我仍然乐意接受答案/新方法,以防IGNORE_DUP_KEY最终产生更多错误。

2 个答案:

答案 0 :(得分:2)

至于您的问题(1),您可以将重复的结果排除在WHERE子句中。由于我们没有dbo.CouponNumber的查询,我只能举个例子:

INSERT INTO dbo.MyTable (MyPKColumn1, MyPKColumn2)
SELECT RowNumber, NextID
FROM   dbo.RandomGenerator
WHERE  NOT EXISTS ( SELECT 1 
                    FROM dbo.MyTable As excludeTbl
                    WHERE excludeTbl.MyPKColumn1 = RowNumber 
                      AND excludeTbl.MyPKColumn2 = NextID  )

至于你的问题(2),IGNORE_KEY_UP是一个索引选项。意思是,您只能在创建/更改索引时进行设置。根据“改变你的桌子”的意思,你有答案。从理论上讲,改变索引不会改变表格。 (注意唯一索引和主键不仅仅是索引。)

编辑:

考虑到你已经尝试了一下NOT EXISTS。我会告诉你,它存在3种方法来排除我所知道的值:NOT IN,NOT EXISTS,LEFT JOIN ON NULL VALUE。

在去IGNORE_KEY_UP之前,您应该尝试其中的3个。 IGNORE_KEY_UP随着时间的推移会给你带来很多问题。

LEFT JOIN ON NULL VALUE的示例:

INSERT INTO dbo.MyTable (MyPKColumn1, MyPKColumn2)
SELECT rg.RowNumber, rg.NextID
FROM   dbo.RandomGenerator rg
LEFT JOIN dbo.MyTable  mt
    ON  rg.RowNumber = mt.MyPKColumn1 
    AND rg.NextID  = mt.MyPKColumn2
WHERE  mt.MyPKColumn1 IS NULL
  AND  mt.MyPKColumn2 IS NULL

编辑2:

关于IGNORE_KEY_UP。哪里可能有问题?

他们有很多理由不使用IGNORE_KEY_UP,但我不是这个特殊原因的专家。我只能给你三个主要原因:

  • 在索引列上使用UNICITY asumption会破坏任何逻辑。 Reworded:你只需要打破索引的UNIQUE / PK部分,并且永远不能指望只有一个记录在这个索引上给出一个过滤器。使用这样,您需要检查索引列上的每个现有代码过滤,以免它们只期望一条记录。
  • IGNORE_KEY_UP会降低索引列上的INSERT或UPDATE。因为它可以在每次插入完整的叶子时重新组织索引B-Tree。
  • 它可能会轻微减慢对具有重复值的索引列的访问时间。

答案 1 :(得分:1)

我不确定我是否真的明白你想要的东西......

如果我做对了,你需要许多独特的随机数。以下代码将通过将NEWID()的前4个字节强制转换为INT来创建准随机列表。 ABS会将反对的价值观变为正面。使用DISTINCT没有重复的数字,这很快......

寻找100.000随机数,立即返回99.997。

DECLARE @Count INT=100000;
WITH 
 Tally(Nr)    AS(SELECT TOP(@Count) ROW_NUMBER() OVER(ORDER BY(SELECT NULL)) FROM master..spt_values CROSS JOIN master..spt_values x CROSS JOIN master..spt_values y)
,Randoms(Rnd) AS(SELECT DISTINCT ABS(CAST(CAST(NEWID() AS VARBINARY(4)) AS INT)) FROM Tally)
SELECT ROW_NUMBER() OVER(ORDER BY (SELECT NULL))
      ,Rnd
FROM  Randoms

您可以添加类似WHERE NOT EXISTS(SELECT 1 FROM YourTable WHERE ExistingCode=Rnd)的内容,以确保返回的列表不包含现有数字...这样您就可以插入 as ,不需要循环...