尝试创建大型动态查询时,不断被截断
Trying to create a large dynamic query, keeps getting truncated
我在 SQL 服务器中有一个 SQL 查询,我试图在其中使用 'union' 在大量数据库上创建大型查询。但是,查询不断被截断。根据我的研究,如果所有 varchar
都转换为 varchar(MAX)
,则不应发生这种情况。我试过这样做,但是,它仍然被截断。最终查询应该在 @finalQuery
变量中。任何人都可以帮助解决以下问题吗?
DECLARE @name VARCHAR(MAX) -- database name
DECLARE @path VARCHAR(MAX) -- path for backup files
DECLARE @fileName VARCHAR(MAX) -- filename for backup
DECLARE @fileDate VARCHAR(MAX) -- used for file name
DECLARE @executeQuery VARCHAR(MAX)
DECLARE @finalQuery VARCHAR(MAX)
SET @finalQuery = ''
DECLARE db_cursor CURSOR FOR
SELECT name
FROM master..sysdatabases
WHERE name NOT IN (CAST('master' AS VARCHAR(MAX)),CAST('model' AS VARCHAR(MAX)),CAST('msdb' AS VARCHAR(MAX)),CAST('tempdb' AS VARCHAR(MAX)))
OPEN db_cursor
FETCH NEXT FROM db_cursor INTO @name
WHILE @@FETCH_STATUS = 0
BEGIN
SET @executeQuery=CAST(
'SELECT TOP 1000
[EmailAddress] as ''Email Address''
,[FirstName] as ''First Name''
,[LastName] as ''Last Name''
,[LastLogin] as ''Last Login'',
Name as ''User Role''
FROM '+@name+'.[dbo].[User] c
INNER JOIN
( SELECT * FROM '+@name+'.[dbo].[SecurityRole] as a
INNER JOIN '+@name+'.[dbo].[SecurityRoleToUser] as b
ON (a.ID=b.SecurityRoleID)
) d
ON (c.ID=d.UserID)
WHERE IsActive=1' AS VARCHAR(MAX))
--PRINT @executeQuery
--PRINT @name
--PRINT @executeQuery
SET @finalQuery = CAST(@executeQuery+' UNION ALL ' +@finalQuery AS VARCHAR(MAX))
--PRINT @executeQUery
--EXEC (@executeQuery)
FETCH NEXT FROM db_cursor INTO @name
END
CLOSE db_cursor
DEALLOCATE db_cursor
PRINT @finalQuery
--EXEC(@finalQuery)
被截断的是 PRINT
,不是您的变量:
来自 PRINT
上的文档:
A message string can be up to 8,000 characters long if it is a non-Unicode string, and 4,000 characters long if it is a Unicode string. Longer strings are truncated. The varchar(max)
and nvarchar(max)
data types are truncated to data types that are no larger than varchar(8000)
and nvarchar(4000)
.
为什么要使用游标?
SELECT
'SELECT TOP 1000
[EmailAddress] as ''Email Address''
,[FirstName] as ''First Name''
,[LastName] as ''Last Name''
,[LastLogin] as ''Last Login'',
Name as ''User Role''
FROM ' + name + '.[dbo].[User] c'
.....
FROM master..sysdatabases
WHERE name NOT IN (CAST('master' AS VARCHAR(MAX)),CAST('model' AS VARCHAR(MAX)),CAST('msdb' AS VARCHAR(MAX)),CAST('tempdb' AS VARCHAR(MAX)))
您最好不要对这么多数据库执行 UNION
。而你不需要。此外,数据库名称等都是 sysname
,等同于 NVARCHAR(128)
,因此最好使用 NVARCHAR(MAX)
而不是 VARCHAR(MAX)
。
第 1 步:生成不太复杂的查询
DECLARE @DatabaseName sysname;
DECLARE @Query NVARCHAR(MAX),
@Template NVARCHAR(MAX);
SET @Query = '';
SET @Template = N'USE [?];
SELECT TOP 1000
[EmailAddress] as [Email Address]
,[FirstName] as [First Name]
,[LastName] as [Last Name]
,[LastLogin] as [Last Login],
Name as [User Role]
FROM [dbo].[User] c
INNER JOIN
( SELECT * FROM [dbo].[SecurityRole] as a
INNER JOIN [dbo].[SecurityRoleToUser] as b
ON (a.ID=b.SecurityRoleID)
) d
ON (c.ID=d.UserID)
WHERE IsActive = 1;
';
SELECT @Query = (@Query + REPLACE(@Template, N'?', sd.[name]))
FROM sys.databases sd
WHERE sd.[name] NOT IN (N'master', N'model', N'msdb', N'tempdb')
AND HAS_DBACCESS(sd.[name]) = 1;
--EXEC(@Query); -- uncomment when not debugging
SELECT LEN(@Query); -- 9506 on my system -- comment out if debugging
print @query; -- truncates at 4000 chars for NVARCHAR -- comment out if debugging
第 2 步:不需要 UNION
与其使用 UNION 将所有内容放入单个结果集中,不如将多个结果集插入到本地临时文件中 table。
CREATE TABLE #tmp (DatabaseName sysname NOT NULL,
EmailAddress NVARCHAR(200), FirstName NVARCHAR(50),
LastName NVARCHAR(50), LastLogin DATETIME, UserRole VARCHAR(50);
DECLARE @Query NVARCHAR(MAX),
@Template NVARCHAR(MAX);
SET @Query = '';
SET @Template = N'USE [?];
SELECT TOP 1000
DB_NAME() AS [DatabaseName],
[EmailAddress] as [Email Address]
,[FirstName] as [First Name]
,[LastName] as [Last Name]
,[LastLogin] as [Last Login],
Name as [User Role]
FROM [dbo].[User] c
INNER JOIN
( SELECT UserID, Name--* -- see Step #3 below
FROM [dbo].[SecurityRole] sr
INNER JOIN [dbo].[SecurityRoleToUser] srtu
ON sr.ID = srtu.SecurityRoleID
) d
ON c.ID = d.UserID
WHERE IsActive = 1;
';
SELECT @Query = (@Query + REPLACE(@Template, N'?', sd.[name]))
FROM sys.databases sd
WHERE sd.[name] NOT IN (N'master', N'model', N'msdb', N'tempdb')
AND HAS_DBACCESS(sd.[name]) = 1;
INSERT INTO #tmp (DatabaseName, EmailAddress, FirstName, LastName, LastLogin, UserRole)
EXEC(@Query);
SELECT * FROM #tmp;
第 3 步:
最好不要在 SELECT * FROM [dbo].[SecurityRole] as a
子查询中使用 SELECT *
。只需 select 您需要的字段,因为它更有可能使用索引。看起来您只需要两个字段:UserID, Name
我在 SQL 服务器中有一个 SQL 查询,我试图在其中使用 'union' 在大量数据库上创建大型查询。但是,查询不断被截断。根据我的研究,如果所有 varchar
都转换为 varchar(MAX)
,则不应发生这种情况。我试过这样做,但是,它仍然被截断。最终查询应该在 @finalQuery
变量中。任何人都可以帮助解决以下问题吗?
DECLARE @name VARCHAR(MAX) -- database name
DECLARE @path VARCHAR(MAX) -- path for backup files
DECLARE @fileName VARCHAR(MAX) -- filename for backup
DECLARE @fileDate VARCHAR(MAX) -- used for file name
DECLARE @executeQuery VARCHAR(MAX)
DECLARE @finalQuery VARCHAR(MAX)
SET @finalQuery = ''
DECLARE db_cursor CURSOR FOR
SELECT name
FROM master..sysdatabases
WHERE name NOT IN (CAST('master' AS VARCHAR(MAX)),CAST('model' AS VARCHAR(MAX)),CAST('msdb' AS VARCHAR(MAX)),CAST('tempdb' AS VARCHAR(MAX)))
OPEN db_cursor
FETCH NEXT FROM db_cursor INTO @name
WHILE @@FETCH_STATUS = 0
BEGIN
SET @executeQuery=CAST(
'SELECT TOP 1000
[EmailAddress] as ''Email Address''
,[FirstName] as ''First Name''
,[LastName] as ''Last Name''
,[LastLogin] as ''Last Login'',
Name as ''User Role''
FROM '+@name+'.[dbo].[User] c
INNER JOIN
( SELECT * FROM '+@name+'.[dbo].[SecurityRole] as a
INNER JOIN '+@name+'.[dbo].[SecurityRoleToUser] as b
ON (a.ID=b.SecurityRoleID)
) d
ON (c.ID=d.UserID)
WHERE IsActive=1' AS VARCHAR(MAX))
--PRINT @executeQuery
--PRINT @name
--PRINT @executeQuery
SET @finalQuery = CAST(@executeQuery+' UNION ALL ' +@finalQuery AS VARCHAR(MAX))
--PRINT @executeQUery
--EXEC (@executeQuery)
FETCH NEXT FROM db_cursor INTO @name
END
CLOSE db_cursor
DEALLOCATE db_cursor
PRINT @finalQuery
--EXEC(@finalQuery)
被截断的是 PRINT
,不是您的变量:
来自 PRINT
上的文档:
A message string can be up to 8,000 characters long if it is a non-Unicode string, and 4,000 characters long if it is a Unicode string. Longer strings are truncated. The
varchar(max)
andnvarchar(max)
data types are truncated to data types that are no larger thanvarchar(8000)
andnvarchar(4000)
.
为什么要使用游标?
SELECT
'SELECT TOP 1000
[EmailAddress] as ''Email Address''
,[FirstName] as ''First Name''
,[LastName] as ''Last Name''
,[LastLogin] as ''Last Login'',
Name as ''User Role''
FROM ' + name + '.[dbo].[User] c'
.....
FROM master..sysdatabases
WHERE name NOT IN (CAST('master' AS VARCHAR(MAX)),CAST('model' AS VARCHAR(MAX)),CAST('msdb' AS VARCHAR(MAX)),CAST('tempdb' AS VARCHAR(MAX)))
您最好不要对这么多数据库执行 UNION
。而你不需要。此外,数据库名称等都是 sysname
,等同于 NVARCHAR(128)
,因此最好使用 NVARCHAR(MAX)
而不是 VARCHAR(MAX)
。
第 1 步:生成不太复杂的查询
DECLARE @DatabaseName sysname;
DECLARE @Query NVARCHAR(MAX),
@Template NVARCHAR(MAX);
SET @Query = '';
SET @Template = N'USE [?];
SELECT TOP 1000
[EmailAddress] as [Email Address]
,[FirstName] as [First Name]
,[LastName] as [Last Name]
,[LastLogin] as [Last Login],
Name as [User Role]
FROM [dbo].[User] c
INNER JOIN
( SELECT * FROM [dbo].[SecurityRole] as a
INNER JOIN [dbo].[SecurityRoleToUser] as b
ON (a.ID=b.SecurityRoleID)
) d
ON (c.ID=d.UserID)
WHERE IsActive = 1;
';
SELECT @Query = (@Query + REPLACE(@Template, N'?', sd.[name]))
FROM sys.databases sd
WHERE sd.[name] NOT IN (N'master', N'model', N'msdb', N'tempdb')
AND HAS_DBACCESS(sd.[name]) = 1;
--EXEC(@Query); -- uncomment when not debugging
SELECT LEN(@Query); -- 9506 on my system -- comment out if debugging
print @query; -- truncates at 4000 chars for NVARCHAR -- comment out if debugging
第 2 步:不需要 UNION
与其使用 UNION 将所有内容放入单个结果集中,不如将多个结果集插入到本地临时文件中 table。
CREATE TABLE #tmp (DatabaseName sysname NOT NULL,
EmailAddress NVARCHAR(200), FirstName NVARCHAR(50),
LastName NVARCHAR(50), LastLogin DATETIME, UserRole VARCHAR(50);
DECLARE @Query NVARCHAR(MAX),
@Template NVARCHAR(MAX);
SET @Query = '';
SET @Template = N'USE [?];
SELECT TOP 1000
DB_NAME() AS [DatabaseName],
[EmailAddress] as [Email Address]
,[FirstName] as [First Name]
,[LastName] as [Last Name]
,[LastLogin] as [Last Login],
Name as [User Role]
FROM [dbo].[User] c
INNER JOIN
( SELECT UserID, Name--* -- see Step #3 below
FROM [dbo].[SecurityRole] sr
INNER JOIN [dbo].[SecurityRoleToUser] srtu
ON sr.ID = srtu.SecurityRoleID
) d
ON c.ID = d.UserID
WHERE IsActive = 1;
';
SELECT @Query = (@Query + REPLACE(@Template, N'?', sd.[name]))
FROM sys.databases sd
WHERE sd.[name] NOT IN (N'master', N'model', N'msdb', N'tempdb')
AND HAS_DBACCESS(sd.[name]) = 1;
INSERT INTO #tmp (DatabaseName, EmailAddress, FirstName, LastName, LastLogin, UserRole)
EXEC(@Query);
SELECT * FROM #tmp;
第 3 步:
最好不要在 SELECT * FROM [dbo].[SecurityRole] as a
子查询中使用 SELECT *
。只需 select 您需要的字段,因为它更有可能使用索引。看起来您只需要两个字段:UserID, Name