英文:
How can I do an insert for two tables at the same time using SCOPE_IDENTITY()?
问题
第一张表很容易,因为我可以直接使用SELECT语句进行插入。
然而,我还需要用第一张表中自动生成的cameraId来填充第二张表。
但是当我尝试运行查询时,出现了这个错误:
'SCOPE_IDENTITY()'附近的语法不正确
代码:
INSERT INTO dbo.camera
(
cameraId, --自动生成的IDENTITY种子值
productName,
productDesc,
productCode
)
SELECT CAMERA_ID, PRODUCT_NAME, PRODUCT_DESCRIPTION, PRODUCT_CODE
FROM [SQL_CANADA].[BASE].[dbo].PRODUCT
WHERE ORIGIN_ID = 19327761
WAITFOR DELAY '00:00:02';
INSERT INTO dbo.cameraLense
(
lenseId,
cameraId, --SCOPE_IDENTITY() ??
type,
materialId,
isCurrentYear,
modelNumber
)
SELECT LENSE_ID, SCOPE_IDENTITY(), LENSE_TYPE, MATERIAL_ID, IS_CURRENT_YEAR, MODEL_NUMBER
FROM [SQL_CANADA].[BASE].[dbo].PRODUCT
WHERE ORIGIN_ID = 19327761
有什么方法可以让这个工作吗?
谢谢!
英文:
I have 2 tables that I need to populate with 100 rows from a remote linked database.
The first table is easy, because I can just do a simple INSERT from a SELECT.
However, I also need to populate a second table, using the cameraId (which is automatically generated) from the first table.
But when I try to run the queries, I get this error:
> Incorrect syntax near 'SCOPE_IDENTITY()'
Code:
INSERT INTO dbo.camera
(
cameraId, --AUTOMATICALLY GENERATED IDENTITY SEED VALUE
productName,
productDesc,
productCode
)
SELECT CAMERA_ID, PRODUCT_NAME, PRODUCT_DESCRIPTION, PRODUCT_CODE
FROM [SQL_CANADA].[BASE].[dbo].PRODUCT
WHERE ORIGIN_ID = 19327761
WAITFOR DELAY '00:00:02';
INSERT INTO dbo.cameraLense
(
lenseId,
cameraId, --SCOPE_IDENTITY() ??
type,
materialId,
isCurrentYear,
modelNumber
)
SELECT LENSE_ID, SCOPE_IDENTITY(), LENSE_TYPE, MATERIAL_ID, IS_CURRENT_YEAR, MODEL_NUMBER
FROM [SQL_CANADA].[BASE].[dbo].PRODUCT
WHERE ORIGIN_ID = 19327761
Is there anything that I could do to make this work?
Thanks!
答案1
得分: 2
SCOPE_IDENTITY()
不是你想要的神奇工具,也不应该真正使用它。相反,使用带有你的插入操作的 OUTPUT
子句。
/* 演示对象 */
DROP TABLE IF EXISTS #Cameras; DROP TABLE IF EXISTS #CameraLenses;
CREATE TABLE #Cameras (CameraID BIGINT IDENTITY PRIMARY KEY, ProductName NVARCHAR(50), ProductDesc NVARCHAR(200), ProductCode NVARCHAR(6));
CREATE TABLE #CameraLenses (LensID BIGINT IDENTITY PRIMARY KEY, CameraID BIGINT FOREIGN KEY REFERENCES #Cameras (CameraID), LensType NVARCHAR(20), MaterialID BIGINT, IsCurrentYear BIT, ModelNumber NVARCHAR(10));
在临时表上创建外键没有意义,除非用于演示,你可以忽略插入期间返回的警告。
DECLARE @Outputs TABLE (InsertedIdent BIGINT, InsertedProductCode NVARCHAR(6));
INSERT INTO #Cameras (ProductName, ProductDesc, ProductCode)
OUTPUT INSERTED.CameraID, INSERTED.ProductCode INTO @Outputs
SELECT ProductName, ProductDesc, ProductCode
FROM (
SELECT 'UberCam' AS ProductName, 'Fine German craftsmanship' AS ProductDesc, 'UBER01' AS ProductCode
UNION ALL
SELECT 'AffreuseCam', 'Shoddy French knock-off', 'AFFR01'
/* 你可以用一个 UDTT 替代它来传递一个相机表 */
) a;
INSERT INTO #CameraLenses (CameraID, LensType, MaterialID, IsCurrentYear, ModelNumber)
SELECT o.InsertedIdent, Type, MaterialID, IsCurrentYear, ModelNumber
FROM (
SELECT 'Digital' AS Type, 1 AS MaterialID, 1 AS IsCurrentYear, 'UBERLENS01' AS ModelNumber, 'UBER01' AS CameraProductCode
UNION ALL
SELECT 'Flash Bulb', 2, 0, 'AFFRLEBS01', 'AFFR01'
UNION ALL
SELECT 'Microscope', 2, 0, 'UBERLENS02', 'UBER01'
/* 你可以用一个 UDTT 替代它来传递一个相机镜头表 */
) a
INNER JOIN @Outputs o
ON a.CameraProductCode = o.InsertedProductCode;
SELECT c.CameraID, c.ProductName, c.ProductDesc, c.ProductCode, cl.LensID, cl.CameraID, cl.LensType, cl.MaterialID, cl.IsCurrentYear, cl.ModelNumber
FROM #Cameras c
INNER JOIN #CameraLenses cl
ON c.CameraID = cl.CameraID;
这里发生了一些事情,让我们逐步解释一下。
首先,我们声明了一个表变量,用于保存新生成的标识列和我们已经知道的某些内容,以便将它们关联起来。
然后,我们对 #Cameras
表执行插入操作。在 INSERT
和 SELECT
之间嵌套了 OUTPUT
。在这里,我们只是要求将伪表 INSERTED
中的值放入其中。
接下来,我们对 #Lenses
表执行插入操作。这次,我们在表变量中有数据,所以我们能够与它进行连接,并收集我们刚刚为 #Cameras
插入的值,并在插入期间引用它们。
最后,我们快速选择刚刚插入的表,以查看发生了什么。
继续深入,你还可以使用用户定义的表类型(User Defined Table Types,UDTT)来传递要插入的值,但它们带有警告。基本上,你可以创建一个 UDTT 来传递一个表。你在 UDTT 创建中定义表的结构,然后可以将其用作任何其他数据类型:
DECLARE @table NewInvoiceTable;
INSERT INTO @table (CustomerID, EmployeeID ...) VALUES
(1, 2 ...),(2, 2 ...);
你只能使用 READONLY
标签将它们定义为参数:
CREATE PROCEDURE dbo.InsertCustomers @NewCustomers NewCustomerTable READONLY...
如果有东西引用它,你不能删除一个 UDTT
,而且你不能在任何情况下使用 ALTER TYPE
。这使得在需要更改它们时变得很麻烦。
英文:
SCOPE_IDENTITY()
is not the magic you want it to be, and shouldn't really be used. Instead, use the OUTPUT
clause with your insert.
/* Demo objects */
DROP TABLE IF EXISTS #Cameras; DROP TABLE IF EXISTS #CameraLenses;
CREATE TABLE #Cameras (CameraID BIGINT IDENTITY PRIMARY KEY, ProductName NVARCHAR(50), ProductDesc NVARCHAR(200), ProductCode NVARCHAR(6));
CREATE TABLE #CameraLenses (LensID BIGINT IDENTITY PRIMARY KEY, CameraID BIGINT FOREIGN KEY REFERENCES #Cameras (CameraID), LensType NVARCHAR(20), MaterialID BIGINT, IsCurrentYear BIT, ModelNumber NVARCHAR(10));
There's no point creating a FK on a temp table, expect for illustration, you can ignore the warning you get back during the insert.
DECLARE @Outputs TABLE (InsertedIdent BIGINT, InsertedProductCode NVARCHAR(6));
INSERT INTO #Cameras (ProductName, ProductDesc, ProductCode)
OUTPUT INSERTED.CameraID, INSERTED.ProductCode INTO @Outputs
SELECT ProductName, ProductDesc, ProductCode
FROM (
SELECT 'UberCam' AS ProductName, 'Fine German craftsmanship' AS ProductDesc, 'UBER01' AS ProductCode
UNION ALL
SELECT 'AffreuseCam', 'Shoddy French knock-off', 'AFFR01'
/* you can substitute this for a UDTT to pass in a table of cameras */
) a;
INSERT INTO #CameraLenses (CameraID, LensType, MaterialID, IsCurrentYear, ModelNumber)
SELECT o.InsertedIdent, Type, MaterialID, IsCurrentYear, ModelNumber
FROM (
SELECT 'Digital' AS Type, 1 AS MaterialID, 1 AS IsCurrentYear, 'UBERLENS01' AS ModelNumber, 'UBER01' AS CameraProductCode
UNION ALL
SELECT 'Flash Bulb', 2, 0, 'AFFRLEBS01', 'AFFR01'
UNION ALL
SELECT 'Microscope', 2, 0, 'UBERLENS02', 'UBER01'
/* you can substitute this for a UDTT to pass in a table of camera lenses */
) a
INNER JOIN @Outputs o
ON a.CameraProductCode = o.InsertedProductCode;
SELECT c.CameraID, c.ProductName, c.ProductDesc, c.ProductCode, cl.LensID, cl.CameraID, cl.LensType, cl.MaterialID, cl.IsCurrentYear, cl.ModelNumber
FROM #Cameras c
INNER JOIN #CameraLenses cl
ON c.CameraID = cl.CameraID;
There's a few things going on here, so let's walk through it.
First, we're declaring a table variable which we will use to hold pairs of the newly generated identity column, and something we already know about to link back to it.
Then we do an insert into the #Cameras
table. Nestled in there between the INSERT
and SELECT is the
OUTPUT. All we're doing here is asking for the values from the pseudo table
INSERTED` to be stuffed into it.
Next we do the insert into the #Lenses
table. This time we have the data in our table variable, so we're able to join to it and collect the values we just inserted for #Cameras
and reference them during our insert.
Finally, we do a quick select on the tables we just inserted into to review what happened.
CameraID | ProductName | ProductDesc | ProductCode | LensID | CameraID | LensType | MaterialID | IsCurrentYear | ModelNumber |
---|---|---|---|---|---|---|---|---|---|
1 | UberCam | Fine German craftsmanship | UBER01 | 1 | 1 | Digital | 1 | 1 | UBERLENS01 |
2 | AffreuseCam | Shoddy French knock-off | AFFR01 | 2 | 2 | Flash Bulb | 2 | 0 | AFFRLEBS01 |
1 | UberCam | Fine German craftsmanship | UBER01 | 3 | 1 | Microscope | 2 | 0 | UBERLENS02 |
Going further with this, you could also use User Defined Table Types to pass in the values you're going to insert, but they do come with a warning. Essentially you can create a UDTT
to pass in a table. You define the table structure in the UDTT create, and then you can use it as if it were any other data type:
DECLARE @table NewInvoiceTable;
INSERT INTO @table (CustomerID, EmployeeID ...) VALUES
(1, 2 ...),(2, 2 ...);
You can only define them as parameters using the READONLY
tag:
CREATE PROCEDURE dbo.InsertCustomers @NewCustomers NewCustomerTable READONLY...
You cannot drop a UDTT
while something is referencing it, and you cannot ALTER TYPE
at all. This makes it a pain if you need to change them.
通过集体智慧和协作来改善编程学习和解决问题的方式。致力于成为全球开发者共同参与的知识库,让每个人都能够通过互相帮助和分享经验来进步。
评论