假设我有一个MS SQL Server表,其中包含主键ID和唯一键名。此外,为Name设置了IGNORE_DUP_KEY。我想要做的是插入一个名称,然后获取其ID,无论操作执行成功与否。即如果名称不重复,则获取新ID,如果是重复,则获取现有ID。
显然,如果我执行INSERT Name然后选择一个IDENTITY,那么在Name是重复的情况下(在SCOPE_IDENTITY()之类的函数中,这将无法正常工作,在这种情况下会返回NULL)。但是,我认为应该有一种方法可以轻松实现这一点,因为INSERT应该已经找到了未成功插入的Name的ID。因此,此ID应在O(1)时间内可用。
此外,请注意,由于性能方面的考虑,我不能选择向服务器发送两个单独的请求(插入和选择)。
答案 0 :(得分:2)
IGNORE_DUP_KEY
表示如果在插入行期间找到重复键,则会丢弃插入行并显示警告而不是错误,并继续插入其余行。
结果是重复行不受insert语句的影响,因此没有要读取的标识。不确定你的意图究竟是什么,但我认为你不能在一个声明中做到这一点。
也许您需要使用Merge声明?
答案 1 :(得分:2)
这听起来像是一个有趣的口号,我将在星期一以博客形式提供此内容,但从未触及过ignore_dup_key选项,我想看看OP是否有办法做,如果一个插入软件基于故障在一个唯一索引上设置ignore_dup_key,是否有一种“捕获”id的简单方法。
总结以下代码墙
此处代码
-- This script demonstrates the usage of
-- IGNORE_DUP_KEY option for an index
-- http://msdn.microsoft.com/en-us/library/ms186869.aspx
-- Why you'd want this behaviour is left as
-- an excercise to the reader
--
SET NOCOUNT ON
IF EXISTS
(
SELECT 1
FROM sys.tables T
WHERE T.name = 'DupesOk'
AND T.schema_id = schema_id('dbo')
)
BEGIN
DROP TABLE dbo.DupesOk
END
CREATE TABLE
dbo.DupesOk
(
dupe_id int identity(1,1) NOT NULL PRIMARY KEY
, name varchar(50) NOT NULL
)
-- Create an index that is unique but
-- violation of the unique constraint is
-- merely discarded with warning instead of
-- blowing up
CREATE UNIQUE INDEX
uq_dupes_name
ON dbo.DupesOk
(
name
)
WITH IGNORE_DUP_KEY
-- Add a name and emit the identity value
-- from the inserted virtual table
INSERT INTO
dbo.DupesOk
OUTPUT
inserted.dupe_id
, inserted.name
SELECT
'Peter Parker'
-- Old-school means of showing the identity
-- values, 1's across the board
-- See earlier posting
-- http://billfellows.blogspot.com/2009/10/scope-and-identity.html
SELECT
@@IDENTITY AS six_of_one
, SCOPE_IDENTITY() half_dozen_of_other
, IDENT_CURRENT('dbo.DupesOk') AS current_identity_value
-- Add a new name and emit the identity value
-- from the inserted virtual table
INSERT INTO
dbo.DupesOk
OUTPUT
inserted.dupe_id
, inserted.name
SELECT
'Spider man'
-- Same as above, 2s across the board
SELECT
@@IDENTITY AS six_of_one
, SCOPE_IDENTITY() half_dozen_of_other
, IDENT_CURRENT('dbo.DupesOk') AS current_identity_value
-- Insert a duplicate value for the unique index
-- watch it not explode with a output message of
-- 'Duplicate key was ignored.'
INSERT INTO
dbo.DupesOk
OUTPUT
-- This won't show anything as there is nothing to show
inserted.dupe_id
, inserted.name
SELECT
'Peter Parker'
-- The first two remain 2's as they belong to the successful
-- insert of Spider man. ident_current shows that the value was
-- incremented. The calling code did not do a lookup to
SELECT
@@IDENTITY AS this_identity_belongs_to_spider_man
, SCOPE_IDENTITY() this_identity_also_belongs_to_spider_man
-- As expected, the value is now 3, it got incremented
, IDENT_CURRENT('dbo.DupesOk') AS current_identity_value
;
MERGE
-- target table
dbo.DupesOk AS T
USING
(
-- source system
SELECT 'Hal Jordan' AS name
) AS S
ON S.name = T.name
WHEN
MATCHED THEN
UPDATE
SET
T.name = S.name
WHEN
NOT MATCHED THEN
INSERT
(
[name]
)
VALUES
(
[name]
)
-- 4 | Hal Jordan | INSERT
OUTPUT
inserted.dupe_id
, inserted.name
, $action
;
-- 4's as expected
SELECT
@@IDENTITY AS hal_jordan
, SCOPE_IDENTITY() still_hal_jordan
, IDENT_CURRENT('dbo.DupesOk') AS current_identity_value
-- Add someone else just to get the ids to flip
INSERT INTO
dbo.DupesOk
OUTPUT
inserted.dupe_id
, inserted.name
SELECT
'Tony Stark'
-- 5's
SELECT
@@IDENTITY AS tony_stark
, SCOPE_IDENTITY() still_tony_stark
, IDENT_CURRENT('dbo.DupesOk') AS current_identity_value
;
-- Try inserting an existing id
MERGE
-- target table
dbo.DupesOk AS T
USING
(
-- source system
SELECT 'Hal Jordan' AS name
) AS S
ON S.name = T.name
WHEN
MATCHED THEN
UPDATE
SET
T.name = S.name
WHEN
NOT MATCHED THEN
INSERT
(
[name]
)
VALUES
(
[name]
)
-- 4 | Hal Jordan | UPDATE
OUTPUT
inserted.dupe_id
, inserted.name
, $action
;
-- Still 5's
SELECT
@@IDENTITY AS tony_stark
, SCOPE_IDENTITY() still_tony_stark
, IDENT_CURRENT('dbo.DupesOk') AS current_identity_value
GO
-- What if we try a trigger?
-- It would need to be an instead of trigger
-- as the value will have already been
-- http://msdn.microsoft.com/en-us/library/ms189799.aspx
-- http://msdn.microsoft.com/en-us/library/ms175089.aspx
CREATE TRIGGER tr_dupes_insert
ON dbo.DupesOk
INSTEAD OF INSERT
AS
BEGIN
SET NOCOUNT ON
-- variety of different approaches here but
-- I'll attempt the insert and if no rows
-- are affected, then we know it's an existing
-- row and lookup the identity
DECLARE
@ident TABLE
(
dupe_id int NOT NULL
, name varchar(50) NOT NULL
)
-- Only n00bs code triggers for single rows
INSERT INTO
dbo.DupesOk
(
name
)
-- output clause
-- http://msdn.microsoft.com/en-us/library/ms177564.aspx
OUTPUT
-- the output's virtual table
-- recursion is deep, yo
inserted.dupe_id
, inserted.name
INTO
@ident
SELECT
I.name
FROM
-- the trigger's virtual table
-- fascinatingly enough, the value for
-- an identity field pre-insert on an
-- instead of trigger is 0 and not NULL
-- as one would assume
inserted I
-- Now we need to add anyone into the
-- table variable that didn't get inserted
-- into @ident in the previous statement
INSERT INTO
@ident
SELECT
D.dupe_id
, D.name
FROM
inserted I
INNER JOIN
dbo.DupesOk D
ON D.name = I.name
LEFT OUTER JOIN
@ident tv
-- can't match on ids here
-- as they all come in as zero
ON tv.name = I.name
WHERE
tv.dupe_id IS NULL
SELECT
I.dupe_id
, I.name
FROM
@ident I
-- To make OUTPUT work correctly, we'd need to
-- "fix" the values in the inserted virtual tables
-- but uncommenting this will result in a
-- trigger creation error of
-- "The logical tables INSERTED and DELETED cannot be updated"
--UPDATE
-- I
--SET
-- dupe_id = -1
--FROM
-- inserted i
-- INNER JOIN
-- @ident TV
-- ON TV.name = i.name
END
GO
DECLARE
@idents TABLE
(
dupe_id int
, name varchar(50)
)
-- We should see
-- 1 | Peter Parker
-- 7 | Barry Allen
--
-- 6 was consumed by the double pump of Hal Jordan
-- results were surprising, to me at least
INSERT INTO
dbo.DupesOk
-- this will generate an error
-- The target table 'dbo.DupesOk' of the DML statement cannot have any enabled triggers if the statement contains an OUTPUT clause without INTO clause.
-- unless we dump output results into a table
OUTPUT
inserted.dupe_id
, inserted.name
INTO
@idents
SELECT
'Peter Parker'
UNION ALL
SELECT
'Barry Allen'
-- The above statement's trigger correctly spits out the rows we emit in the final
-- step of the trigger
-- dupe_id name
-- 7 Barry Allen
-- 1 Peter Parker
-- Look at this, it's the inserted virtual table
-- from the trigger in pristine condition
-- and there's no way to unbugger it
SELECT * FROM @idents I
结果
dupe_id name
----------- --------------------------------------------------
1 Peter Parker
six_of_one half_dozen_of_other current_identity_value
--------------------------------------- --------------------------------------- ---------------------------------------
1 1 1
dupe_id name
----------- --------------------------------------------------
2 Spider man
six_of_one half_dozen_of_other current_identity_value
--------------------------------------- --------------------------------------- ---------------------------------------
2 2 2
dupe_id name
----------- --------------------------------------------------
Duplicate key was ignored.
this_identity_belongs_to_spider_man this_identity_also_belongs_to_spider_man current_identity_value
--------------------------------------- ---------------------------------------- ---------------------------------------
2 2 3
dupe_id name $action
----------- -------------------------------------------------- ----------
4 Hal Jordan INSERT
hal_jordan still_hal_jordan current_identity_value
--------------------------------------- --------------------------------------- ---------------------------------------
4 4 4
dupe_id name
----------- --------------------------------------------------
5 Tony Stark
tony_stark still_tony_stark current_identity_value
--------------------------------------- --------------------------------------- ---------------------------------------
5 5 5
dupe_id name $action
----------- -------------------------------------------------- ----------
4 Hal Jordan UPDATE
tony_stark still_tony_stark current_identity_value
--------------------------------------- --------------------------------------- ---------------------------------------
5 5 5
Duplicate key was ignored.
dupe_id name
----------- --------------------------------------------------
7 Barry Allen
1 Peter Parker
dupe_id name
----------- --------------------------------------------------
0 Peter Parker
0 Barry Allen
答案 2 :(得分:0)
如果我正确理解你的问题,你应该考虑创建一个DML触发器来处理这个逻辑。
如果我误解你的问题,请告诉我。
答案 3 :(得分:-1)
您需要在OUTPUT
子句中获取插入的值。这样做是这样的:
INSERT foo (a, b)
OUTPUT inserted.a,Inserted.b