Database
 sql >> डेटाबेस >  >> RDS >> Database

अपने एमएस एसक्यूएल प्रतिकृति का प्रबंधन

प्रतिकृति MS SQL सर्वर पर सबसे पुरानी तकनीकों में से एक है, जिसे हर डेटाबेस व्यवस्थापक द्वारा पसंद किया जाता है। यह विशेष रूप से वितरित रिपोर्टिंग के लिए डेटा को उपयोगकर्ताओं के करीब लाने के लिए एक महान और विश्वसनीय तकनीक है। इसके लिए धन्यवाद, कई SQL सर्वर और क्षेत्रों में डेटाबेस उपलब्धता बढ़ जाती है।

एसक्यूएल 2005 में प्रतिकृति पेश की गई थी। क्या आप विश्वास कर सकते हैं कि यह पुराना है? हालांकि क्लाउड पर नए प्रबंधित SQL प्लेटफॉर्म नियमित रूप से पेश किए जाते हैं, मेरा मानना ​​है कि SQL प्रतिकृति यहां बनी रहेगी। अगर यह एक कीड़ा या कीड़ा होता, तो मैं इसे कॉकरोच समझता। स्क्वैश करना मुश्किल है!

यदि आप उन प्रशासकों की एक छोटी आबादी से संबंधित हैं, जिन्होंने कभी डेटाबेस का प्रबंधन नहीं किया है, तो इस विषय पर आधिकारिक Microsoft दस्तावेज़ हैं। हालाँकि, ध्यान दें कि क्या यह बहुत लंबा, व्यापक है और आपको छुट्टी या नियोजित द्वि घातुमान टीवी श्रृंखला से कुछ समय के लिए लूट लेगा। साथ ही, Codingsight SQL सर्वर डेटाबेस प्रतिकृति सेटअप और कॉन्फ़िगरेशन मार्गदर्शिका प्रदान करता है।

लेकिन इससे पहले कि आप तकनीकी सामान से अपने हाथ गंदे करें, और मुझे पता है कि आप भी उत्सुक हैं, इसके लिए योजना बनाना महत्वपूर्ण है।

जब आप क्लाउड पर चल रहे SQL सर्वर पर परिनियोजित करते हैं, तो स्थान के संबंध में प्रतिकृति आवश्यकता बदल सकती है। लेकिन एक बार जब SQL प्रतिकृति एक अच्छी तरह से तेल वाली मशीन की तरह चल रही है और उत्पादन डेटा की नकल कर रही है, तो आपको यह योजना बनाने की आवश्यकता है कि आप इसे कैसे प्रबंधित करते हैं।

इस पोस्ट में, मैं आपके लिए कुछ टिप्स और टी-एसक्यूएल स्क्रिप्ट साझा करूंगा, जब आपको यह जांचने की आवश्यकता होगी कि प्रतिकृति कॉन्फ़िगरेशन के बाद कई SQL एजेंट नौकरियां बनाई गई हैं।

प्रतिकृति एजेंटों की निगरानी करें

जब आप SQL प्रतिकृति को सेट और कॉन्फ़िगर करते हैं, तो यह स्टैंडअलोन फ़ंक्शंस और SQL एजेंट नौकरियों का एक सेट भी बनाता है जिसे प्रतिकृति एजेंट के रूप में जाना जाता है। उनका लक्ष्य आपकी तालिकाओं को हिलाने से संबंधित कार्यों को पूरा करना है, जिन्हें लेख . भी कहा जाता है , प्रतिकृति कॉन्फ़िगरेशन में प्रकाशक . से से सदस्य/सदस्यों . आप प्रतिकृति एजेंटों को कमांड लाइन से और प्रतिकृति प्रबंधन ऑब्जेक्ट (आरएमओ) का उपयोग करने वाले अनुप्रयोगों द्वारा चला सकते हैं।

SQL सर्वर प्रतिकृति एजेंटों को प्रतिकृति मॉनिटर और SQL सर्वर प्रबंधन स्टूडियो के माध्यम से मॉनिटर और प्रशासित किया जा सकता है।

डेटाबेस व्यवस्थापक/प्रतिकृति व्यवस्थापक की प्राथमिक चिंता यह सुनिश्चित करना है कि सभी SQL एजेंट प्रतिकृति कार्य चल रहे हैं। यदि प्रतिकृति एजेंट कार्य विफल हो जाता है, तो ग्राहक को डेटा प्राप्त नहीं हो सकता है। इसलिए, जमा की गई पंक्तियों के कारण वितरण डेटाबेस बहुत बड़ा हो सकता है जो ग्राहक डेटाबेस में नहीं जाएगा।

किसी भी प्रतिकृति एजेंट कार्य विफलता के लिए अलर्ट सेट करने के लिए, आप अन्य एजेंट कार्य बना सकते हैं। यह कार्य विफलताओं की जाँच करेगा और समस्याओं की पहचान करने पर आपकी dba टीम को एक ईमेल भेजेगा।

विफल प्रतिकृति एजेंट नौकरियों की जांच करें

नीचे दी गई स्क्रिप्ट का उपयोग करें:

declare @time time 
set @time = dateadd(n,-30,getdate()) 
declare @date date 
set @date = convert(date,getdate()) 
declare @publisher varchar(100) 
set @publisher = @@SERVERNAME
SELECT LEFT(name,50) as [JobName], run_date AS [RunDate], run_time AS [RunTime], LEFT([message],50) AS [Message] 
FROM 
(select distinct b.name,a.run_date, run_time, message 
from msdb..sysjobhistory a inner join msdb..sysjobs b on a.job_id = b.job_id where b.name like 'servername here%' and run_status <> 1 and message like '%error%' 
and convert(date,convert(varchar,a.run_date ))= convert(date,getutcdate()) replace(convert(varchar(8),dateadd(n,-30,getutcdate())),':','') ) a 

नौकरी की विफलता के बारे में सूचित करने के लिए एक ईमेल अलर्ट बनाएं

निम्नलिखित स्क्रिप्ट लागू करें:

exec msdb.dbo.sp_send_dbmail 
@profile_name = 'DBA Alerts', 
@recipients = 'your dba team email here', 
@subject = '[Database name] Replication Jobs Failure', 
@query = 'SELECT LEFT(name,50) as [JobName], run_date AS [RunDate], run_time AS [RunTime], LEFT([message],50) AS [Message] 
FROM 
(select distinct b.name, a.run_date, a.run_time, message 
from msdb.dbo.sysjobhistory a inner join msdb.dbo.sysjobs b on a.job_id = b.job_id 
where b.name like ''servername here %'' and 
convert(date,convert(varchar,a.run_date)) = convert(date,getutcdate()) ) a 
', 
@attach_query_result_as_file = 0 ; 

प्रतिकृति कमांड वाली तालिका की निगरानी करें

msrepl_commands की निगरानी के लिए तालिका में, आप नीचे दी गई एक और स्क्रिप्ट का उपयोग कर सकते हैं। ध्यान दें कि यह तालिका बहुत बड़ी और बहुत तेजी से बढ़नी चाहिए। यदि ऐसा है, तो प्रतिकृति एजेंट कार्य विफल हो सकता है, या प्रतिकृति कॉन्फ़िगरेशन में कोई समस्या हो सकती है।

स्क्रिप्ट इस प्रकार है:

use distribution 
SELECT Getdate() AS CaptureTime, LEFT(Object_name(t.object_id),20) AS TableName, st.row_count 
FROM sys.dm_db_partition_stats st WITH (nolock) 
INNER JOIN sys.tables t WITH (nolock) ON st.object_id = t.object_id INNER JOIN sys.schemas s WITH (nolock) ON t.schema_id = s.schema_id WHERE index_id < 2 AND Object_name(t.object_id) 
IN ('MSsubscriptions', 'MSdistribution_history', 'MSrepl_commands', 'MSrepl_transactions') 
ORDER BY st.row_count DESC

msrepl_commands तालिका वृद्धि प्रवृत्ति आपको यह संकेत भी देती है कि आपकी प्रतिकृति विलंबता कितनी स्वस्थ है। प्रभाव के कई कारक हैं। यदि आपका परिवेश क्लाउड में है, तो क्षेत्र चयन प्रतिकृति विलंबता में एक बड़ा योगदान दे सकता है।

Cप्रतिकृति पर एक साधारण रिपोर्टिंग पढ़ें और उसे ईमेल द्वारा भेजें

आप निम्न स्क्रिप्ट का उपयोग कर सकते हैं:

Declare @Publisher sysname, @PublisherDB sysname 
-- Set Publisher server and database name 
Set @Publisher = 'publication server name'; 
Set @PublisherDB = 'publishing database name'; 
-- Refresh replication monitor data 
USE [distribution] 
Exec sys.sp_replmonitorrefreshjob @iterations = 1; 
With MaxXact (ServerName, PublisherDBID, XactSeqNo) 
As (Select S.name, DA.publisher_database_id, max(H.xact_seqno) From dbo.MSdistribution_history H with(nolock) 
Inner Join dbo.MSdistribution_agents DA with(nolock) On DA.id = H.agent_id 
Inner Join master.sys.servers S with(nolock) On S.server_id = DA.subscriber_id 
Where DA.publisher_db = @PublisherDB 
Group By S.name, DA.publisher_database_id), OldestXact (ServerName, OldestEntryTime) 
As (Select MX.ServerName, Min(entry_time) 
From dbo.msrepl_transactions T with(nolock) 
Inner Join MaxXact MX On MX.XactSeqNo < T.xact_seqno And 
MX.PublisherDBID = T.publisher_database_id 
Group By MX.ServerName) 
Select [Replication Status] = Case MD.status 
When 1 Then 'Started' 
When 2 Then 'Succeeded' 
When 3 Then 'In progress' 
When 4 Then 'Idle' 
When 5 Then 'Retrying' 
When 6 Then 'Failed' 
End, 
Subscriber = SubString(MD.agent_name, Len(MD.publisher) + 
Len(MD.publisher_db) + Len(MD.publication) + 4, 
Charindex('-', MD.agent_name, 
Len(MD.publisher) + Len(MD.publisher_db) + 
Len(MD.publication) + 5) - 
(Len(MD.publisher) + 
Len(MD.publisher_db) + Len(MD.publication) + 4)), 
[Subscriber DB] = A.subscriber_db, 
[Publisher DB] = MD.publisher_db, 
Publisher = MD.publisher, 
[Current Latency (sec)] = MD.cur_latency,
[Current Latency (hh:mm:ss)] = Right('00' + Cast(MD.cur_latency/3600 As varchar), 2) + 
':' + Right('00' + 
Cast((MD.cur_latency%3600)/60 As varchar), 2) + 
':' + Right('00' + 
Cast(MD.cur_latency%60 As varchar), 2), 
[Latency Threshold (min)] = Cast(T.value As Int), 
[Agent Last Stopped (sec)] = DateDiff(hour, agentstoptime, getdate()) - 1, 
[Agent Last Sync] = MD.last_distsync, 
[Last Entry TimeStamp] = OX.OldestEntryTime 
From dbo.MSreplication_monitordata MD with(nolock) 
Inner Join dbo.MSdistribution_agents A with(nolock) On A.id = MD.agent_id Inner Join dbo.MSpublicationthresholds T with(nolock) On T.publication_id = MD.publication_id And T.metric_id = 2 -- Latency 
Inner Join OldestXact OX On OX.ServerName = SubString(MD.agent_name, Len(MD.publisher) + Len(MD.publisher_db) + 
Len(MD.publication) + 4, 
Charindex('-', MD.agent_name, 
Len(MD.publisher) + Len(MD.publisher_db) + 
Len(MD.publication) + 5) - 
(Len(MD.publisher) + 
Len(MD.publisher_db) + Len(MD.publication) + 4)) 
Where MD.publisher = @Publisher 
And MD.publisher_db = @PublisherDB 
And MD.publication_type = 0 -- 0 = Transactional publication And MD.agent_type = 3; -- 3 = distribution agent 
IF (@@ROWCOUNT > 500) 
BEGIN 
-- send alerts here.. 500 rows of undistributed transactions , should be higher. run this on remote distributor 
EXEC msdb.dbo.sp_send_dbmail 
@profile_name = 'DBA Alert', 
@recipients = 'your dba team email here', 
@body = 'This is replication latency alert. Check undistributed transactions query.', 
@subject = 'Replication Latency Alert' ; 
PRINT 'Alert here!' --since email is not yet working 
END

प्रश्नलेखों की सूची बनाएं और सदस्यों के स्वास्थ्य की जांच करें

यदि आप लेन-देन प्रतिकृति पर काम कर रहे हैं, तो ये ऑपरेशन अत्यंत महत्वपूर्ण हैं। यहाँ एक स्क्रिप्ट है:

SELECT DISTINCT LEFT(srv.srvname,50) AS publication_server 
, LEFT(a.publisher_db, 50) AS publisher_db 
, LEFT(p.publication,25) AS publication_name
, LEFT(a.article, 50) AS [article] 
, LEFT(a.destination_object,50) AS destination_object 
, LEFT(ss.srvname,25) AS subscription_server 
, LEFT(s.subscriber_db,25) AS subscriber_db 
, LEFT(da.name,50) AS distribution_agent_job_name 
FROM distribution..MSArticles a 
JOIN distribution..MSpublications p ON a.publication_id = p.publication_id JOIN distribution..MSsubscriptions s ON p.publication_id = s.publication_id JOIN master..sysservers ss ON s.subscriber_id = ss.srvid 
JOIN master..sysservers srv ON srv.srvid = p.publisher_id 
JOIN distribution..MSdistribution_agents da ON da.publisher_id = p.publisher_id AND da.subscriber_id = s.subscriber_id 
ORDER BY 1,2,3 

DBA टीम के लिए एक रिपोर्टिंग सारांश बनाएं

सभी प्रतिकृति आँकड़ों और डिलीवर और डिलीवर किए गए कमांड को संयोजित करने के लिए, आप सभी प्रतिकृति विवरणों को समाहित करने के लिए वितरण डेटाबेस में एक तालिका बना सकते हैं।

इस तालिका से, आप dba टीम को वितरित करने के लिए एक रिपोर्टिंग सारांश बना सकते हैं . मानक डेटाबेस व्यवस्थापक सुबह स्वास्थ्य जांच से अलग दैनिक प्रतिकृति स्वास्थ्य जांच के हिस्से के रूप में इस तालिका को हर दिन ताज़ा किया जा सकता है।

USE [distribution] 
IF OBJECT_ID('Tempdb.dbo.#ReplStats') IS NOT NULL 
DROP TABLE #ReplStats 
CREATE TABLE [dbo].[#ReplStats] ( 
[DistributionAgentName] [nvarchar](100) NOT NULL 
,[DistributionAgentStartTime] [datetime] NOT NULL 
,[DistributionAgentRunningDurationInSeconds] [int] NOT NULL ,[IsAgentRunning] [bit] NULL 
,[ReplicationStatus] [varchar](14) NULL 
,[LastSynchronized] [datetime] NOT NULL 
,[Comments] [nvarchar](max) NOT NULL 
,[Publisher] [sysname] NOT NULL 
,[PublicationName] [sysname] NOT NULL 
,[PublisherDB] [sysname] NOT NULL 
,[Subscriber] [nvarchar](128) NULL 
,[SubscriberDB] [sysname] NULL 
,[SubscriptionType] [varchar](64) NULL 
,[DistributionDB] [sysname] NULL 
,[Article] [sysname] NOT NULL 
,[UndelivCmdsInDistDB] [int] NULL 
,[DelivCmdsInDistDB] [int] NULL 
,[CurrentSessionDeliveryRate] [float] NOT NULL 
,[CurrentSessionDeliveryLatency] [int] NOT NULL 
,[TotalTransactionsDeliveredInCurrentSession] [int] NOT NULL
,[TotalCommandsDeliveredInCurrentSession] [int] NOT NULL ,[AverageCommandsDeliveredInCurrentSession] [int] NOT NULL ,[DeliveryRate] [float] NOT NULL 
,[DeliveryLatency] [int] NOT NULL 
,[TotalCommandsDeliveredSinceSubscriptionSetup] [int] NOT NULL ,[SequenceNumber] [varbinary](16) NULL 
,[LastDistributerSync] [datetime] NULL 
,[Retention] [int] NULL 
,[WorstLatency] [int] NULL 
,[BestLatency] [int] NULL 
,[AverageLatency] [int] NULL 
,[CurrentLatency] [int] NULL 
) ON [PRIMARY] 
INSERT INTO #ReplStats 
SELECT da.[name] AS [DistributionAgentName] 
,dh.[start_time] AS [DistributionAgentStartTime] 
,dh.[duration] AS [DistributionAgentRunningDurationInSeconds] ,md.[isagentrunningnow] AS [IsAgentRunning] 
,CASE md.[status] 
WHEN 1 
THEN '1 - Started' 
WHEN 2 
THEN '2 - Succeeded' 
WHEN 3 
THEN '3 - InProgress' 
WHEN 4 
THEN '4 - Idle' 
WHEN 5 
THEN '5 - Retrying' 
WHEN 6 
THEN '6 - Failed' 
END AS [ReplicationStatus] 
,dh.[time] AS [LastSynchronized] 
,dh.[comments] AS [Comments] 
,md.[publisher] AS [Publisher] 
,da.[publication] AS [PublicationName] 
,da.[publisher_db] AS [PublisherDB] 
,CASE 
WHEN da.[anonymous_subid] IS NOT NULL 
THEN UPPER(da.[subscriber_name]) 
ELSE UPPER(s.[name]) 
END AS [Subscriber] 
,da.[subscriber_db] AS [SubscriberDB] 
,CASE da.[subscription_type] 
WHEN '0' 
THEN 'Push' 
WHEN '1' 
THEN 'Pull'
WHEN '2' 
THEN 'Anonymous' 
ELSE CAST(da.[subscription_type] AS [varchar](64)) 
END AS [SubscriptionType] 
,md.[distdb] AS [DistributionDB] 
,ma.[article] AS [Article] 
,ds.[UndelivCmdsInDistDB] 
,ds.[DelivCmdsInDistDB] 
,dh.[current_delivery_rate] AS [CurrentSessionDeliveryRate] ,dh.[current_delivery_latency] AS [CurrentSessionDeliveryLatency] ,dh.[delivered_transactions] AS 
[TotalTransactionsDeliveredInCurrentSession] 
,dh.[delivered_commands] AS [TotalCommandsDeliveredInCurrentSession] ,dh.[average_commands] AS [AverageCommandsDeliveredInCurrentSession] ,dh.[delivery_rate] AS [DeliveryRate] 
,dh.[delivery_latency] AS [DeliveryLatency] 
,dh.[total_delivered_commands] AS 
[TotalCommandsDeliveredSinceSubscriptionSetup] 
,dh.[xact_seqno] AS [SequenceNumber] 
,md.[last_distsync] AS [LastDistributerSync] 
,md.[retention] AS [Retention] 
,md.[worst_latency] AS [WorstLatency] 
,md.[best_latency] AS [BestLatency] 
,md.[avg_latency] AS [AverageLatency] 
,md.[cur_latency] AS [CurrentLatency] 
FROM [distribution]..[MSdistribution_status] ds 
INNER JOIN [distribution]..[MSdistribution_agents] da ON da.[id] = ds.[agent_id] 
INNER JOIN [distribution]..[MSArticles] ma ON ma.publisher_id = da.publisher_id 
AND ma.[article_id] = ds.[article_id] 
INNER JOIN [distribution]..[MSreplication_monitordata] md ON [md].[job_id] = da.[job_id] 
INNER JOIN [distribution]..[MSdistribution_history] dh ON [dh].[agent_id] = md.[agent_id] 
AND md.[agent_type] = 3 
INNER JOIN [master].[sys].[servers] s ON s.[server_id] = da.[subscriber_id] 
--Created WHEN your publication has the immediate_sync property set to true. This property dictates 
--whether snapshot is available all the time for new subscriptions to be initialized. 
--This affects the cleanup behavior of transactional replication. If this property is set to true, 
--the transactions will be retained for max retention period instead of it getting cleaned up 
--as soon as all the subscriptions got the change. 
WHERE da.[subscriber_db] <> 'virtual' 
AND da.[anonymous_subid] IS NULL
AND dh.[start_time] = ( 
SELECT TOP 1 start_time 
FROM [distribution]..[MSdistribution_history] a 
INNER JOIN [distribution]..[MSdistribution_agents] b ON a.[agent_id] = b.[id] 
AND b.[subscriber_db] <> 'virtual' 
WHERE [runstatus] <> 1 
ORDER BY [start_time] DESC 
) 
AND dh.[runstatus] <> 1 
SELECT 'Transactional Replication Summary' AS [Comments]; 
SELECT [DistributionAgentName] 
,[DistributionAgentStartTime] 
,[DistributionAgentRunningDurationInSeconds] 
,[IsAgentRunning] 
,[ReplicationStatus] 
,[LastSynchronized] 
,[Comments] 
,[Publisher] 
,[PublicationName] 
,[PublisherDB] 
,[Subscriber] 
,[SubscriberDB] 
,[SubscriptionType] 
,[DistributionDB] 
,SUM([UndelivCmdsInDistDB]) AS [UndelivCmdsInDistDB] 
,SUM([DelivCmdsInDistDB]) AS [DelivCmdsInDistDB] 
,[CurrentSessionDeliveryRate] 
,[CurrentSessionDeliveryLatency] 
,[TotalTransactionsDeliveredInCurrentSession] 
,[TotalCommandsDeliveredInCurrentSession] 
,[AverageCommandsDeliveredInCurrentSession] 
,[DeliveryRate] 
,[DeliveryLatency] 
,[TotalCommandsDeliveredSinceSubscriptionSetup] 
,[SequenceNumber] 
,[LastDistributerSync] 
,[Retention] 
,[WorstLatency] 
,[BestLatency] 
,[AverageLatency] 
,[CurrentLatency] 
FROM #ReplStats 
GROUP BY [DistributionAgentName] 
,[DistributionAgentStartTime] 
,[DistributionAgentRunningDurationInSeconds] 
,[IsAgentRunning]
,[ReplicationStatus] 
,[LastSynchronized] 
,[Comments] 
,[Publisher] 
,[PublicationName] 
,[PublisherDB] 
,[Subscriber] 
,[SubscriberDB] 
,[SubscriptionType] 
,[DistributionDB] 
,[CurrentSessionDeliveryRate] 
,[CurrentSessionDeliveryLatency] 
,[TotalTransactionsDeliveredInCurrentSession] 
,[TotalCommandsDeliveredInCurrentSession] 
,[AverageCommandsDeliveredInCurrentSession] 
,[DeliveryRate] 
,[DeliveryLatency] 
,[TotalCommandsDeliveredSinceSubscriptionSetup] 
,[SequenceNumber] 
,[LastDistributerSync] 
,[Retention] 
,[WorstLatency] 
,[BestLatency] 
,[AverageLatency] 
,[CurrentLatency] 
SELECT 'Transactional Replication Summary Details' AS [Comments]; 
SELECT [Publisher] 
,[PublicationName] 
,[PublisherDB] 
,[Article] 
,[Subscriber] 
,[SubscriberDB] 
,[SubscriptionType] 
,[DistributionDB] 
,SUM([UndelivCmdsInDistDB]) AS [UndelivCmdsInDistDB] ,SUM([DelivCmdsInDistDB]) AS [DelivCmdsInDistDB] 
FROM #ReplStats 
GROUP BY [Publisher] 
,[PublicationName] 
,[PublisherDB] 
,[Article] 
,[Subscriber] 
,[SubscriberDB] 
,[SubscriptionType] 
,[DistributionDB]

सारांश

मुझे आशा है कि ऊपर प्रदान की गई ये कुछ टी-एसक्यूएल स्क्रिप्ट आपके प्रतिकृति एजेंटों की निगरानी में आपकी सहायता करेंगी। मैं अत्यधिक अनुशंसा करता हूं कि आप उनकी बारीकी से निगरानी करें। अन्यथा, ग्राहक की ओर से उपयोगकर्ता वास्तविक समय डेटा (करीब) नहीं होने के बारे में अंतहीन शिकायत कर सकते हैं।

आने वाले लेखों में, मैं दुनिया के किसी भी हिस्से में डेटा की प्रतिकृति बनाने की SQL तकनीक में गहराई से उतरूंगा। हैप्पी मॉनिटरिंग!


  1. Database
  2.   
  3. Mysql
  4.   
  5. Oracle
  6.   
  7. Sqlserver
  8.   
  9. PostgreSQL
  10.   
  11. Access
  12.   
  13. SQLite
  14.   
  15. MariaDB
  1. पुनरावर्तनीय पढ़ें अलगाव स्तर

  2. टेबल एक्सप्रेशन के मूल तत्व, भाग 10 - दृश्य, चुनें *, और डीडीएल परिवर्तन

  3. शीर्ष 30 SQL क्वेरी साक्षात्कार प्रश्न जिनका आपको 2022 में अभ्यास करना चाहिए

  4. क्या Google डेटा एनालिटिक्स प्रोफेशनल सर्टिफिकेट इसके लायक है?

  5. SQL में टिप्पणियाँ कैसे जोड़ें?