为什么我不能像我想象的那样在 T-SQL 中使用变量?

gar*_*eth 20 sql-server-2008 t-sql

原谅我,我是一名已经转向 SQL 世界的开发人员。我以为我可以通过添加变量来改进一些 SQL,但它并没有像我预期的那样运行。有人能告诉我为什么这不起作用吗?我不想解决问题,我想知道为什么这不像我想象的那样工作,因为我确信有一个很好的理由,但目前它并没有跳出来。

DECLARE @DatabaseName varchar(150)
SET @DatabaseName = 'MyAmazingDatabaseName'

CREATE DATABASE @DatabaseName
GO

USE @DatabaseName
GO
Run Code Online (Sandbox Code Playgroud)

Bob*_*mes 21

根据变量的书籍在线页面

变量只能在表达式中使用,不能代替对象名称或关键字。要构造动态 SQL 语句,请使用 EXECUTE。

例如,如果您在 where 子句中使用了您的变量,它将以您期望的方式工作。至于为什么,我认为这与解析器无法评估变量并因此检查是否存在有关。执行时,首先解析查询的语法和对象,然后,如果解析成功,则执行查询,此时变量将被设置。

DECLARE @name varchar(20);
SET @name = 'test';

CREATE TABLE [#tmp]([val] varchar(10));

insert into #tmp
values('test')

SELECT *
FROM [#tmp]
WHERE [val] = @name;
Run Code Online (Sandbox Code Playgroud)

  • 请注意,应尽可能避免使用动态 SQL。它类似于在 JavaScript 和 Python 等过程语言中使用 `eval` 函数的 SQL 类比。这是创建安全漏洞的快速方法。 (3认同)
  • @jpmc26:OP 的示例看起来像“代码优先”ORM 可能会在数据库中设置表的操作。虽然原则上动态 SQL 是不安全的,但最终用户永远不会接触该特定代码。 (2认同)

gra*_*j42 18

在 SQL 语句中使用变量的限制源于 SQL 的体系结构。

SQL 语句的处理分为三个阶段:

  1. 准备 - 解析语句并编译执行计划,指定访问哪些数据库对象,如何访问它们以及它们如何相关。执行计划保存在计划缓存中
  2. 绑定 - 语句中的任何变量都替换为实际值。
  3. 执行 - 使用绑定值执行缓存计划。

SQL Server 对程序员隐藏了准备步骤,并且执行速度比 Oracle 和 DB2 等更传统的数据库快得多。出于性能原因,SQL 可能会花费大量时间来确定最佳执行计划,但仅在重新启动后第一次遇到该语句时才这样做。

所以在静态 SQL 中,变量只能用在不会使执行计划失效的地方,所以不能用于表名、列名(包括 WHERE 条件中的列名)等。

动态 SQL存在于无法绕过限制的情况下,并且程序员知道执行时间会稍长一些。动态 SQL 容易受到恶意代码注入,所以要小心!


Bra*_*adC 7

如您所见,“为什么”问题需要不同类型的答案,包括语言的历史基本原理和基本假设,我不确定我是否真的能做到这一点。

SQL MVP Erland Sommarskog 的这篇综合性文章确实试图提供一些基本原理以及机制:

动态 SQL 的诅咒和祝福

缓存查询计划

您在 SQL Server 中运行的每个查询都需要一个查询计划。当您第一次运行查询时,SQL Server 会为其构建一个查询计划——或者按照术语的说法——它会编译查询。SQL Server 将计划保存在缓存中,下次运行查询时,将重复使用该计划。

这(和安全性,见下文)可能是最大的原因。

SQL 操作的前提是查询不是一次性操作,而是会被反复使用。如果表(或数据库!)实际上没有在查询中指定,它就无法生成和保存执行计划以备将来使用。

是的,不是我们运行的每一个查询都会被重用,但这是SQL的默认运行前提,所以“例外”意味着例外。

Erland 列出的其他一些原因(请注意,他明确列出了使用存储过程的优势,但其中许多也是参数化(非动态)查询的优势):

  • 权限系统:如果 SQL 引擎不知道您将操作的表(或数据库),则它无法预测您是否有权运行查询。使用动态 SQL 的“权限链”很麻烦。
  • 减少网络流量:通过网络传递存储过程的名称和一些参数值比长查询语句要短。
  • 封装逻辑:您应该熟悉封装来自其他编程环境的逻辑的优点。
  • 跟踪使用的内容:如果我需要更改列定义,如何找到调用它的所有代码?系统过程的存在是为了在 SQL 数据库中查找依赖项,但前提是代码在存储过程中。
  • 易于编写 SQL 代码:当您创建或修改存储过程时会进行语法检查,因此希望产生的错误更少。
  • 解决错误和问题:与不断变化的动态 SQL 相比,DBA 可以更轻松地跟踪和衡量单个存储过程的性能。

同样,每一个都有一百个细微差别,我不会在这里讨论。