【问题标题】:When does SELECT not make a table as an outputSELECT 何时不将表格作为输出
【发布时间】:2019-10-24 09:16:12
【问题描述】:

我是 SQL 新手,我正在学习网络教程。我遇到了一段代码,其中 SELECT 语句为变量赋值。我怀疑这样的 SELECT 语句不会因此生成表。例如。当我想将分配与列名混合时:

DECLARE 
    @columns NVARCHAR(MAX) = '';

SELECT 
    @columns += QUOTENAME(category_name) + ',',
    category_name
FROM 
    production.categories
ORDER BY 
    category_name;

SET @columns = LEFT(@columns, LEN(@columns) - 1);

PRINT @columns;

我收到以下错误:

消息 141,第 15 级,状态 1,第 4 行
为变量赋值的 SELECT 语句不得与数据检索操作结合使用。

(通过database在互联网上关注此course

我的问题是,是否存在另一种形式的数据操作(作为赋值)在 SELECT 语句中不会生成表(如果我的假设是正确的)。

【问题讨论】:

  • 错误告诉你问题所在。您正在尝试分配@columns 的值返回category_name 的值。恐怕这个错误再清楚不过了。 SELECT 只能做一个或另一个,但不能同时做。
  • 我不明白你的问题:如果存在另一种形式的数据操作(作为赋值),在 SELECT 语句中不会生成表(如果我的假设是正确的) ..如果您从该语句中取出category_name,它将是一个 SELECT 执行分配而不产生结果
  • 是的,你是对的。但我的问题是:“是否存在另一种形式的数据操作(作为赋值),在 SELECT 语句中不会生成表(如果我的假设是正确的)。”
  • 如果你想把所有的值赋值给变量,下面的代码会帮助你; @columns = STRING_AGG(category_name,';') WITHIN GROUP (ORDER BY category_name)

标签: sql-server tsql select variable-assignment


【解决方案1】:

无论你做什么,SELECT 总是会返回一个 TABLE。

创建具有大量条件的存储过程让您可以选择返回 TABLE 或返回 VARIABLE。

DECLARE @VARIABLE_INPUT VARCHAR(50);
DECLARE @VARIABLE_OUTPUT VARCHAR(50);

SET @VARIABLE_INPUT = 'THIS_IS_INPUT';
EXEC storedProcedureTEST @VARIABLE_INPUT, @VARIABLE_OUTPUT OUTPUT;

PRINT 'The Output of storedProcedureTEST is: ' + @VARIABLE_OUTPUT ;

在你的storedProcedureTEST:

CREATE PROCEDURE storedProcedureTEST
    @VARIABLE_INPUT VARCHAR(50),
    @VARIABLE_OUTPUT VARCHAR(50)='' OUTPUT
AS
BEGIN
    SET @VARIABLE_OUTPUT = @VARIABLE_INPUT + '_ABC';
END

结果:

storedProcedureTEST 的输出为:THIS_IS_INPUT_ABC

【讨论】:

  • 这可能是一个品味问题,但在我看来,为此使用存储过程是一个非常糟糕的建议。最好的是一个内联 TVF,您可以通过 APPLY 查询。这个 SP 迫使你进入程序性思维,远离更好的基于集合的思维...
  • 你的答案实际上并没有回答这个问题...... OP想要创建一个表中找到的所有category_name值的串联列表......我希望你不建议使用@ 987654328@连同这个SP...
【解决方案2】:

从字面上回答你的问题:

SELECT 何时不将表格作为输出

永远不会!

SELECT 返回一个结果集,由行组成,每行由列组成。

有一种特殊情况,当您的结果集返回时正好一行单列。这可以作为一个标量值,因此被分配给一个变量。

您可以使用TOP 1WHERE 子句强制执行单行,以保证返回不超过一行。但引擎不会提前看到这一点。您提供的代码提到了两列,一列带有 QUOTENAME() 和 - 在逗号之后 - 又是 category_name。这就是你得到异常的原因。

这是一个类似的情况,当你想测试一个列表时,例如与

WHERE SomeID IN(SELECT TheId FROM AnotherTable)

IN() 中的 SELECT 将返回 ID 值列表,但只有一列。

更新您的代码的一些提示...

你在这里做什么:

SELECT 
    @columns += QUOTENAME(category_name) + ',',
    category_name
FROM 

... 被称为 quirky update 并且可以在没有第二次提及 category_name 的情况下工作。它大致是对表格值的迭代,其中每一行添加到先前设置的@column值。

这种方法非常危险,应该避免(或与某些backgorund knowledge 一起使用)。

从 v2017 开始有 STRING_AGG() 之前我更喜欢使用 FOR XML PATH('') 的方法

示例

试试这个:

USE master;
DECLARE @columns VARCHAR(MAX)='';

--你的古怪更新方法

SELECT 
    @columns += QUOTENAME(COLUMN_NAME) + ',' 
FROM 
INFORMATION_SCHEMA.COLUMNS WHERE TABLE_NAME='spt_fallback_db';

SELECT @columns;

--使用 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;

--并且 - 需要 v2017+ - 使用 STRING_AGG()

SELECT @columns=STRING_AGG(QUOTENAME(COLUMN_NAME),',')
FROM 
INFORMATION_SCHEMA.COLUMNS WHERE TABLE_NAME='spt_fallback_db';

SELECT @columns;

【讨论】:

  • 感谢您的回答,Shnugo。它让我对 SELECT 的运作方式有所了解。
【解决方案3】:

你想从行转换为单行列

DECLARE 
    @columns NVARCHAR(MAX) = '';

set @columns = (select concat(quotename ,' : ', category_name) from production.categories order by category_name for xml path(''))


PRINT @columns;

【讨论】:

  • 谢谢,这是开箱即用的快速解决方案。
猜你喜欢
  • 2012-09-05
  • 1970-01-01
  • 2021-08-03
  • 1970-01-01
  • 2023-01-28
  • 1970-01-01
  • 1970-01-01
  • 2019-11-20
  • 2017-04-02
相关资源
最近更新 更多