ПЕРЕЛІК ДИСЦИПЛІН:
  • Адміністративне право
  • Арбітражний процес
  • Архітектура
  • Астрологія
  • Астрономія
  • Банківська справа
  • Безпека життєдіяльності
  • Біографії
  • Біологія
  • Біологія і хімія
  • Ботаніка та сільське гос-во
  • Бухгалтерський облік і аудит
  • Валютні відносини
  • Ветеринарія
  • Військова кафедра
  • Географія
  • Геодезія
  • Геологія
  • Етика
  • Держава і право
  • Цивільне право і процес
  • Діловодство
  • Гроші та кредит
  • Природничі науки
  • Журналістика
  • Екологія
  • Видавнича справа та поліграфія
  • Інвестиції
  • Іноземна мова
  • Інформатика
  • Інформатика, програмування
  • Юрист по наследству
  • Історичні особистості
  • Історія
  • Історія техніки
  • Кибернетика
  • Комунікації і зв'язок
  • Комп'ютерні науки
  • Косметологія
  • Короткий зміст творів
  • Криміналістика
  • Кримінологія
  • Криптология
  • Кулінарія
  • Культура і мистецтво
  • Культурологія
  • Російська література
  • Література і російська мова
  • Логіка
  • Логістика
  • Маркетинг
  • Математика
  • Медицина, здоров'я
  • Медичні науки
  • Міжнародне публічне право
  • Міжнародне приватне право
  • Міжнародні відносини
  • Менеджмент
  • Металургія
  • Москвоведение
  • Мовознавство
  • Музика
  • Муніципальне право
  • Податки, оподаткування
  •  
    Бесплатные рефераты
     

     

     

     

     

     

         
     
    MS SQL Server 9 "Yukon". Інтеграція з. NET
         

     

    Інформатика, програмування

    MS SQL Server 9 "Yukon". Інтеграція з. NET

    Антон Злигостев (Sinclair)

    ЮКОН

    - річка в Канаді і США (Аляска). Довжина 3700 км, площа басейну 855 тис.кв.км. Витоки - в горах, що обмежують з південного сходу плоскогір'я Юкон; головний витік - річка Льюїс, що бере початок у Кордильєр. Впадає в затоку Нортон Берингове моря, утворюючи дельту.

    Велика Радянська Енциклопедія

    Не так давно в руки деяких представників RSDN Team потрапив попередній реліз наступної версії MS SQL Server. Це навіть не бета-версія, (що не дивно - адже до очікуваного надходження фінального варіанту в продаж залишився цілий рік), але ми не могли встояти перед спокусою заглянути в майбутнє.

    З усією відповідальністю заявляю - маркетингові матеріали, що публікуються з даного продукту, не відображають і малої частки нових можливостей. З одного перерахування нововведень можна було б зробити журнальну статтю, тому дослідники розділили обов'язки між собою. У даній статті розглядається технологія інтеграції. NET і MS SQL Server.

    Загальна інформація

    Передісторія

    Сучасні комерційні РСУБД не можуть дозволити собі обмежитися роллю пасивного сховища даних, підтримуючи тільки SQL. Необхідна підтримка можливостей процедурного програмування. До недавнього часу типовим рішенням цієї проблеми було специфічне для виробника розширення стандартного SQL для написання тригерів і збережених процедур (PL/SQL, T-SQL). На той не такі вже й рідкісний випадок, коли цього розширення не вистачало для задоволення потреб виробників, що пропонувалися не менше специфічні способи використовувати «зовнішній» по відношенню до сервера код (для користувача функції в Interbase, розширені збережені процедури в MS SQL і т.д.).

    Основним недоліком першого рішення є відверта вузькість SQL, хоча б і розширеного. «Дотягти» SQL до повноцінного мови програмування загального призначення - нереально. До того ж, він по необхідності є інтерпретується мовою, що обмежує його швидкодію при виході за межі табличних операцій. Наприклад, написати збережену процедуру для шифрування PGP цілком можна й на T-SQL (благо там не потрібно нічого, крім арифметики). Але швидкість її роботи буде, м'яко кажучи, недостатньою.

    При використанні зовнішнього коду виникає інша проблема - в більшості випадків йому важко отримати доступ до контексту РСУБД, що використовує його. Як правило, всі взаємодія відбувається через «замкову свердловину »точки входу в DLL. Так, для обчислення хешу MD5 така технологія цілком підходить, але можливості взаємодії подібного коду з ядром СУБД дуже обмежені.

    Кілька років тому Oracle запропонував використовувати Java як мова програмування для своєї РСУБД. Ця практика не пройшла непоміченою в Редмонді, і ось тепер Microsoft готує удар у відповідь.

    Наступні об'єкти MS SQL Server можуть бути створені з використанням. NET:

    Збережені процедури.

    тригери.

    Функції (скалярні і табличні).

    агрегуються функції.

    Користувацькі типи даних.

    Для написання коду цих об'єктів буде потрібно. NET Framework версії 1.2 або вище.

    Завантаження коду в базу даних

    На відміну від розширених збережених процедур, код яких знаходиться у зовнішніх динамічних бібліотеках, код. NET зберігається всередині відповідної бази даних. Це забезпечує додаткову зручність при адмініструванні - відновлення бази з резервної копії або перенесення на інший сервер (detach/attach) не порушить цілісності програми.

    Є два основних способи виконати завантаження збірки в базу даних:

    вручну, за допомогою операторів T-SQL і будь-якого клієнтського додатку (піде навіть старий Query Analyzer. Він хоч і не так гарний в строю, як новий MS SQL Server Workbench, зате в нього немає звички падати і віджиматися в самі невідповідні моменти).

    За допомогою засобів автоматичного розгортання, вбудованих в MS Visual Studio. NET codename Whidbey.

    Як тільки збірка завантажена в базу даних, її код можна використовувати для створення різних об'єктів. Особливості цих двох способів описані в наступних двох підрозділах.

    Завантаження коду за допомогою T-SQL

    Завантаження коду проводиться за допомогою оператора CREATE ASSEMBLY:        

    CREATE ASSEMBLY assembly_name   

    [AUTHORIZATION owner_name]   

    FROM ( |   [,... n])   

    [WITH PERMISSION_SET = (SAFE | EXTERNAL_ACCESS | UNSAFE)]   

    :: =   

      '[machine_name] share_name [path] manifest_file_name'   

    :: =   

    (varbinary_literal |   varbinary_expression)     

    Код буде завантажений в поточну базу даних з'єднання. Щоб змінити базу, попередньо виконавши команду USE database_name.        

    Ім'я параметру         

    Опис             

    assembly_name         

    Назва збірки. NET (assembly).   Воно має бути унікальним в межах поточної бази даних, задовольняти   вимогам MS SQL Server, що пред'являються до ідентифікаторів, і при цьому   збігатися з ім'ям файлу збірки.             

    AUTHORIZATION owner_name         

    Вказує ім'я ролі, яка   буде вважатися власником цієї збірки. Можна вказувати тільки ті ролі, в   які входить поточний користувач, або ті, на які у нього є права   IMPERSONATE. За замовчуванням власником стане сам поточний користувач.             

            

    рядковий вираз,   яке вказує локальний або мережевий шлях до файлу збірки. Можна завантажувати   тільки однофайловие збірки. Всі збірки, від яких залежить зазначена, також   будуть автоматично завантажені в базу даних. Якщо вони вже присутні в   базі, то у що виконує операцію користувача повинні бути права REFERENCES   для них. Якщо збірки не знайдені в базі, у тому ж каталозі, де і   основна, або на них немає відпо прав, оператор не буде виконаний.             

    assembly_bits         

    Бінарне подання   складання. Якщо збірок декілька, то першою повинна йти головна з них, а потім   ті, від яких вона залежить. Така технологія дозволяє уникнути фазу запису   на диск при динамічній генерації коду і уникнути проблем з доступом до   файлової системи. Саме таким чином відбувається розгортання збірок з-під   MS Visual Studio 8.0 (Whidbey).             

    PERMISSION_SET (SAFE | EXTERNAL_ACCESS | UNSAFE)         

    Рівень прав, які MS   SQL Server надасть коду збірки при його виконанні. За замовчуванням   використовується SAFE.SAFE - самий обмежений рівень. Код в такій складання не   може отримати доступ до зовнішнього світу (файлової системи, мережі, змінним   оточення або реєстру). EXTERNAL_ACCESS дозволяє виходити за межі MS SQL   Server, але з певними обмеженнями. Повний список обмежень на даний   момент недоступний, але принаймні виконання не-менеджед коду   запрещено.UNSAFE надає необмежений доступ до ресурсів, як зовнішнім,   так і внутрішнім. Такі збірки можуть завантажити в базу даних тільки члени   групи sysadmin.В більшості випадків має вистачати рівня SAFE. При цьому   є гарантія, що зловмисний розробник не надасть бінарний код,   що робить будь-які гидоти від імені сервісу SQL Server або поточного   користувача. Усі приклади в цій статті працюють з рівнем SAFE.     

    Таблиця 1. Параметри оператора CREATE ASSEMBLY.

    Завантаження за допомогою Visual Studio

    Завантажувати код вручну не дуже-то зручно. Особливо це позначається під час розробки програми, коли налагоджувати код раз у раз необхідно виправляти. Нова версія Visual Studio дозволяє істотно полегшити цей процес. Досить створити в студії проект типу SQL Server Project (не плутати з Database Project!), І в меню Build з'являться команди Deploy, які (крім компіляції вихідного коду) зроблять всю необхідну роботу з розміщення ваших збірок в базі даних. При відкритті проекту Visual Studio попросить вказати сервер і базу даних, до якої буде виконуватися розгортання.

    Природно, «всередині» Visual Studio використовує ті ж самі команди T-SQL. Як правило, параметри цих команд визначаються автоматично. Для управління процесом розгортання використовуються (звичайно ж!) атрибути. Класи цих атрибутів реалізовані в збірці Microsoft.VisualStudio.DataTools.SqlAttributes.        

    РАДА   

    Якщо у вас виникне бажання   розробити свої засоби автоматичного розгортання проектів SQL Server,   зверніть увагу на ці атрибути. Підтримка їх допоможе використовувати код,   написаний іншими розробниками, без додаткових зусиль     

    На рівні збірки для управління процесом розгортання визначений один клас атрибута System.Data.Sql.SqlAssemblyAttribute. При створенні проекту SQL Server Project в файл AssemblyInfo.cs автоматично буде додати цей атрибут. Його використання виглядає так:        

    [assembly:   SqlAssembly (, Authorization = )]     

    Рядковий параметр name відповідає параметру assembly_name оператора CREATE ASSEMBLY, параметр Authorization - параметру owner_name (див. попередній розділ).

    Для того, щоб Visual Studio могла коректно зареєструвати ваші типи, процедури, функції або тригери, їх код теж треба буде забезпечити відповідними атрибутами. Подробиці наведені далі.

    Налагодження коду

    Кажуть, що ідеальні програмісти з першого разу пишуть безпомилковий код. Якщо ви один з них, то можете пропустити цей розділ і переходити прямо до написання коду.

    Однак якщо ви, як і я, періодично відчуваєте вибух емоцій, побачивши повідомлення про помилку, яке обрізане рівно перед тим місцем, де має бути номер рядка та ім'я файлу, то вас поза сумнівом порадує той факт, що код. NET, що зберігається в базі даних, можна налагоджувати зі зручністю і комфортом. Особисто я користувався для налагодження все тієї ж Visual Studio Whidbey, і виглядало це приблизно так:

    Перш за все, потрібно виконати розгортання проекту (меню Build-> Deploy). Наполегливо рекомендую вибирати налагоджувальну конфігурацію проекту.

    Тепер з'ясуйте ідентифікатор процесу (PID) MS SQL Server. Процес називається "sqlservr.exe". Ті, у кого запущений тільки один екземпляр SQL Server, можуть одразу переходити до пункту 3. У мене Yukon варто поруч з MSDE, тому таких процесів знайшлося два. Щоб уникнути неоднозначності, можна просто зупинити зайві сервери, а можна підключитися до потрібного і виконати команду SELECT ServerProperty ( 'ProcessID')

    Тепер потрібно підключитися до цього процесу для налагодження. Меню Debug-> Attach to Process ... покаже діалог підключення до процесу. Виберіть потрібний процес, і сміливо тисніть Attach.

    Все. Тепер ви можете ставити точки зупину в вихідних текстах класів, завантажених в сервер. Крім того, за замовчуванням відладчик буде перехоплювати всі виключення. NET.

    Не забудьте зробити Debug-> Detach All перед тим, як перекомпіліровать проект. Усунення несправностей Visual Studio блокує файли з налагоджування, що заважає компілятору призвести Build.

    Збережені процедури

    У новій версії MS SQL Server синтаксис оператора CREATE PROCEDURE було розширено. Ось фрагмент із SQL Server Books Online:        

    CREATE PROC [EDURE] [schema_name.] procedure_name [; number]   

    [(@ parameter data_type)   

    [VARYING] [= default] [[   OUT [PUT]   

    ] [, ... n]      

    [WITH [, ... n]      

    [FOR REPLICATION]      

    AS ( [... n] |   <. NET_Framework_reference>)   

    -   

    <. NET_Framework_reference> ::=   

    EXTERNAL NAME   assembly_name: class_name [:: method_name]     

    Як видно з цього фрагмента, тепер замість вказівки тіла процедури на T-SQL можна вказати метод класу з завантаженої раніше складання. До цього методу ставляться такі вимоги:

    Це повинен бути статичний метод (не конструктор і не деструктор класу)

    Число параметрів має збігатися із числом параметрів в описі збереженої процедури, а їх типи повинні бути сумісні з типами даних відповідних параметрів. Якщо параметр процедури оголошений як OUTPUT, то відповідний параметр методу повинен передаватися по посиланню.

    Метод має або не мати значення, що повертається, або повертати значення одного з таких типів: SQLInt32, SQLInt16, System.Int32, System.Int16

    Для успішного створення такої збереженої процедури необхідно бути власником відповідної складання або мати для неї права REFERENCES.

    Давайте перейдемо від слів до справи і спробуємо створити збережену процедуру.

    Мінімальний код збереженої процедури на C # виглядає ось таким чином:        

    using System;   

    using System.Data;   

    using System.Data.Sql;   

    using System.Data.SqlServer;   

    using System.Data.SqlTypes;      

    public class StoredProcedure   

    (   

    [SqlProcedure]   

    public static void   MyProcedure ()   

    (   

    )   

    );     

    Очевидно, він не дуже функціональний. Тим не менше, метод StoredProcedure.MyProcedure вже можна зареєструвати в базі даних як збереженої процедури, викликати (наприклад, з Query Analyzer), і переконатися, що він успішно виконується (тобто нічого не робить).

    Зверніть увагу на атрибут SqlProcedure (System.Data.Sql.SqlProcedureAttribute). Цей атрибут не несе ніякої інформації для MS SQL Server. Він використовується MS Visual Studio Whidbey при розгортанні проекту - для методів, позначених таким атрибутом, автоматично будуть викликані відповідні оператори CREATE PROCEDURE. За замовчуванням буде зроблена спроба призначити збереженої процедури таке ж ім'я, як і у методу. Це поведінку можна змінити, скориставшись єдиною властивістю атрибута - Name. Якщо замінити дев'ятий рядок прикладу вище на [SqlProcedure ( "MyProcName")], то збережена процедура буде називатися MyProcName.

    Привіт, світ

    Зупинятися на те, яким чином збережена процедура обробляє дані, сенсу немає - це звичайний C #, і його особливості добре відомі. Давайте навчимо її спілкуватися із зовнішнім світом. Для початку доведемо її до рівня Керніган та Рітчі:        

    using System;   

    using System.Data;   

    using System.Data.Sql;   

    using System.Data.SqlServer;   

    using System.Data.SqlTypes;      

    public class StoredProcedure   

    (   

      [SqlProcedure ( "HelloWorld ")]   

    public static void   MyProcedure ()   

    (   

      SqlContext.GetPipe (). Send ( "Hello, Yukon !");   

    )   

    );     

    Ця процедура демонструє ще один важливий компонент, зв'язує. NET з MS SQL Server: клас System.Data.SqlServer.SqlContext. Цей клас містить кілька статичних методів, що забезпечують доступ до контексту, в якому виконується код. У даному випадку ми отримуємо доступ до об'єкту класу System.Data.SqlServer.SqlPipe, який представляє серверну сторону з'єднання з клієнтом. Саме в цю «трубу» SQL Server відправляє результати виконання запитів. Якщо зберігається процедура повинна повертати якісь дані в клієнтську програму, то без SqlPipe не обійтися.

    У цьому прикладі ми використовуємо метод SqlPipe.Send (String msg), призначений для відправлення текстових повідомлень. Його функціональність аналогічна команді print в T-SQL. Решта методів SqlPipe призначені для відправки табличних даних:        

    Метод або властивість         

    Опис             

    public void Execute (System.Data.SqlServer.SqlCommand command) public   void Execute   

    (System.Data.SqlServer.SqlExecutionContext request)         

    Виконує зазначену команду   або запит і повертає результат клієнтові. Аналог виконання оператора SELECT   ... FROM ... в збереженої процедури на T-SQL.             

    public void Send (System.Data.SqlServer.SqlError se)         

    Повертає клієнту   зазначену помилку.             

    public void Send (System.Data.Sql.ISqlReader reader)         

    Відправляє клієнтові всі   записи із заданого набору.             

    public void SendResultsStart (System.Data.Sql.ISqlRecord record, bool   sendRow)         

    Посилає клієнту першого   запис в наборі записів. Встановлює властивість SendingResults в true.             

    public System.Boolean SendingResults (get;)         

    Вказує, що процес   відправки набору записів не закінчений.             

    public void SendResultsRow (System.Data.Sql.ISqlRecord record) public   void Send (System.Data.Sql.ISqlRecord record)         

    Посилає клієнту чергову   запис у наборі. Вимагає SendingResults == true.             

    public void SendResultsEnd   ()         

    Сигналізує про закінчення   набору записів і встановлює властивість SendingResults в false.     

    Таблиця 2.

    Таким чином, крім передачі клієнтові набору даних, отриманого від сервера, можна формувати результати вручну. З точки зору клієнта це буде виглядати як звичайний набір записів.

    Повертаємо випадкових символів

    Поки що документація досить скупо висвітлює цей питання, але після декількох експериментів мені вдалося створити ось таку процедуру:        

    [SqlProcedure ()]   

    public static void CurrencyCourse (   

      [SqlMapping (typeof (SqlDateTime))] DateTime start,   

      [SqlMapping (typeof (SqlDateTime))] DateTime end)   

    (   

    using (SqlCommand cmd =   SqlContext.GetCommand ())   

    (   

    cmd.CommandText = @ "   

    select changeDate, course   from Course   

    where changeDate between   @ start and @ end ";   

    cmd.Parameters.AddWithValue ( "@ start",   start);   

      cmd.Parameters.AddWithValue ( "@ end", end);   

    DateTime current = start;   

    SqlDecimal course =   SqlDecimal.Null;// спочатку курс відсутня;   

    SqlMetaData [] recstruct =   new SqlMetaData [2];   

    recstruct [0] = new   SqlMetaData ( "D", SqlDbType.DateTime);   

    recstruct [1] = new   SqlMetaData ( "course", SqlDbType.Decimal, 10, 4);   

    SqlDataRecord rec = new   SqlDataRecord (recstruct);   

    SqlPipe pipe =   SqlContext.GetPipe ();   

    pipe.SendResultsStart (rec,   false);   

    using (SqlDataReader r =   cmd.ExecuteReader ())   

    (   

    while (r.Read ())   

    (   

    rec.SetSqlDecimal (1,   course);   

    while (current <   r.GetDateTime (0))   

    ?? (   

    rec.SetDateTime (0,   current);   

    pipe.SendResultsRow (rec);   

    current =   current.AddDays (1);   

    )   

    course =   r.GetDecimal (1);   

    )   

    )   

    rec.SetSqlDecimal (1,   course);   

    while (current <= end)   

    (   

    rec.SetDateTime (0,   current);   

    pipe.SendResultsRow (rec);   

    current = current.AddDays (1);   

    )   

    pipe.SendResultsEnd ();   

    )   

    )     

    Ця процедура перетворює дані в таблиці зміни курсів певної валюти (Course) в таблицю щоденних значень курсу, повторюючи попереднє значення для тих днів, що змін не відбувалося.

    На цей раз у процедури є параметри. Щоб допомогти інструментів автоматичного розгортання (наприклад, той же MS VS Whidbey) визначити SQL-типи параметрів збереженої процедури, для параметрів методу можна вказати атрибут SqlMapping (System.Data.Sql.SqlMappingAttribute). Його єдиний параметр і задає тип для параметра процедури. У даному випадку цей атрибут є надмірним - параметри типу DateTime автоматично відображаються в тип SQL datetime (якому відповідає тип CLR System.Data.SqlTypes.SqlDateTime), але в більш складних випадках їм доведеться користуватися для усунення неоднозначності.

    Щоб виконати запит до даних сервера, ми скористаємося ще одним статичним методом класу SqlContext -- SqlContext.GetCommand ().

    Щоб повернути дані клієнта, потрібен примірник класу, що реалізовує інтерфейс System.Data.Sql.ISqlRecord. У даному випадку використаний System.Data.Sql.SqlDataRecord. Його конструктор вимагає вказати бажану структуру запису. Ця структура описується масивом об'єктів класу System.Data.Sql.SqlMetaData. У кожному об'єкті задається ім'я і тип відповідної колонки. Ми описуємо структуру, яка відповідає в термінах SQL ось такий «таблиці»:        

    (   

      D datetime,   

      course decimal (10, 4)   

    )     

    Створивши профіль, ми ініціюємо процес відправки за допомогою виклику:        

    pipe.SendResultsStart (rec, false);     

    Другий параметр говорить про те, що саму запис відправляти клієнтові не потрібно, а замість цього метадані запису використовуються для ініціалізації відправляється набору записів.

    Далі все просто - ми читаємо чергову запис з SqlDataReader, отриманого в результаті виконання команди, заповнюємо поля в SqlDataRecord, і відправляємо її клієнтові. Додатковий цикл в кінці досилає запису для дат між останньою зміною і кінцем запитаного інтервалу.

    Відправивши все, що хотілося, ми сигналізуючи клієнту про закінчення набору за допомогою дзвінка        

    pipe.SendResultsEnd ();     

    Варто зазначити, що результати повертаються безпосередньо клієнту, тобто код, який викликав процедуру, яка не має над цим процесом ніякого контролю. Повторне використання такого коду в серверної частини програми малоймовірно. У наступному розділі ми дізнаємося про те, як можна обійти це обмеження.

    Опції

    У рамках T-SQL функції поділяються на два види: скалярні і табличні.        

    ПРИМІТКА   

    Є ще агрегатні функції, але їх   реалізація істотно відрізняється від «звичайних», і тому ми   розглянемо їх у наступному розділі.     

    З точки зору. NET, ці два типи функцій влаштовані майже однаково. Як і процедури, що зберігаються, вони реалізуються за допомогою статичних методів класу. Відмінність полягає в тому, як вони повертають значення. Є три варіанти:

    Повертаємо значення довільного типу. Це скалярна функція.

    Повертаємо System.Data.Sql.ISqlReader. Структура даних у ньому повинна збігатися з декларованої структурою результату функції. Це таблична функція.

    Повертаємо void. Усередині функції вручну формуємо що повертаються дані через SqlContext.GetReturnResultSet (). Це теж таблична функція.

    Всі ці варіанти докладно розглянуті далі.        

    ПРИМІТКА   

    На відміну від вбудованих функцій,   звертатися до «саморобним» потрібно з повагою - випереджаючи ім'я функції ім'ям   схеми (яке за замовчуванням збігається з іменем її власника). Наприклад, я   викликав функцію з наступного підрозділу приблизно ось так:   

    select   dbo.RevertString ( "Beavis rulez")     

    Скалярні функції

    Це найпростіша різновид функцій. В якості прикладу напишемо свій варіант вбудованої функції reverse:        

    [SqlFunc ()]   

    [SqlFunction (   

    DataAccess =   DataAccessKind.None,   

    SystemDataAccess =   SystemDataAccessKind.None,   

    IsDeterministic = true,   

    IsPrecise = true)]   

    public static SqlString RevertString (SqlString str)   

    (   

    if (str.IsNull)   

    return SqlString.Null;   

    System.Text.StringBuilder sb =   new   

      System.Text.StringBuilder (str.Value.Length);   

    for (int i = str.Value.Length-1;   i> = 0; i -)   

    sb.Append (str.Value [i ]);   

    return new   SqlString (sb.ToString ());   

    )     

    Оскільки реалізація самої функції примітивна, зупинимося на тому, що її оточує.

    По-перше, до методу застосований атрибут SqlFunc. Як і SqlProcedure, він дозволяє вказати засобам автоматичного розгортання інформацію, необхідну для правильної побудови команди CREATE FUNCTION. У даному випадку ніяких параметрів не використано - атрибут просто вказує, що даний метод треба буде зареєструвати як функцію. Більш докладно ми розглянемо можливості цього атрибуту трохи пізніше.

    А от наступний атрибут - SQLFunction - вже використовується «всередині» MS SQL Server для визначення того, як можна цю функцію використовувати. У таблиці 3 наведено опис параметрів цього атрибуту:        

    Ім'я параметру         

    Опис             

    DataAccess         

    Який доступ здійснює   функція до користувача даних в базі: DataAccessKind.None --   нікакого.DataAccessKind.Read - читає дані.             

    SystemDataAccess         

    Який доступ здійснює   функція до системних даними в базі: SystemDataAccessKind.None --   нікакого.SystemDataAccessKind.Read - читає дані.             

    IsDeterministic         

    Чи є функція   детерміністичних, тобто чи залежить її повертається значення тільки від   переданих параметрів.             

    IsPrecise         

    Чи виконує функція   округлення в процесі роботи.     

    Таблиця 3.

    У нашому випадку ні до яких даними доступу не відбувається, що повертає значення залежить тільки від переданого параметра, і значення є точним, а не наближеним.        

    ПРИМІТКА   

    Це дозволяє використовувати цю функцію   в максимально широкому контексті - наприклад, можна створити обчислювані колонку   на її основі, і навіть індекс по цій колонці. Це може бути корисно для   сортування, наприклад, списку одержувачів e-mail. Сортування за зверненому   адресою поставить поруч адреса в одному домені, і можна буде оптимізувати   розсилання листів.     

    Повертаємо ISqlReader

    У багатьох випадках таблична функція виконує роль параметризрвані view - дані беруться з таблиць, і, після застосування операторів SQL до вихідних даних і параметрів, результат повертається в викликає код. Створимо функцію, яка буде повертати список змін курсу валют, проізшедшіх в заданому діапазоні дат:        

    [SqlFunc (TableDefinition = "D datetime, course decimal (10,   4 )")]   

    [SqlFunction (DataAccess = DataAccessKind.Read,   

    SystemDataAccess =   SystemDataAccessKind.None,   

    IsDeterministic = false,   IsPrecise = true)]   

    public static ISqlReader GetCourseChanges (DateTime start, DateTime   end)   

    (   

    SqlCommand cmd =   SqlContext.GetCommand ();   

    cmd.CommandText = @ "   

    select changeDate, course   from Course   

    where changeDate between   @ start and @ end ";   

      cmd.Parameters.AddWithValue ( "@ start", start);   

      cmd.Parameters.AddWithValue ( "@ end", end);   

    return cmd.ExecuteReader ();   

    )             

    ПОПЕРЕДЖЕННЯ   

    На жаль, поки що мені не   вдалося змусити цей приклад працювати. Сервер неухильно повертає помилку   «Reader is closed». Яким чином уникнути закриття Reader після повернення   його серверу, я поки не зрозумів.       

    Працюємо з SqlResultSet

    Для тих випадків, коли необхідно сформувати повертається набір даних вручну, передбачений доступ до нього через метод контексту SqlContext.GetReturnResultSet (). Об'єкт, що повертається цим методом, вже ініціалізованим першим відповідно до декларованої структурою функції. У нього слід додати необхідні записи. У принципі, можна як додавати, так і видаляти/змінювати записи, якщо це здається необхідним. Відтворимо поведінка збереженої процедури CurrencyCourse, створеної в кінці попереднього розділу:        

    [SqlFunc (TableDefinition = "D datetime, course decimal (10, 4)   NULL ")]   

    [SqlFunction (DataAccess = DataAccessKind.Read,   

    SystemDataAccess =   SystemDataAccessKind.None,   

    IsDeterministic = false,   IsPrecise = true)]   

    public static void GetCourseTable (DateTime start, DateTime end)   

    (   

    using (SqlCommand cmd =   SqlContext.GetCommand ())   

    (   

    cmd.CommandText = @ "   

    select changeDate, course   from Course   

    where changeDate between   @ start and @ end ";   

      cmd.Parameters.AddWithValue ( "@ start", start);   

    cmd.Parameters.AddWithValue ( "@ end",   end);   

    DateTime current = start;   

    SqlDecimal course =   SqlDecimal.Null;   

    SqlResultSet source =   cmd.ExecuteResultSet (ResultSetOptions.None);   

    SqlResultSet dest =   SqlContext.GetReturnResultSet ();   

    SqlDataRecord rec;   

    while (source.Read ())   

    (   

    while (current <   source.GetDateTime (0))   

    (   

    rec =   dest.CreateRecord ();   

    rec.SetSqlDecimal (1,   course);   

    rec.SetDateTime (0,   current);   

    dest.Insert (rec);   

    current =   current.AddDays (1);   

    )   

    course = source.GetDecimal (1);   

    )   

    while (current <= end)   

    (   

    rec = dest.CreateRecord ();   

    rec.SetDateTime (0,   current);   

    rec.SetSqlDecimal (1,   course);   

    dest.Insert (rec);   

    current =   current.AddDays (1);   

    )   

    )   

    )     

    Зверніть увагу, що тепер в атрибуті SqlFunction міститься значення властивості DataAccess = DataAccessKind.Read, вказуючи на те, що функція читає дані з бази.        

    ПОПЕРЕДЖЕННЯ   

    Зверніть увагу також на те, що на   Цього разу для доступу до даних ми використовуємо SqlResultSet замість   SqlDataReader. Справа в тому, що одночасно читати з бази і працювати з   що повертається набором записів можна - виникає виключення з повідомленням про   те, що дане з'єднання вже використовується. Можливо, ця особливість   поведінки буде змінена при випуску фінальної версії. Але поки що єдиним   способом написати подібну функцію є читання даних цілком до початку   формування вихідного набору даних.     

    агрегуються функції

    Більшості розробників для побудови своїх додатків цілком вистачає стандартного набору агрегуються функцій. Однак тепер настав свято і для рідкісних любителів зробити щось незвичайне - у новому MS SQL Server можна реалізувати свій спосіб вийти за межі SUM, AVG і СOUNT.

    Створюються вони за допомогою оператора CREATE AGGREGATE:        

    CREATE AGGREGATE [schema_name. ] Aggregate_name   

    (@ param_name <   input_sqltype>)   

    RETURNS   

    EXTERNAL NAME assembly_name [: class_name]      

    ::=   

    system_scalar_type | ([   udt_schema_name. ] Udt_type_name)   

    ::=   

    system_scalar_type | ([   udt_schema_name. ] Udt_type_name)     

    Цього разу написання одного методу недостатньо. Замість цього для підрахунку агрегатів використовуються об'єкти. Ідея проста - у міру перегляду вихідних даних ми накопичуємо те, що потрібно накопичувати, а зетем виводимо накопичене у вихідний набір. Відповідно для кожного з цих дій потрібно реалізувати за методом:        

    Назва методу         

    Опис             

    public void Init () public void Init (input_type value)         

    ініціалізує об'єкт.   Викликається один раз на групу агрегіруемих значень. Якщо реалізована версія   методу з одним параметром, то SQL Server може використовувати її для передачі   перше значення в групі. Тип параметра value (input_type) повинен бути   сумісним з тим типом, яка зазначена як input_sqltype в операторові CREATE   AGGREGATE.             

    public void Accumulate (input_type value)         

    Після ініціалізації   об'єкта, сервер викликає цей метод по одному разу для кожного агрегіруемого   значення. (На список що подаються на вхід значень, крім складу полів у   операторі GROUP BY, впливає також і наявність ключового слова   distinct перед агрегіруемим виразом. Як і для вбудованих функцій, це   ключове слово призведе до того, що в список для кожної групи потраплять тільки   різні значення агрегіруемого вирази). Тип параметра value повинен бути   сумісним з тим типом, яка зазначена як input_sqltype в операторові CREATE   AGGREGATE.             

    public return_type   Terminate ()         

    Незважаючи на страшне   назву, цей метод всього лише повинен повернути те саме агреговане   значення, що було обчислено для групи вхідних значень. Тип результату   повинен бути сумісним з тим типом, яка зазначена як return_sqltype в   операторі CREATE AGGREGATE.             

    public void Merge (udagg_type group)         

    Цей метод призначений для   випадків, коли SQL Server створює більше одного агрегує об'єкта на одну   групу вхідних значень. Наприклад, при виконанні запиту на   багатопроцесорної машині, вхідні дані можуть бути розділені на кілька   потоків для одночасної обробки. Перед висновком даних необхідно   виконати злиття розрахованих агрегатних значень. Саме це і робить цей   метод. Він приймає єдиний параметр того ж класу, в якому оголошено.     

    Таблиця 4.

    Крім цих методів, у класу повинен бути визначений конструктор без аргументів (інакше SQL Server не зможе створювати об'єкти цього класу). Крім того, повинна бути забезпечена можливість сериализации об'єктів -- для випадків, коли серверу потрібно зберегти проміжний результат на диск. Ми відкладемо опис подробиць сериализации до наступного розділу, а поки що спробуємо зробити свою функцію для обчислення середнього геометричного.

    Тим, хто погано пам'ятає шкільний курс, нагадаю, що середнє геометричне з N чисел - це корінь N-ною мірою через їхні твори. (А середнє арифметичне N чисел, що звичайно і мається на увазі під терміном «середнє значення» - це сума цих чисел, поділена на N).        

    ПРИМІТКА   

    На жаль, розрахунок середнього   геометричного за визначенням дуже швидко призводить до переповнення навіть на   дуже невеликих наборах вхідних даних - твір росте дуже швидко.   Тому ми схитрував і скористаємося тим математичним фактом, що   твір N чисел одно експоненті від суми їх логарифмів. Замість   витягання кореня ступеня N (а це те ж саме, що і зведення в ступінь   1/N) ми поділимо на N суму логарифмів перед застосуванням функції Exp ().             

    [Serializable]   

    [SqlUserDefinedAggregate (Format.Native, IsInvariantToDuplicates =   false, IsInvariantToNulls = true, IsInvariantToOrder = true, IsNullIfEmpty =   true)]   

    [StructLayout (LayoutKind.Sequential)]   

    public class AvgGeom: INullable   

    (   

    private double _agg;   

    private int _count;   

    private bool _isNull = true;      

    # region User-Defined Attribute   Required Methods   

    public void Init ()   

    (   

    _agg = 0;   

    _count = 0;   

    _isNull = true;   

    )   

    public void   Accumulate (SqlDouble Value)   

    (   

    if (! Value.IsNull)   

    (   

    _agg + =   System.Math.Log (Value.Value);   

    _count ++;   

    _isNull = false;   

    )   

    )   

    public void Merge (AvgGeom   Group)   

    (   

    if (! Group.IsNull)   

    (   

    _agg + = Group._agg;   

    _count + = Group._count;   

    _isNull = false;   

    )   

    )   

    public SqlDouble Terminate ()   

    (   

    if (IsNull)   

    return SqlDouble.Null;   

    else   

    return new   SqlDouble (System.Math.Exp (_agg/_count ));   

    )   

    # endregion      

    # region INullable Members   

    public bool IsNull   

    (   

    get   

    (   

    return _isNull;   

    )   

    )   

    # endregion   

    )     

    В першу чергу звернемо увагу на атрибут SqlUserDefinedAggregate, що передує опис нашого класу. У ньому визначено кілька параметрів (таблиця 5).        

    Ім'я параметру         

    Опис             

    Format         

    Формат сериализации   об'єктів цього класу. Подробиці - в наступному розділі.             

    MaxByteSize         

    Максимальний розмір   серіалізованного об'єкта. Подробиці - в наступному розділі.             

    IsInvariantToDuplicates         

    Чи залежить агреговане   значення від наявності дублікатів у вхідних даних (за замовчуванням - так). Наприклад,   для функції MIN () зовсім неважливо, скільки разів повторюються вхідні   значення, а для функції SUM () - важливо. Оптимізатор запитів SQL Server може   використовувати цю інформацію для мінімізації кількості викликів методу   Accumulate.             

    IsInvariantToNulls         

    Чи впливає наявність   NULL-значень у вхідних даних на агреговане значення. Для більшості   вбудованих агрегуються функцій (крім COUNT ()) це так.             

    IsNullIfEmpty         

    означає, що агрегуються   функція повертає NULL для порожніх вхідних наборів. Наприклад, функція MIN при   виконання на порожньому наборі повертає якраз NULL, а функція COUNT () - 0.             

    IsInvariantToOrder         

    Цей параметр поки не   документований; судячи з назви, він повинен визначати, чи впливає на   результат порядок надання значень в метод Accumulate (). Див примітку після   таблиці     

    Таблиця 5.        

    ПОПЕРЕДЖЕННЯ   

    Всі вбудовані агрегуються функції (а   також наш приклад) є комутативність, що дозволяє серверу вибирати   порядок сканування вхідних даних на свій розсуд. Однак, наприклад,   результат функцій типу First () або Last (), (доторие повинні повертати   відповідно перше або останнє значення в наборі), очевидним чином   залежить від порядку вхідних значень. Тим не менш, поки незрозуміло, як можна   використовувати подібні функції - справа в тому, що синтаксис SQL не дозволяє   визначати порядок агрегування записів. Оператор ORDER BY застосовується лише для   вихідного набору записів, і використовувати в ньому можна тільки ті поля, за   яким виконується угрупування. У звичайних вкладених запити (за результатами   яких можна будувати запити з угрупованням) застосування ORDER BY заборонено.   Швидше за все (це тільки моє припущення!) Розробники MS SQL Server Yukon   передбачають використовувати властивість   SqlUserDefinedAggregateAttribute.IsInvariantToOrder для тих випадків, коли   програміст будь-яким чином все ж таки може гарантувати певний   упорядкування вхідних даних - це властивість має переконати сервер   утриматися від переупорядочіванія записів перед агрегування. Поки що мені   не вдалося виявити будь-якого впливу цієї властивості на поведінку   сервера.     

    Для того, щоб наш об'єкт міг приймати значення NULL, необхідно реалізувати інтерфейс INullable. Цей інтерфейс визначає єдине read-only властивість bool IsNull. Всі класи з System.Data.SqlTypes реалізують цей інтерфейс. У нашому прикладі об'єкт приймає значення NULL при ініціалізації, і перестає бути Null відразу, як тільки йому буде передано не-NULL значення в метод Accumulate або Merge.

    Користувацькі типи даних

    Систему типів SQL Server можна розширити за допомогою користувацьких типів даних (User-defined Types, UDT). Користувацькі типи реалізуються як керований клас на будь-якому з CLR-мов і реєструються в SQL Server. Такий тип можна використовувати для визначення типу колонки в таблиці, або як змінну (параметр процедури) у виразі Т-SQL. При цьому методи об'єктів можна викликати прямо з T-SQL.

    Створення користувацького типу даних

    В T-SQL призначений для користувача тип даних реєструється при допомогою оператора CREATE TYPE:        

    CREATE   TYPE [type_schema_name. ] Type_name   

    ([   FROM base_type [(precision [, scale]) | ( 'urn: schema-namespace')]   

    [NULL | NOT NULL]]   

    | [EXTERNAL NAME [assembly_schema_name. ]   assembly_name [: class_name]]   

    )     

    У операторі вказується ім'я класу із заздалегідь завантаженої в базу збірки.

    Альтернативою прямого використання T-SQL, як і в інших випадках, служить автоматичне розгортання проектів MS Visual Studio . Net Whidbey. Класи, помічені атрибутом SqlUserDefinedType (ми докладно розглянемо його трохи пізніше - під час обговорення сериализации) автоматично реєструються як користувацьких типів при розгортанні проектів типу SQL Server Project.

    Для того, щоб клас. NET можна було використовувати в як для користувача типу даних SQL Server, він повинен виконувати деякі обов'язки:

    Мати конструктор без параметрів. Як правило, він повертає екземпляр, що відповідає значенню NULL (про це далі).

    Підтримувати NULL-значення. Клас повинен реалізовувати інтерфейс INullable, який описаний у попередньому розділі. Також необхідна реалізація в класі статичної властивості Null, Котор

         
     
         
    Реферат Банк
     
    Рефераты
     
    Бесплатные рефераты
     

     

     

     

     

     

     

     
     
     
      Все права защищены. Reff.net.ua - українські реферати ! DMCA.com Protection Status