To answer your question literally:
When does SELECT not make a table as an output
Never!
The SELECT
returns a result set, consisting of rows, and each row consisting of columns.
There is a special situation, when your result set comes back with exactly one row with one single column. This can be taken as a scalar value and therefore be assigned to a variable.
You can enforce a single row with a TOP 1
or with a WHERE
-clause that guarantees to return never more than one row. But the engine will not see this in advance. Your provided code mentions two columns, the one with the QUOTENAME()
and - after the comma - once more the category_name
. That's why you get the exception.
It is a similar situation, when you want to test a list, e.g. with
WHERE SomeID IN(SELECT TheId FROM AnotherTable)
The SELECT within the IN()
will return a list of ID values, but there will be just one column.
UPDATE Some hints upon your code...
What you are doing here:
SELECT
@columns += QUOTENAME(category_name) + ',',
category_name
FROM
... is called quirky update and might work without the second mention of category_name
. It is roughly an iteration through the table's values where each row adds to the previously set value of @column
.
This approach is very dangerous and should be avoided (or used with some backgorund knowledge).
Starting with v2017 there is STRING_AGG()
and before I'd prefer the approach with FOR XML PATH('')
Example
Try this:
USE master;
DECLARE @columns VARCHAR(MAX)='';
--Your approach with the quirky update
SELECT
@columns += QUOTENAME(COLUMN_NAME) + ','
FROM
INFORMATION_SCHEMA.COLUMNS WHERE TABLE_NAME='spt_fallback_db';
SELECT @columns;
--The recommended approach with XML
SET @columns=
STUFF(
(SELECT ','+QUOTENAME(COLUMN_NAME)
FROM
INFORMATION_SCHEMA.COLUMNS WHERE TABLE_NAME='spt_fallback_db'
FOR XML PATH(''),TYPE).value('.','nvarchar(max)'),1,1,'');
SELECT @columns;
--and - v2017+ needed - with STRING_AGG()
SELECT @columns=STRING_AGG(QUOTENAME(COLUMN_NAME),',')
FROM
INFORMATION_SCHEMA.COLUMNS WHERE TABLE_NAME='spt_fallback_db';
SELECT @columns;