web-dev-qa-db-ja.com

すべてのntext列をnvarchar(max)に変換します

データベース内のすべてのntext列をnvarchar(max)に変換するストアドプロシージャを作成しようとしています。

これはコードです

_ALTER PROCEDURE [dbo].[usp_SL_ConvertNtextToNvarchar]
AS
BEGIN
  SET NOCOUNT ON;

  DECLARE @table_name nvarchar(128)
  DECLARE @column_name nvarchar(128)
  DECLARE @totalCount int
  DECLARE @count int

  SET @totalCount = 0;
  SET @count = 0;

  -- Eventlogic
  DECLARE tables_cursor CURSOR FOR 

    SELECT so.name as table_name, sc.name as column_name
      FROM sys.objects so
      JOIN sys.columns sc ON so.object_id = sc.object_id
      JOIN sys.types stp ON sc.user_type_id = stp.user_type_id
                        AND stp.name = 'ntext'
     WHERE so.type = 'U' -- to show only user tables
  OPEN tables_cursor

  FETCH NEXT FROM tables_cursor INTO @table_name, @column_name
  WHILE @@FETCH_STATUS = 0
  BEGIN
    EXEC ('ALTER TABLE Eventlogic.dbo.' + @table_name + ' ALTER COLUMN ' + @column_name + ' nvarchar(max);')
    EXEC ('UPDATE Eventlogic.dbo.' + @table_name + ' SET ' + @column_name + '=' + @column_name + ' ')
    SET @count = @count + 1;
    IF @count > 0
      PRINT ('Eventlogic.dbo.' + @table_name + '.' + @column_name + ' ' + CAST(@count AS nvarchar(10)))
    SET @totalCount = @totalCount  + @count;
    FETCH NEXT FROM tables_cursor INTO @table_name, @column_name
  END 
  CLOSE tables_cursor
  DEALLOCATE tables_cursor
  PRINT ('Total columns updated: ' + CAST(@totalCount AS nvarchar(10)))  

END;
_

実行しようとすると、次のエラーが発生します。

メッセージ16924、レベル16、状態1、プロシージャusp_SL_ConvertNtextToNvarchar、行37
Cursorfetch:INTOリストで宣言された変数の数は、選択された列の数と一致する必要があります。

レガシーのため、異なるテーブル全体にntextとして338列があります。

SQL Server 2008 R2にアップグレードしたため、これらの列をnvarchar(max)に変換します。

ここでアドバイスに従いたい ntext vs nvarchar(max) ここで、Conwellは変更後に更新を行うことを提案しています。

SQLサーバーは、テキストをLOB構造からテーブルに移動します(8,000バイト未満の場合)。したがって、IO STATISTICSを使用して選択を再度実行すると、LOB読み取りは0になります。

このエラーの助けがあればすばらしいでしょう。

[〜#〜]更新[〜#〜]

マーティンが述べたようにコードを更新しました。実際には、エラーが発生する前にリストの最初の1つだけが変更されています。

2回目の更新

2番目のフェッチを修正するためにコードを変更した後、SQL Management Studioを閉じました。 SQL Management Studioを再度開いて実行すると、問題なく動作しました。マーティンに再度感謝します。

前もって感謝します。

フェデリコ

1
Federico Giust

OK、SPをQUOTENAMEと@MartinSmithのようなschemaを使用して書き換えました。 SPを使用しています。これは、SPラッパーを使用しないスタンドアロンコードのように見えるので、このようなものにはより意味があります。

名前の奇数文字を処理するには、QUOTENAMEを使用します。たとえば、Table-Testは有効なテーブル名ですが、[]sをその周囲に[Table-Test]を付けない限り、コードでは機能しません。 QUOTENAMEがそれを処理します。また、名前に ']が含まれている場合も処理します。 schemasを含め、必要に応じてQUOTENAMEを使用する動的SQLを実行する場合は、一般的にベストプラクティスと見なされています。

USE [EventLogic]
GO

/****** Object:  StoredProcedure [dbo].[usp_SL_ConvertNtextToNvarchar]    Script Date: 08/08/2013 16:28:58 ******/
SET ANSI_NULLS ON
GO

SET QUOTED_IDENTIFIER ON
GO

ALTER PROCEDURE [dbo].[usp_SL_ConvertNtextToNvarchar]
AS
/*
*/
BEGIN
  SET NOCOUNT ON;

  DECLARE @sql nvarchar(max)
  DECLARE @table_schema nvarchar(128)
  DECLARE @table_name nvarchar(128)
  DECLARE @column_name nvarchar(128)
  DECLARE @totalCount int
  DECLARE @count int

  SET @totalCount = 0;
  SET @count = 0;
  SET @sql = '';

  -- Eventlogic
  DECLARE tables_cursor CURSOR FOR 
    SELECT SCHEMA_NAME(so.schema_id) AS table_schema, so.name as table_name, 
            sc.name as column_name
      FROM sys.objects so
      JOIN sys.columns sc ON so.object_id = sc.object_id
      JOIN sys.types stp ON sc.user_type_id = stp.user_type_id
                        AND stp.name = 'ntext'
     WHERE so.type = 'U' -- to show only user tables
  OPEN tables_cursor

  FETCH NEXT FROM tables_cursor INTO @table_schema, @table_name, @column_name
  WHILE @@FETCH_STATUS = 0
  BEGIN
    SET @sql = 'ALTER TABLE Eventlogic.'+QUOTENAME(@table_schema)+'.' + 
                    QUOTENAME(@table_name) + ' ALTER COLUMN ' + 
                    QUOTENAME(@column_name) + ' nvarchar(max);'
    EXEC sp_executesql @sql
    --PRINT @sql

    SET @sql = 'UPDATE Eventlogic.'+QUOTENAME(@table_schema)+'.' + 
                    QUOTENAME(@table_name) + ' SET ' + 
                    QUOTENAME(@column_name) + '=' + 
                    QUOTENAME(@column_name) + ' '
    EXEC sp_executesql @sql
    --PRINT @sql

    SET @count = @count + 1;
    IF @count > 0
      PRINT ('Eventlogic.'+@table_schema+'.' + @table_name + '.' + @column_name + ' ' + CAST(@count AS nvarchar(10)))
    SET @totalCount = @totalCount  + @count;
    FETCH NEXT FROM tables_cursor INTO @table_schema, @table_name, @column_name
  END 
  CLOSE tables_cursor
  DEALLOCATE tables_cursor
  PRINT ('Total columns updated: ' + CAST(@totalCount AS nvarchar(10)))  

END;

GO
1
Kenneth Fisher

私の回答を読む前にこれを並べ替えて、特定のケースでは役に立たないようにする場合(複数のデータベースでこれを行う必要がない限り)、このタイプの作業では、カーソルよりも文字列連結を優先します。主に、文字列を実行する代わりに出力できるため、少なくとも最初の8K相当が適切で正しいように見えることを確認します。

DECLARE @sql NVARCHAR(MAX) = N'USE Eventlogic;';

SELECT @sql += N'
  ALTER TABLE ' + QUOTENAME(s.name) + '.' + QUOTENAME(t.name)
  + ' ALTER COLUMN ' + QUOTENAME(c.name) 
  + CASE WHEN c.system_type_id = 99 THEN ' N' ELSE ' ' END
  + 'VARCHAR(MAX)' + CASE WHEN c.is_nullable = 0 THEN ' NOT NULL' ELSE '' END 
  + ';
  UPDATE ' + QUOTENAME(s.name) + '.' + QUOTENAME(t.name) 
    + ' SET ' + QUOTENAME(c.name) + ' = ' + QUOTENAME(c.name) + ';
  PRINT ''' + QUOTENAME(s.name) + '.' + QUOTENAME(REPLACE(t.name,'''','''''')) 
    + '.' + QUOTENAME(c.name) + ' (' 
    + CONVERT(VARCHAR(12), COUNT(*) OVER(PARTITION BY c.[object_id])) 
    + ' columns)'';'
FROM sys.columns AS c
INNER JOIN sys.tables AS t
  ON c.[object_id] = t.[object_id]
INNER JOIN sys.schemas AS s
  ON t.[schema_id] = s.[schema_id]
  WHERE c.system_type_id IN (35,99);

SET @sql += N'
PRINT ''Total columns updated: ' + CONVERT(VARCHAR(12), @@ROWCOUNT) + ''';';

PRINT @sql;
--EXEC sp_executesql @sql;

PRINTの出力に満足したら、EXECのコメントを外して、もう一度実行します。

Jon Seigelのスクリプト は、追加の機能を提供します(FULLTEXTインデックスに参加する列を扱います)。

5
Aaron Bertrand

以下は、分析のためにステートメントを出力してから実行できる1つのバリエーションです。

DECLARE @CurrentDataType VARCHAR(max)
DECLARE @DataTypeToChange VARCHAR(max)

SET @CurrentDataType = 'datetime'       ---CHANGE HERE !!
SET @DataTypeToChange = 'smalldatetime' ---CHANGE HERE !!

SELECT 'alter table ' + OBJECT_SCHEMA_NAME(T.[object_id], DB_ID()) + '.' + T.[name] + CHAR(10) + ' alter column ' + C.[name] + ' ' + @DataTypeToChange + ' ' + CASE 
        WHEN C.[is_nullable] = 0
            THEN 'not null '
        ELSE ' null '
        END
FROM sys.[tables] AS T
INNER JOIN sys.[all_columns] C ON T.[object_id] = C.[object_id]
INNER JOIN sys.[types] DTY ON C.[system_type_id] = DTY.[system_type_id]
    AND C.[user_type_id] = DTY.[user_type_id]
WHERE T.[is_ms_shipped] = 0
    AND DTY.NAME = '' + @CurrentDataType + ''
ORDER BY T.[name]
    ,C.[column_id]
1
Kin Shah