जब आप कहते हैं, "ट्रिगर्स का उपयोग किए बिना", तो क्या आपका मतलब किसी भी ट्रिगर या केवल पंक्ति-दर-पंक्ति ट्रिगर्स टेबल पर है?
मैं पूछता हूं क्योंकि आप फ़ंक्शन के विवेकपूर्ण उपयोग के साथ जो आप चाहते हैं वह प्राप्त करने में सक्षम हो सकते हैं CONTEXT_INFO()
, लेकिन आपको यह सुनिश्चित करने की आवश्यकता होगी कि SET CONTEXT_INFO
आपके ऑपरेशन होने से पहले इसे सही ढंग से कहा गया था।
ऐसा करने के लिए एक स्थान सर्वर-स्तरीय लॉगऑन ट्रिगर हो सकता है (जैसे डेटाबेस / ऑब्जेक्ट-स्तरीय ट्रिगर नहीं), जैसे:
USE master
GO
CREATE TRIGGER tr_audit_login
ON ALL SERVER
WITH EXECUTE AS 'sa'
AFTER LOGON
AS BEGIN
BEGIN TRY
DECLARE @eventdata XML = EVENTDATA();
IF @eventdata IS NOT NULL BEGIN
DECLARE @spid INT;
DECLARE @client_host VARCHAR(64);
SET @client_host = @eventdata.value('(/EVENT_INSTANCE/ClientHost)[1]', 'VARCHAR(64)');
SET @spid = @eventdata.value('(/EVENT_INSTANCE/SPID)[1]', 'INT');
-- pack the required data into the context data binary
-- (spid is just an example of packing multiple data items in a single field: you would probably use @@SPID at the point of use, instead)
DECLARE @context_data VARBINARY(128);
SET @context_data = CONVERT(VARBINARY(4), @spid)
+ CONVERT(VARBINARY(64), @client_host);
-- persist the spid and host into session-level memory
SET CONTEXT_INFO @context_data;
END
END TRY
BEGIN CATCH
/* do better error handling here...
* logon trigger can lock all users out of server, so i am just swallowing everything
*/
DECLARE @msg NVARCHAR(4000) = ERROR_MESSAGE();
RAISERROR('%s', 10, 1, @msg) WITH LOG;
END CATCH
END
आप संदर्भ (संग्रह की गति के लिए) को संग्रहीत करने के लिए अपनी तालिका में डिफ़ॉल्ट बाधा जोड़ सकते हैं:
ALTER TABLE cdc.schema_table_CT
ADD ContextInfo varbinary(128) NULL DEFAULT(CONTEXT_INFO())
आपके पास एक बार, आप उस ContextInfo
कॉलम को थोड़ा टुकड़ा-और-पासा के साथ क्वेरी कर सकते हैं :
SELECT *
,spid = CONVERT(INT, SUBSTRING(ContextInfo, 1, 4))
,client = CONVERT(VARCHAR(64), SUBSTRING(ContextInfo, 5, 64))
FROM cdc.schema_table_CT
तकनीकी रूप से, आप ऐसा कर सकते हैं SUBSTRING
और CONVERT
अपने डिफ़ॉल्ट बाधा के हिस्से के रूप में सामान कर सकते हैं , और बस क्लाइंट आईपी को वहां स्टोर कर सकते हैं, लेकिन यह पूरे संदर्भ को स्टोर करने के लिए तेज हो सकता है (जैसा कि यह हर पर किया जाता है INSERT
), और केवल मानों को एक में निकालें SELECT
जब आपको उनकी आवश्यकता हो
मैं अपने सभी को लपेटने के लिए इच्छुक हो सकता हूं SUBSTRING
और CONVERT
एकल-पंक्ति इनलाइन तालिका-मूल्यवान फ़ंक्शन में कॉल कर सकता हूं , जो मुझे CROSS APPLY
आवश्यक होगा । यह एक ही स्थान पर अनपैकिंग तर्क रखता है:
CREATE FUNCTION fn_context (
@context_info VARBINARY(128)
)
RETURNS TABLE
AS RETURN (
SELECT
spid = CONVERT(INT, SUBSTRING(@context_info, 1, 4))
,client = CONVERT(VARCHAR(64), SUBSTRING(@context_info, 5, 64))
)
GO
SELECT *
FROM cdc.schema_table_CT s
CROSS APPLY dbo.fn_context(s.ContextInfo) c
ध्यान दें कि CONTEXT_INFO
केवल एक 128-बाइट है VARBINARY
। यदि आपको 128 बाइट में फिट होने से अधिक डेटा की आवश्यकता है, तो मैं उस सभी डेटा को रखने के लिए एक तालिका बनाऊंगा, लॉगऑन ट्रिगर में उस 'सत्र' के लिए पंक्ति के रूप में डालें और CONTEXT_INFO
उस तालिका के सरोगेट कुंजी मान पर सेट करें
आपको यह भी ध्यान देना चाहिए, क्योंकि यह केवल एक डिफ़ॉल्ट बाधा है, यह एक उपयुक्त-विशेषाधिकार प्राप्त उपयोगकर्ता के लिए तुच्छ है, जो संदर्भ डेटा को आराम तालिका में अधिलेखित कर देता है। बेशक, 'ऑडिट' शैली की तालिकाओं के अन्य सभी स्तंभों के लिए भी यही सच है।
यह अच्छा होगा यदि यह एक डिफ़ॉल्ट के बजाय एक निरंतर गणना वाला स्तंभ हो सकता है, लेकिन CONTEXT_INFO()
फ़ंक्शन गैर-नियतात्मक है, इसलिए यह एक नो-गो है (आप कुछ FUNCTION
आस-पास के कुछ चालबाजी का उपयोग करने में सक्षम हो सकते हैं VIEW
, लेकिन मैं नहीं करूंगा )।
यह उस उपयोगकर्ता के लिए भी तुच्छ है, जिसके पास SET CONTEXT_INFO
खुद को बुलाने और अपने दिन को गड़बड़ करने के लिए पर्याप्त उपयोग है (जैसे नकली मान, या विशेष रूप से तैयार किए गए इंजेक्शन के साथ), इसलिए संदेह और देखभाल के साथ सामग्री का इलाज करें, इसे प्रदर्शित करने से पहले एनकोड करें, और अपवादों को संभालें। कुंआ।
Hostname के लिए, मुझे लगता है कि ClientHost
तत्व EVENTDATA()
आपको IP पता (या एक <local machine>
संकेतक) देता है। जब आप तकनीकी रूप से CLR का उपयोग रिवर्स-डीएनएस लुक्स को होस्टनाम में करने के लिए कर सकते हैं, तो ये हर करने के लिए बहुत धीमे होते हैं INSERT
, इसलिए मैं ऐसा नहीं करने की सलाह दूंगा।
यदि आपके पास एक होस्टनाम होना है, तो आप अपने स्थानीय डीएचसीपी सर्वर या डीएनएस ज़ोन फ़ाइल से मौजूदा पट्टों के साथ एक आउट-ऑफ-बैंड प्रक्रिया के रूप में और उसके LEFT JOIN
लिए समय-समय पर एक अलग तालिका को पॉप्युलेट करने के लिए SQL एजेंट नौकरी का उपयोग करना चाह सकते हैं । भविष्य के प्रश्नों (या FUNCTION
बिंदु-समय के लिए एक डिफ़ॉल्ट बाधा के लिए एक मूल्य प्रदान करने के लिए स्केलर में लपेटें )।
फिर से, आपको ध्यान देना चाहिए कि यदि आवेदन में किसी भी प्रकार का सार्वजनिक-सामना करने वाला घटक है, तो आईपी पते और होस्टनाम अविश्वसनीय हैं (जैसे NAT के कारण)। यहां तक कि अगर यह सार्वजनिक-सामना नहीं कर रहा है, तो अधिकांश आईपी / होस्टनाम मानचित्रों के लिए एक निश्चित समय-आधारित घटक है, जिसे आपको कारक करना पड़ सकता है।
अंत में, अपने लॉगिन ट्रिगर को लागू करने से पहले, यह आपके सर्वर के समर्पित व्यवस्थापक कनेक्शन को चालू करने योग्य हो सकता है। यदि लॉगिन ट्रिगर किसी भी तरह से टूट जाता है, तो यह सभी उपयोगकर्ताओं को (sysadmin खातों सहित) लॉग इन करने से रोक सकता है:
USE master
GO
-- you may want to do this, so you have a back-out if the login trigger breaks login
EXEC sp_configure 'remote admin connections', 1
GO
RECONFIGURE
GO
यदि आप लॉक आउट हो जाते हैं, तो लॉगिन ट्रिगर को गिराने या निष्क्रिय करने के लिए DAC का उपयोग किया जा सकता है:
C:\> sqlcmd -S localhost -d master -A
1> DISABLE TRIGGER tr_audit_login ON ALL SERVER
2> GO