I have the following 2 tables defined in a SQL Server database:
create table Project
(
Id int,
Name varchar(100)
IdIdentity int
)
create table ProjectHistory
(
Id int,
Name varchar(100)
IdIdentity int,
DtChange datetime
)
When I updated "Project" I would like to insert new record to table ProjectHistory with setting DtChange to actual date.
I use Entity Framework 4.1. Can you suggest me some way how this can be accomplished?
There is possibility change sql that generate Entity Framework when saving changes.
The conventional way would be to use an update trigger on the project table.
CREATE TRIGGER trgProjectUpdate
ON Project
AFTER UPDATE
AS
BEGIN
insert ProjectHistory (id, name, dtchange)
select id, name, GETDATE() from deleted
END
Related
I am facing this weird problem and spent several hours. Little help would be greatly appreciated.
This is an ASP.NET MVC app. For simplicity, I have two SQL tables, Employee (ID, Name, JoiningDate) and Benefits (ID, EmployeeID). Both IDs are identity colums. When a new employee joins the company, an entry is created in the Employee table as well as Benefits table.
The stored procedure looks like this
alter procedure usp_CreateEmployee
#Name nvarchar(100),
#JoiningDate datetime
as
declare #lastIdentity int
insert into Employee(Name, JoiningDate) values(#Name, #JoiningDate)
select #lastIdentity = ident_current('Employee')
insert into Benefits(EmployeeID) values(#lastIdentity)
C# side I am using Dapper
var parameters = new DynamicParameters();
parameters.Add("#Name", name);
parameters.Add("#JoiningDate", joiningDate);
affectedRows = connection.Execute("usp_CreateEmployee", parameters, null, commandType: CommandType.StoredProcedure);
When I execute the stored procedure in SSMS, everything works perfect. (ident_current returns the last inserted id). However, when the user interface creates employee (through razor page), a NULL gets inserted as EmployeeID in Benefits table. Employee table shows correct Employee ID.
Doesn't look like a SQL problem. Is there anything wrong with my code? Could this be Dapper related (though I dont think so)?
I think the problem was on the "ident_current". Please refer here: https://sqlperformance.com/2014/01/t-sql-queries/ident-current
alternatively, you may try below sql script.
alter procedure usp_CreateEmployee
#Name nvarchar(100),
#JoiningDate datetime
as
declare #lastIdentity int
insert into Employee(Name, JoiningDate) values(#Name, #JoiningDate)
select top(1) #lastIdentity=ID from Employee where Name=#Name
order by ID desc
insert into Benefits(EmployeeID) values(#lastIdentity)
I want to create a SQL Server database project where a new database is created. Here is the structure of my project :
SQL Server Database Project Structure
Basically, I want 4 tables, so I have added 4 .sql scripts for them.
CREATE TABLE [dbo].[MovieActors]
(
[Id] INT NOT NULL PRIMARY KEY,
[ActorID] INT NOT NULL,
[MovieID] INT NOT NULL
)
CREATE TABLE [dbo].[Actors]
(
[Id] INT NOT NULL PRIMARY KEY,
[Name] NVARCHAR(50) NOT NULL,
[Bio] NVARCHAR(MAX) NULL,
[Sex] NCHAR(10) NOT NULL,
[DOB] DATE NULL
)
CREATE TABLE [dbo].[Movies]
(
[Id] INT NOT NULL PRIMARY KEY,
[Name] NVARCHAR(50) NOT NULL,
[Year of Release] SMALLINT NULL,
[PLOT] NVARCHAR(MAX) NULL,
[Poster] NVARCHAR(MAX) NULL,
[ProducerId] int FOREIGN KEY REFERENCES Producers(Id)
)
CREATE TABLE [dbo].[Producers]
(
[Id] INT NOT NULL PRIMARY KEY,
[Name] NVARCHAR(50) NOT NULL,
[Bio] NVARCHAR(MAX) NULL,
[Sex] NCHAR(10) NULL,
[DOB] DATE NULL
)
And I have also added pre-deployment (for deleting existing data) and post-deployment scripts (for seeding initial data).
Pre-Deployment:
DROP TABLE [dbo].MovieActors
DROP TABLE [dbo].Movies
DROP TABLE dbo.Actors
DROP TABLE dbo.Producers
Post-Deployment:
INSERT INTO dbo.Actors (Id, Name, Sex)
VALUES (1, 'Actor1', 'MALE')
INSERT INTO dbo.Producers (Id, Name, Sex)
VALUES (1, 'Producer1', 'MALE')
INSERT INTO dbo.Movies (Id, Name, ProducerId)
VALUES (1, 'Movie1', 1)
INSERT INTO dbo.MovieActors (Id, MovieID, ActorID)
VALUES (1, 1, 1)
But everytime I am building the project it does not get deployed - only showing build succeeded
Moreover, where is the database creation script.
When I right clicked on publish and chose option to generate script , only pre and post deployment scripts were in the main script. -
Right Click Project -> Publish option
This is the script that gets generated -
/*
Deployment script for MoviesDatabase
This code was generated by a tool.
Changes to this file may cause incorrect behavior and will be lost if
the code is regenerated.
*/
GO
SET ANSI_NULLS, ANSI_PADDING, ANSI_WARNINGS, ARITHABORT, CONCAT_NULL_YIELDS_NULL, QUOTED_IDENTIFIER ON;
SET NUMERIC_ROUNDABORT OFF;
GO
:setvar DatabaseName "MoviesDatabase"
:setvar DefaultFilePrefix "MoviesDatabase"
:setvar DefaultDataPath "C:\Users\viiye\AppData\Local\Microsoft\VisualStudio\SSDT"
:setvar DefaultLogPath "C:\Users\viiye\AppData\Local\Microsoft\VisualStudio\SSDT"
GO
:on error exit
GO
/*
Detect SQLCMD mode and disable script execution if SQLCMD mode is not supported.
To re-enable the script after enabling SQLCMD mode, execute the following:
SET NOEXEC OFF;
*/
:setvar __IsSqlCmdEnabled "True"
GO
IF N'$(__IsSqlCmdEnabled)' NOT LIKE N'True'
BEGIN
PRINT N'SQLCMD mode must be enabled to successfully execute this script.';
SET NOEXEC ON;
END
GO
USE [$(DatabaseName)];
GO
/*
Pre-Deployment Script Template
--------------------------------------------------------------------------------------
This file contains SQL statements that will be executed before the build script.
Use SQLCMD syntax to include a file in the pre-deployment script.
Example: :r .\myfile.sql
Use SQLCMD syntax to reference a variable in the pre-deployment script.
Example: :setvar TableName MyTable
SELECT * FROM [$(TableName)]
--------------------------------------------------------------------------------------
*/
Drop table [dbo].MovieActors
Drop table [dbo].Movies
Drop table dbo.Actors
Drop table dbo.Producers
GO
GO
/*
Post-Deployment Script Template
--------------------------------------------------------------------------------------
This file contains SQL statements that will be appended to the build script.
Use SQLCMD syntax to include a file in the post-deployment script.
Example: :r .\myfile.sql
Use SQLCMD syntax to reference a variable in the post-deployment script.
Example: :setvar TableName MyTable
SELECT * FROM [$(TableName)]
--------------------------------------------------------------------------------------
*/
INSERT INTO dbo.Actors (Id,Name,Sex)
values (1,'Actor1','MALE')
INSERT INTO dbo.Producers (Id,Name,Sex)
values (1,'Producer1','MALE')
INSERT INTO dbo.Movies (Id,Name,ProducerId)
values (1,'Movie1',1)
Insert into dbo.MovieActors (Id,MovieID,ActorID)
values (1,1,1)
GO
GO
PRINT N'Update complete.';
GO
As can be seen , the create tables script are not there in this script which is used for publish .Why? and is this the correct way to go about requirement where I need to create tables from scratch?
This is are the simplified steps how the project is published:
Project is built and DACPAC file is the output of this step
DACPAC is compared with the destination database and the publish script is generated
Publish script is executed against the destination database
The problem in your case is that at the time when the 2nd step is executed the tables existed at the destination database, so their creation was not included to the publish script. Pre deployment script is executed at the step 3!
Basically what you need to do is not to drop tables. Just TRUNCATE them in pre script and populate in the post. Or simply use MERGE statements in the post script. You can use generate-sql-merge procedure that would generate MERGE statement for needed table.
I'm using SQLite, which doesn't support adding a constraint to an existing table.
So I can't do something like this (just as an example):
ALTER TABLE [Customer]
ADD CONSTRAINT specify_either_phone_or_email
CHECK (([Phone] IS NOT NULL) OR ([Email] IS NOT NULL));
Are there any workarounds for this scenario?
I know:
I can add a constraint for a new table, but it isn't new (and it's generated by my ORM, EF Core)
I can do a "table rebuild" (rename table, create new one, copy old data, drop temp table) but that seems really complex
Ideas
Can I somehow make a copy of the table into a new table, with some schema changes?
Or "get" the schema somehow, and edit it in a SQL script, then add a table with that schema?
To make a copy of a table with some schema changes, you have to do the creation and the copying manually:
BEGIN;
CREATE TABLE Customer_new (
[...],
CHECK ([...])
);
INSERT INTO Customer_new SELECT * FROM Customer;
DROP TABLE Customer;
ALTER TABLE Customer_new RENAME TO Customer;
COMMIT;
To read the schema, execute .schema Customer in the sqlite3 command-line shell.
This gives you the CREATE TABLE statement, which you can edit and execute.
To change the table in place, you can use a backdoor.
First, read the actual table definition (this is the same as what you would get from .schema):
SELECT sql FROM sqlite_master WHERE type = 'table' AND name = 'Customer';
Add your CHECK constraint to that string, then enable write access to sqlite_master with PRAGMA writable_schema=1; and write your new table definition into it:
UPDATE sqlite_master SET sql='...' WHERE type='table' AND name='Customer';
Then reopen the database.
WARNING: This works only for changes that do not change the on-disk format of the table. If you do make any change that changes the record format (such as adding/removing fields, or modifying the rowid, or adding a constraint that needs an internal index), your database will blow up horribly.
Is it possible to add an ON DELETE CASCADE to a table after it has been created?
My schema is as follows:
CREATE TABLE skills(name varchar, skill varchar, level int, foreign key(name) references runners(name), primary key(name, skill));
And I would like to cascade if the foreign key is deleted.
SQLite's ALTER TABLE command cannot do what you want.
However, it is possible to bypass the SQL interpreter and change the internal table definition directly.
SQLite stores table definitions as a textual copy of the CREATE TABLE command in its sqlite_master table; check out the result of this query:
SELECT sql FROM sqlite_master WHERE type='table' AND name='skills';
Add your cascade specification to that string, then enable write access to sqlite_master with PRAGMA writable_schema=1; and write your new table definition into it:
UPDATE sqlite_master SET sql='...' WHERE type='table' AND name='skills';
Then reopen the database.
WARNING: This works only for changes that do not change the on-disk format of the table. If you do make any change that changes the record format (such as adding/removing fields, or modifying the rowid), your database will blow up horribly.
Is it possible to add an ON DELETE CASCADE to a table after it has been created?
My schema is as follows:
CREATE TABLE skills(name varchar, skill varchar, level int, foreign key(name) references runners(name), primary key(name, skill));
And I would like to cascade if the foreign key is deleted.
SQLite's ALTER TABLE command cannot do what you want.
However, it is possible to bypass the SQL interpreter and change the internal table definition directly.
SQLite stores table definitions as a textual copy of the CREATE TABLE command in its sqlite_master table; check out the result of this query:
SELECT sql FROM sqlite_master WHERE type='table' AND name='skills';
Add your cascade specification to that string, then enable write access to sqlite_master with PRAGMA writable_schema=1; and write your new table definition into it:
UPDATE sqlite_master SET sql='...' WHERE type='table' AND name='skills';
Then reopen the database.
WARNING: This works only for changes that do not change the on-disk format of the table. If you do make any change that changes the record format (such as adding/removing fields, or modifying the rowid), your database will blow up horribly.