我们正在执行一个迁移项目,并希望将大型行仓库索引替换为大型数据仓库的群集列存储索引。我们在身份列上添加了唯一索引。
有人有没有脚本来更改所有100多个表的运行,并用Columnstore Index替换主键聚集索引?
进行测试以查看列存储索引是否有助于在迁移时进行性能调整。
**顺便问一下,在Columnstore中是否建议使用Identity列?需要标识每一行的方法(有时标识列可能会重置,因此设置了唯一约束,还是应该使用Guid?)。如果不是标识,请随时使用Guid或其他方法删除标识列。
当前
:404 error
预期:
CREATE TABLE [dbo].[Fact_SalesTransaction]
(
[FactSalesTransactionId] INT IDENTITY (1, 1) NOT NULL,
[DimCustomerId] INT NOT NULL,
[DimSellerId] INT NOT NULL,
[SalesDatetime] DATETIME NULL,
[DimSalesDateId] INT NULL,
[SalesAmount] DECIMAL (28, 2) NULL,
[ETLCreateDate] DATETIME NULL,
CONSTRAINT [pk_SalesTransactionId] PRIMARY KEY CLUSTERED ([SalesTransactionId] ASC)
);
我们只想使用T-SQL在现有数据库上进行此操作。
评论中的有用资源:Generate SQL Create Scripts for existing tables with Query
答案 0 :(得分:4)
Devart回答了this个问题,询问如何为表生成脚本。我调整了他的代码以省略主键部分,并按照他的常规模式将其替换为唯一的键和列存储创建脚本。我还不得不使用marc_s对this问题的答案,即如何确定表的主键是否是集群的。我将所有这些都放入了一个名为#scriptTable的存储过程中。这是下面的内容:
create procedure #scriptTable
@table_name sysname,
@sql nvarchar(max) output
as
DECLARE
@object_name SYSNAME
, @object_id INT
SELECT
@object_name = '[' + s.name + '].[' + o.name + ']'
, @object_id = o.[object_id]
FROM sys.objects o WITH (NOWAIT)
JOIN sys.schemas s WITH (NOWAIT) ON o.[schema_id] = s.[schema_id]
WHERE s.name + '.' + o.name = @table_name
AND o.[type] = 'U'
AND o.is_ms_shipped = 0
;WITH index_column AS
(
SELECT
ic.[object_id]
, ic.index_id
, ic.is_descending_key
, ic.is_included_column
, c.name
FROM sys.index_columns ic WITH (NOWAIT)
JOIN sys.columns c WITH (NOWAIT) ON ic.[object_id] = c.[object_id] AND ic.column_id = c.column_id
WHERE ic.[object_id] = @object_id
),
fk_columns AS
(
SELECT
k.constraint_object_id
, cname = c.name
, rcname = rc.name
FROM sys.foreign_key_columns k WITH (NOWAIT)
JOIN sys.columns rc WITH (NOWAIT) ON rc.[object_id] = k.referenced_object_id AND rc.column_id = k.referenced_column_id
JOIN sys.columns c WITH (NOWAIT) ON c.[object_id] = k.parent_object_id AND c.column_id = k.parent_column_id
WHERE k.parent_object_id = @object_id
)
SELECT @sql = 'CREATE TABLE ' + @object_name + CHAR(13) + '(' + CHAR(13) + STUFF((
SELECT CHAR(9) + ', [' + c.name + '] ' +
CASE WHEN c.is_computed = 1
THEN 'AS ' + cc.[definition]
ELSE UPPER(tp.name) +
CASE WHEN tp.name IN ('varchar', 'char', 'varbinary', 'binary', 'text')
THEN '(' + CASE WHEN c.max_length = -1 THEN 'MAX' ELSE CAST(c.max_length AS VARCHAR(5)) END + ')'
WHEN tp.name IN ('nvarchar', 'nchar', 'ntext')
THEN '(' + CASE WHEN c.max_length = -1 THEN 'MAX' ELSE CAST(c.max_length / 2 AS VARCHAR(5)) END + ')'
WHEN tp.name IN ('datetime2', 'time2', 'datetimeoffset')
THEN '(' + CAST(c.scale AS VARCHAR(5)) + ')'
WHEN tp.name = 'decimal'
THEN '(' + CAST(c.[precision] AS VARCHAR(5)) + ',' + CAST(c.scale AS VARCHAR(5)) + ')'
ELSE ''
END +
CASE WHEN c.collation_name IS NOT NULL THEN ' COLLATE ' + c.collation_name ELSE '' END +
CASE WHEN c.is_nullable = 1 THEN ' NULL' ELSE ' NOT NULL' END +
CASE WHEN dc.[definition] IS NOT NULL THEN ' DEFAULT' + dc.[definition] ELSE '' END +
CASE WHEN ic.is_identity = 1 THEN ' IDENTITY(' + CAST(ISNULL(ic.seed_value, '0') AS CHAR(1)) + ',' + CAST(ISNULL(ic.increment_value, '1') AS CHAR(1)) + ')' ELSE '' END
END + CHAR(13)
FROM sys.columns c WITH (NOWAIT)
JOIN sys.types tp WITH (NOWAIT) ON c.user_type_id = tp.user_type_id
LEFT JOIN sys.computed_columns cc WITH (NOWAIT) ON c.[object_id] = cc.[object_id] AND c.column_id = cc.column_id
LEFT JOIN sys.default_constraints dc WITH (NOWAIT) ON c.default_object_id != 0 AND c.[object_id] = dc.parent_object_id AND c.column_id = dc.parent_column_id
LEFT JOIN sys.identity_columns ic WITH (NOWAIT) ON c.is_identity = 1 AND c.[object_id] = ic.[object_id] AND c.column_id = ic.column_id
WHERE c.[object_id] = @object_id
ORDER BY c.column_id
FOR XML PATH(''), TYPE).value('.', 'NVARCHAR(MAX)'), 1, 2, CHAR(9) + ' ')
+ ')' + CHAR(13)
+ ISNULL((SELECT (
SELECT CHAR(13) +
'ALTER TABLE ' + @object_name + ' WITH'
+ CASE WHEN fk.is_not_trusted = 1
THEN ' NOCHECK'
ELSE ' CHECK'
END +
' ADD CONSTRAINT [' + fk.name + '] FOREIGN KEY('
+ STUFF((
SELECT ', [' + k.cname + ']'
FROM fk_columns k
WHERE k.constraint_object_id = fk.[object_id]
FOR XML PATH(''), TYPE).value('.', 'NVARCHAR(MAX)'), 1, 2, '')
+ ')' +
' REFERENCES [' + SCHEMA_NAME(ro.[schema_id]) + '].[' + ro.name + '] ('
+ STUFF((
SELECT ', [' + k.rcname + ']'
FROM fk_columns k
WHERE k.constraint_object_id = fk.[object_id]
FOR XML PATH(''), TYPE).value('.', 'NVARCHAR(MAX)'), 1, 2, '')
+ ')'
+ CASE
WHEN fk.delete_referential_action = 1 THEN ' ON DELETE CASCADE'
WHEN fk.delete_referential_action = 2 THEN ' ON DELETE SET NULL'
WHEN fk.delete_referential_action = 3 THEN ' ON DELETE SET DEFAULT'
ELSE ''
END
+ CASE
WHEN fk.update_referential_action = 1 THEN ' ON UPDATE CASCADE'
WHEN fk.update_referential_action = 2 THEN ' ON UPDATE SET NULL'
WHEN fk.update_referential_action = 3 THEN ' ON UPDATE SET DEFAULT'
ELSE ''
END
+ CHAR(13) + 'ALTER TABLE ' + @object_name + ' CHECK CONSTRAINT [' + fk.name + ']' + CHAR(13)
FROM sys.foreign_keys fk WITH (NOWAIT)
JOIN sys.objects ro WITH (NOWAIT) ON ro.[object_id] = fk.referenced_object_id
WHERE fk.parent_object_id = @object_id
FOR XML PATH(N''), TYPE).value('.', 'NVARCHAR(MAX)')), '')
+ ISNULL(((SELECT
CHAR(13) + 'CREATE' + CASE WHEN i.is_unique = 1 THEN ' UNIQUE' ELSE '' END
+ ' NONCLUSTERED INDEX [' + i.name + '] ON ' + @object_name + ' (' +
STUFF((
SELECT ', [' + c.name + ']' + CASE WHEN c.is_descending_key = 1 THEN ' DESC' ELSE ' ASC' END
FROM index_column c
WHERE c.is_included_column = 0
AND c.index_id = i.index_id
FOR XML PATH(''), TYPE).value('.', 'NVARCHAR(MAX)'), 1, 2, '') + ')'
+ ISNULL(CHAR(13) + 'INCLUDE (' +
STUFF((
SELECT ', [' + c.name + ']'
FROM index_column c
WHERE c.is_included_column = 1
AND c.index_id = i.index_id
FOR XML PATH(''), TYPE).value('.', 'NVARCHAR(MAX)'), 1, 2, '') + ')', '') + CHAR(13)
FROM sys.indexes i WITH (NOWAIT)
WHERE i.[object_id] = @object_id
AND i.is_primary_key = 0
AND i.[type] = 2
FOR XML PATH(''), TYPE).value('.', 'NVARCHAR(MAX)')
), '')
+ char(13)
-- Replaced "create primary key" logic in the original with what's below:
+ ISNULL(
(
select 'create clustered columnstore index [ccx_' + @table_name + '] on ' + @object_name + char(13) +
'create unique ' + convert(nvarchar(max), i.type_desc) + ' index [' + replace(k.name, 'pk_', 'unx_') + '] ' +
'on ' + @object_name + ' (' +
(
SELECT STUFF((
SELECT ', [' + ic.name + '] ' + CASE WHEN ic.is_descending_key = 1 THEN 'DESC' ELSE 'ASC' END
FROM index_column ic
WHERE ic.is_included_column = 0
AND ic.[object_id] = k.parent_object_id
AND ic.index_id = k.unique_index_id
FOR XML PATH(N''), TYPE
).value('.', 'NVARCHAR(MAX)'), 1, 2, '')
) +
')' + CHAR(13)
FROM sys.key_constraints k WITH (NOWAIT)
join sys.indexes i with (nowait) on k.unique_index_id = i.index_id and k.parent_object_id = i.object_id
WHERE k.parent_object_id = @object_id
AND k.[type] = 'PK'
),
''
);
您可以像这样使用#scriptTable:
declare @sql nvarchar(max);
exec #scriptTable 'dbo.Fact_SalesTransaction', @sql output;
print (@sql);
在准备好后,将'print'替换为'exec'或使用sp_executeSql。
要在所有表上使用它,请首先捕获要修改的表:
declare @tables table (
rowId int identity(1,1),
name nvarchar(max)
);
insert @tables
select schema_name(schema_id) + '.' + name
from sys.tables
where type_desc = 'user_table'
现在您准备循环表并应用#scriptTable:
declare @rowId int = 1;
declare @table nvarchar(max);
declare @sql nvarchar(max);
while @rowId <= (select max(rowId) from @tables) begin
select @table = name from @tables where rowId = @rowId;
exec #scriptTable @table, @sql output;
print (@sql); -- turn 'print' into 'exec' or otherwise use sp_executeSql
set @rowId += 1;
end
像以前一样,在准备好后将'print'替换为'exec'或使用sp_executeSql。
请注意,如果您的表名需要用括号括起来,则可能需要进一步修改。
编辑: 对代码进行了一些更新,以简化(略)并使用需要在方括号内的表名称。
答案 1 :(得分:0)
要在所有表上创建CCI,可以使用以下内容:
DECLARE @SQLscript nVARCHAR(1000) =
'CREATE CLUSTERED COLUMNSTORE INDEX &&& ON @@@ WITH (DROP_EXISTING = OFF,COMPRESSION_DELAY = 0)'
declare @tables table (
rowId int identity(1,1),
name nvarchar(max)
);
insert @tables
select schema_name(schema_id) + '.' + name
from sys.tables
where type_desc = 'user_table'
declare @rowId int = 1;
declare @table nvarchar(max);
declare @sql nvarchar(max);
while @rowId <= (select max(rowId) from @tables) begin
select @table = name from @tables where rowId = @rowId;
SET @SQLscript = REPLACE(REPLACE(@SQLscript, '@@@',@table) ,'&&&','cci_' + SUBSTRING(REPLACE(@table,'].[','_'),CHARINDEX('.',REPLACE(@table,'].[','_'))+1,LEN(REPLACE(@table,'].[','_'))))
BEGIN TRY
EXEC sp_executeSql @SQLscript;
END TRY
BEGIN CATCH
PRINT @SQLscript
END CATCH
SET @SQLscript = 'CREATE CLUSTERED COLUMNSTORE INDEX &&& ON @@@ WITH (DROP_EXISTING = OFF,COMPRESSION_DELAY = 0)'
set @rowId += 1;
end
答案 2 :(得分:0)
@pwilcox的答案是正确的,除了最后一部分吐出行存储索引的代码。该索引不应该聚簇,因为上面的行已经创建了聚簇的列存储索引。
修复:
-- Replaced "create primary key" logic in the original with what's below:
+ ISNULL(
(
select 'create clustered columnstore index [ccx_' + @table_name + '] on ' + @object_name + char(13) +
'create unique index [' + replace(k.name, 'pk_', 'unx_') + '] ' +
'on ' + @object_name + ' (' +
(
SELECT STUFF((
SELECT ', [' + ic.name + '] ' + CASE WHEN ic.is_descending_key = 1 THEN 'DESC' ELSE 'ASC' END
FROM index_column ic
WHERE ic.is_included_column = 0
AND ic.[object_id] = k.parent_object_id
AND ic.index_id = k.unique_index_id
FOR XML PATH(N''), TYPE
).value('.', 'NVARCHAR(MAX)'), 1, 2, '')
) +
')' + CHAR(13)
FROM sys.key_constraints k WITH (NOWAIT)
join sys.indexes i with (nowait) on k.unique_index_id = i.index_id and k.parent_object_id = i.object_id
WHERE k.parent_object_id = @object_id
AND k.[type] = 'PK'
),
''
);
答案 3 :(得分:-1)
您需要使用预期的架构创建一个新的空数据库,然后使用架构比较(我使用Visual Studio)来生成数据迁移脚本。