sql >> Databasteknik >  >> RDS >> Database

Föråldrade funktioner att ta ur din verktygslåda – Del 3

Jag har nyligen diskuterat några funktioner som Microsoft avråder från att använda, och som jag tycker du bör glömma att de också finns. Det fanns ett fall där en kollega ständigt främjade den utfasade bakåtkompatibilitetsvyn sys.sysprocesses istället för nyare dynamiska hanteringsvyer (DMV) och ett annat fall där en annan kollega tog ner en produktionsserver med SQL Server Profiler.

Min senaste inkörning med saker som bäst glöms bort är en ny lagrad procedur med en ntext parameter. Jag kontrollerade och visst stämmer datatypen överens med schemat för den underliggande tabellen. Mina tankar började rasa om dessa äldre datatyper för att förklara varför vi verkligen inte borde använda dem längre:

  • bild
  • ntext
  • text

Dessa typer finns på den föråldrade listan av många anledningar och har haft en permanent plats på den listan sedan de ersattes med max typer långt tillbaka i SQL Server 2005. Några av dessa smärtpunkter inkluderar:

  • du kan inte använda många strängfunktioner, som LEFT() , RTRIM() , UPPER() , och de flesta jämförelseoperatörer;
  • du måste använda funktioner som TEXTPTR , WRITETEXT och UPDATETEXT för ändringar;
  • du kan inte använda typerna som lokala variabler;
  • du kan inte referera till kolumnerna i DISTINCT , GROUP BY , ORDER BY , eller som en inkluderad kolumn (inte för att du skulle vilja göra något av dessa);
  • mindre värden som kan passa i rad kan bara göra det med text in row alternativ.

Det är inte en uttömmande lista; det finns andra skillnader som du kanske anser vara mer eller mindre viktiga. Det mest pressande skälet för mig är att du inte kan bygga om det klustrade indexet online om tabellen innehåller en av dessa datatyper.

Låt oss skapa en enkel databas med några tabeller:

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

Låt oss nu försöka utföra onlineoperationer på borden:

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

Medan den första satsen lyckas, ger den andra ett felmeddelande som detta:

Msg 2725, Level 16, State 2
En onlineoperation kan inte utföras för index 'PK__t2__3213E83FEEA1E0AD' eftersom indexet innehåller kolumn 'msg' av datatyp text, ntext, image eller FILESTREAM. För ett icke-klustrat index kan kolumnen vara en inkluderande kolumn i indexet. För ett klustrat index kan kolumnen vara vilken kolumn som helst i tabellen. Om DROP_EXISTING används kan kolumnen vara en del av ett nytt eller gammalt index. Operationen måste utföras offline.

I ett engångsscenario är felmeddelandet tillräckligt lätt att hantera:antingen hoppar du över tabellen eller, om tabellen absolut måste byggas om, arbetar du med dina team för att schemalägga ett avbrott. När du automatiserar en lösning för indexunderhåll eller distribuerar nya komprimeringsinställningar i din miljö, är det lite mer jobbigt att få din lösning att hantera nuvarande eller framtida tillstånd.

Vad ska jag göra?

Du kan börja ersätta dessa kolumner med deras mer moderna motsvarigheter. Här är en fråga som hjälper dig att spåra dem med hjälp av sys.columns katalogvy, men du är på egen hand för alla explicita referenser som kan finnas i din applikationskod:

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];

Utdata:

Det kan vara frestande att gå in i SSMS och manuellt ändra datatyperna för dessa kolumner, men det kan också ha andra konsekvenser. Till exempel kan kolumnerna ha standardbegränsningar kopplade till dem. Och du kan ha lagrade procedurer med parametrar som bör uppdateras samtidigt:

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

För att hitta alla dessa fall kan du anpassa frågan ovan för att söka mot sys.parameters katalogvy istället:

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];

Utdata:

Om du behöver returnera denna data i alla databaser kan du ta sp_ineachdb , en procedur jag skrev (och dokumenterade här och här) för att övervinna flera av begränsningarna i buggy, odokumenterade och ostödda sp_MSforeachdb . Sedan kan du göra så här:

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];';

En intressant sidoanteckning här:om du kör det mot alla databaser kommer du att upptäcka att Microsoft fortfarande använder några av dessa gamla typer, även i SQL Server 2019.

Du kan automatisera det ytterligare genom att köra det från PowerShell eller vilket automationsverktyg du än använder för att hantera flera instanser av SQL Server.

Naturligtvis är det bara början – det producerar bara en lista. Du kan utöka den ytterligare för att generera en utkastversion av ALTER TABLE kommandon du skulle behöva för att uppdatera alla tabeller, men dessa kommandon skulle behöva granskas innan du kör dem, och du skulle fortfarande behöva modifiera procedurerna själv (genererar ALTER PROCEDURE kommandon som bara har dessa parametertypnamn ersatta korrekt är inte en lätt övning på något sätt). Här är ett exempel som genererar ALTER TABLE kommandon, med hänsyn till nollbarhet men inga andra komplikationer som standardbegränsningar:

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

Utdata:

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

Och om du undrar, nej, du kan inte göra den här engångsoperationen med en explicit WITH (ONLINE = ON) alternativ:

Msg 11427, Level 16, State 1
Online-operationen ALTER COLUMN kan inte utföras för tabellen 't2' eftersom kolumnen 'msg' för närvarande har eller håller på att ändras till en datatyp som inte stöds:text, ntext, image, CLR-typ eller FILESTRÖM. Operationen måste utföras offline.

Slutsats

Förhoppningsvis ger detta lite bra bakgrund om varför du vill eliminera dessa föråldrade typer, och en utgångspunkt för att faktiskt göra ändringarna. Microsoft lärde sig den hårda vägen att det inte finns mycket funktionalitet som de bara kan slita ur produkten, så jag är inte orolig för att dessa någonsin faktiskt kommer att sluta existera under min livstid. Men rädsla för borttagning bör inte vara din enda motivation.


  1. ECONNREFUSED för Postgres på nodeJS med dockare

  2. Hur installerar man MySQLdb (Python data access library to MySQL) på Mac OS X?

  3. Mätning av faktisk MySQL-frågetid

  4. Infogar i Oracle Nested Table i Java