Язык

Изменение данных Large-Value (максимум) в ADO.NET

Типы данных больших объектов (LOB) — это типы данных, превышающие максимальный размер строки размером 8 килобайт (КБ). SQL Server предоставляет спецификатор для max, varchar и nvarchar типов данных, чтобы позволить хранение значений размером до 2^32 байт. Столбцы таблиц и Transact-SQL переменные могут указывать varchar(max)или nvarchar(max)varbinary(max) типы данных. В ADO.NET max типы данных можно получить с помощью DataReader, и их также можно указать как значения входного и выходного параметров без специальной обработки. Для больших varchar типов данных данные можно извлекать и обновлять постепенно.

Типы данных max можно использовать для сравнения, в качестве переменных Transact-SQL и для объединения. Их также можно использовать в DISTINCTпредложениях инструкции SELECT , ORDER BYGROUP BY а также в агрегатах, соединениях и вложенных запросах.

Дополнительные сведения см. в разделе "Использование типов данных Large-Value".

Ограничения типов Large-Value

Следующие ограничения применяются к типам max данных, которые не существуют для небольших типов данных:

  • sql_variant не может содержать крупный тип данных varchar.

  • Большие varchar столбцы нельзя указать в качестве ключевого столбца в индексе. Они разрешены в включенном столбце в некластеризованном индексе.

  • Большие varchar столбцы нельзя использовать в качестве секционирования ключевых столбцов.

Работа с типами Large-Value в Transact-SQL

Функция Transact-SQL OPENROWSET — это одноразовый метод подключения и доступа к удаленным данным. Он содержит все сведения о подключении, необходимые для доступа к удаленным данным из источника данных OLE DB. OPENROWSET можно ссылаться в FROM предложении запроса, как будто это имя таблицы. На нее также можно ссылаться в качестве целевой таблицы инструкции INSERT, UPDATE или DELETE, в зависимости от возможностей поставщика OLE DB.

Функция OPENROWSET включает поставщика набора строк BULK, который позволяет напрямую считывать данные из файла без загрузки данных в целевую таблицу. Это позволяет использовать OPENROWSET в простом INSERT SELECT операторе.

Аргументы OPENROWSET BULK параметра обеспечивают значительный контроль над началом и завершением чтения данных, способом обработки ошибок и интерпретацией данных. Например, можно указать, что файл данных считывается как набор строк, состоящий из одной строки и одного столбца, типа varbinary, varchar или nvarchar.

В следующем примере фотография вставляется в таблицу ProductPhoto в примере базы данных AdventureWorks. При использовании BULK OPENROWSET поставщика необходимо указать именованный список столбцов, даже если вы не вставляете значения в каждый столбец. Первичный ключ в этом случае определяется как столбец идентификатора и может быть опущен из списка столбцов. Обратите внимание, что необходимо также указать имя корреляции в конце инструкции OPENROWSET , которая в данном случае — ThumbnailPhoto. Это коррелирует с столбцом в ProductPhoto таблице, в которую загружается файл.

INSERT Production.ProductPhoto (
    ThumbnailPhoto,
    ThumbnailPhotoFilePath,
    LargePhoto,
    LargePhotoFilePath)
SELECT ThumbnailPhoto.*, null, null, N'tricycle_pink.gif'
FROM OPENROWSET
    (BULK 'c:\images\tricycle.jpg', SINGLE_BLOB) ThumbnailPhoto

Обновление данных с помощью UPDATE .WRITE

Оператор Transact-SQL UPDATE имеет новый WRITE синтаксис для изменения содержимого столбцов varchar(max)nvarchar(max)или varbinary(max) столбцов. Это позволяет выполнять частичные обновления данных. Синтаксис UPDATE .WRITE показан здесь в сокращенной форме:

UPDATE

{ <объект> }

SET

{ column_name = { .WRITE ( expression , @Offset , @Length ) }

Метод WRITE указывает, что будет изменена часть значения column_name. Выражение — это значение, которое будет скопировано в column_name, @Offset является начальной точкой, в которой будет записано выражение, а @Length аргумент — длина раздела в столбце.

Если Затем
Выражение имеет значение NULL @Length игнорируется, а значение в column_name усечено по указанному параметру @Offset.
@Offset равен NULL Операция обновления добавляет выражение в конец существующего значения в имя_столбца, при этом @Length игнорируется.
@Offset больше, чем длина значения столбца column_name SQL Server возвращает ошибку.
@Length равен NULL Операция обновления удаляет все данные с @Offset до конца значения column_name.

Замечание

Ни @Offset ни @Length не могут быть отрицательным числом.

Пример

Этот пример Transact-SQL показывает, как обновить частичное значение в DocumentSummary, nvarchar(max) столбце таблицы Document в базе данных AdventureWorks. Слово "компоненты" заменяется словом "функции" путем указания слова замены, начального расположения (смещения) слова, заменяемого в существующих данных, и количества символов, которые необходимо заменить (длина). В этом примере содержатся SELECT операторы до и после оператора UPDATE для сравнения результатов.

USE AdventureWorks;
GO
--View the existing value.
SELECT DocumentSummary
FROM Production.Document
WHERE DocumentID = 3;
GO
-- The first sentence of the results will be:
-- Reflectors are vital safety components of your bicycle.

--Modify a single word in the DocumentSummary column
UPDATE Production.Document
SET DocumentSummary .WRITE (N'features',28,10)
WHERE DocumentID = 3 ;
GO
--View the modified value.
SELECT DocumentSummary
FROM Production.Document
WHERE DocumentID = 3;
GO
-- The first sentence of the results will be:
-- Reflectors are vital safety features of your bicycle.

Работа с типами Large-Value в ADO.NET

Используйте объекты SqlParameter с SqlCommand для отправки больших значений. Получите результаты с помощью SqlDataReader или заполните объекты DataSet или DataTable с помощью SqlDataAdapter. Вы обрабатываете большие типы значений, как связанные типы данных меньшего размера.

Получение данных с помощью GetSqlBytes

Чтобы получить содержимое столбца varbinary(max) как SqlBytes, вызовите GetSqlBytes. В следующем фрагменте предполагается, что cmd это SqlCommand выбор двоичных данных и reader его SqlDataReader извлечение.

reader = cmd.ExecuteReader(CommandBehavior.CloseConnection)
While reader.Read()
    Dim bytes As SqlBytes = reader.GetSqlBytes(0)
End While
reader = cmd.ExecuteReader(CommandBehavior.CloseConnection);
while (reader.Read())
    {
        SqlBytes bytes = reader.GetSqlBytes(0);
    }

Использование GetSqlChars для извлечения данных

Чтобы получить содержимое столбца nvarchar(max) или varchar(max), вызовите GetSqlChars. В следующем фрагменте предполагается, что cmd — это SqlCommand, который выбирает данные nvarchar(max), а reader — это SqlDataReader, который их извлекает.

reader = cmd.ExecuteReader(CommandBehavior.CloseConnection)
While reader.Read()
    Dim buffer As SqlChars = reader.GetSqlChars(0)
End While
reader = cmd.ExecuteReader(CommandBehavior.CloseConnection);
while (reader.Read())
{
    SqlChars buffer = reader.GetSqlChars(0);
}

Использование GetSqlBinary для извлечения данных

Чтобы получить содержимое столбца varbinary(max) в качестве SqlBinary значения, вызовите GetSqlBinary. В следующем фрагменте предполагается, что cmd — это SqlCommand, который выбирает двоичные данные, а reader — это SqlDataReader, который их извлекает.

reader = cmd.ExecuteReader(CommandBehavior.CloseConnection)
While reader.Read()
    Dim binaryStream As SqlBinary = reader.GetSqlBinary(0)
End While
reader = cmd.ExecuteReader(CommandBehavior.CloseConnection);
while (reader.Read())
    {
        SqlBinary binaryStream = reader.GetSqlBinary(0);
    }

Использование GetBytes для извлечения данных

Метод GetBytes считывает байты из указанного смещения столбца в массив байтов с указанным смещением массива. В следующем фрагменте предполагается, что reader — это SqlDataReader, который извлекает байты. В отличие от GetSqlBytes, для GetBytes требуется указать размер буфера массива.

While reader.Read()
    Dim buffer(4000) As Byte
    Dim byteCount As Integer = _
    CInt(reader.GetBytes(1, 0, buffer, 0, 4000))
End While
while (reader.Read())
{
    byte[] buffer = new byte[4000];
    long byteCount = reader.GetBytes(1, 0, buffer, 0, 4000);
}

Использование GetValue для извлечения данных

Метод GetValue возвращает значение столбца в виде объекта. В следующем фрагменте предполагается, что reader это SqlDataReader. В C# двоичный столбец возвращает byte[], а символьный столбец возвращает string. В Visual Basic соответствующими типами являются Byte() и String.

While reader.Read()
    ' Read the data from varbinary(max) column
    Dim binaryData() As Byte = CType(reader.GetValue(0), Byte())

    ' Read the data from varchar(max) or nvarchar(max) column
    Dim stringData As String = CStr(reader.GetValue(1))
End While
while (reader.Read())
{
    // Read the data from varbinary(max) column
    byte[] binaryData = (byte[])reader.GetValue(0);

    // Read the data from varchar(max) or nvarchar(max) column
    String stringData = (String)reader.GetValue(1);
}

Преобразование из типов Large Value в типы CLR

Содержимое столбца nvarchar(max) или varchar(max) можно преобразовать с помощью методов преобразования строк, таких как ToString. В следующем фрагменте предполагается, что reader — это SqlDataReader, который получает данные.

While reader.Read()
    Dim str as String = reader(0).ToString()
    Console.WriteLine(str)
End While
while (reader.Read())
{
     string str = reader[0].ToString();
     Console.WriteLine(str);
}

Пример

Следующий код извлекает имя и LargePhoto значение из ProductPhoto таблицы в AdventureWorks базе данных и сохраняет его в файл. В этом примере преобразования изображений требуются Windows и пакет NuGet System.Drawing.Common. Это ограничение применяется к преобразованию изображений, а не к извлечению двоичных данных провайдера. Метод GetSqlBytes возвращает SqlBytes, который предоставляет доступ к свойству Stream. Код использует поток для создания Bitmap объекта и сохраняет его в формате GIF.

static void TestGetSqlBytes(int documentID, string filePath)
{
    // Assumes GetConnectionString returns a valid connection string.
    using (SqlConnection connection =
               new(GetConnectionString()))
    {
        SqlCommand command = connection.CreateCommand();
        SqlDataReader reader = default!;
        try
        {
            // Setup the command
            command.CommandText =
                "SELECT LargePhotoFileName, LargePhoto "
                + "FROM Production.ProductPhoto "
                + "WHERE ProductPhotoID=@ProductPhotoID";
            command.CommandType = CommandType.Text;

            // Declare the parameter
            SqlParameter paramID =
                new("@ProductPhotoID", SqlDbType.Int)
                {
                    Value = documentID
                };
            command.Parameters.Add(paramID);
            connection.Open();

            string photoName = default!;

            reader = command.ExecuteReader(CommandBehavior.CloseConnection);

            if (reader.HasRows)
            {
                while (reader.Read())
                {
                    // Get the name of the file.
                    photoName = reader.GetString(0);

                    // Ensure that the column isn't null
                    if (reader.IsDBNull(1))
                    {
                        Console.WriteLine($"{photoName} is unavailable.");
                    }
                    else
                    {
                        SqlBytes bytes = reader.GetSqlBytes(1);
                        using (Bitmap productImage = new(bytes.Stream))
                        {
                            var fileName = filePath + photoName;

                            // Save in gif format.
                            productImage.Save(fileName, ImageFormat.Gif);
                            Console.WriteLine($"Successfully created {fileName}.");
                        }
                    }
                }
            }
            else
            {
                Console.WriteLine("No records returned.");
            }
        }
        catch (Exception ex)
        {
            Console.WriteLine(ex.Message);
        }
        finally
        {
            reader?.Dispose();
        }
    }
}
Private Sub GetPhoto(
  ByVal documentID As Integer, ByVal filePath As String)
    ' Assumes GetConnectionString returns a valid connection string.
    Using connection As New SqlConnection(GetConnectionString())
        Dim command As SqlCommand = connection.CreateCommand()
        Dim reader As SqlDataReader
        Try
            ' Setup the command
            command.CommandText =
              "SELECT LargePhotoFileName, LargePhoto FROM" _
                & " Production.ProductPhoto" _
                & " WHERE ProductPhotoID=@ProductPhotoID"
            command.CommandType = CommandType.Text

            ' Declare the parameter
            Dim paramID As SqlParameter =
                New SqlParameter("@ProductPhotoID", SqlDbType.Int)
            paramID.Value = documentID
            command.Parameters.Add(paramID)
            connection.Open()

            Dim photoName As String

            reader =
             command.ExecuteReader(CommandBehavior.CloseConnection)

            If reader.HasRows Then
                While reader.Read()
                    ' Get the name of the file
                    photoName = reader.GetString(0)

                    ' Ensure that the column isn't null
                    If (reader.IsDBNull(1)) Then
                        Console.WriteLine("{0} is unavailable.", photoName)
                    Else
                        Dim bytes As SqlBytes = reader.GetSqlBytes(1)
                        Using productImage As New Bitmap(bytes.Stream)
                            Dim fileName As String = filePath & photoName

                            ' Save in gif format.
                            productImage.Save(
                              fileName, ImageFormat.Gif)
                            Console.WriteLine("Successfully created {0}.", fileName)
                        End Using
                    End If
                End While
            Else
                Console.WriteLine("No records returned.")
            End If
        Catch ex As Exception
            Console.WriteLine("Exception: {0}", ex.Message)
        End Try
    End Using
End Sub

Использование параметров больших типов значений

Крупные типы значений можно использовать в SqlParameter объектах так же, как и в объектах меньшего размера SqlParameter . Вы можете получить большие типы значений в виде SqlParameter значений, как показано в следующем примере. В коде предполагается, что в примере базы данных AdventureWorks существует следующая хранимая процедура GetDocumentSummary. Хранимая процедура принимает входной параметр с именем @DocumentID и возвращает содержимое столбца DocumentSummary в выходном параметре @DocumentSummary .

CREATE PROCEDURE GetDocumentSummary
(
    @DocumentID int,
    @DocumentSummary nvarchar(MAX) OUTPUT
)
AS
SET NOCOUNT ON
SELECT  @DocumentSummary=Convert(nvarchar(MAX), DocumentSummary)
FROM    Production.Document
WHERE   DocumentID=@DocumentID

Пример

Код ADO.NET создает SqlConnection и SqlCommand объекты для выполнения хранимой процедуры GetDocumentSummary и получения сводки документа, которая хранится как большой тип значения. Код передает значение входного @DocumentID параметра и отображает результаты, переданные обратно в выходной @DocumentSummary параметр в окне консоли.

static string? GetDocumentSummary(int documentID)
{
    // Assumes GetConnectionString returns a valid connection string.
    using (SqlConnection connection =
               new(GetConnectionString()))
    {
        connection.Open();
        SqlCommand command = connection.CreateCommand();
        try
        {
            // Set up the command to execute the stored procedure.
            command.CommandText = "GetDocumentSummary";
            command.CommandType = CommandType.StoredProcedure;

            // Set up the input parameter for the DocumentID.
            SqlParameter paramID =
                new("@DocumentID", SqlDbType.Int)
                {
                    Value = documentID
                };
            command.Parameters.Add(paramID);

            // Set up the output parameter to retrieve the summary.
            SqlParameter paramSummary =
                new("@DocumentSummary",
                SqlDbType.NVarChar, -1)
                {
                    Direction = ParameterDirection.Output
                };
            command.Parameters.Add(paramSummary);

            // Execute the stored procedure.
            command.ExecuteNonQuery();
            Console.WriteLine((string)paramSummary.Value);
            return (string)paramSummary.Value;
        }
        catch (Exception ex)
        {
            Console.WriteLine(ex.Message);
            return null;
        }
    }
}
Private Function GetDocumentSummary( _
  ByVal documentID As Integer) As String

    ' Assumes GetConnectionString returns a valid connection string.
    Using connection As New SqlConnection(GetConnectionString())
        connection.Open()
        Dim command As SqlCommand = connection.CreateCommand()

        ' Setup the command to execute the stored procedure.
        command.CommandText = "GetDocumentSummary"
        command.CommandType = CommandType.StoredProcedure

        ' Set up the input parameter for the DocumentID.
        Dim paramID As SqlParameter = _
            New SqlParameter("@DocumentID", SqlDbType.Int)
        paramID.Value = documentID
        command.Parameters.Add(paramID)

        ' Set up the output parameter to retrieve the summary.
        Dim paramSummary As SqlParameter = _
            New SqlParameter("@DocumentSummary", _
               SqlDbType.NVarChar, -1)
        paramSummary.Direction = ParameterDirection.Output
        command.Parameters.Add(paramSummary)

        ' Execute the stored procedure.
        command.ExecuteNonQuery()
        Console.WriteLine(paramSummary.Value)
        Return paramSummary.Value.ToString
    End Using
End Function

См. также