sql >> Databáze >  >> RDS >> Database

Zastaralé funkce, které je třeba vyjmout ze sady nástrojů – část 3

Nedávno jsem diskutoval o několika funkcích, které Microsoft nedoporučuje používat a na které byste podle mě také měli zapomenout. Došlo k případu, kdy kolega neustále propagoval zastaralé zobrazení zpětné kompatibility sys.sysprocesses namísto novějších pohledů dynamické správy (DMV) a další případ, kdy jiný kolega stáhl produkční server pomocí SQL Server Profiler.

Můj poslední problém s věcmi, které je nejlepší zapomenout, je nová uložená procedura s ntext parametr. Zkontroloval jsem a, jistě, datový typ odpovídá schématu pro podkladovou tabulku. Moje mysl se začala prohánět nad těmito staršími datovými typy, abych vysvětlila, proč bychom je už opravdu neměli používat:

  • obrázek
  • ntext
  • text

Tyto typy jsou na seznamu zastaralých z mnoha důvodů a od doby, kdy byly nahrazeny, si na tomto seznamu drží stálé místo podle max typů v SQL Server 2005. Některé z těchto bolestivých bodů zahrnují:

  • nelze použít mnoho řetězcových funkcí, jako je LEFT() , RTRIM() , UPPER() a většina operátorů srovnání;
  • musíte použít funkce jako TEXTPTR , WRITETEXT a UPDATETEXT pro úpravy;
  • typy nelze použít jako lokální proměnné;
  • nemůžete odkazovat na sloupce v DISTINCT , GROUP BY , ORDER BY , nebo jako zahrnutý sloupec (ne že byste něco z toho měli chtít dělat);
  • menší hodnoty, které se vejdou do řádku, to lze provést pouze pomocí text in row možnost.

Toto není vyčerpávající seznam; existují další rozdíly, které můžete považovat za více či méně důležité. Nejnaléhavějším důvodem pro mě je, že nemůžete znovu sestavit seskupený index online, pokud tabulka obsahuje jeden z těchto datových typů.

Vytvořme jednoduchou databázi s několika tabulkami:

CREATE DATABASE BadIdeas;
GO
 
USE BadIdeas;
GO
 
CREATE TABLE dbo.t1(id bigint IDENTITY PRIMARY KEY, msg nvarchar(max));
CREATE TABLE dbo.t2(id bigint IDENTITY PRIMARY KEY, msg ntext);
>

Nyní se pokusíme provést online operace na stolech:

ALTER TABLE dbo.t1 REBUILD WITH (ONLINE = ON);
GO
ALTER TABLE dbo.t2 REBUILD WITH (ONLINE = ON);

Zatímco první příkaz uspěje, druhý zobrazí chybovou zprávu, jako je tato:

Msg 2725, Level 16, State 2
Pro index 'PK__t2__3213E83FEEA1E0AD' nelze provést online operaci, protože index obsahuje sloupec 'msg' datového typu text, ntext, image nebo FILESTREAM. U indexu bez klastrů může být sloupcem začleněný sloupec indexu. U seskupeného indexu může být sloupcem jakýkoli sloupec tabulky. Pokud se použije DROP_EXISTING, sloupec může být součástí nového nebo starého indexu. Operace musí být provedena offline.

V jednorázovém scénáři je dost snadné se s chybovou zprávou vypořádat:buď přeskočíte stůl, nebo, pokud je nutné stůl bezpodmínečně přestavět, ve spolupráci se svými týmy naplánujete výpadek. Když automatizujete řešení údržby indexu nebo nasazujete nová nastavení komprese ve svém prostředí, je trochu složitější, aby vaše řešení zvládlo současný nebo budoucí stav.

Co dělat?

Tyto sloupky můžete začít nahrazovat jejich modernějšími protějšky. Zde je dotaz, který vám pomůže je vystopovat pomocí sys.columns katalogové zobrazení, ale všechny explicitní odkazy, které by mohly existovat v kódu vaší aplikace, jste na to sami:

SELECT [Schema]    = s.name, 
       [Object]    = o.name,
       [Column]    = c.name,
       [Data Type] = TYPE_NAME(c.user_type_id) + CASE 
         WHEN c.system_type_id <> c.user_type_id 
         THEN N' (' + TYPE_NAME(c.system_type_id) + N')' 
         ELSE N'' END
  FROM sys.columns AS c
  INNER JOIN sys.objects AS o
    ON c.[object_id] = o.[object_id]
  INNER JOIN sys.schemas AS s
    ON o.[schema_id] = s.[schema_id]
  WHERE c.system_type_id IN (34, 35, 99)
  ORDER BY [Schema], [Object], [Column];

Výstup:

Může být lákavé přejít do SSMS a ručně změnit datové typy těchto sloupců, ale může to mít i jiné důsledky. Například ke sloupcům mohou být přiřazena výchozí omezení. A možná máte uložené procedury s parametry, které by měly být aktualizovány v tandemu:

CREATE PROCEDURE dbo.sp1 @p1 ntext AS PRINT 1;
GO

Chcete-li najít všechny tyto případy, můžete upravit výše uvedený dotaz pro vyhledávání podle sys.parameters místo toho zobrazení katalogu:

SELECT [Schema]  = s.name, 
       [Object]   = o.name, 
       [Parameter] = p.name, 
       [Data Type] = TYPE_NAME(p.user_type_id) + CASE 
         WHEN p.system_type_id <> p.user_type_id 
         THEN N' (' + TYPE_NAME(p.system_type_id) + N')' 
         ELSE N'' END
  FROM sys.objects AS o
  INNER JOIN sys.schemas AS s
    ON o.[schema_id] = s.[schema_id]
  INNER JOIN sys.parameters AS p
    ON p.[object_id] = o.[object_id]
  WHERE p.system_type_id IN (34, 35, 99)
  ORDER BY [Schema], [Object], [Parameter];

Výstup:

Pokud potřebujete tato data vrátit napříč všemi databázemi, můžete použít sp_ineachdb , postup, který jsem napsal (a zdokumentoval zde a zde), abych překonal několik omezení v chybovém, nezdokumentovaném a nepodporovaném sp_MSforeachdb . Pak můžete udělat toto:

EXEC master.dbo.sp_ineachdb @command = N'SELECT [Database]  = DB_NAME(), 
       [Schema]    = s.name, 
       [Object]    = o.name,
       [Column]    = c.name,
       [Data Type] = TYPE_NAME(c.user_type_id) + CASE 
         WHEN c.system_type_id <> c.user_type_id 
         THEN N'' ('' + TYPE_NAME(c.system_type_id) + N'')'' 
         ELSE N'''' END
  FROM sys.columns AS c
  INNER JOIN sys.objects AS o
    ON c.[object_id] = o.[object_id]
  INNER JOIN sys.schemas AS s
    ON o.[schema_id] = s.[schema_id]
  WHERE c.system_type_id IN (34, 35, 99)
  ORDER BY [Schema], [Object], [Column];
 
SELECT [Database]  = DB_NAME(),
       [Schema]    = s.name, 
       [Object]    = o.name, 
       [Parameter] = p.name, 
       [Data Type] = TYPE_NAME(p.user_type_id) + CASE 
         WHEN p.system_type_id <> p.user_type_id 
         THEN N'' ('' + TYPE_NAME(p.system_type_id) + N'')''
         ELSE N'''' END
  FROM sys.objects AS o
  INNER JOIN sys.schemas AS s
    ON o.[schema_id] = s.[schema_id]
  INNER JOIN sys.parameters AS p
    ON p.[object_id] = o.[object_id]
  WHERE p.system_type_id IN (34, 35, 99)
  ORDER BY [Schema], [Object], [Parameter];';

Zajímavá vedlejší poznámka:pokud to spustíte se všemi databázemi, zjistíte, že i v SQL Server 2019 Microsoft stále používá některé z těchto starých typů.

Můžete to dále automatizovat spuštěním z PowerShellu nebo jakéhokoli automatizačního nástroje, který používáte ke správě více instancí SQL Server.

To je samozřejmě jen začátek – vytváří pouze seznam. Můžete ji dále rozšířit a vytvořit verzi konceptu ALTER TABLE příkazy, které byste potřebovali k aktualizaci všech tabulek, ale tyto příkazy by bylo nutné zkontrolovat, než je spustíte, a stále byste museli sami upravit procedury (generování ALTER PROCEDURE příkazy, které mají správně nahrazeny pouze tyto názvy typů parametrů, není v žádném případě snadné). Zde je příklad, který generuje ALTER TABLE příkazy s přihlédnutím k možnosti nulování, ale bez dalších komplikací, jako jsou výchozí omezení:

SELECT N'ALTER TABLE ' + QUOTENAME(s.name)
  + N'.' + QUOTENAME(o.name)
  + N' ALTER COLUMN ' + QUOTENAME(c.name) + N' '
  + CASE c.system_type_id
      WHEN 34 THEN N'varbinary'
      WHEN 35 THEN N'varchar'
      WHEN 99 THEN N'nvarchar'
    END + N'(max)' 
  + CASE c.is_nullable 
      WHEN 0 THEN N' NOT' 
      ELSE N'' END + N' NULL;'
FROM sys.columns AS c
INNER JOIN sys.objects AS o
  ON c.[object_id] = o.[object_id]
INNER JOIN sys.schemas AS s
  ON o.[schema_id] = s.[schema_id]
  WHERE c.system_type_id IN (34, 35, 99);

Výstup:

ALTER TABLE [dbo].[t2] ALTER COLUMN [msg] nvarchar(max) NULL;

A v případě, že by vás to zajímalo, ne, tuto jednorázovou operaci nemůžete provést s explicitním WITH (ONLINE = ON) možnost:

Msg 11427, Level 16, State 1
Online operaci ALTER COLUMN nelze provést pro tabulku 't2', protože sloupec 'msg' aktuálně obsahuje nebo se mění na nepodporovaný datový typ:text, ntext, image, typ CLR nebo FILESTREAM. Operace musí být provedena offline.

Závěr

Doufejme, že to poskytuje dobré pozadí, proč chcete odstranit tyto zastaralé typy, a výchozí bod pro skutečné provedení změn. Microsoft se tvrdě naučil, že není mnoho funkcí, které mohou z produktu jen tak vytrhnout, takže se neobávám, že by tyto funkce někdy v mém životě skutečně přestaly existovat. Ale strach z odstranění by neměl být vaším jediným motivátorem.


  1. Co je CHAR_LENGTH() v MySQL?

  2. Co je SQL Server?

  3. Rychlejší způsob vkládání pomocí skriptu v Oracle?

  4. Použití StringWriter pro serializaci XML