Is there any way to track changes from views in MS Sql Server? - sql-server

I'm looking for how to track changes from a view in MS Sql-Server 2012. And, the role of the log-in user is Public. So, it's hard to do it.
For example, Assuming that there is the schema.
CREATE TABLE [dbo].[USER_CREDENTIAL](
[USERID] [nvarchar](48) NOT NULL,
[VALID_FROM] DATETIME NULL,
[EXPIRED_AT] DATETIME NULL,
[CREDENTIAL_ID] int NOT NULL,
CONSTRAINT [UNIQUE_USERID] PRIMARY KEY CLUSTERED( [USERID] ASC)
) ;
CREATE VIEW [VIEW_OF_USER_CREDENTIAL] as
SELECT * FROM dbo.[USER_CREDENTIAL];
It can be only permitted to access the view. The view will be changed when some data is inserted/updated/deleted from the USER_CREDENTIAL table. I will do query to the view.
I saw the document. I tried that, but the target to track should be the data table and the login user is lack of the role. I got the error message.
Object 'foo' is of a data type that is not supported by the CHANGETABLE function. The object must be a user-defined table.
I tried the following. I added the temporary table and the trigger which make changed-data be inserted to the temporary table when the view is changed. But, it was also failed because it was permission denied.
CREATE TABLE dbo.[CHANGES_FROM_A_VIEW] (
[VERSION] [int] IDENTITY(1,1) NOT NULL,
[USERID] [nvarchar](48) NOT NULL,
CONSTRAINT [UNIQUE_VERSION] PRIMARY KEY CLUSTERED ( [VERSION] ASC)
)
CREATE TRIGGER [SOMETHING_CHANGED] ON dbo.[VIEW_OF_USER_CREDENTIAL] ...
ALTER DATABASE database_name
SET CHANGE_TRACKING = ON (CHANGE_RETENTION = 2 DAYS,AUTO_CLEANUP = ON)
ALTER TABLE [CHANGES_FROM_A_VIEW]
ENABLE CHANGE_TRACKING WITH (TRACK_COLUMNS_UPDATED = ON)
SELECT * FROM CHANGETABLE(CHANGES dbo.CHANGES_FROM_A_VIEW, 0) AS C
Anyone knows any way to solve this?

Related

On my local machine, I am unable to drop a table because it doesn't exist or I don't have permission

I am going through college and I'm doing an database class where we're introduced to the SQL Server Management Studio, so I'm very new to all of this. That being said, I've been following along and keeping up, making sure to note his queries... However, I noticed something. In the queries I've been making while keeping notes of my class, I have the error "cannot drop table because it does not exist or you don't have permissions".
Now this is odd to me, as I am the only user of this laptop, I am basically the administrator, I've created the database and tables as per instructions and yet, this issue is popping up and I'm unable to run my queries to see how they work.
Here's a snippet of my code, though I'm not sure how much it'd help...
-- Dropping tables in case they already exist
drop table Movie
drop table Genre
drop table Theater
drop table MovieTheater
-- Create table
create table Movie
(
MovieID int not null constraint PK_Movie primary key,
Title varchar(200) not null,
Budget money null,
ReleaseDate date null,
GenreCode char(1) not null constraint FK_MovieToGenre references Genre(GenreCode),
Released bit not null,
MovieLength decimal(5,2) null
)
create table Genre
(
GenreCode char(1) not null constraint PK_Genre primary key,
GenreDescription varchar(30) not null
)
create table Theater
(
TheaterID int not null constraint PK_Theater primary key,
TheaterName varchar(100) not null,
Address varchar(50) not null,
City varchar(50) not null,
Province char(2) not null,
PostalCode char(7) not null,
PhoneNumber char(13) not null
)
create table MovieTheater
(
MovieID int not null,
TheaterID int not null,
StartDate date not null,
EndDate date null,
constraint PK_MovieTheater primary key (MovieID, TheaterID)
)
I attempted changing the permissions of the database but it wouldn't allow me. Other solutions I've looked up all assume that it's connecting to a database for other purposes (likely work related)
The error is pretty clear. cannot drop table because it does not exist. You can't DROP a table that doesn't exist, just as you can't CREATE a table that already exists.
In SQL Server 2016 and later you can use DROP TABLE IF EXISTS to drop a table if it exists. Since the oldest version in mainstream support is SQL Server 2019, you can reasonably expect that IF EXISTS will work on any new database
DROP TABLE IF EXISTS [dbo].[MyTable0];
In older, now unsupported, versions you had to check explicitly in an IF :
IF OBJECT_ID(N'dbo.MyTable0', N'U') IS NOT NULL
DROP TABLE [dbo].[MyTable0];
The SQL Server DROP TABLE IF EXISTS Examples shows all these options. Use DROP TABLE IF EXISTS unless you really need to work with an unsupported database version.
From your code, I think the problem is you tried to drop the tables before you created them
-- Dropping tables in case they already exist
drop table Movie
drop table Genre
drop table Theater
drop table MovieTheater
To drop the table if only it has already exist, you could try:
IF OBJECT_ID('tableName', 'U') IS NOT NULL
DROP TABLE tableName;
Explain:
The OBJECT_ID function returns the object ID of the specified table. If the table does not exist, it returns NULL. The 'U' parameter indicates that we are looking for a user-defined table.
The IF statement checks whether the table exists by checking the result of the OBJECT_ID function. Simple as that!

How do I create system versioned tables using SSDT in Visual Studio 2019?

I'm trying to create Table with system versioning using Database Project.
Following schema gives error:
SQL70633: System-versioned temporal table must have history table name explicitly provided.
CREATE TABLE [dbo].[Products]
(
[Id] INT NOT NULL PRIMARY KEY,
[Name] NVARCHAR(255) NOT NULL,
[ModifiedBy] NVARCHAR(127) NULL
)
WITH (SYSTEM_VERSIONING = ON)
GO
With explicit name:
SQL71501: Table: [dbo].[Products] has an unresolved reference to Table [history].[ProductsHistory].
SQL46010: Incorrect syntax near ].
CREATE TABLE [dbo].[Products]
(
[Id] INT NOT NULL PRIMARY KEY,
[Name] NVARCHAR(255) NOT NULL,
[ModifiedBy] NVARCHAR(127) NULL
)
WITH (SYSTEM_VERSIONING = ON (HISTORY_TABLE = [history].ProductsHistory))
GO
I've tried both, latest version of Visual Studio 2019 (16.7.5) and latest preview (16.8.0 Preview 3.2).
The syntax in both cases is invalid. Executing the first query in SSMS returns:
Cannot set SYSTEM_VERSIONING to ON when SYSTEM_TIME period is not defined.
The command needs a PERIOD FOR SYSTEM_TIME clause specifying the columns used to specify the validity period of a record.
The documentation examples show how to create a temporal table with a default, automatically named history table :
CREATE TABLE [dbo].[Products]
(
[Id] INT NOT NULL PRIMARY KEY,
[Name] NVARCHAR(255) NOT NULL,
[ModifiedBy] NVARCHAR(127) NULL,
SysStartTime DATETIME2 GENERATED ALWAYS AS ROW START NOT NULL,
SysEndTime DATETIME2 GENERATED ALWAYS AS ROW END NOT NULL,
PERIOD FOR SYSTEM_TIME (SysStartTime,SysEndTime)
)
WITH (SYSTEM_VERSIONING = ON)
In this case, the SysStartTime and SysEndTime are used to specify the validity period of a record.
Similar syntax is needed to create a temporal table with a user-specified table name
create TABLE [dbo].[Products]
(
[Id] INT NOT NULL PRIMARY KEY,
[Name] NVARCHAR(255) NOT NULL,
[ModifiedBy] NVARCHAR(127) NULL,
SysStartTime DATETIME2 GENERATED ALWAYS AS ROW START NOT NULL,
SysEndTime DATETIME2 GENERATED ALWAYS AS ROW END NOT NULL,
PERIOD FOR SYSTEM_TIME (SysStartTime,SysEndTime)
)
WITH (SYSTEM_VERSIONING = ON (HISTORY_TABLE = dbo.ProductHistory))
It's possible to create the history table on a different schema, eg history, as long as that schema exists, BUT it's probably not a good idea unless this solves some specific problem. The current and history table represent the same entity, depend on each other and have specific security restrictions so storing them in separate schemas can make life harder.
To create the table in a separate schema, first create the schema :
CREATE SCHEMA history
Then use the schema in the table definition:
WITH (SYSTEM_VERSIONING = ON (HISTORY_TABLE = history.ProductHistory))

Checking who added data to a table

I have a table in a production server which occasionally has mysterious data occur in it. Is it possible to track the history of adding data to the table to find out why/who/when...? I am reluctant to put a trigger on the table (and it would only help for future checking) because the data volume is usually huge and this might affect performance.
I only have db-owner privilege, not sa privilege.
You can use a default value in a new column and save there the user name with SUSER_SNAME().
Example
CREATE TABLE [dbo].[Table_1](
[id] [INT] NULL,
[name] [NCHAR](100) NULL
) ON [PRIMARY]
GO
ALTER TABLE [dbo].[Table_1] ADD CONSTRAINT [DF_Table_1_name] DEFAULT (SUSER_SNAME()) FOR [name]
GO

Load user extract with stored procedure

I have a ASP MVC web application that is required to load a user extract each day from a file. The users in the database should be updated accordingly: deleted if not in source, updated if in source and target, and created if only in source. While doing this, certain rights should also automatically be given to the users. If there is any error, nothing should happen at all.
First I tried to do this with Entity Framework, but the SaveChanges call takes around two minutes to return, which is a lot for the relatively small amount of users (~140 000).
My idea now is to write a stored procedure that would do the updating. I would pass the list of new users as a parameter. The type of my temporary table:
CREATE TYPE [dbo].[TempUserType] AS TABLE
(
[Uid] NVARCHAR(80) NOT NULL PRIMARY KEY,
[GivenName] NVARCHAR(80) NOT NULL,
[FamilyName] NVARCHAR(80) NOT NULL,
[Email] NVARCHAR(256) NOT NULL,
[GiveRight1] BIT NOT NULL,
[GiveRight2] BIT NOT NULL,
[GiveRight3] BIT NOT NULL
)
The users:
CREATE TABLE [dbo].[User] (
[Id] INT IDENTITY (1, 1) NOT NULL,
[Uid] NVARCHAR (80) NOT NULL,
[GivenName] NVARCHAR (80) NOT NULL,
[FamilyName] NVARCHAR (80) NOT NULL,
[Email] NVARCHAR (256) NOT NULL,
PRIMARY KEY CLUSTERED ([Id] ASC),
UNIQUE NONCLUSTERED ([Uid] ASC)
);
The user roles:
CREATE TABLE [dbo].[UserRole] (
[UserId] INT NOT NULL,
[RoleId] INT NOT NULL,
CONSTRAINT [PK_UserRole] PRIMARY KEY CLUSTERED ([UserId] ASC, [RoleId] ASC),
CONSTRAINT [FK_UserRole_User] FOREIGN KEY ([UserId]) REFERENCES [dbo].[User] ([Id]),
CONSTRAINT [FK_UserRole_Role] FOREIGN KEY ([RoleId]) REFERENCES [dbo].[Role] ([Id])
);
The procedure I am stuck writing:
CREATE PROCEDURE [dbo].[UpdateUsers]
#extractedUsers TempUserType READONLY
AS
BEGIN TRANSACTION
MERGE
[dbo].[User] AS trg
USING
#extractedUsers AS src
ON
(trg.[Uid] = src.[Uid])
WHEN MATCHED THEN
UPDATE SET
trg.GivenName = src.GivenName,
trg.FamilyName = src.FamilyName,
trg.Email = src.Email
WHEN NOT MATCHED BY TARGET THEN
INSERT
([Uid], GivenName, FamilyName, Email)
VALUES
(src.[Uid], src.GivenName, src.FamilyName, src.Email)
WHEN NOT MATCHED BY SOURCE THEN
DELETE;
COMMIT TRANSACTION
My question: is the use of a procedure with merge appropriate in this case to achieve the performance improvement over EF? How can I attribute roles according to the 3 boolean values that are in the source table?
Roles can be hadcoded, meaning I know that the Right1 corresponds to the RoleId 1, Right 2 to RoleId 2 and Right 3 to RoleId3.
After reading the question I want to leave an idea for the solution.
For me it makes more sense to have an IF / ELSE for calling the update or the insert, not using the merge since you need the UserId that you are updating/inserting to add it's role permissions.
You can check if UId exists and if so update the user details, if does not exists then create and keep the new Identity value.
In both cases, when having the user ID add the corresponding permissions according to the boolean values with IF statements.
Pseudo-code:
If UserId exists in UsersTable
Update
Store UserId
Else
Insert
Store created UserId (using the ##IDENTITY)
If bool1 add permission 1
If bool3 add permission 2
If bool3 add permission 3

How to add the column by default in sql server 2005

I have 2 tables called login and Roles.
In the login table, I have these fields:
CREATE TABLE [dbo].[login]
([Id] [int] IDENTITY(1,1) NOT NULL,
[Uname] [varchar](50) COLLATE SQL_Latin1_General_CP1_CI_AS NOT NULL,
[Pwd] [varchar](50) COLLATE SQL_Latin1_General_CP1_CI_AS NOT NULL,
CONSTRAINT [PK_login_1] PRIMARY KEY CLUSTERED([Uname] ASC)
In the roles table I have these fields:
CREATE TABLE [dbo].[Roles]
([Uname] [varchar](50) COLLATE SQL_Latin1_General_CP1_CI_AS NOT NULL,
[Valid] [int] NOT NULL
)
Now what I need is if I fill the uname as some xyz I would like to fill the same uname in the role table automatically in the corresponding field that i makes as foreign key...
You could do this using a Trigger. You may or may not want to execute this code on an Insert and / or Update Further details on triggers can be found here
CREATE TRIGGER trgInsertUserIntoRoles ON Login
FOR Insert
AS
INSERT INTO Roles (UName, Valid)
SELECT Uname, 1
FROM Inserted
Although I think it would be better if you just added the code to insert the username into the Roles table within the Stored Procedure to create the user.
Also, you are aware that you are creating all this on the master database?
A solution is to put a trigger on inserts to the original table.
This microsoft article on triggers will tell you how they work.

Resources