尝试创建大型动态查询,不断被截断

时间:2015-06-29 20:46:29

标签: sql sql-server tsql casting dynamic-sql

我在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)

3 个答案:

答案 0 :(得分:1)

PRINT会被截断,而不是变量:

来自PRINT上的文档:

  

如果消息字符串是非Unicode字符串,则该消息字符串最长可达8,000个字符;如果是Unicode字符串,则消息字符串最长可达4,000个字符。更长的字符串被截断。 varchar(max)nvarchar(max)数据类型被截断为不大于varchar(8000)nvarchar(4000)的数据类型。

答案 1 :(得分:1)

如果没有在这么多数据库中执行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将所有内容都集成到一个结果集中。

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 *子查询中使用SELECT * FROM [dbo].[SecurityRole] as a。只需选择您需要的字段,因为它更有可能使用索引。看起来您只需要两个字段:UserID, Name

答案 2 :(得分:0)

为什么要使用光标?

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)))