varchar(max)命令变量的构建被截断

时间:2014-08-15 04:04:20

标签: sql sql-server sql-server-2008

像许多其他人一样,我没有多少经验。我被指示将非聚集索引转换为聚簇(不存在聚簇索引)。在附加的查询中,我打印出@sql变量以查看我的命令是什么样的,它只有大约一半的表。

第一个问题。字符串可以执行或打印的时间有限制吗?

我试着评论SET @sql = @sql +' UNION ALL'希望我一次可以打印或执行一个命令但不打印任何内容。我真的不想执行,直到我确信我有正确的语法。

SET NOCOUNT ON
IF OBJECT_ID('tempdb..##ListIndex') IS NOT NULL DROP TABLE ##ListIndex
Create Table ##ListIndex (MySchema nvarchar(max), MyTable nvarchar(max), MyIndexName nvarchar(max), MyColumn nvarchar(max), IndexType int) 
Insert into ##ListIndex
select s.name, t.name, i.name, c.name, i.type
 from sys.tables t
inner join sys.schemas s on t.schema_id = s.schema_id
inner join sys.indexes i on i.object_id = t.object_id
inner join sys.index_columns ic on ic.object_id = t.object_id
    inner join sys.columns c on c.object_id = t.object_id and
        ic.column_id = c.column_id
where i.index_id =2
and i.type in (1, 2) -- clustered & nonclustered only

and i.is_disabled = 0
and i.is_hypothetical = 0
and ic.key_ordinal > 0

order by s.name asc, t.name desc --ic.key_ordinal


IF OBJECT_ID('tempdb..##ListIndex2') IS NOT NULL DROP TABLE ##ListIndex2
Create Table ##ListIndex2 (MySchema nvarchar(max), MyTable nvarchar(max), MyIndexName nvarchar(max), MyColumn nvarchar(max))

Insert into ##ListIndex2
SELECT  DISTINCT
        MySchema
,       MyTable
,       MyIndexName
,       STUFF(
                (
                    SELECT ', ' + MyColumn + ' ASC'
                    FROM    ##ListIndex TInner  -- replace with your table
                    WHERE   TOuter.MyTable      = TInner.MyTable
                    AND     TOuter.MyIndexName  = TInner.MyIndexName
                    AND     TOuter.MySchema     = TInner.MySchema
                    FOR XML PATH('')
                ), 1, 2, ''
            ) MyColumn

FROM    ##ListIndex TOuter 
--select * from ##ListIndex2


SET NOCOUNT ON

DECLARE MyCursor CURSOR FOR
SELECT 
    MySchema
    , MyTable
    , MyIndexName
    ,MyColumn
FROM ##ListIndex2 c


OPEN MyCursor

DECLARE @MySchema VARCHAR(100), @MyTable VARCHAR(100), @MyIndexName VARCHAR(100), @MyColumn VARCHAR(300)
DECLARE @sql VARCHAR(MAX)='';

FETCH FROM MyCursor INTO @MySchema, @MyTable, @MyIndexName, @MyColumn
WHILE @@FETCH_STATUS=0
BEGIN   
    IF LEN(@sql) > 0 
    SET @sql = @sql + ' UNION ALL '
    SET @sql= @sql + 'CREATE UNIQUE CLUSTERED INDEX ' + @MyIndexName + ' on ' + @MySchema + '.' + @MyTable +' (' + @MyColumn +') WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, SORT_IN_TEMPDB = OFF, DROP_EXISTING = ON,ONLINE = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON, DATA_COMPRESSION = PAGE) ON [DefFG]  GO  '

    FETCH FROM MyCursor INTO @MySchema, @MyTable, @MyIndexName, @MyColumn

END

CLOSE MyCursor
DEALLOCATE MyCursor

--exec sp_executesql @sql

print @sql

4 个答案:

答案 0 :(得分:1)

对于带有字符串变量的select语句,SQL Server Management Studio确实有大约44,000个字符的限制(至少对于SQL Server 2008,我上次遇到此问题)。但是,完整的SQL仍然会被执行(根据我的经验)。

但是你的问题看起来有所不同。我不确定你要做什么,但是

create unique clustered index . . .
union all
create unique clustered index . . .

不是有效的声明。也许你应该用分号结束并忘记union all。您使用union all进行select次查询。

答案 1 :(得分:0)

聚簇索引通常是表的主键,因为表通过此索引进行物理排序...这使得它成为查找查询的候选者。每次修改索引行时,索引都是"重建"并且表重新组织,因此修改聚簇索引并不是一个好主意,通常使用整数(任何类型,无论是smallint,bigint等)来标识唯一行。复合聚簇索引也是如此。

它们的大小约束是最多16个包含列的900字节。所以,我的建议是创建一个聚簇主键而不是将nvarchar列转换为聚簇索引

答案 2 :(得分:0)

这里有几点需要改进。一些建议会使其他人过时(主要是关于使用CTE的#14会使其余大部分内容无效),但你说你是SQL Server的新手,所以我会提及它们,因为它们可能有助于未来的项目。为了清楚起见,我强调了两个关键/功能性建议。关于字符串限制(与打印和执行相关)的所述问题的答案低于建议的改进列表。

  1. 不需要全局临时表(##),而应使用本地临时表(#
  2. ##ListIndex中4个名称字段的数据类型应为SYSNAME,等同于NVARCHAR(128)
  3. ##ListIndex2全局临时表(全部设置为NVARCHAR(MAX))与本地变量(即DECLARE @MySchema VARCHAR(100), @MyTable VARCHAR(100)...)之间的数据类型不匹配
  4. ##ListIndex2MySchemaMyTableMyIndexName)中的前3个字段以及所有3个匹配变量的数据类型应为SYSNAME,等同于到NVARCHAR(128)
  5. IndexType中来自##ListIndex的{​​{1}}字段的数据类型应为sys.indexes.type而非TINYINT(根据{{1}的文档3}})
  6. INT的条件不合理,因为:
    1. 您目前没有聚集索引,因此您不需要查找它们
    2. 群集索引的i.[index_id] = 2 AND i.[type] IN (1, 2)总是为1,因此无论如何都会被index_id条件过滤掉
    3. 您将如何处理具有多个非聚集索引的表?选择第一个?这就是i.[index_id] = 2的作用。那是你要的吗?在任何一种情况下,您只需要其中一个条件:
      1. 如果要查看每个表的所有非聚簇索引,以便选择最适合作为聚簇索引的索引,请删除i.[index_id] = 2并将i.[index_id] = 2 AND更改为i.[type] IN (1, 2) < / LI>
      2. 如果您只想让每个表的第一个(即使不是最合适的)索引成为聚集索引,请删除i.[type] = 2
  7. 摆脱AND i.[type] IN (1, 2)查询的ORDER BY;它与如何从Insert into ##ListIndex select...表中检索行无关,因此浪费资源
  8. 执行##ListIndex的{​​{1}}函数中的子查询有两个问题:
    1. 您没有按照原始订单排序列。您需要在STUFF中为FOR XML PATH添加TINYINT字段,并从KeyOrdinal填充该字段。然后,您可以在该子查询中的此新字段上添加##ListIndex子句。
    2. 您假设所有列都是ic.key_ordinal,当一个或多个列也可以ORDER BY时,对吧?除非您确定所有索引字段都定义为ASC,否则您还需要为DESC添加ASC字段BIT并填充##ListIndex }}。您可以在IsDescending中使用ic.is_descending_keyCASE启动IIF代替硬编码SELECT的子查询中使用此值。 < / LI>
  9. 您不需要第二个' ASC'(就在SET NOCOUNT ON上方),因为您已经在脚本的开头已经拥有它了
  10. 您的DECLARE MyCursor CURSOR变量应该定义为@sql而不是NVARCHAR(MAX)
  11. 您的VARCHAR(MAX)应该是FETCH FROM
  12. 删除FETCH NEXT FROM行和以下IF LEN行,因为它们不正确且无用
  13. 在您构建SET...'UNION ALL'的行末尾的字符串中,将CREATE UNIQUE...放在下一行
  14. 您可以使用CTE完成所有这些操作并删除两个临时表和光标,但我没有时间显示它并且查询当前是否正常工作
  15. 关于字符串执行时间的任何可能限制:GOVARCHAR(MAX)中的任何内容(根据sys.indexes的文档)。我已经在NVARCHAR(MAX)变量( well 超过4000个字符)中构建了相当多的查询集,并在其上调用EXEC而没有任何问题。

    在打印NVARCHAR(MAX)时,您只看到大约一半表的原因是@sql命令最多只能打印8000个PRINT或4000个{{1}个字符的字符VARCHAR 1}},即使您的变量是NVARCHAR类型。为了打印它,您需要使用MAX遍历每个4000或8000个字符(取决于NVARCHAR与VARCHAR变量)。

    在评论SUBSTRING()行时您没有看到任何内容的原因是我总是建议在所有SET @sql = @sql + ' UNION ALL ' / IF / etc构造周围放置BEGIN / END标记,即使对于只需一行即可执行。这意味着,通过评论WHILE行,并且很可能SETIF一起评论,然后将IF语句应用于剩余的SET 1}}并且只运行SET @sql= @sql + 'CREATE UNIQUE... IF LEN(@sql)&gt; 0,它永远不会; - )。

答案 3 :(得分:0)

关于将UNION ALL与动态SQL一起使用的说明。

如果您需要加入多个查询或批处理,您只需连接像

这样的字符串
SET @sql = 'SELECT 1 ' + ' SELECT 2 '

当您使用UNION ALL创建语句并且您不知道将有多少SELECT(或者您不想打扰它)时 - 创建一个以UNION ALL结尾的字符串,然后减去最后9-10个字符。

SET @sql = 'SELECT 1 AS col FROM MyTable' + ' UNION ALL ' + ... + ' SELECT 1 FROM MyTable UNOIN ALL'
SET @sql = LEFT (@sql, len(@sql) - 10