How to audit who ran query on abcd database im Snowflake? - snowflake-cloud-data-platform

Have abcd database. one user has executed query on abcd database. Another user has executed another query on abcd database. Like this whenever different user has executed different user has executed query on abcd database. Need to capture user execution time and who has executed query..etc

This information is gathered from https://docs.snowflake.com/en/user-guide/access-history.html which is basically the access history details. Also, the same detail can be checked from Snowflake - Query History - Filters where you can use username or any other parameter to look up the details.

You can use Query_history view available in Snowflake.ACCOUNT_USAGE schema to get the complete information of the query within the last 365 days including user_name, execution time, etc
Please note Latency for the view may be up to 45 minutes.
https://docs.snowflake.com/en/sql-reference/account-usage/query_history.html#query-history-view
Also, you can use QUERY_HISTORY function available in information_schema to retrieve query information within the last 7 days and with no latency.
Please review the below documentation for more information.
https://docs.snowflake.com/en/sql-reference/functions/query_history.html#query-history-query-history-by
https://docs.snowflake.com/en/sql-reference/account-usage.html#differences-between-account-usage-and-information-schema

You can use below query(you can add the columns as you need):
select distinct QH.query_id, QH.USER_NAME,qh.database_name,Qh.start_time, qh.EXECUTION_TIME from
"SNOWFLAKE"."ACCOUNT_USAGE"."QUERY_HISTORY" QH
where
-- QH.query_id='' --If you know the query id,use it here
-- QH.user_name='USERNAME' -- You can filter by user id
QH.database_name='DBNAME' --you can filter by databasename
and qh.start_time > '2022-06-29 12:45:36.291'-- you can filter by date
;
If you want to track the IP address and application from where query was run, you can use below query as well:
select distinct QH.query_id,LH.client_ip, QH.USER_NAME,s.client_application_id,qh.database_name,Qh.start_time, qh.EXECUTION_TIME from snowflake.account_usage.login_history LH
inner join "SNOWFLAKE"."ACCOUNT_USAGE"."QUERY_HISTORY" QH
on QH.USER_NAME=LH.user_name
inner join "SNOWFLAKE"."ACCOUNT_USAGE"."SESSIONS" S on S.session_id=QH.session_id
and s.LOGIN_EVENT_ID=lh.EVENT_ID
where
-- QH.query_id='' --If you know the query id,use it here
-- QH.user_name='USERNAME' --If you know the user id,use it here
QH.database_name='DBNAME' --If you know the DB id,use it here
and qh.start_time > '2022-06-29 12:45:36.291'-- filter by date as required
;

Related

How can I find out if someone modified a row in SQL server in a specific date?

I am just wondering, can I find out if somebody wrote a query and updated a row against specific table in some date?
I tried this :
SELECT id, name
FROM sys.sysobjects
WHERE NAME = ''
SELECT TOP 1 *
FROM ::fn_dblog(NULL,NULL)
WHERE [Lock Information] LIKE '%TheOutoput%'
It does not show me ?
Any suggestions.
No, row level history/change stamps is not built into SQL Server. You need to add that in the table design. If you want an automatic update date column it would typically be set by a trigger on the table.
There is however a way if you really need to find out what happened in a forensics scenario. But that is only available if you have the right backup plans. What you can do then is to use the DB transaction log to find when the modification was done. Note that this is not anything an application can or should do runtime.

How to create a "Ghost Table" in SQL Server based off of other tables?

I need to create a "ghost" table in SQL Server, which doesn't actually exist but is a result set of a SQL Query. Pseudo code is below:
SELECT genTbl_col1, genTblcol2
FROM genTbl;
However, "genTbl" is actually:
SELECT table1.col AS genTbl_col1,
table2.col AS genTbl_col2
FROM table1 INNER JOIN table2 ON (...)
In other words, I need that every time a query is run on the server trying to select from "genTbl", it simply creates a result set from the query and treats it like a real table.
The situation is that I have a software that runs queries on a database. I need to modify it, but I cannot change the software itself, so I need to trick it into thinking it can actually query "genTbl", when it actually doesn't exist but is simply a query of other tables.
To clarify, the query would have to be a sort of procedure, available by default in the database (i.e. every time there is a query for "genTbl").
Use #TMP
SELECT genTbl_col1, genTblcol2
INTO #TMP FROM genTbl;
It exists only in current session. You can also use ##TMP for all sessions.

User login information not pulling active users

Need some help on this.
I have a job that pulls user logins once a day and inserts into a table. The purpose of this is to gather user info to see what accounts can be dropped after a while.
Insert into [User_Login_Audit]
Select
login_name, max (login_time) as last_login_time,
last_successful_logon, (select ##servername) as server_instance, getdate()
from
sys.dm_exec_sessions
group by
login_name, last_successful_logon;
I am using the query below to gather the user information using registered servers.
SELECT
[Login_name],
MAX([last_login_time]) AS Last_login_date,
[server_instance],
DATEDIFF(day, getdate(), max([last_login_time])) Days
FROM
[Benefitfocus_DBA].[dbo].[User_Login_Audit]
WHERE
Login_name NOT IN ('NT AUTHORITY\SYSTEM', 'sa','')
AND last_successful_logon IS NOT NULL
GROUP BY
Login_name, server_instance
I've noticed that the top query pulls all information from sys.dm_exec_sessions. Some of the logins it records have been removed from security on that instance. I need only the users that are Active and / or present on the instance. Can someone suggest a way to either modify the Insert query to pull only users currently in security on the instance or a way on the second query to sort which users are active on the instance?
I would appreciate any insight into this.
Take a look at sys.server_principals and see if it will work for you. You would do a join on the 'name' column in that table from the login_name that you currently have in your query. I'm not sure if you are deleting the logins or disabling them, but that system view could tell you if either condition was true.
The big caveat that I know of is that if a user has access to the server through an Active Directory group, you will find the login_name of the user in your current query but it will not match up to anything in the system view because the relevant entry there will be for the AD group. There are some ways to handle that, but not knowing if that is even relevant in your system I will not go into detail here.
After trying a few things I found that adding the following in the first collection script limited it to the current users:
where login_name in (SELECT name FROM sys.database_principals where type<>'R')

How to delete a table after a period of inactivity?

For the purpose of my project I cannot use session based temp tables. They need to be persistent but automatically deleted after a certain period of inactivity (no CRUD performed). Is this at all possible?
You can use the SQL Server Agent to Schedule a Job that calls a Stored Procedure that does this work for you. (How to Schedule a Job?)
How do you identify the tables that have not updated since X amount of time ?
Use this Query:
SELECT OBJECT_NAME(OBJECT_ID) AS TableName, last_user_update,
FROM sys.dm_db_index_usage_stats
WHERE database_id = DB_ID('DatabaseName')
AND OBJECT_NAME(OBJECT_ID) LIKE '%%' -- Here is the template name for your tables
AND DATEDIFF(MINUTE, last_user_update, GETDATE()) > 10 -- Last updated more than 10 minutes
Now that you have the tables to be deleted, you can use whatever logic you want to DROP them (Cursor, While, Procedure)
Sure it is. Write it into your program layer.
AUTOMATICALLY - within SQL Server: no. Well, you cold use the agent to start a script regularly.
Tracking what "inactivity" means - your responsibility.
You need save modification date of this table somewhere (for example in the same table or in another special table) and then you can create job, which checks last modification date and then drops the table.

Tracking User activity log for SQL Server database

I have a database with multiple tables and I want to log the users activity via my MVC 3 web application.
User X updated category HELLO. Name changed from 'HELLO' to 'Hi There' on 24/04/2011
User Y deleted vehicle Test on 24/04/2011.
User Z updated vehicle Bla. Name changed from 'Blu' to 'Bla' on 24/04/2011.
User Z updated vehicle Bla. Wheels changed from 'WheelsX' to 'WheelsY' on 24/04/2011.
User Z updated vehicle Bla. BuildProgress changed from '20' to '50' on 24/04/2011
My initial idea is to have on all of my actions that have database crud, to add a couple lines of code that would enter those strings in a table.
Is there a better way of checking which table and column has been modified than to check every column one by one with if statements (first I select the current values, then check each of them with the value of the textbox) I did that for another ASPX web app and it was painful.
Now that I'm using MVC and ADO.NET Entity Data Model I'm wondering if a faster way to find the columns that were changed and build a log like the one above.
You can also accomplish this by putting your database into full recovery mode and then reading the transaction log.
When database is in a full recovery mode then sql server logs all Update, insert and delete (and others such as create, alter, drop..) statements into it's transaction log.
So, using this approach you dont need to make any additinal changes to your application or your database structure.
But you will need 3rd party sql transaction log reader. Red gate has a free solution for sql server 2000 only. If your server is 2005 or higher you would probably want to go with ApexSQL Log
Also, this approach will not be able to audit select statements but it's definately the easiest to implement if you dont really need to audit select queries.
The way I see, you have two options:
Create triggers in the database side, mapping changes in a table by table basis and getting result into a Log table
OR
Having the code handle the changes. You would have a base class with data and with reflection you could iterate all object properties and see what has changed. And then save that into your Log table. Of course, that coding would be on your Data Access Layer.
By the way, if you have a good code structure/architecture, I would go with the second option.
You could have a trigger (AFTER insert/update/deelte) on each table you want to monitor. The beauty is columns_updated() which returns a barbinary value, indicating which columns have been updated.
Here is some snippet of code that I put in each trigger:
IF (##ROWCOUNT = 0) return
declare #AuditType_ID int ,
#AuditDate datetime ,
#AuditUserName varchar(128),
#AuditBitMask varbinary(10)
select #AuditDate = getdate() ,
#AuditUserNAme = system_user,
#AuditBitMask = columns_updated()
-- Determine modification type
IF (exists (select 1 from inserted) and exists (select 1 from deleted))
select #AuditType_ID = 2 -- UPDATE
ELSE IF (exists (select * from inserted))
select #AuditType_ID = 1 -- INSERT
ELSE
select #AuditType_ID = 3 -- DELETE
(record this data to your table of choice)
I have a special function that can decode the bitmask values, but for some reason it is not pasting well here. Message me and I'll email it to you.

Resources