插入后更新带有标识的临时表

时间:2018-01-23 21:33:42

标签: sql sql-server sql-server-2016

我有一个如下所示的临时表:

FirstName
LastName
DOB
Sex
Age
ExternalID

在我的存储过程中,我将这些值插入到具有以下结构的常规表中:

ID  identity(1,1)
FirstName
LastName

所以,我这样做:

Insert into myTable
select FirstName, LastName from TempTable

在插入过程中,我需要将主表中的主键插回临时表" ExternalID"柱。怎么能实现这一目标?

我尝试使用OUTPUT语句,但它只允许插入一个单独的表,然后我无法映射回临时表

我需要在插入后立即将生成的ID插入临时表中的ExternalID列。 FirstName和LastName不是唯一的。

一种可能的解决方案是使用循环并一次插入一行。这样,我可以使用scope_identity()更新临时表行。但我想避免使用循环。

5 个答案:

答案 0 :(得分:0)

您只需插入以外的列标识列:

Insert into myTable(FirstName, LastName)
    select FirstName, LastName
    from TempTable;

identity列会自动计算。

一条建议:使用insert时,请始终列出要插入的列名。永远不要依赖默认订购。

答案 1 :(得分:0)

正如您所说,FirstName和LastName不是唯一的。这意味着您无法使用trigger,因为可能存在相同的FirstName + LastName,因此您无法加入它们。

但是你可以做相反的事情:首先update你的临时表ExternalID(我建议你使用sequence对象然后只做update #t set ExternalID = next value for dbo.seq1;)然后插入您的行包括ExternalIDmyTable。为了能够插入到身份字段,您可以使用set identity_insert myTable on,或者您可以重新设计目标表,根本不包含任何身份,因为现在您使用sequence用于相同的目的。

答案 2 :(得分:0)

我们需要一个唯一的列,以便在插入后的更新操作中进行比较。这就是我们暂时使用ExternalID列的原因。由row_nubmber更新的ExternalID。

;WITH CTE AS
(
    SELECT *, RN = ROW_NUMBER() OVER(ORDER BY (SELECT NULL)) FROM @TempTable
)
UPDATE CTE SET ExternalID = RN

我们将插入操作的输出保存在临时表中。诀窍是带有ExternalID的order by,它将帮助我们为同一个姓氏制作唯一的行号

DECLARE @output TABLE (
ID  INT,
FirstName VARCHAR(10),
LastName VARCHAR(10))

Insert into @myTable
OUTPUT inserted.ID, inserted.FirstName, inserted.LastName  INTO @output(ID, FirstName, LastName) 
select FirstName, LastName from @TempTable T 
order by ExternalID

为了用插入的id值替换ExternalID列,我们将与名字,姓氏和行号进行比较。

;WITH TMP_T AS(
    SELECT *, RN = ROW_NUMBER() OVER(PARTITION BY FirstName, LastName ORDER BY ExternalID) FROM @TempTable )
,OUT_T AS( 
    SELECT *, RN = ROW_NUMBER() OVER(PARTITION BY FirstName, LastName ORDER BY ID) FROM @output )
UPDATE TMP_T SET ExternalID = OUT_T.ID
FROM 
TMP_T INNER JOIN OUT_T ON 
    TMP_T.FirstName = OUT_T.FirstName 
    AND TMP_T.LastName = OUT_T.LastName 
    AND TMP_T.RN = OUT_T.RN

示例数据:

DECLARE @TempTable TABLE (
FirstName VARCHAR(10),
LastName VARCHAR(10),
DOB VARCHAR(10),
Sex VARCHAR (10),
Age VARCHAR(10),
ExternalID INT)

INSERT INTO @TempTable VALUES
('Serkan1', 'Arslan1', 'A','M','1',NULL),
('Serkan2', 'Arslan2', 'B','M','1',NULL),
('Serkan3', 'Arslan', 'C','M','1',NULL),
('Serkan3', 'Arslan', 'D','M','1',NULL)

DECLARE @myTable TABLE (
ID  INT identity(100,1), -- started from 100 for see the difference
FirstName VARCHAR(10),
LastName VARCHAR(10))

结果:

MyTable的

ID          FirstName  LastName
----------- ---------- ----------
100         Serkan1    Arslan1
101         Serkan2    Arslan2
102         Serkan3    Arslan
103         Serkan3    Arslan

不是Temptable

FirstName  LastName   DOB        Sex        Age        ExternalID
---------- ---------- ---------- ---------- ---------- -----------
Serkan1    Arslan1    A          M          1          100
Serkan2    Arslan2    B          M          1          101
Serkan3    Arslan     C          M          1          102
Serkan3    Arslan     D          M          1          103

答案 3 :(得分:0)

尝试使用MERGE代替INSERT

MERGE允许您输出未插入的列,例如临时表上的标识符。使用此方法,您可以构建另一个临时表,该临时表将您的临时表映射到插入的行(在下面的示例中名为@TempIdTable)。

首先,给#TempTable自己的主键。我称它为TempId。我还将假设您在#TempTable上有一列存储从MyTableID返回的主键。

--Make a place to store the associated ID's
DECLARE @TempIdTable TABLE
    ([TempId] INT NOT NULL
    ,[ID] INT NOT NULL)

--Will only insert, as 1 never equals 0.
MERGE INTO myTable
USING #TempTable AS tt
    ON 1 = 0
WHEN NOT MATCHED
    THEN
        INSERT ([FirstName]
                ,[LastName])
        VALUE (t.[FirstName]
                ,t.[LastName])
OUTPUT tt.[TempId], inserted.[ID] --Here's the magic
INTO @TempIdTable

--Associate the new primary keys with the temp table
UPDATE #TempTable
SET [ID] = t.[ID]
FROM @TempIdTable t
WHERE #TempTable.[TempId] = t.[TempId]

我正在研究类似的问题,并在这里找到了这个窍门:Is it possible to for SQL Output clause to return a column not being inserted?

这是我在自己的测试中使用的完整代码。

CREATE TABLE [MQ]
    ([MESSAGEID] INT IDENTITY PRIMARY KEY
    ,[SUBJECT] NVARCHAR(255) NULL);

CREATE TABLE [MR]
    ([MESSAGESEQUENCE] INT IDENTITY PRIMARY KEY
    ,[TO] NVARCHAR(255) NOT NULL
    ,[CC] NVARCHAR(255) NOT NULL
    ,[BCC] NVARCHAR(255) NOT NULL);

CREATE TABLE #Messages (
    [subject] nvarchar(255) NOT NULL
    ,[to] nvarchar(255) NOT NULL
    ,[cc] nvarchar(255) NULL
    ,[bcc] nvarchar(255) NULL
    ,[MESSAGEID] INT NULL
    ,[sortKey] INT IDENTITY PRIMARY KEY
    );

INSERT INTO #Messages
VALUES ('Subject1','to1','cc1','bcc1', NULL)
    ,('Subject2','to2', NULL, NULL, NULL);

SELECT * FROM #Messages;

DECLARE @outputSort TABLE (
    [sortKey] INT NOT NULL
    ,[MESSAGEID] INT NOT NULL
    ,[subject] NVARCHAR(255)
    );

MERGE INTO [MQ]
USING #Messages M
    ON 1 = 0
WHEN NOT MATCHED
    THEN
        INSERT ([SUBJECT])
        VALUES (M.[subject])
OUTPUT M.[SORTKEY]
    ,inserted.[MESSAGEID]
    ,inserted.[SUBJECT]
INTO @outputSort;

SELECT * FROM @outputSort;

SELECT * FROM [MQ];

UPDATE #Messages
SET MESSAGEID = O.[MESSAGEID]
FROM @outputSort O
WHERE #Messages.[sortKey] = O.[sortKey];

SELECT * FROM #Messages;

DROP TABLE #Messages;

答案 4 :(得分:0)

一种方法是将数据复制到第二个临时表中,如下所示:

let ALLOWED_ORIGINS = ["http://serverabc.com", "http://localhost:8080"];
app.use((req, res, next) => {
    let origin = req.headers.origin;
    let theOrigin = (ALLOWED_ORIGINS.indexOf(origin) >= 0) ? origin : ALLOWED_ORIGINS[0];
    res.header("Access-Control-Allow-Origin", theOrigin);
    res.header("Access-Control-Allow-Headers", "Origin, X-Requested-With, Content-Type, Accept");

    next();
})