Sql server 使用while循环在SQL Server实例上创建数据库表和表信息
我想知道为什么在下面的while循环中不能用DB名称替换变量@DBNAME: 我可以使用变量作为DB_名称,但它不允许我将DB名称替换为sys.tables 它给了我以下错误:“.”附近的语法不正确Sql server 使用while循环在SQL Server实例上创建数据库表和表信息,sql-server,variables,while-loop,Sql Server,Variables,While Loop,我想知道为什么在下面的while循环中不能用DB名称替换变量@DBNAME: 我可以使用变量作为DB_名称,但它不允许我将DB名称替换为sys.tables 它给了我以下错误:“.”附近的语法不正确 DROP TABLE #TABLE_INFO; CREATE TABLE #TABLE_INFO ( DB_NAME VARCHAR(100), TABLE_NAME VARCHAR(100), CREATE_DATE DATE ); DECLARE @CO
DROP TABLE #TABLE_INFO;
CREATE TABLE #TABLE_INFO
(
DB_NAME VARCHAR(100),
TABLE_NAME VARCHAR(100),
CREATE_DATE DATE
);
DECLARE @COUNT AS INT = 1
DECLARE @DBNAME AS VARCHAR(100)
WHILE @COUNT < (SELECT MAX(DATABASE_ID) +1 FROM SYS.DATABASES)
BEGIN
SET @DBNAME = (SELECT NAME FROM SYS.DATABASES WHERE DATABASE_ID = @COUNT)
INSERT INTO #TABLE_INFO
SELECT
@DBNAME AS DB_NAME,
A1.NAME AS TABLE_NAME,
A1.CREATE_DATE
FROM @DBNAME.SYS.TABLES A1
SET @COUNT = @COUNT+1
END
SELECT * FROM #TABLE_INFO;
T-SQL不允许对数据库对象标识符进行参数化。
…因此您必须使用动态SQL,即在字符串值内建立查询并将其传递给sp_executesql。
显然,这是危险的,因为存在SQL注入的风险,或者只是忘记正确转义名称使用QUOTENAME,这可能会导致意外的数据丢失,例如,如果有人实际将表命名为[DELETE FROM Users]。
因此,尽可能少地使用动态SQL非常重要。
您可以使用INSERT INTO@tableVariable EXECUTE sp_executesql@query将存储过程(或任何动态SQL)的结果存储到表变量或临时表中,而无需直接输出到客户端连接,然后使用非动态SQL处理结果。
如果可以将所有动态SQL逻辑移动到存储过程中,也可以使用交叉应用。
试试这个:
顺便说一句,我将您的WHILE循环更改为使用静态只读游标,这避免了您对sys.databases的数据库id如何工作的假设,例如,如果sys.databases仅列出这4个数据库id值1、2、99997、99998会怎么样?您的WHILE循环将浪费时间检查3、4、5、6、…、99996。
我还使用表变量而不是临时表,因为与临时表相比,表变量的作用域和生存期更容易推理。也就是说,虽然可以将TV作为表值参数传递,但TV与TTs相比实际上没有任何性能优势,这很好,而且通常比使用TTs将数据传递给存储过程要好得多。
我能够将此查询复制并粘贴到SSMS中,并在我的SQL Server 2017框中运行它,而无需修改,它将返回预期结果
T-SQL不允许对数据库对象标识符进行参数化。
…因此您必须使用动态SQL,即在字符串值内建立查询并将其传递给sp_executesql。
显然,这是危险的,因为存在SQL注入的风险,或者只是忘记正确转义名称使用QUOTENAME,这可能会导致意外的数据丢失,例如,如果有人实际将表命名为[DELETE FROM Users]。
因此,尽可能少地使用动态SQL非常重要。
您可以使用INSERT INTO@tableVariable EXECUTE sp_executesql@query将存储过程(或任何动态SQL)的结果存储到表变量或临时表中,而无需直接输出到客户端连接,然后使用非动态SQL处理结果。
如果可以将所有动态SQL逻辑移动到存储过程中,也可以使用交叉应用。
试试这个:
顺便说一句,我将您的WHILE循环更改为使用静态只读游标,这避免了您对sys.databases的数据库id如何工作的假设,例如,如果sys.databases仅列出这4个数据库id值1、2、99997、99998会怎么样?您的WHILE循环将浪费时间检查3、4、5、6、…、99996。
我还使用表变量而不是临时表,因为与临时表相比,表变量的作用域和生存期更容易推理。也就是说,虽然可以将TV作为表值参数传递,但TV与TTs相比实际上没有任何性能优势,这很好,而且通常比使用TTs将数据传递给存储过程要好得多。
我可以将此查询复制并粘贴到SSMS中,并在我的SQL Server 2017框中运行它,而无需修改,它将返回预期结果。T-SQL不支持数据库对象标识符的参数化数据库名称、表名称、列名等,只能参数化值。实际上,我不知道有哪种SQL方言允许对数据库对象标识符进行参数化。唯一的解决方法是使用动态SQL。这是否回答了您的问题?T-SQL不支持数据库对象标识符的参数化数据库名称、表名、列名等,只能参数化值。实际上,我不知道有哪种SQL方言允许对数据库对象标识符进行参数化。唯一的解决方法是使用动态SQL。这是否回答了您的问题?谢谢你,戴,第二个答案正是我想要的结果,第一个建议给了我一些关于我所面临问题的背景知识。非常感谢!谢谢你,戴,第二个答案正是我想要的结果,第一个建议给了我一些关于我所面临问题的背景知识。非常感谢!
DECLARE @results TABLE (
DatabaseName nvarchar(100) NOT NULL,
SchemaName nvarchar(50) NOT NULL,
TableName nvarchar(100) NOT NULL,
Created datetime NOT NULL
);
DECLARE @dbName nvarchar(100);
DECLARE c CURSOR STATIC READ_ONLY FOR
SELECT QUOTENAME( [name] ) FROM sys.databases;
OPEN c;
FETCH NEXT FROM c INTO @dbName;
WHILE @@FETCH_STATUS = 0
BEGIN
DECLARE @dbQuery nvarchar(1000) = N'
SELECT
''' + @dbName + N''' AS DatabaseName,
s.[name] AS SchemaName,
t.[name] AS TableName,
t.create_date AS Created
FROM
' + @dbName + N'.sys.tables AS t
INNER JOIN sys.schemas AS s ON t.schema_id = s.schema_id
ORDER BY
s.[name],
t.[name]
';
INSERT INTO @results
( DatabaseName, SchemaName, TableName, Created )
EXECUTE sp_executesql @dbQuery;
FETCH NEXT FROM c INTO @dbName;
END;
CLOSE c;
DEALLOCATE c;
--------------------------
SELECT
*
FROM
@results
ORDER BY
DatabaseName,
SchemaName,
TableName;