In my Are you ready for SQL Server 2012 or are you still partying like it is 1999? post, I wrote about how you should start using SQL Server 2005 and SQL Server 2008 functionality now in order to prepare for SQL Server 2012. I still see tons of code that is written in the pre 2005 style and people still keep using those functions, procs and statements even though SQL Server 2005 and 2008 have much better functionality.

Today we are going to take a look at the Table Value Constructor, sometimes also called Row Value Constructor. The Table Value Constructor allows multiple rows of data to be specified in a single DML statement. Instead of doing the following

T-SQL
1
2
3
4
INSERT TABLE
SELECT 3, '20020101','B' UNION ALL 
SELECT 5, '20030101','C' UNION ALL 
SELECT 7, '20040101','D'
INSERT TABLE
SELECT 3, '20020101','B' UNION ALL 
SELECT 5, '20030101','C' UNION ALL 
SELECT 7, '20040101','D'

you can do this

T-SQL
1
2
3
4
5
INSERT TABLE
VALUES
(3, '20020101','B'), 
(5, '20030101','C'), 
(7, '20040101','D')
INSERT TABLE
VALUES
(3, '20020101','B'), 
(5, '20030101','C'), 
(7, '20040101','D')

Here are some limitations as described in Books On Line

The maximum number of rows that can be constructed using the table value constructor is 1000. To insert more than 1000 rows, create multiple INSERT statements, or bulk import the data by using the bcp utility or the BULK INSERT statement.

Only single scalar values are allowed as a row value expression. A subquery that involves multiple columns is not allowed as a row value expression

Let’s get started, first create this simple table

T-SQL
1
2
CREATE TABLE Test(ID int,SomeDate date,SomeChar char(1))
GO
CREATE TABLE Test(ID int,SomeDate date,SomeChar char(1))
GO

Here is how to do a simple insert

T-SQL
1
2
3
4
5
6
7
INSERT Test
VALUES  (1, '20010101','A'),
        (3, '20020101','B'), 
        (5, '20030101','C'), 
        (7, '20040101','D'), 
        (9, '20050101','E')
GO
INSERT Test
VALUES  (1, '20010101','A'),
		(3, '20020101','B'), 
		(5, '20030101','C'), 
		(7, '20040101','D'), 
		(9, '20050101','E')
GO

Now if we check the table

T-SQL
1
SELECT * FROM Test
SELECT * FROM Test

ID	SomeDate	SomeChar
--  ----------  --------
1	2001-01-01	A
3	2002-01-01	B
5	2003-01-01	C
7	2004-01-01	D
9	2005-01-01	E

We can see we have 5 rows. You can also use queries instead of hard coding values, below is an example

T-SQL
1
2
3
4
5
6
7
8
9
INSERT Test
VALUES  (1, '20010101','A'),
        (3, '20020101','B'), 
        (5, '20030101','C'), 
        (7, '20040101','D'), 
        (9, '20050101','E'),
        ((SELECT id FROM Test where id = 1),
                (SELECT SomeDAte FROM Test where id = 1),
                (SELECT SomeChar FROM Test where id = 1))
INSERT Test
VALUES  (1, '20010101','A'),
		(3, '20020101','B'), 
		(5, '20030101','C'), 
		(7, '20040101','D'), 
		(9, '20050101','E'),
		((SELECT id FROM Test where id = 1),
				(SELECT SomeDAte FROM Test where id = 1),
				(SELECT SomeChar FROM Test where id = 1))

Now, we should have 3 rows with the value 1 for ID

T-SQL
1
2
SELECT * FROM Test 
WHERE id = 1
SELECT * FROM Test 
WHERE id = 1

ID  SomeDate	SomeChar
--  ----------  --------
1   2001-01-01	A
1   2001-01-01	A
1   2001-01-01	A

Remember the warning from before: A subquery that involves multiple columns is not allowed as a row value expression?
If you try to run the same insert query, you will get the error message below, this is because the subquery now returns more than 1 value for the rows where the value for ID is 1

Msg 512, Level 16, State 1, Line 1
Subquery returned more than 1 value. This is not permitted when the subquery follows =, !=, <, <= , >, >= or when the subquery is used as an expression.
The statement has been terminated.

Besides using Table Value Constructor for inserts, you can also use them for selects
The following UNION query can be rewritten to be a Table Value Constructor

T-SQL
1
2
3
4
5
6
SELECT 1 as a, '20010101' as b,'A' as c UNION ALL
SELECT 3, '20020101','B' UNION ALL 
SELECT 5, '20030101','C' UNION ALL 
SELECT 7, '20040101','D' UNION ALL 
SELECT 9, '20050101','E'
GO
SELECT 1 as a, '20010101' as b,'A' as c UNION ALL
SELECT 3, '20020101','B' UNION ALL 
SELECT 5, '20030101','C' UNION ALL 
SELECT 7, '20040101','D' UNION ALL 
SELECT 9, '20050101','E'
GO

Here is what the Table Value Constructor version looks like, I think it is a little neater

T-SQL
1
2
3
4
5
6
7
8
SELECT *
FROM (VALUES
        (1, '20010101','A'),
        (3, '20020101','B'), 
        (5, '20030101','C'), 
        (7, '20040101','D'), 
        (9, '20050101','E') ) AS SomeTable(a, b,c);
GO
SELECT *
FROM (VALUES
		(1, '20010101','A'),
		(3, '20020101','B'), 
		(5, '20030101','C'), 
		(7, '20040101','D'), 
		(9, '20050101','E') ) AS SomeTable(a, b,c);
GO

a   b		c
--  --------    --
1   20010101	A
3   20020101	B
5   20030101	C
7   20040101	D
9   20050101	E

That is all for today, come back tomorrow for part 13