sql >> Database >  >> RDS >> Database

Typ die door de tabel gewaardeerde parameters sterk

Parameters met tabelwaarde bestaan ​​al sinds SQL Server 2008 en bieden een handig mechanisme voor het verzenden van meerdere rijen gegevens naar SQL Server, samengebracht als een enkele aanroep met parameters. Alle rijen zijn dan beschikbaar in een tabelvariabele die vervolgens kan worden gebruikt in standaard T-SQL-codering, waardoor het niet meer nodig is om gespecialiseerde verwerkingslogica te schrijven om de gegevens weer op te splitsen. Door hun definitie zijn tabelwaardeparameters sterk getypeerd naar een door de gebruiker gedefinieerd tabeltype dat moet bestaan ​​in de database waar de aanroep wordt gedaan. Sterk getypt is echter niet echt strikt "sterk getypt" zoals je zou verwachten, zoals dit artikel zal aantonen, en de prestaties kunnen hierdoor worden beïnvloed.

Om de potentiële prestatie-effecten van onjuist getypte tabelwaardeparameters met SQL Server te demonstreren, gaan we een voorbeeld van een door de gebruiker gedefinieerd tabeltype maken met de volgende structuur:

CREATE TYPE dbo.PharmacyData AS TABLE
(
  Dosage        int,
  Drug          varchar(20),
  FirstName     varchar(50),
  LastName      varchar(50),
  AddressLine1  varchar(250),
  PhoneNumber   varchar(50),
  CellNumber    varchar(50),
  EmailAddress  varchar(100),
  FillDate      datetime
);

Dan hebben we een .NET-toepassing nodig die dit door de gebruiker gedefinieerde tabeltype gaat gebruiken als invoerparameter voor het doorgeven van gegevens aan SQL Server. Om een ​​tabelwaardeparameter uit onze toepassing te gebruiken, wordt meestal een DataTable-object ingevuld en vervolgens doorgegeven als de waarde voor de parameter met het type SqlDbType.Structured. De DataTable kan op meerdere manieren worden gemaakt in de .NET-code, maar een gebruikelijke manier om de tabel te maken is ongeveer de volgende:

System.Data.DataTable DefaultTable = new System.Data.DataTable("@PharmacyData");
DefaultTable.Columns.Add("Dosage",       typeof(int));
DefaultTable.Columns.Add("Drug",         typeof(string));
DefaultTable.Columns.Add("FirstName",    typeof(string));
DefaultTable.Columns.Add("LastName",     typeof(string));
DefaultTable.Columns.Add("AddressLine1", typeof(string));
DefaultTable.Columns.Add("PhoneNumber",  typeof(string));
DefaultTable.Columns.Add("CellNumber",   typeof(string));
DefaultTable.Columns.Add("EmailAddress", typeof(string));
DefaultTable.Columns.Add("Date",         typeof(DateTime));

U kunt de DataTable ook als volgt maken met behulp van de inline-definitie:

System.Data.DataTable DefaultTable = new System.Data.DataTable("@PharmacyData")
{
  Columns =
  {
    {"Dosage",       typeof(int)},
    {"Drug",         typeof(string)},
    {"FirstName",    typeof(string)},
    {"LastName",     typeof(string)},
    {"AddressLine1", typeof(string)},
    {"PhoneNumber",  typeof(string)},
    {"CellNumber",   typeof(string)},
    {"EmailAddress", typeof(string)},
    {"Date",         typeof(DateTime)},
  },
  Locale = CultureInfo.InvariantCulture
};

Elk van deze definities van het DataTable-object in .NET kan worden gebruikt als tabelwaardeparameter voor het door de gebruiker gedefinieerde gegevenstype dat is gemaakt, maar let op de typeof(string)-definitie voor de verschillende stringkolommen; deze kunnen allemaal "juist" zijn getypt, maar ze zijn niet echt sterk getypt voor de gegevenstypen die zijn geïmplementeerd in het door de gebruiker gedefinieerde gegevenstype. We kunnen de tabel vullen met willekeurige gegevens en deze doorgeven aan SQL Server als een parameter voor een zeer eenvoudige SELECT-instructie die exact dezelfde rijen teruggeeft als de tabel die we hebben doorgegeven, als volgt:

using (SqlCommand cmd = new SqlCommand("SELECT * FROM @tvp;", connection))
{
  var pList = new SqlParameter("@tvp", SqlDbType.Structured);
  pList.TypeName = "dbo.PharmacyData";
  pList.Value = DefaultTable;
  cmd.Parameters.Add(pList);
  cmd.ExecuteReader().Dispose();
}

We kunnen dan een debug-onderbreking gebruiken, zodat we de definitie van DefaultTable tijdens de uitvoering kunnen inspecteren, zoals hieronder weergegeven:

We kunnen zien dat de MaxLength voor de stringkolommen is ingesteld op -1, wat betekent dat ze via TDS naar SQL Server worden doorgegeven als LOB's (Large Objects) of in wezen als MAX-kolommen met gegevenstype, en dit kan de prestaties op een negatieve manier beïnvloeden. Als we de .NET DataTable-definitie wijzigen om sterk getypeerd te worden in de schemadefinitie van het door de gebruiker gedefinieerde tabeltype als volgt en kijken naar de MaxLength van dezelfde kolom met behulp van een debug-onderbreking:

System.Data.DataTable SchemaTable = new System.Data.DataTable("@PharmacyData")
{
  Columns =
  {
    {new DataColumn() { ColumnName = "Dosage",        DataType = typeof(int)} },
    {new DataColumn() { ColumnName = "Drug",          DataType = typeof(string), MaxLength = 20} },
    {new DataColumn() { ColumnName = "FirstName",     DataType = typeof(string), MaxLength = 50} },
    {new DataColumn() { ColumnName = "LastName",      DataType = typeof(string), MaxLength = 50} },
    {new DataColumn() { ColumnName = "AddressLine1",  DataType = typeof(string), MaxLength = 250} },
    {new DataColumn() { ColumnName = "PhoneNumber",   DataType = typeof(string), MaxLength = 50} },
    {new DataColumn() { ColumnName = "CellNumber",    DataType = typeof(string), MaxLength = 50} },
    {new DataColumn() { ColumnName = "EmailAddress",  DataType = typeof(string), MaxLength = 100} },
    {new DataColumn() { ColumnName = "Date",          DataType = typeof(DateTime)} },
  },
  Locale = CultureInfo.InvariantCulture
};

We hebben nu de juiste lengtes voor de kolomdefinities en we zullen ze niet doorgeven als LOB's via TDS aan SQL Server.

Welke invloed heeft dit op de prestaties, vraagt ​​u zich misschien af? Het is van invloed op het aantal TDS-buffers dat via het netwerk naar SQL Server wordt verzonden, en het heeft ook invloed op de algehele verwerkingstijd voor de opdrachten.

Door exact dezelfde gegevensset te gebruiken voor de twee gegevenstabellen en door gebruik te maken van de methode RetrieveStatistics op het SqlConnection-object, kunnen we de statistieken ExecutionTime en BuffersSent krijgen voor de aanroepen van dezelfde SELECT-opdracht, en alleen de twee verschillende DataTable-definities als parameters gebruiken en door de ResetStatistics-methode van het SqlConnection-object aan te roepen, kunnen de uitvoeringsstatistieken tussen tests worden gewist.

De GetSchemaTable-definitie specificeert de MaxLength voor elk van de tekenreekskolommen correct, waarbij GetTable alleen kolommen van het type tekenreeks toevoegt waarvan de MaxLength-waarde is ingesteld op -1, wat resulteert in 100 extra TDS-buffers die worden verzonden voor 861 rijen met gegevens in de tabel en een runtime van 158 milliseconden vergeleken met slechts 250 buffers die worden verzonden voor de sterk getypeerde DataTable-definitie en een looptijd van 111 milliseconden. Hoewel dit misschien niet veel lijkt in het grote geheel van dingen, is dit een enkele oproep, enkele uitvoering, en de geaccumuleerde impact in de loop van de tijd voor vele duizenden of miljoenen van dergelijke uitvoeringen is waar de voordelen beginnen op te lopen en een merkbare impact hebben op werklastprestaties en doorvoer.

Waar dit echt een verschil kan maken, is bij cloudimplementaties waar u voor meer betaalt dan alleen reken- en opslagbronnen. Naast de vaste kosten van hardwarebronnen voor Azure VM, SQL Database of AWS EC2 of RDS, zijn er extra kosten voor netwerkverkeer van en naar de cloud die worden verrekend met de facturering voor elke maand. Het verminderen van de buffers die over de draad gaan, zal de TCO voor de oplossing in de loop van de tijd verlagen, en de codewijzigingen die nodig zijn om deze besparingen door te voeren, zijn relatief eenvoudig.


  1. Oracle SQL-uren Verschil tussen datums in UU:MM:SS

  2. Hoe panda's DataFrame upsert naar PostgreSQL-tabel?

  3. DevOps-overwegingen voor productieklare database-implementaties

  4. 3 manieren om erachter te komen of een kolom een ​​berekende kolom is in SQL Server