Fragensteller
Änderungen an Tabelle soll User anzeigen

Allgemeine Diskussion
-
Hallo zusammen,
ich habe einen Trigger geschrieben und zusammengebaut, dieser hält mir die Änderungen an einer Tabelle fest und schreibt diese in eine andere Tabelle.
Das funktioniert ziemlich gut.
Jetzt habe ich nur noch ein paar kurze Fragen:
1. Gibt es die Möglichkeit nicht nur die Änderungen in die Tabelle einzutragen, sondern auch den LogIn oder besser noch den Computer von dem aus die Änderungen erzeugt werden ???
2. Gibt es eigentlich eine Möglichkeit alle Anfragen an eine DB mitzuloggen ??? Also nicht nur die Inserts, Updates und Deletes sondern auch alle Select und andere Anfragen ???
Und als letztes.
3. In meinem Skript werden ja die Änderungen an der Tabelle gespeichert, kann ich auch den verwendeten TSQL Befehl speichern ???Danke im Voraus für eure Bemühungen.
Und Liebe Grüße
StephanP.S.:Hier auch einmal das gekürtzte Skript. Die Basis hatte ich hier im Forum gefunden.
use [Datenbank] go
/* Tabelle erzeugen */ CREATE TABLE [dbo].[CreatedObjects_Delete]( [Activity] nvarchar(10) NOT NULL, [ID] [nvarchar](50) NULL, [ShortName] [nvarchar](100) NULL ) ON [PRIMARY]; go
/* Trigger erzeugen */ CREATE TRIGGER dbo.TR_CreatedObjects ON dbo.CreatedObjects FOR INSERT, UPDATE, DELETE AS SET NOCOUNT ON; DECLARE @Activity nvarchar(10) IF EXISTS(SELECT * FROM deleted) BEGIN SET @Activity = N'UPDATE'; IF NOT EXISTS(SELECT * FROM inserted) BEGIN SET @Activity = N'DELETE'; END IF @Activity = N'DELETE' OR UPDATE([ID]) OR UPDATE([ShortName]) BEGIN INSERT INTO dbo.CreatedObjects_Delete ( Activity, [ID], [ShortName]) SELECT @Activity AS Activity, d.[ID], d.[ShortName] FROM deleted AS d; END END ELSE BEGIN SET @Activity = N'INSERT'; INSERT INTO dbo.CreatedObjects_Delete ( Activity, [ID], [ShortName]) SELECT @Activity AS Activity, i.[ID], i.[ShortName] FROM inserted AS i; END; GO
/* Test Sektion
In Tabelle einfügen*/ INSERT INTO dbo.CreatedObjects ([ID], [ShortName]) VALUES ('999_Private', 'test.par');/* Tabelle vor Änderung anzeigen */ SELECT * FROM [dbo].[CreatedObjects_Delete];
/* Tabelle ändern */ UPDATE dbo.CreatedObjects SET [ShortName] = 'Neuer Test.par' WHERE [ID] = '999_Private';
/* Datensatz löschen */ DELETE dbo.CreatedObjects WHERE [ID] = '999_Private';
/* geänderte Tabelle anzeigen */ SELECT * FROM [dbo].[CreatedObjects_Delete]; GO
- Typ geändert Alex Pitulice Freitag, 22. März 2013 09:17 Warten auf Testen
Alle Antworten
-
Hi,
den Benutzer bekommst Du bspw. über:
SUSER_SNAME()
Den Hostnamen über:
HOST_NAME()
Details darüber findest Du hier:
http://msdn.microsoft.com/de-de/library/ms174427.aspx
http://msdn.microsoft.com/de-de/library/ms178598.aspx
Einfach mal das hier ausführen, dann siehst Du, was bei den einzelnen Werten herauskommt:
SELECT SUSER_SNAME(), HOST_NAME()
Gruß, Stefan
Microsoft MVP - Visual Developer ASP/ASP.NET
http://www.asp-solutions.de/ - Consulting, Development
http://www.aspnetzone.de/ - ASP.NET Zone, die ASP.NET Community -
Hallo Stefan,
eingeschränkt funktioniert - fast - alles :). Was m. E. nicht so einfach geht, ist das SQL-Statement. Das ist technisch bedingt, da das letzte SQL-Statement immer das "CREATE TRIGGER" sein wird, wenn es IM Trigger abgefragt wird :( Das nachfolgende Beispiel sollte aber schon mal einen Eindruck von der generellen Funktionsweise geben.
Was nichts bringt ist die Verwendung von HOST_NAME() im SQL-String. Sie gibt immer den Namen des SQL Servers aus. Du mußt über die dmv's gehen, um an die Connection-Eigenschaften zu gelangen.
USE tempdb GO DROP TABLE dbo.foo; DROP TABLE dbo.foo_History; CREATE TABLE dbo.foo ( Id int NOT NULL IDENTITY (1, 1) PRIMARY KEY CLUSTERED, col1 char(20) NOT NULL, col2 char(20) NOT NULL, col3 char(20) NOT NULL ) GO CREATE TABLE dbo.foo_History ( Id int, Action char(20), col1 char(20), col2 char(20), col3 char(20), InsertUser sysname DEFAULT (ORIGINAL_LOGIN()), InsertDate datetime DEFAULT (getdate()), InsertHost sysname, InsertApp sysname, SQLStmt nvarchar(max), CONSTRAINT pk_foo_History_Id_InsertDate PRIMARY KEY CLUSTERED (Id, InsertDate) ) GO CREATE TRIGGER dbo.foo_Update_Delete ON dbo.foo FOR UPDATE, DELETE AS SET NOCOUNT ON DECLARE @HostName sysname; DECLARE @Application sysname; DECLARE @SQLStmt nvarchar(max); SELECT @Hostname = s.host_name, @Application = s.program_name, @SQLStmt = st.text FROM sys.dm_exec_connections c INNER JOIN sys.dm_exec_sessions s ON (c.session_id = s.session_id) CROSS APPLY sys.dm_exec_sql_text(most_recent_sql_handle) st WHERE c.session_id = @@spid; INSERT INTO dbo.foo_History SELECT d.id, CASE WHEN i.Id IS NULL THEN 'Delete' ELSE 'Update' END, d.col1, d.col2, d.col3, ORIGINAL_LOGIN(), GETDATE(), @HostName, @Application, @SQLStmt FROM deleted d LEFT JOIN inserted i ON (d.id = i.id) SET NOCOUNT OFF GO -- Test INSERT INTO dbo.foo (col1, col2, col3) VALUES ('Uwe', 'Ricken', 'UR'), ('Beate', 'Ricken', 'BR') SELECT * FROM dbo.foo; SELECT * FROM dbo.foo_History GO UPDATE dbo.foo SET col3 = 'AR' WHERE Id = 1 GO SELECT * FROM dbo.foo; SELECT * FROM dbo.foo_History GO
Weitere Informationen zu den dmv's findest Du hier.
sys.dm_exec_connections: http://msdn.microsoft.com/de-de/library/ms181509.aspx
sys.dm_exec_sessions: http://msdn.microsoft.com/de-de/library/ms176013.aspx
sys.dm_exec_sql_text(): http://msdn.microsoft.com/de-de/library/ms181929.aspxUwe Ricken
MCSE - SQL Server 2012
MCSA - SQL Server 2012
MCITP Database Administrator 2005
MCITP Database Administrator 2008
MCITP Microsoft SQL Server 2008, Database Development
db Berater GmbH
http://www-db-berater.de
SQL Server Blog (german only) -
Hallo Stefan,
[klugscheissmodus an]
SUSER_SNAME() würde ich nicht empfehlen, da seit SQL 2005 IMPERSONATION möglich ist. Besser ist immer ORIGINAL_LOGIN()HOST_NAME() schlägt leider fehl, da es immer nur den Hostnamen des SQL-Servers ausgibt und nicht den der Workstation
[klugscheissmodus aus]USE tempdb GO CREATE USER test WITHOUT LOGIN; GO EXECUTE AS User = 'Test' SELECT SUSER_SNAME(), USER_NAME(), ORIGINAL_LOGIN(); REVERT DROP USER test;
SCNR ;)
Uwe Ricken
MCSE - SQL Server 2012
MCSA - SQL Server 2012
MCITP Database Administrator 2005
MCITP Database Administrator 2008
MCITP Microsoft SQL Server 2008, Database Development
db Berater GmbH
http://www-db-berater.de
SQL Server Blog (german only)- Bearbeitet Uwe RickenMVP Mittwoch, 20. März 2013 09:03
-
Hallo Uwe,
also bei mir gibt HOST_NAME() den Namen des Clients aus. Sowohl für (die gerade getesteten) SQL Server 2000 und 2008 als auch 2008 R2.
Ok, bei Impersonation ist das sicher richtig. Falls man noch einen alten SQL Server 2000 hat, funktioniert ORIGINAL_LOGIN() aber nicht, da es das dort wohl noch nicht gibt. SUSER_SNAME() hingegen schon^^
Gruß, Stefan
Microsoft MVP - Visual Developer ASP/ASP.NET
http://www.asp-solutions.de/ - Consulting, Development
http://www.aspnetzone.de/ - ASP.NET Zone, die ASP.NET Community
- Bearbeitet Stefan FalzModerator Mittwoch, 20. März 2013 10:07
-
Servus Stefan,
joo - hast Recht. Da habe ich mich vertan (mea culpa). Jedoch empfiehlt Microsoft selbst nicht die Verwendung von HOST_NAME als Sicherheitsfunktion!
"<sentencetext xmlns="http://www.w3.org/1999/xhtml">Die Clientanwendung stellt den Namen der Arbeitsstation bereit, und sie kann fehlerhafte Daten angeben.</sentencetext><sentencetext xmlns="http://www.w3.org/1999/xhtml">Verwenden Sie HOST_NAME nicht als Sicherheitsfunktion."</sentencetext>http://msdn.microsoft.com/de-de/library/ms178598.aspx
Da der TE nicht gesagt hat, mit welcher Version von SQL Server er sich die Zeit vertreibt, bin ich mal still und leise davon ausgegangen, dass es schon etwas moderner ist :) aber auch hier hast Du natürlich Recht, wenn es sich um Versionen <= 2000 handelt...
Uwe Ricken
MCSE - SQL Server 2012
MCSA - SQL Server 2012
MCITP Database Administrator 2005
MCITP Database Administrator 2008
MCITP Microsoft SQL Server 2008, Database Development
db Berater GmbH
http://www-db-berater.de
SQL Server Blog (german only) -
Da der TE nicht gesagt hat, mit welcher Version von SQL Server er sich die Zeit vertreibt, bin ich mal still und leise davon ausgegangen, dass es schon etwas moderner ist :) aber auch hier hast Du natürlich Recht, wenn es sich um Versionen <= 2000 handelt...
Was heisst denn "TE"?
Übrigens funktioniert weder SUSER_SNAME() noch ORIGINAL_LOGIN() zufriedenstellend, wenn der Client eine WebGUI unter IIS ist. Es sei denn, man möchte den Service Account haben, unter dem der App Pool der Website läuft...
-- Frank Kalis Microsoft SQL Server MVP Webmaster: http://www.insidesql.org
-
Hallo Frank,
ich für meinen Teil arbeite bei Webanwendungen überwiegend mit SQL Server Benutzern und hier je nach Anforderung mit einem zentralen User oder einem SQL Benutzer pro Websitebenutzer.
In diesen Fällen liefert SUSER_SNAME() zumindest bei mir immer den richtigen Wert. Wenn man versucht, die Verbindung mit dem Benutzer herzustellen, der den Application Pool ausführt, würde das natürlich diesen Benutzer liefern. Aber eigentlich ist es meiner Erfahrung nach sehr selten, dass man das so macht.
Gruß, Stefan
Microsoft MVP - Visual Developer ASP/ASP.NET
http://www.asp-solutions.de/ - Consulting, Development
http://www.aspnetzone.de/ - ASP.NET Zone, die ASP.NET Community -
Hallo Frank,
hallo Stefan,TE = Threadersteller
Ich arbeite ich mit einem Application Pool und führe eine "Impersonation" durch.
Das ist zwar minimal langsamer aber vertretbar (für mich) :)Bei reinen Webanwendungen ist das natürlich Blödsinn!
Uwe Ricken
MCSE - SQL Server 2012
MCSA - SQL Server 2012
MCITP Database Administrator 2005
MCITP Database Administrator 2008
MCITP Microsoft SQL Server 2008, Database Development
db Berater GmbH
http://www-db-berater.de
SQL Server Blog (german only) -
Hallo an alle,
ich möchte mich für alle Antworten bedanken.
Da habe ich erst einmal eine Menge zu lesen und zum ausprobieren.Da ich es leider vergessen habe zu erwähnen *schäm* reiche ich es nach.
Ich nutze derzeit auf meinem Laptop MS SQL 2012 Express und auf dem Server haben wir MS SQL 2008 R2 laufen.
Ein großteil der Datenbank welche ich mit meinem Skript mitlogge ist unter Access 2003 entstanden und wurde anschließend über den Upsizing Assistenten nach MS SQL 2005 gebracht und dort weiter verarbeitet. -
Jedoch empfiehlt Microsoft selbst nicht die Verwendung von HOST_NAME als Sicherheitsfunktion!
Und das aus gutem Grund, denn den Hostname kann man frei über den Connection String mit "WSID = Workstation ID" mitgeben:
using System; using System.Data.SqlClient; namespace HostTest { class Program { static void Main(string[] args) { var conn = new SqlConnection(@"WSID=IchBinUweSeinRechner; Data Source=.\SQLEXPRESS; Integrated Security=TRUE;"); var cmd = new SqlCommand(@"SELECT HOST_NAME()", conn); conn.Open(); var hostname = cmd.ExecuteScalar(); conn.Close(); Console.WriteLine(hostname.ToString()); Console.ReadKey(); } } }
Olaf Helper
Blog Xing- Bearbeitet Olaf HelperMVP Donnerstag, 21. März 2013 08:27 Hinweis WSID
-
Hallo Olaf,
das ist ein wichtiger Hinweis und führt dann auch bei meinem Beispiel zu möglichen Mißverständnissen da ja diese Informationen in sys.dm_exec_sessions abgelegt werden :(
Aber eine andere Möglichkeit (für die bessere Nachverfolgung wäre dann, zusätzlich die IP-Adresse zu speichern. Fragt sich nur, ob das dann noch wirklich prakikabel ist im Zeitalter von DHCP :)
Uwe Ricken
MCSE - SQL Server 2012
MCSA - SQL Server 2012
MCITP Database Administrator 2005
MCITP Database Administrator 2008
MCITP Microsoft SQL Server 2008, Database Development
db Berater GmbH
http://www-db-berater.de
SQL Server Blog (german only)