Who was the last to modify a database object? - sql-server

This article describes how to run a standard report to display recent DDL changes.
If the data is captured, it's probably in a table somewhere. I would like to Trace this location so I can construct my own reports.
Is this possible?

Option #1
select * FROM sys.traces where is_default = 1 ;
This query contains path column. Copy the path of your trace file and now use the below query
SELECT * FROM fn_trace_gettable('Path Column value from sys.traces', default)
Which Table(Object) is modified and Who Modified ?
select ObjectName, LoginName
from ::fn_trace_gettable( 'Path Column value from sys.traces', default)
where EventClass in (46,47,164) and EventSubclass = 0
and DatabaseID = db_id() ;
select ObjectName,
ObjectID,
DatabaseName,
StartTime,
EventClass,
EventSubClass,
ObjectType,
ServerName,
LoginName,
NTUserName,
ApplicationName
from ::fn_trace_gettable( 'Trace File Path', default )
where EventClass in (46,47,164) and EventSubclass = 0 and DatabaseID = db_id();
Option #2
SQL Server – Auditing Schema Changes using DDL Triggers
This approach will tell which column was added, or what command was used to add

Related

Cannot open the properties tab in SSMS for Managed Instance Database

I am using Azure Managed Instance for some migration tasks. I have multiple databases in that all are working fine.
But when I try to open the properties for a database named GCDCalculation, I am getting the following error:-
And the whole error is:-
Cannot show requested dialog.
===================================
Cannot show requested dialog. (SqlMgmt)
------------------------------
Program Location:
at Microsoft.SqlServer.Management.SqlMgmt.DefaultLaunchFormHostedControlAllocator.AllocateDialog(XmlDocument initializationXml, IServiceProvider dialogServiceProvider, CDataContainer dc)
at Microsoft.SqlServer.Management.SqlMgmt.DefaultLaunchFormHostedControlAllocator.Microsoft.SqlServer.Management.SqlMgmt.ILaunchFormHostedControlAllocator.CreateDialog(XmlDocument initializationXml, IServiceProvider dialogServiceProvider)
at Microsoft.SqlServer.Management.SqlMgmt.LaunchForm.InitializeForm(XmlDocument doc, IServiceProvider provider, ISqlControlCollection control)
at Microsoft.SqlServer.Management.SqlMgmt.LaunchForm..ctor(XmlDocument doc, IServiceProvider provider)
at Microsoft.SqlServer.Management.UI.VSIntegration.ObjectExplorer.ToolMenuItemHelper.OnCreateAndShowForm(IServiceProvider sp, XmlDocument doc)
at Microsoft.SqlServer.Management.SqlMgmt.RunningFormsTable.RunningFormsTableImpl.ThreadStarter.StartThread()
===================================
Subquery returned more than 1 value. This is not permitted when the subquery follows =, !=, <, <= , >, >= or when the subquery is used as an expression. (.Net SqlClient Data Provider)
------------------------------
For help, click: https://learn.microsoft.com/sql/relational-databases/errors-events/mssqlserver-512-database-engine-error
------------------------------
Server Name: my-project-database.database.windows.net, 3342
Error Number: 512
Severity: 16
State: 1
Line Number: 7
------------------------------
Program Location:
at Microsoft.SqlServer.Management.SqlManagerUI.DBPropGeneralData.InitProp()
at Microsoft.SqlServer.Management.SqlManagerUI.DBPropGeneralData..ctor(CDataContainer context)
at Microsoft.SqlServer.Management.SqlManagerUI.DBPropGeneral..ctor(CDataContainer dataContainer, DatabasePrototype prototype)
at Microsoft.SqlServer.Management.SqlManagerUI.DBPropSheet.Init(CDataContainer dataContainer)
at Microsoft.SqlServer.Management.SqlManagerUI.DBPropSheet..ctor(CDataContainer context)
All other Databases are working fine.
Added to this, I want to know that, is there specific query is triggered in the backend when we try to open the properties tab (Or any other tab).
Finally, one I can answer. Kollira is on the right track with this, the reason you get the error is that backupset contains records linking more than one database to the same ID. This happens because the DB was probably brought into SQL MI via the migration tool which creates the backupset with a GUID instead of the database name. When you do your own copy-only backup to URL, it creates a new backupset with the real name. This is not an optimised query to prove it but here goes.
select db_id(database_name),database_name, [type], max(backup_finish_date) as latest from msdb..backupset where ([type] = 'D' or [type] = 'L' or [type]='I') and db_id(database_name) = (select database_id from sys.databases where name = 'Your_Database') group by database_name, [type]
Then we can see there is more than one DB listed, you can probably see the wrong one as it was created just when you backed up. So with this list of probably 2 DBs
Select * from msdb..backupset where database_name in('DB1', 'DB2')
We can see conflicting backupsets. In my case the one with the real name was actually the new one that I wanted to remove, the other was a GUID I assume from my migration. So I just deleted the backupset as there were no backups associated. So simply:
Delete from msdb.dbo.backupset where database_name = 'TheDatabaseName'
Boom, now properties rendered, and the original backup chain was still correct.
I am not a SQL pro, so use at your own risk. Also, since this is no longer a Managed instance unique problem, maybe a SQL pro can advise a better solution.
The problem is with backups. Run this query and update the database name to the one which is latest.
create table #tempbackup (database_name nvarchar(128), [type] char(1), backup_finish_date datetime)
insert into #tempbackup
select database_name, [type], max(backup_finish_date) from msdb..backupset where [type] = 'D' or [type] = 'L' or [type]='I' group by database_name, [type]
SELECT
(select backup_finish_date from #tempbackup where type = 'D' and db_id(database_name) = dtb.database_id)
AS [LastBackupDate]
FROM
master.sys.databases AS dtb
WHERE
(dtb.name='Your Database Name')
drop table #tempbackup

How to drop duplicate records in the SQL Server using Python?

I have a .csv file and it gets updated every day. Below is the example of my .csv file
I am pushing this .csv file into SQL Server using Python. My script reads the .csv file and uploads it into a SQL Server database.
This is my Python script:
import pandas as pd
import pyodbc
df = pd.read_csv ("C:/Users/Dhilip/Downloads/test.csv")
print(df)
conn = pyodbc.connect('Driver={SQL Server};'
'Server=DESKTOP-7FCK7FG;'
'Database=test;'
'Trusted_Connection=yes;')
cursor = conn.cursor()
#cursor.execute('CREATE TABLE people_info (Name nvarchar(50), Country nvarchar(50), Age int)')
for row in df.itertuples():
cursor.execute('''
INSERT INTO test.dbo.people_info (Name, Country, Age)
VALUES (?,?,?)
''',
row.Name,
row.Country,
row.Age
)
conn.commit()
The script is working fine. I am trying to automate my Python script using batch file and task scheduler, and it's working fine. However, whenever I add new data in the .csv file and SQL Server gets updated with new data and the same time it prints the old data multiple times.
Example, if I add new record called Israel, the output appears in SQL Server as below
I need output as below,
Can anyone advise me the change I need to do in the above python script?
You can use below query in your python script. if Not exists will check if the record already exists based on the condition in where clause and if record exists then it will go to else statement where you can update or do anything.
checking for existing records in database works faster than checking using python script.
if not exists (select * from Table where Name = '')
begin
insert into Table values('b', 'Japan', 70)
end
else
begin
update Table set Age=54, Country='Korea' where Name = 'A'
end
to find existing duplicate records then use the below query
select Name, count(Name) as dup_count from Table
group by Name having COUNT(Name) > 1
I find duplicates like this
def find_duplicates(table_name):
"""
find duplicates inside table
:param table_name:
:return:
"""
connection = sqlite3.connect("./k_db.db")
cursor = connection.cursor()
findduplicates = """ SELECT a.*
FROM {} a
JOIN (
SELECT shot, seq, lower(user), date_time,written_by, COUNT(*)
FROM {}
GROUP BY shot, seq, lower(user), date_time,written_by
HAVING count(*) > 1 ) b
ON a.shot = b.shot
AND a.seq = b.seq
AND a.date_time = b.date_time
AND a.written_by = b.written_by
ORDER BY a.shot;""".format(
table_name, table_name
)
# print(findduplicates)
cursor.execute(findduplicates)
connection.commit()
records = cursor.fetchall()
cursor.close()
connection.close()
You could rephrase your insert such that it checks for existence of the tuple before inserting:
for row in df.itertuples():
cursor.execute('''
INSERT INTO test.dbo.people_info (Name, Country, Age)
SELECT ?, ?, ?
WHERE NOT EXISTS (SELECT 1 FROM test.dbo.people_info
WHERE Name = ? AND Country = ? AND Age = ?)
''', (row.Name, row.Country, row.Age, row.Name, row.Country, row.Age,))
conn.commit()
An alternative to the above would be to add a unique index on (Name, Country, Age). Then, your duplicate insert attempts would fail and generate an error.

Find out when the last failover occurred in AlwaysOn availability groups

I used below metioned query to find out if any failover happened in the last 30 minutes
create table #errormsg(duration datetime,errornum int,dbmessage varchar(max))
DECLARE #tags3 VARCHAR(5000)SET #tags3 = (SELECT CAST( t.target_data AS XML ).value('(EventFileTarget/File/#name)[1]', 'VARCHAR(MAX)') FROM sys.dm_xe_sessions s INNER JOIN sys.dm_xe_session_targets t ON s.address = t.event_session_address WHERE t.target_name = 'event_file' and s.name='AlwaysOn_health');
IF #tags3 is Not NULL begin WITH cte_HADR AS (SELECT object_name, CONVERT(XML, event_data) AS data FROM sys.fn_xe_file_target_read_file(#tags3, null, null, null)WHERE object_name = 'error_reported')
insert into #errormsg SELECT data.value('(/event/#timestamp)[1]','datetime')AS [timestamp],data.value('(/event/data[#name=''error_number''])[1]','int') AS [error_number],data.value('(/event/data[#name=''message''])[1]','varchar(max)') AS [message] FROM cte_HADR WHERE data.value('(/event/data[#name=''error_number''])[1]','int') = 1480 select distinct GETDATE() as currenttime, er.duration,dbs.name from #errormsg er inner join sys.databases dbs on er.dbmessage LIKE '%"' +dbs.name+'"%' where er.duration>=(DATEADD(mi,-30,GETDATE()) );
drop table #errormsg;end
else IF OBJECT_ID(N'TempDB.dbo.#errormsg', N'U') IS NOT NULL drop table #errormsg;
But I did not get the result I was expecting because of the "Incorrect Timestamp on Events in Extended Events".
In SSMS -->Management-->Extended Events-->Sessions-->AlwaysOn_health--> click event file.
In that event file,
I checked recently role changed time for 'availablity_replica_state_change'.
In MSSQL log folder--> availablity_replica_state_change time in "AlwaysOn_health" file
timestamp in (1) and (2) needs to be same.
But for me its shows different time. So I didn't get the proper result.
Instead of using extended events, Is there any query to read the MS SQL error logs?
Is there any query to find out if any failover happened in the last 30 minutes?
Please help me to find a solution for this .

Why SQL SERVER 2008 Suddenly change to SINGLE USER from MULTI USER

My production database suddenly changed to single user model.
I changed it back to MULTI USER and now everything is running normally.
Why did this happen?
A database doesn't just get set to single user by itself, there should be some process or person actually issued the command to happened the same.
That ALTER DATABASE command counts as a DDL operation, and it thus logged in the Default Trace.
you can see some quick investigation info from the Schema Changes history Report, or query the default trace directly:
--SELECT * from sys.traces
declare #TraceIDToReview int
declare #path varchar(255)
SET #TraceIDToReview = 1 --this is the trace you want to review!
SELECT #path = path from sys.traces WHERE id = #TraceIDToReview
SELECT
TE.name As EventClassDescrip,
v.subclass_name As EventSubClassDescrip,
T.*
FROM ::fn_trace_gettable(#path, default) T
LEFT OUTER JOIN sys.trace_events TE ON T.EventClass = TE.trace_event_id
LEFT OUTER JOIN sys.trace_subclass_values V
ON T.EventClass = V.trace_event_id AND T.EventSubClass = V.subclass_value
WHERE DatabaseName ='Blank'
AND IndexID = 15 --Single User
OR IndexID = 16 --Multi User

How to determine who performed DROP/DELETE on Sql Server database objects?

There is always a need to find out details, either intentionally Or mistakenly someone executed DROP/DELETE command on any of following SQL Server database objects.
DROPPED - Table from your database
DROPPED - Stored Procedure from your database
DELETED - Rows from your database table
Q. Is there TSQL available to find db user who performed DELETE/DROP?
Q. What kind of permissions are needed for user to find out these details?
Did you check this ?
Right click on database.
Go to as shown in image :
Solution 2 :
This query gives alot of useful information for a database(apply filter as required) :
DECLARE #filename VARCHAR(255)
SELECT #FileName = SUBSTRING(path, 0, LEN(path)-CHARINDEX('\', REVERSE(path))+1) + '\Log.trc'
FROM sys.traces
WHERE is_default = 1;
SELECT gt.HostName,
gt.ApplicationName,
gt.NTUserName,
gt.NTDomainName,
gt.LoginName,
--gt.SPID,
-- gt.EventClass,
te.Name AS EventName,
--gt.EventSubClass,
-- gt.TEXTData,
gt.StartTime,
gt.EndTime,
gt.ObjectName,
gt.DatabaseName,
gt.FileName,
gt.IsSystem
FROM [fn_trace_gettable](#filename, DEFAULT) gt
JOIN sys.trace_events te ON gt.EventClass = te.trace_event_id
WHERE EventClass in (164) --AND gt.EventSubClass = 2
ORDER BY StartTime DESC;

Resources