表值构造函数 (Transact-SQL)

适用于:SQL ServerAzure SQL 数据库Azure SQL 托管实例Microsoft Fabric SQL 数据库

指定要构建到某一表中的一组行值表达式。 Transact-SQL 表值构造函数允许在单个 DML 语句中指定多行数据。 可以将表值构造函数指定为VALUES语句的INSERT ... VALUES子句,也可以指定为语句或USING子句的子句MERGE中的FROM派生表。

Transact-SQL 语法约定

语法

VALUES ( <row value expression list> ) [ ,...n ]   

<row value expression list> ::=  
    {<row value expression> } [ ,...n ]  

<row value expression> ::=  
    { DEFAULT | NULL | expression }  

参数

价值观

介绍行值表达式列表。 每个列表都必须用括号括起来并由逗号分隔。

在每个列表中指定的值的数目必须相同,并且值必须采用与表中的列相同的顺序。 表中每个列的值必须指定,或者列列表必须显式为每个传入值指定列。

违约

强制数据库引擎插入为列定义的默认值。 如果列不存在默认值,并且该列允许 null 值, NULL 则插入。 DEFAULT 对标识列无效。 在表值构造函数中指定时, DEFAULT 只允许在语句中使用 INSERT

expression

常量、变量或表达式。 表达式不能包含语句 EXECUTE

局限性

当指定为派生表时,行数没有限制。

用作 VALUES 语句的 INSERT ... VALUES 子句时,限制为 1,000 行。 如果行数超过最大值,则返回错误 10738。 若要插入 1,000 多行,请使用以下方法之一:

只允许单个标量值作为行值表达式。 涉及多列的子查询不允许作为行值表达式。 例如,以下代码导致语法错误,因为第三个行值表达式列表包含具有多列的子查询。

USE AdventureWorks2022;  
GO  
CREATE TABLE dbo.MyProducts (Name VARCHAR(50), ListPrice MONEY);  
GO  
-- This statement fails because the third values list contains multiple columns in the subquery.  
INSERT INTO dbo.MyProducts (Name, ListPrice)  
VALUES ('Helmet', 25.50),  
       ('Wheel', 30.00),  
       (SELECT Name, ListPrice FROM Production.Product WHERE ProductID = 720);  
GO  

但是,可以通过单独在子查询中指定每一列,重新编写该语句。 下面的示例成功地将三行插入 MyProducts 表中。

INSERT INTO dbo.MyProducts (Name, ListPrice)  
VALUES ('Helmet', 25.50),  
       ('Wheel', 30.00),  
       ((SELECT Name FROM Production.Product WHERE ProductID = 720),  
        (SELECT ListPrice FROM Production.Product WHERE ProductID = 720));  
GO  

数据类型

多行 INSERT 语句中指定的值遵循语法的 UNION ALL 数据类型转换属性。 这会导致不匹配的类型隐式转换为更高 数据类型优先级的类型。 如果此转换不是所支持的隐式转换,则返回错误。 例如,以下语句将整数值和字符值插入到类型为 char 的列中

CREATE TABLE dbo.t (a INT, b CHAR);  
GO  
INSERT INTO dbo.t VALUES (1,'a'), (2, 1);  
GO  

INSERT运行语句时,SQL Server 会尝试将“a”转换为整数,因为数据类型优先级指示整数的类型高于字符。 转换失败,并且返回错误。 您可以根据需要显式转换值,从而避免发生此错误。 例如,前面的语句可以编写为:

INSERT INTO dbo.t VALUES (1,'a'), (2, CONVERT(CHAR,1));  

示例

答: 插入多行数据

下面的示例创建表 dbo.Departments,然后使用表值构造函数将五行数据插入到该表中。 由于提供了所有列的值并按表中各列的顺序列出这些值,因此不必在列列表中指定列名。

USE AdventureWorks2022;  
GO  
INSERT INTO Production.UnitMeasure  
VALUES (N'FT2', N'Square Feet ', '20080923'), (N'Y', N'Yards', '20080923'),
       (N'Y3', N'Cubic Yards', '20080923');  
GO  

B. 插入包含 DEFAULT 和 NULL 值的多行

下面的示例演示了 DEFAULT 指定和使用 NULL 表值构造函数将行插入表中时。

USE AdventureWorks2022;  
GO  
CREATE TABLE Sales.MySalesReason(  
SalesReasonID int IDENTITY(1,1) NOT NULL,  
Name dbo.Name NULL ,  
ReasonType dbo.Name NOT NULL DEFAULT 'Not Applicable' );  
GO  
INSERT INTO Sales.MySalesReason   
VALUES ('Recommendation','Other'), ('Advertisement', DEFAULT), (NULL, 'Promotion');  

SELECT * FROM Sales.MySalesReason;  

C. 在 FROM 子句中将多个值指定为派生表

以下示例使用表值构造函数在语句的FROM子句中SELECT指定多个值。

SELECT a, b FROM (VALUES (1, 2), (3, 4), (5, 6), (7, 8), (9, 10) ) AS MyTable(a, b);  
GO  
-- Used in an inner join to specify values to return.  
SELECT ProductID, a.Name, Color  
FROM Production.Product AS a  
INNER JOIN (VALUES ('Blade'), ('Crown Race'), ('AWC Logo Cap')) AS b(Name)   
ON a.Name = b.Name;  

D. 在 MERGE 语句中将多个值指定为派生源表

以下示例通过 MERGE 更新或插入行来修改 SalesReason 表。 当源表中的值NewName与目标表 (Name) 列中的值SalesReason匹配时,目标表中的ReasonType列将更新。 当 NewName 的值不匹配时,就会将源行插入到目标表中。 此源表是一个派生表,它使用 Transact-SQL 表值构造函数指定源表的多个行。

USE AdventureWorks2022;  
GO  
-- Create a temporary table variable to hold the output actions.  
DECLARE @SummaryOfChanges TABLE(Change VARCHAR(20));  

MERGE INTO Sales.SalesReason AS Target  
USING (VALUES ('Recommendation','Other'), ('Review', 'Marketing'), ('Internet', 'Promotion'))  
       AS Source (NewName, NewReasonType)  
ON Target.Name = Source.NewName  
WHEN MATCHED THEN  
UPDATE SET ReasonType = Source.NewReasonType  
WHEN NOT MATCHED BY TARGET THEN  
INSERT (Name, ReasonType) VALUES (NewName, NewReasonType)  
OUTPUT $action INTO @SummaryOfChanges;  

-- Query the results of the table variable.  
SELECT Change, COUNT(*) AS CountPerChange  
FROM @SummaryOfChanges  
GROUP BY Change;  

E. 插入超过 1,000 行

以下示例演示如何将表值构造函数用作派生表。 这允许从单个表值构造函数插入 1,000 多行。

CREATE TABLE dbo.Test ([Value] INT);  

INSERT INTO dbo.Test ([Value])  
  SELECT drvd.[NewVal]
  FROM   (VALUES (0), (1), (2), (3), ..., (5000)) drvd([NewVal]);