Send additional data to SQL along with the query? - sql

Simplified case:
Database table Cars
ID (int)
Name (varchar)
Model (varchar)
On this table there is a trigger on UPDATE that will insert something like 'Name of car with ID # has been changed' to another table
From the backend a simple query is run
UPDATE Cars SET Name='My First Car' WHERE ID = 1
What I would like to accomplish is to send an additional value alongside the query so I can alter the trigger to do something like this: 'Name of car with ID # has been changed by user with ID #'
I am using SQL SERVER 2017 (v14)
What are my options?

If using SQL Server 2016 use SESSION_CONTEXT otherwise use CONTEXT_INFO and store the user information before calling the update.
Then, either way, within the trigger extract the user information and use it for your logging information.

The information available to a trigger is limited to information in the table that the trigger is on, or information which is available to the system in general.
Since the user is not in the original table, you need the data to be available to the system in general. There's quite a lot of data like this. For example, you can know the name of the database you're in, using the built in db_name() function.
There is some information the system can give you based on the connection properties. For example, the orignal_login() function will give you the name of the user who actually made the connection to the SQL Server.
If you have a table of Users somewhere, that has, say, (UserName, UserId),and if the user names in that table match the actual login names of the sql server logins, then you could use the result of original_login() to join onto that table, pull out the username, and now you have the user id:
create trigger t on cars after update as begin
set nocount on;
insert myloggingtable(Message, EntryDateTime)
select concat('car with id ', inserted.id, ' was updated by ', u.UserId), getdate()
from inserted i
join Users u on u.UserName = original_login()
end
As mentioned in the comments, since your application is connecting as an application specific account, this of course won't help you. In which case you could try context_info

Related

How to make only one user can insert data at a time in SQL Server?

I have a SQL Server database, multi-user can insert to it.
But for many reasons, I want only 1 user can insert at a time.
Example:
User 1 want to insert 100 record, while user 1 is inserting (100 record not saved to table). Other user can not insert to the table.
I have thought to use a flag, but I want to find another way.
Is there any SQL statement that can do that?
Thanks for reading!
It seems that you need to use
INSERT INTO TABLE with (rowlock)
Read the following post to have a better understanding.
Using ROWLOCK in an INSERT statement (SQL Server)
Updated
SQL supports us to handle 1 record at a time e. And your case is to want multiple records to handle serialized format.
I think the best you put into the temp table, there is a window service running real-time (Background service: using quartz job or hangfire): insert and delete then the temporary table with a column named IsInserted.
For that purpose you can used table lock or row loack concept.
ALTER TABLE Table_name SET (LOCK_ESCALATION = < TABLE | AUTO | DISABLE > –One of those options)
For more details you can also visit this link
locking in SQL Server

How can I know what changed in a SQL database for a certain time?

I have a system to manage printers of a company and I need to understand how the workflow between the Website and database works by knowing what is added/changed in the database with each interaction of the user. Is there a way to find or create some kind of log for a database or even the entire SQL Server that can show me what I need?
you can use extended events feature in some case :
https://learn.microsoft.com/en-us/sql/relational-databases/extended-events/quick-start-extended-events-in-sql-server?view=sql-server-2017
I think what you're looking for are triggers.
You can make tables to log the currently updated or changed data and use triggers to automatically feed data in the log table on any change
CREATE OR REPLACE TRIGGER [trigger_name]
BEFORE DELETE OR INSERT OR UPDATE ON [table_name]
FOR EACH ROW
WHEN [some condition]
DECLARE
[variable declaration]
BEGIN
[create an entry in the log table here]
END;
You can use NEW and OLD keywords to refer to the data (new referring to the most recent update of data)
Just for the record, a tool for that exactly purpose exists and is installed together with SQL Server, it is called SQL Server Profiler.

Cant add table to database via linked server

I am trying to add a table to an existing database in a linked server, but getting:
The object name 'name.mycompany.com.DataAg.dbo.Secure30AWSMap' contains more than the maximum number of prefixes. The maximum is 2.
What am I doing wrong? Based on this link it seems like I cant use a 4 part name. However this link sounds like it might be a workaround using an Exec statement. Is there a way to solve this without messing with the linked servers DDL though?
create table [name.mycompany.com].[DataAg].[dbo].[Secure30AWSMap]([servername] [nvarchar](50),[username] [nvarchar](50),[full_name][nvarchar](50),[awscoid][nvarchar](50))
if RPC is configured true for linked server, it can help.
EXEC('create table [DataAg].[dbo].[Secure30AWSMap]([servername] [nvarchar](50),[username] [nvarchar](50),[full_name][nvarchar](50),[awscoid][nvarchar](50))') AT [name.mycompany.com]
You need to go to your linked server and manually create the table and then try this.
INSERT INTO [linkedserver].[database].[dbo].[table]
SELECT servername, username, fullname, awscoid
FROM [database].[dbo].[table]
WHERE ID = userID;

Customized Table Names in Sql Server

I have a table called Table 1. I'm trying to create an after-insert trigger for a Table 1; whereby, whenever a user enters a record, the trigger will create a new table named after the record that triggered its creation.
Please help, I'm using SQL Server 2008
This sounds super non-relational-database-design-ish. I would heavily advise against this in almost every case. And I say "almost" only to allow for artistic freedom of development, I can't think of a single case where this would be appropriate.
That said, if you do in fact want this, you can use dynamic SQL to create a table.
You can build the SQL in your trigger, but basically you want something like:
EXEC 'CREATE TABLE ' + #tableName + ' (ID INT IDENTITY(1,1))';
Of course, the columns are up to you, but that should get you started.
But while we're at it, what you should (probably) be doing is using a single table with a one-to-many relationship to the table on which your trigger is currently assigned.
For instance, if you have a table Users with a column for email and you're looking to create a table for each user's favorites on your website, you should instead consider adding an identity column for user IDs, then reference that in a single UserFavorites table that has UserId and PostId columns, and the appropriate foreign keys implemented.

Create table - dynamic name of table

I've got some problem with creating a query in Access.
I'd like to have a query, which after running will ask about name and surname of new record in table Buyers, and then create new table for this buyer(e.g. for John Smith - called SmiJoh - 3 letters from Surname and 3 from Name).
Any ideas how to do that without VBA, using only SQL?
PS. If there's no chance to do all of this using SQL is there any possibility to do only query creating a table with this name?
You asked whether you can do something like this with SQL in Access, where table_name is a value you supply when the statement executes.
CREATE TABLE [table_name](
id COUNTER CONSTRAINT pkey PRIMARY KEY,
date_field DATETIME);
No, Access' database engine will not allow you to use a parameter for the table name.
But like the comments you've received, I encourage you to re-consider your plan to create a separate table for each buyer. That will be a complicated mess to build and maintain.
Use a single table to hold data from all buyers. Include a field to identify the buyer. Then use a query which retrieves only the rows where the buyer_id field matches the current user's buyer_id. Build a form with that query as its record source. Here is a sample table where the uname field holds the Windows account name.
id uname time_only
5018 fred 7:00:00 AM
5063 hans 2:00:00 AM
5072 hans 3:00:00 AM
With Dev Ashish's fOSUserName() function (Get Login name), this query returns only the rows where uname matches my Windows user name (hans).
SELECT d.id, d.uname, d.time_only
FROM discardme AS d
WHERE d.uname = fOSUserName();
I created a form based on that query which includes a text box bound to uname with these properties on the Data tab of its property sheet: Default Value =fOSUserName(); and Enabled No. If you don't want the user to even see the uname value, set Visible No on the Format tab.
You would still need to lock down the application to prevent the users from opening the table directly. But that would also be required with your original scheme to create a separate table for each buyer.
A similar approach could work if you have set up ULS (user-level security), which requires an MDB format db; it's not supported in the newer ACCDB db format. In that case the, the VBA CurrentUser() function will return the name of the Access security user name. Change the query accordingly:
SELECT d.id, d.uname, d.time_only
FROM discardme AS d
WHERE d.uname = CurrentUser();
Note that without ULS, CurrentUser() will give you the name of the default user account, Admin.
Finally consider your security requirements. Doing this with Access' Jet/ACE for data storage will amount to offering guidance to cooperative users. However, whether or not you adopt ULS, you can't absolutely prevent a user from viewing any of the data. If your security requirements are more stringent, move the data storage to a client-server database (SQL Server, for example). You can still use your Access application as a front-end by substituting ODBC links to the server tables for the existing native Jet/ACE tables.
Have you tried using an EXEC
DECLARE #Name VARCHAR(6)
SET #Name = 'SMIJOH' --Query your table name
EXEC ('CREATE TABLE ' + #Name + ' (ID INT IDENTITY(1,1), etc....)')