Freigeben über


CLR-Tabellenwertfunktionen

Gilt für:SQL Server

Eine Tabellenwertfunktion ist eine benutzerdefinierte Funktion, die eine Tabelle zurückgibt.

SQL Server erweitert die Funktionalität von Tabellenwertfunktionen, indem Sie eine Tabellenwertfunktion in jeder verwalteten Sprache definieren können. Daten werden von einer Tabellenwertfunktion über ein IEnumerable- oder IEnumerator-Objekt zurückgegeben.

Bei Tabellenwertfunktionen können die Spalten des Rückgabetabellentyps keine Zeitstempelspalten oder Nicht-Unicode-Datentypspalten (z. B. Zeichen, varcharund Text) enthalten. Die NOT NULL Einschränkung wird nicht unterstützt.

Unterschiede zwischen Transact-SQL- und CLR-Tabellenwertfunktionen

Transact-SQL-Tabellenwertfunktionen materialisieren die Ergebnisse des Aufrufens der Funktion in eine Zwischentabelle. Da sie eine Zwischentabelle verwenden, können sie Einschränkungen und eindeutige Indizes der Ergebnisse unterstützen. Diese Features können nützlich sein, wenn große Ergebnisse zurückgegeben werden.

Im Gegensatz dazu stellen clR-Tabellenwertfunktionen (Common Language Runtime) eine Streaming-Alternative dar. Es ist nicht erforderlich, dass der gesamte Satz von Ergebnissen in einer einzelnen Tabelle materialisiert wird. Das von der verwalteten Funktion zurückgegebene IEnumerable-Objekt wird direkt vom Ausführungsplan der Abfrage aufgerufen, der die Tabellenwertfunktion aufruft, und die Ergebnisse werden inkrementell verarbeitet. Beim Streamingmodell werden Ergebnisse sofort verarbeitet, sobald die erste Zeile verfügbar ist, und nicht erst, wenn die gesamte Tabelle aufgefüllt wurde. Es ist auch eine bessere Alternative, wenn Sie eine große Anzahl von Zeilen zurückgegeben haben, da sie nicht im Gesamten im Arbeitsspeicher materialisiert werden müssen. Beispielsweise könnte eine verwaltete Tabellenwertfunktion verwendet werden, um eine Textdatei zu analysieren und jede Zeile als Tabellenzeile zurückzugeben.

Implementieren von Tabellenwertfunktionen

Implementieren Sie Tabellenwertfunktionen als Methoden für eine Klasse in einer .NET Framework-Assembly. Ihr Tabellenwertfunktionscode muss die IEnumerable Schnittstelle implementieren. Die IEnumerable-Schnittstelle wird in .NET Framework definiert. Typen, die Arrays und Auflistungen in .NET Framework darstellen, implementieren bereits die IEnumerable Schnittstelle. Dies vereinfacht das Schreiben von Tabellenwertfunktionen, die eine Auflistung oder ein Array in ein Resultset konvertieren.

Parameter mit Tabellenwert

Tabellenwertparameter sind benutzerdefinierte Tabellentypen, die an eine Prozedur oder Funktion übergeben werden, und bieten eine effiziente Methode zum Übergeben mehrerer Datenzeilen an den Server. Tabellenwertparameter bieten ähnliche Funktionen wie Parameterarrays, bieten jedoch eine größere Flexibilität und engere Integration in Transact-SQL. Außerdem verfügen sie auch über ein besseres Leistungspotenzial.

Außerdem tragen Tabellenwertparameter dazu bei, die Anzahl von Roundtrips zum Server zu reduzieren. Anstatt mehrere Anforderungen an den Server zu senden, wie beispielsweise bei einer Liste von skalaren Parametern, können Daten als Tabellenwertparameter an den Server gesendet werden. Ein benutzerdefinierter Tabellentyp kann nicht als Tabellenwertparameter übergeben oder von einer verwalteten gespeicherten Prozedur oder Funktion zurückgegeben werden, die im SQL Server-Prozess ausgeführt wird. Weitere Informationen zu Tabellenwertparametern finden Sie unter Verwenden von Tabellenwertparametern (Datenbankmodul).

Ausgabeparameter und Tabellenwertfunktionen

Informationen können von Tabellenwertfunktionen mithilfe von Ausgabeparametern zurückgegeben werden. Der entsprechende Parameter im Implementierungscode der Tabellenwertfunktion sollte einen als Verweis zu übergebenden Parameter als Argument verwenden. Visual Basic .NET unterstützt Ausgabeparameter nicht auf die gleiche Weise wie C#. Sie müssen den Parameter nach Verweis angeben und das attribut <Out()> anwenden, um einen Ausgabeparameter darzustellen, wie im folgenden Beispiel gezeigt:

Imports System.Runtime.InteropServices
...
Public Shared Sub FillRow ( <Out()> ByRef value As SqlInt32)

Definieren einer Tabellenwertfunktion in Transact-SQL

Die Syntax zum Definieren einer CLR-Tabellenwertfunktion ähnelt der einer Transact-SQL Tabellenwertfunktion mit dem Hinzufügen der EXTERNAL NAME-Klausel. Zum Beispiel:

CREATE FUNCTION GetEmpFirstLastNames()
RETURNS TABLE (
    FirstName NVARCHAR (4000),
    LastName NVARCHAR (4000)
)
AS EXTERNAL NAME MyDotNETAssembly.[MyNamespace.MyClassname].GetEmpFirstLastNames;

Tabellenwertfunktionen werden verwendet, um Daten in relationalem Format zur weiteren Verarbeitung in Abfragen darzustellen, z. B.:

SELECT *
FROM func();

SELECT *
FROM tbl
     INNER JOIN func() AS f
         ON tbl.col = f.col;

SELECT *
FROM tbl AS t
CROSS APPLY func(t.col);

Tabellenwertfunktionen können in den folgenden Fällen eine Tabelle zurückgegeben:

  • Wenn sie aus skalaren Eingabeargumenten erstellt wurde. Beispielsweise eine Tabellenwertfunktion, die eine durch Trennzeichen getrennte Zeichenfolge von Zahlen in einer Tabelle anordnet.

  • Wenn sie aus externen Daten erstellt wurde. Beispielsweise eine Tabellenwertfunktion, die das Ereignisprotokoll liest und es als Tabelle bereitstellt.

Hinweis

Eine Tabellenwertfunktion kann nur den Datenzugriff über eine Transact-SQL Abfrage in der InitMethod-Methode und nicht in der FillRow-Methode ausführen. Die InitMethod sollte mit der SqlFunction.DataAccess.Read Attributeigenschaft gekennzeichnet werden, wenn eine Transact-SQL Abfrage ausgeführt wird.

Beispieltabellenwertfunktion

Die folgende Tabellenwertfunktion gibt Informationen aus dem Systemereignisprotokoll zurück. Die Funktion liest ein einzelnes Zeichenfolgenargument, das den Namen des Ereignisprotokolls enthält.

Beispielcode

using System;
using System.Data.Sql;
using Microsoft.SqlServer.Server;
using System.Collections;
using System.Data.SqlTypes;
using System.Diagnostics;

public class TabularEventLog
{
    [SqlFunction(FillRowMethodName = "FillRow")]
    public static IEnumerable InitMethod(String logname)
    {
        return new EventLog(logname).Entries;
    }

    public static void FillRow(Object obj, out SqlDateTime timeWritten, out SqlChars message, out SqlChars category, out long instanceId)
    {
        EventLogEntry eventLogEntry = (EventLogEntry)obj;
        timeWritten = new SqlDateTime(eventLogEntry.TimeWritten);
        message = new SqlChars(eventLogEntry.Message);
        category = new SqlChars(eventLogEntry.Category);
        instanceId = eventLogEntry.InstanceId;
    }
}

Deklarieren und Verwenden der Beispieltabellenwertfunktion

Nachdem die Beispieltabellenwertfunktion kompiliert wurde, kann sie in Transact-SQL wie folgt deklariert werden:

USE master;
-- Replace SQL_Server_logon with your SQL Server user credentials.

GRANT EXTERNAL ACCESS ASSEMBLY TO [SQL_Server_logon];

-- Modify the following line to specify a different database.
ALTER DATABASE master
SET TRUSTWORTHY ON;

-- Modify the next line to use the appropriate database.
CREATE ASSEMBLY tvfEventLog
    FROM 'D:\assemblies\tvfEventLog\tvfeventlog.dll'
    WITH PERMISSION_SET = EXTERNAL_ACCESS;
GO

CREATE FUNCTION ReadEventLog
(@logname NVARCHAR (100))
RETURNS TABLE (
    logTime DATETIME,
    Message NVARCHAR (4000),
    Category NVARCHAR (4000),
    InstanceId BIGINT)
AS EXTERNAL NAME tvfEventLog.TabularEventLog.InitMethod;
GO

Visual C++-Datenbankobjekte, die mit /clr:pure kompiliert wurden, werden für die Ausführung auf SQL Server nicht unterstützt. Zu solchen Datenbankobjekten gehören beispielsweise Tabellenwertfunktionen.

Um das Beispiel zu testen, probieren Sie den folgenden Transact-SQL-Code aus:

-- Select the top 100 events,
SELECT TOP 100 *
FROM dbo.ReadEventLog(N'Security') AS T;
GO

-- Select the last 10 login events.
SELECT TOP 10 T.logTime,
              T.Message,
              T.InstanceId
FROM dbo.ReadEventLog(N'Security') AS T
WHERE T.Category = N'Logon/Logoff';
GO

Beispiele

Die Transact-SQL Codebeispiele in diesem Artikel verwenden die AdventureWorks2022- oder AdventureWorksDW2022 Beispieldatenbank, die Sie von der Microsoft SQL Server Samples and Community Projects Homepage herunterladen können.

Ein. Zurückgeben der Ergebnisse einer SQL Server-Abfrage

Das folgende Beispiel zeigt eine Tabellenwertfunktion, die eine SQL Server-Datenbank abfragt.

Nennen Sie die Quellcodedatei FindInvalidEmails.cs bzw. FindInvalidEmails.vb.

using System;
using System.Collections;
using System.Data;
using System.Data.SqlClient;
using System.Data.SqlTypes;

using Microsoft.SqlServer.Server;

public partial class UserDefinedFunctions {
   private class EmailResult {
      public SqlInt32 CustomerId;
      public SqlString EmailAdress;

      public EmailResult(SqlInt32 customerId, SqlString emailAdress) {
         CustomerId = customerId;
         EmailAdress = emailAdress;
      }
   }

   public static bool ValidateEmail(SqlString emailAddress) {
      if (emailAddress.IsNull)
         return false;

      if (!emailAddress.Value.EndsWith("@adventure-works.com"))
         return false;

      // Validate the address. Put any more rules here.
      return true;
   }

   [SqlFunction(
       DataAccess = DataAccessKind.Read,
       FillRowMethodName = "FindInvalidEmails_FillRow",
       TableDefinition="CustomerId int, EmailAddress nvarchar(4000)")]
   public static IEnumerable FindInvalidEmails(SqlDateTime modifiedSince) {
      ArrayList resultCollection = new ArrayList();

      using (SqlConnection connection = new SqlConnection("context connection=true")) {
         connection.Open();

         using (SqlCommand selectEmails = new SqlCommand(
             "SELECT " +
             "[CustomerID], [EmailAddress] " +
             "FROM [AdventureWorksLT2022].[SalesLT].[Customer] " +
             "WHERE [ModifiedDate] >= @modifiedSince",
             connection)) {
            SqlParameter modifiedSinceParam = selectEmails.Parameters.Add(
                "@modifiedSince",
                SqlDbType.DateTime);
            modifiedSinceParam.Value = modifiedSince;

            using (SqlDataReader emailsReader = selectEmails.ExecuteReader()) {
               while (emailsReader.Read()) {
                  SqlString emailAddress = emailsReader.GetSqlString(1);
                  if (ValidateEmail(emailAddress)) {
                     resultCollection.Add(new EmailResult(
                         emailsReader.GetSqlInt32(0),
                         emailAddress));
                  }
               }
            }
         }
      }

      return resultCollection;
   }

   public static void FindInvalidEmails_FillRow(
       object emailResultObj,
       out SqlInt32 customerId,
       out SqlString emailAdress) {
      EmailResult emailResult = (EmailResult)emailResultObj;

      customerId = emailResult.CustomerId;
      emailAdress = emailResult.EmailAdress;
   }
};

Kompilieren Sie den Quellcode zu einer DLL, und kopieren Sie die DLL in das Stammverzeichnis von Laufwerk C:. Führen Sie dann die folgende Transact-SQL-Abfrage aus.

USE AdventureWorksLT2022;
GO

IF EXISTS (SELECT name
           FROM sysobjects
           WHERE name = 'FindInvalidEmails')
    DROP FUNCTION FindInvalidEmails;
GO

IF EXISTS (SELECT name
           FROM sys.assemblies
           WHERE name = 'MyClrCode')
    DROP ASSEMBLY MyClrCode;
GO

CREATE ASSEMBLY MyClrCode
    FROM 'C:\FindInvalidEmails.dll'
    WITH PERMISSION_SET = SAFE;
GO

CREATE FUNCTION FindInvalidEmails
(@ModifiedSince DATETIME)
RETURNS TABLE (
    CustomerId INT,
    EmailAddress NVARCHAR (4000))
AS EXTERNAL NAME MyClrCode.UserDefinedFunctions.[FindInvalidEmails];
GO

SELECT *
FROM FindInvalidEmails('2000-01-01');
GO