CLR i MS SQL Adam Pelikant
Assemblies i funkcje using System; public class wynik { [Microsoft.SqlServer.Server.SqlFunction] public static double reszta(double? a, int b) double wyn = (double)a % b; return wyn; } } Assemblies i funkcje DROP FUNCTION reszta GO DROP ASSEMBLY funkcja CREATE ASSEMBLY [funkcja] AUTHORIZATION [dbo] FROM 'C:\hurtownia_ap\funkcja\funkcja\bin\Debug\funkcja.dll' WITH PERMISSION_SET = SAFE CREATE FUNCTION Reszta(@a float, @b int) RETURNS float AS EXTERNAL NAME funkcja.wynik.reszta
Tworzenie Funkcji składnia CREATE FUNCTION [<schema name>.]<function name> ( [ <@parameter name> [AS] [<schema name>.]<scalar data type> [ = <default value>] [ ,...n ] ] ) RETURNS {<scalar type>|TABLE [(<Table Definition>)]} [ WITH [ENCRYPTION]|[SCHEMABINDING]| [ RETURNS NULL ON NULL INPUT | CALLED ON NULL INPUT ] | [EXECUTE AS { CALLER|SELF|OWNER|<'user name'>} ] ] [AS] { EXTERNAL NAME <<i>external method</i>> | BEGIN [<function statements>] {RETURN <type as defined in RETURNS clause>|RETURN (<SELECT statement>)} END }[;]
Assemblies i funkcje SQL Server data type CLR data type (SQL Server) CLR data type (.NET Framework) bigint SqlInt64 Int64, Nullable<Int64> binary SqlBytes, SqlBinary Byte[] bit SqlBoolean Boolean, Nullable<Boolean> char None cursor date SqlDateTime DateTime, Nullable<DateTime> datetime datetime2 DATETIMEOFFSET DateTimeOffset, Nullable<DateTimeOffset> decimal SqlDecimal Decimal, Nullable<Decimal> float SqlDouble Double, Nullable<Double> image int SqlInt32 Int32, Nullable<Int32> money SqlMoney nchar SqlChars, SqlString String, Char[] ntext
Assemblies i funkcje SQL Server data type CLR data type (SQL Server) CLR data type (.NET Framework) numeric SqlDecimal Decimal, Nullable<Decimal> nvarchar(1), nchar(1) SqlChars, SqlString Char, String, Char[], Nullable<char> real SqlSingle Single, Nullable<Single> rowversion None Byte[] smallint SqlInt16 Int16, Nullable<Int16> smallmoney SqlMoney sql_variant Object table text time TimeSpan TimeSpan, Nullable<TimeSpan> timestamp tinyint SqlByte Byte, Nullable<Byte> uniqueidentifier SqlGuid Guid, Nullable<Guid> varbinary SqlBytes, SqlBinary varbinary(1), binary(1) byte, Byte[], Nullable<byte> varchar xml SqlXml
Assemblies i funkcje SQL Server data type CLR data type (SQL Server) CLR data type (.NET Framework) geography SqlGeography is defined in Microsoft.SqlServer.Types.dll, which is installed with SQL Server and can be downloaded from the SQL Server 2008 feature pack. None geometry SqlGeometry is defined in Microsoft.SqlServer.Types.dll, which is installed with SQL Server and can be downloaded from the SQL Server 2008 feature pack. hierarchyid SqlHierarchyId is defined in Microsoft.SqlServer.Types.dll, which is installed with SQL Server and can be downloaded from the SQL Server 2008 feature pack. nvarchar SQLChars is a better match for data transfer and access, and SQLString is a better match for performing String operations. String, Char[] User-defined type(UDT) The same class that is bound to the user-defined type in the same assembly or a dependent assembly.
Assemblies i procedura using System; public class wynik { [Microsoft.SqlServer.Server.SqlFunction] public static double reszta(double? a, int b) double wyn = (double)a % b; return wyn; } [Microsoft.SqlServer.Server.SqlProcedure] public static void resztap(double? a, int b, out double wyn) wyn = (double)a % b;
Assemblies i procedura CREATE Procedure ResztaP(@a float, @b int, @c float OUTPUT) AS EXTERNAL NAME funkcja.wynik.resztap
Assemblies i funkcja tabelaryczna using System; using System.Data.Sql; using Microsoft.SqlServer.Server; using System.Collections; using System.Data.SqlTypes; using System.Diagnostics; public class wynik { [Microsoft.SqlServer.Server.SqlFunction] public static double reszta(double? a, int b) { double wyn = (double)a % b; return wyn; } [Microsoft.SqlServer.Server.SqlProcedure] public static void resztap(double? a, int b, out double wyn) { wyn = (double)a % b; }
Assemblies i funkcja tabelaryczna [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; } } Niedostateczne uprawnienia SELECT TOP 100 * FROM dbo.ReadEventLog(N'Security') as T
Assemblies i funkcja tabelaryczna CREATE ASSEMBLY funkcja FROM 'C:\hurtownia_ap\funkcja\funkcja\bin\Debug\funkcja.dll' WITH PERMISSION_SET = EXTERNAL_ACCESS --Lub = UNSAFE GO CREATE FUNCTION ReadEventLog(@logname nvarchar(100)) RETURNS TABLE (logTime datetime,Message nvarchar(4000),Category nvarchar(4000),InstanceId bigint) AS EXTERNAL NAME funkcja.wynik.InitMethod ALTER ASSEMBLY funkcja WITH PERMISSION_SET = UNSAFE Niedostateczne uprawnienia SELECT TOP 100 * FROM dbo.ReadEventLog(N'Security') as T
Assemblies i funkcja tabelaryczna 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 Podniesienie niedostatecznych uprawnień SELECT TOP 100 * FROM dbo.ReadEventLog(N'Application') as T --FROM dbo.ReadEventLog(N'System') as T --FROM dbo.ReadEventLog(N'Security') as T --FROM dbo.ReadEventLog(N'Internet Explorer') as T logTime Message Category InstanceId 2011-03-16 10:05:46.000 Using 'xpstar.dll' version '2007.100.1600' to execute extended stored procedure 'xp_instance_regread'. This is an informational message only; no user action is required. Server 1073749952 2011-03-16 10:05:48.000 A new instance of the full-text filter daemon host process has been successfully started. 1073771914 2011-03-16 10:05:49.000 Usługa została uruchomiona pomyślnie. %1
Assemblies i procedura odwołująca się do bazy sing System; using Microsoft.SqlServer.Server; using System.Collections; using System.Data; using System.Data.Sql; using System.Data.SqlTypes; using System.Data.SqlClient; using System.Diagnostics; public class wynik { .... [Microsoft.SqlServer.Server.SqlProcedure] public static void wiersze(String tabela, out SqlInt32 ile) ile=0; SqlConnection conn = new SqlConnection("context connection=true"); conn.Open(); SqlCommand zap = new SqlCommand("SELECT count(*) FROM "+ tabela, conn); SqlDataReader zapReader = zap.ExecuteReader(); while (zapReader.Read()) ile = zapReader.GetInt32(0); } } }
Assemblies i procedura odwołująca się do bazy DROP ASSEMBLY funkcja GO CREATE ASSEMBLY [funkcja] AUTHORIZATION [dbo] FROM 'C:\hurtownia_ap\funkcja\funkcja\bin\Debug\funkcja.dll' WITH PERMISSION_SET = SAFE CREATE PROCEDURE wiersze(@a nvarchar(33), @b int OUTPUT) AS EXTERNAL NAME funkcja.wynik.wiersze DEclare @ile int EXEC wiersze 'Dzialy', @ile OUTPUT PRINT @ile
Assemblies i funkcja zwracająca tabelę odwołująca się do bazy using System; using Microsoft.SqlServer.Server; using System.Collections; using System.Data; using System.Data.Sql; using System.Data.SqlTypes; using System.Data.SqlClient; using System.Diagnostics; public class wynik ..... private class ZapResult { public SqlInt32 idosoby; public SqlString nazwisko; public ZapResult(SqlInt32 IdOsoby, SqlString Nazwisko) { idosoby = IdOsoby; nazwisko = Nazwisko; } }
Assemblies i funkcja zwracająca tabelę odwołująca się do bazy [SqlFunction(DataAccess = DataAccessKind.Read, FillRowMethodName = "Pobierz")] public static IEnumerable wysocy(double mini) { ArrayList resultCollection = new ArrayList(); SqlConnection conn = new SqlConnection("context connection=true"); conn.Open(); SqlCommand zap = new SqlCommand("SELECT IdOsoby, Nazwisko FROM Osoby WHERE Wzrost>" + mini, conn); //String.Format(CultureInfo.GetCultureInfo("en-US"), "{0,0}", mini), conn); //min.ToString(CultureInfo.InvariantCulture.NumberFormat),conn); SqlDataReader zapReader = zap.ExecuteReader(); while (zapReader.Read()) resultCollection.Add(new ZapResult(zapReader.GetSqlInt32(0), zapReader.GetSqlString(1))); } return resultCollection; }
Assemblies i funkcja zwracająca tabelę odwołująca się do bazy public static void Pobierz( object zapResultObj, out SqlInt32 idosoby, out SqlString nazwisko) { ZapResult zapResult = (ZapResult)zapResultObj; idosoby = zapResult.idosoby; nazwisko = zapResult.nazwisko; }
Assemblies i funkcja zwracająca tabelę odwołująca się do bazy DROP FUNCTION wysocyf GO DROP ASSEMBLY funkcja CREATE ASSEMBLY [funkcja] AUTHORIZATION [dbo] FROM 'C:\hurtownia_ap\funkcja\funkcja\bin\Debug\funkcja.dll' WITH PERMISSION_SET = SAFE CREATE FUNCTION wysocyf(@mini float) RETURNS TABLE ( kto int, Nazwisko nvarchar(4000) ) AS EXTERNAL NAME funkcja.wynik.wysocy
Pomocnicza tabela dla Triggera CLR oraz to co na końcu CREATE TABLE TAudit (Id int IDENTITY PRIMARY KEY, Nazwa varchar(15)) CREATE ASSEMBLY [trig] AUTHORIZATION [dbo] FROM 'C:\hurtownia\triggerCLR\triggerCLR\bin\Debug\triggerCLR.dll' WITH PERMISSION_SET = SAFE CREATE TRIGGER DzialyAudit ON Dzialy FOR INSERT, UPDATE, DELETE AS EXTERNAL NAME trig.CLRTriggers.Sprawdz
Klasa C# dla triggera na tabeli Dzialy using System; using System.Data; using System.Data.Sql; using Microsoft.SqlServer.Server; using System.Data.SqlClient; using System.Data.SqlTypes; public class CLRTriggers { [SqlTrigger(Name = @"Sprawdz", Target = "Dzialy", Event = "FOR INSERT, UPDATE, DELETE")] public static void Sprawdz() string Nazwa; SqlCommand polecenie; SqlTriggerContext kontekst = SqlContext.TriggerContext; SqlPipe pipe = SqlContext.Pipe; SqlDataReader reader;
Klasa C# dla triggera na tabeli Dzialy switch (kontekst.TriggerAction) { case TriggerAction.Insert: // Uzyskaj połączenie używane przez trigger using (SqlConnection conn = new SqlConnection(@"context connection=true")) conn.Open(); polecenie = new SqlCommand(@"SELECT * FROM INSERTED;", conn); reader = polecenie.ExecuteReader(); reader.Read(); Nazwa = (string)reader[1]; reader.Close(); polecenie = new SqlCommand( @"INSERT TAudit VALUES ('" + Nazwa + @"');", conn); pipe.Send(polecenie.CommandText); polecenie.ExecuteNonQuery(); pipe.Send("Wstawiles: " + Nazwa); } break;
Klasa C# dla triggera na tabeli Dzialy case TriggerAction.Update: // Uzyskaj połączenie używane przez trigger using (SqlConnection conn = new SqlConnection(@"context connection=true")) { conn.Open(); polecenie = new SqlCommand(@"SELECT * FROM INSERTED;", conn); reader = polecenie.ExecuteReader(); if (reader.HasRows) { while (reader.Read()) Nazwa = (string)reader[1]; pipe.Send(@"zmieniłeś: '" + Nazwa + @"'"); for (int columnNumber = 0; columnNumber < kontekst.ColumnCount; columnNumber++) { pipe.Send("Zmodyfikowano " + reader.GetName(columnNumber) + "? " + kontekst.IsUpdatedColumn(columnNumber).ToString() ); } } } reader.Close(); } break;
Klasa C# dla triggera na tabeli Dzialy case TriggerAction.Delete: using (SqlConnection connection = new SqlConnection(@"context connection=true")) { connection.Open(); polecenie = new SqlCommand(@"SELECT * FROM DELETED;", connection); reader = polecenie.ExecuteReader(); if (reader.HasRows) pipe.Send(@"Usunąłeś wiersze:"); while (reader.Read()) { pipe.Send(@"'" + reader.GetString(1) + @"'"); } reader.Close(); //Alternatywnie aby wysłać tabelaryczny wynik możemy zastosować //pipe.ExecuteAndSend(command); } else { pipe.Send("Nie zmieniono żadnych wierszy"); } break; } }}
Skutek działania triggera INSERT TAudit VALUES ('dodany'); Wstawiles: dodany (1 row(s) affected) INSERT TAudit VALUES (‘Nowy'); Wstawiles: Nowy INSERT INTO Dzialy Values('dodany') INSERT INTO Dzialy Values('Nowy') zmieniłeś: 'NOWY' Zmodyfikowano IdDzialu? False Zmodyfikowano Nazwa? True zmieniłeś: 'DODANY' (2 row(s) affected) UPDATE Dzialy SET Nazwa=UPPER(Nazwa) WHERE IdDzialu>10 DELETE FROM Dzialy WHERE IdDzialu>10 Usunąłeś wiersze: 'DODANY' 'NOWY' (2 row(s) affected)
W tej samej przestrzeni nazw możemy stworzyć kolejną klasę, tym razem dla triggera na bazie danych public static void DDLTrigger() { SqlTriggerContext kontekst = SqlContext.TriggerContext; switch (kontekst.TriggerAction) case TriggerAction.DropTable: SqlContext.Pipe.Send("Tabela jest kasowana! Dane z EventData:"); SqlContext.Pipe.Send(kontekst.EventData.Value); break; default: SqlContext.Pipe.Send("Inna akcja DDL! Dane z EventData:"); }
Tworzenie assembly i triggerów DROP TRIGGER DzialyAudit GO DROP TRIGGER AuditDDL ON DATABASE DROP ASSEMBLY [trig] CREATE ASSEMBLY [trig] AUTHORIZATION [dbo] FROM 'C:\hurtownia_ap\trigerCLR\trigerCLR\bin\Debug\trigerCLR.dll' WITH PERMISSION_SET = SAFE go CREATE TRIGGER DzialyAudit ON Dzialy FOR INSERT, UPDATE, DELETE AS EXTERNAL NAME trig.CLRTriggers.Sprawdz CREATE TRIGGER AuditDDL ON DATABASE FOR DDL_DATABASE_LEVEL_EVENTS EXTERNAL NAME trig.CLRTriggers.DDLTrigger
Działanie Triggera CLR dla DDL CREATE TABLE TT (Id int PRIMARY KEY) (1 row(s) affected) Inna akcja DDL! Dane z EventData: <EVENT_INSTANCE><EventType>CREATE_TABLE</EventType><PostTime>2010-12-21T19:56:54.507</PostTime><SPID>52</SPID><ServerName>PC22431</ServerName><LoginName>sa</LoginName><UserName>dbo</UserName><DatabaseName>test</DatabaseName><SchemaName>dbo</SchemaName><ObjectName>TT</ObjectName><ObjectType>TABLE</ObjectType><TSQLCommand><SetOptions ANSI_NULLS="ON" ANSI_NULL_DEFAULT="ON" ANSI_PADDING="ON" QUOTED_IDENTIFIER="ON" ENCRYPTED="FALSE" /><CommandText>CREATE TABLE TT (Id int PRIMARY KEY)</CommandText></TSQLCommand></EVENT_INSTANCE>
Działanie Triggera CLR dla DDL ALTER TABLE TT ADD Opis varchar(11) (1 row(s) affected) Inna akcja DDL! Dane z EventData: <EVENT_INSTANCE><EventType>ALTER_TABLE</EventType><PostTime>2010-12-21T19:59:52.537</PostTime><SPID>52</SPID><ServerName>PC22431</ServerName><LoginName>sa</LoginName><UserName>dbo</UserName><DatabaseName>test</DatabaseName><SchemaName>dbo</SchemaName><ObjectName>TT</ObjectName><ObjectType>TABLE</ObjectType><AlterTableActionList><Create><Columns><Name>Opis</Name></Columns></Create></AlterTableActionList><TSQLCommand><SetOptions ANSI_NULLS="ON" ANSI_NULL_DEFAULT="ON" ANSI_PADDING="ON" QUOTED_IDENTIFIER="ON" ENCRYPTED="FALSE" /><CommandText>ALTER TABLE TT ADD Opis varchar(11)</CommandText></TSQLCommand></EVENT_INSTANCE>
Działanie Triggera CLR dla DDL DROP TABLE TT (1 row(s) affected) Tabela jest kasowana! Dane z EventData: <EVENT_INSTANCE><EventType>DROP_TABLE</EventType><PostTime>2010-12-21T20:00:22.177</PostTime><SPID>52</SPID><ServerName>PC22431</ServerName><LoginName>sa</LoginName><UserName>dbo</UserName><DatabaseName>test</DatabaseName><SchemaName>dbo</SchemaName><ObjectName>TT</ObjectName><ObjectType>TABLE</ObjectType><TSQLCommand><SetOptions ANSI_NULLS="ON" ANSI_NULL_DEFAULT="ON" ANSI_PADDING="ON" QUOTED_IDENTIFIER="ON" ENCRYPTED="FALSE" /><CommandText>DROP TABLE TT </CommandText></TSQLCommand></EVENT_INSTANCE>
Działanie Triggera CLR dla DDL CREATE LOGIN nowy WITH PASSWORD = 'kajak'; Tutaj trigger nie zadziała ponieważ to jest zdarzenie z poziomu ALL SERVER
Typy danych użytkownika CLR using System; using System.Data; using System.Data.SqlTypes; using Microsoft.SqlServer.Server; using System.Text; [Serializable] [Microsoft.SqlServer.Server.SqlUserDefinedType(Format.Native, IsByteOrdered = true, ValidationMethodName = "SprawdzPunkt")] public struct Punkt : INullable { private bool is_Null; private Int32 _x; private Int32 _y; public bool IsNull get {return (is_Null);} }
Typy danych użytkownika CLR public static Punkt Null { get { Punkt pt = new Punkt(); pt.is_Null = true; return pt; } } public override string ToString() if (this.IsNull) return "NULL"; else StringBuilder builder = new StringBuilder(); builder.Append(_x); builder.Append(","); builder.Append(_y); return builder.ToString(); }
Typy danych użytkownika CLR [SqlMethod(OnNullCall = false)] public static Punkt Parse(SqlString s) { if (s.IsNull) return Null; Punkt pt = new Punkt(); string[] xy = s.Value.Split(",".ToCharArray()); pt.X = Int32.Parse(xy[0]); pt.Y = Int32.Parse(xy[1]); if (!pt.SprawdzPunkt()) throw new ArgumentException("Invalid XY coordinate values."); return pt; }
Typy danych użytkownika CLR // współrzędne X i Y są ustawiane jako właściwościtypu. public Int32 X { get { return this._x; } set { Int32 temp = _x; _x = value; if (!SprawdzPunkt()) _x = temp; throw new ArgumentException("Zła współrzędna X."); } } } public Int32 Y { return this._y; } Int32 temp = _y; _y = value; _y = temp; Typy danych użytkownika CLR
Typy danych użytkownika CLR // metoda walidująca współrzędne X Y private bool SprawdzPunkt() { if ((_x >= 0) && (_y >= 0)) { return true; } else {return false;} } // Odległość od 0,0. [SqlMethod(OnNullCall = false)] public Double Odleglosc() { return OdlegloscOdXY(0, 0); } // Odległość od wskazanego punktu public Double OdlegloscOd(Punkt pFrom) { return OdlegloscOdXY(pFrom.X, pFrom.Y); } // Odległość od wskazanego punktu. public Double OdlegloscOdXY(Int32 iX, Int32 iY) return Math.Sqrt(Math.Pow(iX - _x, 2.0) + Math.Pow(iY - _y, 2.0)); } } Typy danych użytkownika CLR
Typy danych użytkownika CLR po stronie TSQL DROP Type Punkt GO DROP ASSEMBLY [Punkt_a] CREATE ASSEMBLY [Punkt_a] AUTHORIZATION [dbo] FROM 'C:\hurtownia_ap\udt\udt\bin\Debug\udt.dll' WITH PERMISSION_SET = SAFE CREATE TYPE dbo.Punkt EXTERNAL NAME Punkt_a.Punkt; Typy danych użytkownika CLR po stronie TSQL DROP TABLE Punkty GO CREATE TABLE dbo. Punkty (ID int IDENTITY(1,1) PRIMARY KEY, PunktyXY Punkt) INSERT INTO Punkty VALUES (CONVERT(Punkt, '3,4')); INSERT INTO Punkty VALUES (CONVERT(Punkt, '1,5')); INSERT INTO Punkty VALUES (CAST ('1,99' AS Punkt));
Typy danych użytkownika CLR po stronie TSQL ID PunktyXY 1 0x008000000380000004 2 0x008000000180000005 3 0x008000000180000063 (3 row(s) affected) 1 3,4 2 1,5 3 1,99 ID SELECT ID, PunktyXY FROM Punkty GO SELECT ID, PunktyXY.ToString() AS PunktyXY FROM Punkty; SELECT ID, CAST(PunktyXY AS varchar) SELECT ID, CONVERT(varchar, PunktyXY)
Typy danych użytkownika CLR po stronie TSQL PunktXY 1,5 (1 row(s) affected) ID PunktyXY 1 3,4 2 1,5 3 1,99 (3 row(s) affected) DECLARE @PunktXY Punkt; SET @PunktXY = (SELECT PunktyXY FROM Punkty WHERE ID = 2); SELECT @PunktXY.ToString() AS PunktXY; GO SELECT ID, PunktyXY.ToString() AS PunktyXY FROM Punkty WHERE PunktyXY > CONVERT(Punkt, '2,2'); WHERE PunktyXY.X < PunktyXY.Y;
Typy danych użytkownika CLR po stronie TSQL SELECT ID, PunktyXY.X AS PunktX, PunktyXY.Y AS PunktY, PunktyXY.Odleglosc() AS OdlegloścOdPoczatku FROM Punkty; go SELECT ID, PunktyXY.ToString() AS Pkt, PunktyXY.OdlegloscOd(CONVERT(Punkt, '1,99')) AS OdlegloscOdPunktu ID PunktX PunktY OdlegloścOdPoczatku 1 3 4 5 2 1 5 5,09901951359278 3 1 99 99,0050503762308 (3 row(s) affected) ID Pkt OdlegloscOdPunktu 1 3,4 95,0210502993942 2 1,5 94 3 1,99 0
Typy danych użytkownika CLR po stronie TSQL UPDATE Punkty SET PunktyXY = CAST('1,88' AS Punkt) WHERE ID = 3 GO SELECT ID, PunktyXY.X AS PunktX, PunktyXY.Y AS PunktY, PunktyXY.Odleglosc() AS OdlegloscOdZero FROM Punkty; GO SET PunktyXY.Y = 99 Typy danych użytkownika CLR po stronie TSQL ID PunktX PunktY OdlegloscOdZero 1 3 4 5 2 1 5 5,09901951359278 1 88 88,0056816347672 3 1 99 99,0050503762308
Typy danych użytkownika CLR po stronie TSQL UPDATE Punkty SET PunktyXY = '4,5' WHERE PunktyXY = '3,4'; GO SELECT ID, PunktyXY.X AS PunktX, PunktyXY.Y AS PunktY, PunktyXY.Odleglosc() AS OdlegloscOdZero FROM Punkty; GO ID PunktX PunktY OdlegloscOdZero 1 4 5 6,40312423743285 2 1 5 5,09901951359278 3 1 99 99,0050503762308 (3 row(s) affected)
Typy danych użytkownika CLR po stronie TSQL UPDATE Punkty SET PunktyXY.X = 5, PunktyXY.Y = 99 WHERE ID = 3 GO /*SELECT ID, PunktyXY.X AS PunktX, PunktyXY.Y AS PunktY, PunktyXY.Odleglosc() AS OdlegloscOdZero FROM Punkty;*/ GO SET PunktyXY.SetXY(5, 99) WHERE ID = 3 FROM Punkty; */ Typy danych użytkownika CLR po stronie TSQL Msg 264, Level 16, State 1, Line 1 The column name 'PunktyXY' is specified more than once in the SET clause. A column cannot be assigned more than one value in the same SET clause. Modify the SET clause to make sure that a column is updated only once. If the SET clause updates columns of a view, then the column name 'PunktyXY' may appear twice in the view definition. Msg 6506, Level 16, State 10, Line 3 Could not find method 'SetXY' for type 'Punkt' in assembly 'udt'
Pusty
Skośność i kurtoza dobre using System; using System.Data; using System.Data.SqlClient; using System.Data.SqlTypes; using Microsoft.SqlServer.Server; [Serializable] [SqlUserDefinedAggregate( Format.Native, IsInvariantToDuplicates = false, IsInvariantToNulls = true, IsInvariantToOrder = true, IsNullIfEmpty = false)] public struct Skew { private double rx; // running sum of current values (x) private double rx2; // running sum of squared current values (x^2) private double r2x; // running sum of doubled current values (2x) private double rx3; // running sum of current values raised to power 3 (x^3) private double r3x2; // running sum of tripled squared current values (3x^2) private double r3x; // running sum of tripled current values (3x) private Int64 rn; // running count of rows
Skośność i kurtoza dobre public void Init() { rx = 0; rx2 = 0; r2x = 0; rx3 = 0; r3x2 = 0; r3x = 0; rn = 0; } public void Accumulate(double? inpVal) { if (inpVal.IsNull) { return; } rx = rx + inpVal.Value; rx2 = rx2 + Math.Pow(inpVal.Value, 2); r2x = r2x + 2 * inpVal.Value; rx3 = rx3 + Math.Pow(inpVal.Value, 3); r3x2 = r3x2 + 3 * Math.Pow(inpVal.Value, 2); r3x = r3x + 3 * inpVal.Value; rn = rn + 1; }
Skośność i kurtoza dobre public void Merge(Skew Group) { this.rx = this.rx + Group.rx; this.rx2 = this.rx2 + Group.rx2; this.r2x = this.r2x + Group.r2x; this.rx3 = this.rx3 + Group.rx3; this.r3x2 = this.r3x2 + Group.r3x2; this.r3x = this.r3x + Group.r3x; this.rn = this.rn + Group.rn; }
Skośność i kurtoza dobre public double? Terminate() { if (rn == 0) { return null; } else { double myAvg = (rx / rn); if (rn - 1 == 0 & 2d == 0) { return null; } double myStDev = Math.Pow((rx2 - r2x * myAvg + rn * Math.Pow(myAvg, 2)) / (rn - 1), 1d / 2d); double mySkew = (rx3 - r3x2 * myAvg + r3x * Math.Pow(myAvg, 2) - rn * Math.Pow(myAvg, 3)) / Math.Pow(myStDev, 3) * rn / (rn - 1) / (rn - 2); return (dounle?)mySkew; } } }
Skośność i kurtoza dobre [Serializable] [SqlUserDefinedAggregate( Format.Native, IsInvariantToDuplicates = false, IsInvariantToNulls = true, IsInvariantToOrder = true, IsNullIfEmpty = false)] public struct Kurt { private double rx; // running sum of current values (x) private double rx2; // running sum of squared current values (x^2) private double r2x; // running sum of doubled current values (2x) private double rx4; // running sum of current values raised to power 4 (x^4) private double r4x3; // running sum of quadrupled current values raised to power 3 (4x^3) private double r6x2; // running sum of squared current values multiplied by 6 (6x^2) private double r4x; // running sum of quadrupled current values (4x) private Int64 rn; // running count of rows
Skośność i kurtoza dobre public void Init() { rx = 0; rx2 = 0; r2x = 0; rx4 = 0; r4x3 = 0; r6x2 = 0; r4x = 0; rn = 0; } public void Accumulate(double? inpVal) { if (inpVal.IsNull) { return; } rx = rx + inpVal.Value; rx2 = rx2 + Math.Pow(inpVal.Value, 2); r2x = r2x + 2 * inpVal.Value; rx4 = rx4 + Math.Pow(inpVal.Value, 4); r4x3 = r4x3 + 4 * Math.Pow(inpVal.Value, 3); r6x2 = r6x2 + 6 * Math.Pow(inpVal.Value, 2); r4x = r4x + 4 * inpVal.Value; rn = rn + 1; }
Skośność i kurtoza dobre public void Merge(Kurt Group) { this.rx = this.rx + Group.rx; this.rx2 = this.rx2 + Group.rx2; this.r2x = this.r2x + Group.r2x; this.rx4 = this.rx4 + Group.rx4; this.r4x3 = this.r4x3 + Group.r4x3; this.r6x2 = this.r6x2 + Group.r6x2; this.r4x = this.r4x + Group.r4x; this.rn = this.rn + Group.rn; }
Skośność i kurtoza dobre public SqlDouble Terminate() { if (rn == 0) { return null; } else { double myAvg = (rx / rn); if (rn - 1 == 0 & 2d == 0) { return null; } { double myStDev = Math.Pow((rx2 - r2x * myAvg + rn * Math.Pow(myAvg, 2)) / (rn - 1), 1d / 2d); if (rn - 3 == 0) { return null; } { double myKurt = (rx4 - r4x3 * myAvg + r6x2 * Math.Pow(myAvg, 2) - r4x * Math.Pow(myAvg, 3) + rn * Math.Pow(myAvg, 4)) / Math.Pow(myStDev, 4) * rn * (rn + 1) / (rn - 1) / (rn - 2) / (rn - 3) - 3 * Math.Pow((rn - 1), 2) / (rn - 2) / (rn - 3); return (double?)myKurt; } } } } }
Skośność i kurtoza dobre USE BazaRelacyjna; GO DROP AGGREGATE dbo.Skew go DROP AGGREGATE dbo.Kurt drop assembly DescriptiveStatistics CREATE ASSEMBLY DescriptiveStatistics FROM 'C:\hurtownia_ap\kurtosa\kurtosa\bin\Debug\kurtosa.dll' WITH PERMISSION_SET = SAFE; CREATE AGGREGATE dbo.Skew(@s float) RETURNS float EXTERNAL NAME DescriptiveStatistics.Skew; CREATE AGGREGATE dbo.Kurt(@s float) EXTERNAL NAME DescriptiveStatistics.Kurt;
Skośność i kurtoza dobre SELECT IdDzialu, dbo.Skew(wzrost) AS Skew, dbo.Kurt(Wzrost) AS Kurt FROM Osoby GROUP BY IdDzialu IdDzialu Skew Kurt 1 -1,58473281038587 2,54637337119658 2 -0,00343206841022187 -5,98018821685084 3 0,0908690671351654 -1,22037280498897 4 -0,478529141113873 0,0683495230558329 5 0,598997432758433 1,18386633804673 6 -0,595910035619708 -1,59937651656481 7 -0,114078107575618 -4,72793182348213 8 1,73205080758596 NULL
Pusty
Zastosowanie listy do obliczania funkcji statystycznych using System.Data; using System.Data.SqlClient; using System.Data.SqlTypes; using System.Collections; using System.Collections.Generic; using Microsoft.SqlServer.Server; using System.Text; [Serializable] [SqlUserDefinedAggregate(Format.UserDefined, IsInvariantToDuplicates = false, IsInvariantToOrder= false, MaxByteSize =8000 , Name="OdchylenieStandardowe")] public struct OdchStd : IBinarySerialize { public List<double> posr; private int licznik; // zmienna zliczająca liczbę wierszy nie mających wartości NULL private double suma; // zmienna przechowująca sumę pól private double temp; // zmienna pomocnicza
Zastosowanie listy do obliczania funkcji statystycznych public void Init() { posr = new List<double>(); licznik = 0; suma = 0; } public void Accumulate(double? value) if (value != null) // wyznaczanie sumy dla pól nie mających wartości NULL licznik++; temp = (double)value; suma = suma + temp; posr.Add(temp);
Zastosowanie listy do obliczania funkcji statystycznych public void Merge(OdchStd Group) { this.suma += Group.suma; this.licznik += Group.licznik; this.temp += Group.temp; // kiedy obliczenia równoległe suma staje się sumą z wszystkich procesów } public double? Terminate() //SqlString if (licznik == 0) return null; else return (double?)(this.temp); //zwrócenie ostatecznej wartości obliczeń } }
Zastosowanie listy do obliczania funkcji statystycznych public void Write(System.IO.BinaryWriter w) { temp = 0; double sr = suma / licznik; foreach (double d in posr) temp = temp + Math.Pow((d- sr),2); } temp = temp / (licznik-1); temp = Math.Pow(temp, 0.5); w.Write(temp); //w.Write(suma); w.Write(licznik); public void Read(System.IO.BinaryReader r) temp = r.ReadDouble(); //suma = r.ReadDouble(); licznik = r.ReadInt32(); } }
Zastosowanie listy do obliczania funkcji statystycznych USE [BazaRelacyjna] GO DROP AGGREGATE ODCHSTD DROP ASSEMBLY [ODCHSTD_a] CREATE ASSEMBLY [ODCHSTD_a] AUTHORIZATION [dbo] FROM 'C:\hurtownia\cowariancja\cowariancja\bin\Debug\cowariancja.dll' WITH PERMISSION_SET = SAFE CREATE AGGREGATE OdchStd(@value float) RETURNS float EXTERNAL NAME ODCHSTD_a.OdchStd;
Zastosowanie listy do obliczania funkcji statystycznych SELECT Opis, stdev(wzrost), dbo.OdchStd(Wzrost) FROM Dzialy LEFT JOIN osoby ON Dzialy.IdDzialu=Osoby.IdDzialu GROUP BY Opis Opis funkcja Assembly wsp Administracja 0.158823801742683 0.158823801742686 -2.02718152826315E-12 Dyrekcja 0.0761577310586359 0.0761577310586391 -4.19115846996519E-12 Gospodarczy 0.0692820323027577 0.069282032302755 3.84592537276698E-12 Handlowy 0.121475773866076 0.121475773866083 -5.14094647415167E-12 Inny 0.126015872016184 0.126015872016187 -2.4448260715437E-12 Nowy NULL Obsługa 0.154148969383205 0.154148969383207 -1.00831827854216E-12 Pomocniczy 0.15649813630413 0.156498136304132 -1.09959500401001E-12 Techniczny 0.145764442602689 -3.80827794763123E-13
Zastosowanie listy macierzy do obliczania funkcji statystycznych [Serializable] [SqlUserDefinedAggregate(Format.UserDefined, IsInvariantToDuplicates = false, IsInvariantToOrder = false, MaxByteSize = 8000, Name = "Kowariancja")] public struct Kowariancja : IBinarySerialize { double[] a ; public ArrayList posr; private int licznik; private double sumaX; private double sumaY; private double tempX; private double tempY; private double temp;
Zastosowanie listy macierzy do obliczania funkcji statystycznych public void Init() { a = new double[2]; posr = new ArrayList(a); posr.Clear(); temp = 0; licznik = 0; sumaX = 0; sumaY = 0; }
Zastosowanie listy macierzy do obliczania funkcji statystycznych public void Accumulate(double? valueX, double? valueY) { if (valueX != null && valueY != null) { licznik++; a[0] = (double)valueX; a[1] = (double)valueY; sumaX = sumaX + a[0]; sumaY = sumaY + a[1]; posr.Add(a.Clone()); } } public void Merge(Kowariancja Group) this.sumaX += Group.sumaX; this.sumaY += Group.sumaY; this.licznik += Group.licznik; posr.AddRange(Group.posr); }
Zastosowanie listy macierzy do obliczania funkcji statystycznych public double? Terminate() { if (licznik == 0) { return null; } else { return (double?)(this.temp); //zwrócenie ostatecznej wartości obliczeń } } public void Read(System.IO.BinaryReader r) temp = r.ReadDouble(); licznik = r.ReadInt32();
Zastosowanie listy macierzy do obliczania funkcji statystycznych public void Write(System.IO.BinaryWriter w) { temp = 0; double srX = sumaX / licznik; double srY = sumaY / licznik; foreach (double[] d in posr) tempX = (d[0]- srX); tempY = (d[1] - srY); temp = temp + (tempX * tempY); } temp = temp/ licznik; w.Write(temp); w.Write(licznik);
Zastosowanie listy macierzy do obliczania funkcji statystycznych USE [BazaRelacyjna] GO DROP AGGREGATE COVAR DROP AGGREGATE ODCHSTD DROP ASSEMBLY [ODCHSTD_a] CREATE ASSEMBLY [ODCHSTD_a] AUTHORIZATION [dbo] FROM 'C:\hurtownia\cowariancja\cowariancja\bin\Debug\cowariancja.dll' WITH PERMISSION_SET = SAFE CREATE AGGREGATE OdchStd(@value float) RETURNS float EXTERNAL NAME ODCHSTD_a.OdchStd; CREATE AGGREGATE CoVar(@value1 float, @value2 float) EXTERNAL NAME ODCHSTD_a.Kowariancja;
Zastosowanie listy macierzy do obliczania funkcji statystycznych Tabela Test A B 1 9 SELECT AVG(A)AS sr, stdevp(a) AS odch, dbo.Covar(A, B) AS covara, (AVG(A * B)- AVG(A)*AVG(B))as Covar FROM Test sr odch covara Covar 5 4 16
Zastosowanie listy macierzy do obliczania funkcji statystycznych SELECT Opis, stdevp(wzrost) AS funOdch2, dbo.OdchStd(Wzrost)AS AssOdch, dbo.Covar(Wzrost, Wzrost)AS AssCoVar , (AVG(WZROST * wzrost)- AVG(wzrost)*AVG(wzrost))/(stdevp(wzrost)*stdevp(wzrost)) as Corr, dbo.Covar(Wzrost, Wzrost)/(stdevp(Wzrost)*stdevp(Wzrost))AS AssCorr FROM Dzialy LEFT JOIN osoby ON Dzialy.IdDzialu=Osoby.IdDzialu GROUP BY Opis Opis funOdch2 AssOdch AssCoVar Corr AssCorr Administracja 0.137545447034786 0.158823801742686 0.01891875 1.00001321440374 1.00000000000004 Dyrekcja 0.0659545297913618 0.0761577310586391 0.00435 1.00000000000008 Gospodarczy 0.0565685424949259 0.069282032302755 0.00319999999999999 0.999999999999925 0.999999999999923 Handlowy 0.115822605880961 0.121475773866083 0.0134148760330579 1.00000924100552 1.0000000000001 Inny 0.115036226178247 0.126015872016187 0.0132333333333333 0.999974811083172 1.00000000000005 Nowy NULL Obsługa 0.142714214214195 0.154148969383207 0.0203673469387755 1.00008116232467 1.00000000000002 Pomocniczy 0.135531361684297 0.156498136304132 0.01836875 1.00001361007147 Techniczny 0.1389809428639 0.145764442602689 0.0193157024793388 1.00006717439672 1.00000000000001