I have the following stored procedure, I have two tables here, Movie and Director. Both need to be updated when a new movie has been created or added. How do you handle FKs in stored procedures? The FK in this case is director_id. It is a primary key in Director but a FK in Movie Do I need to specify it twice like so? I am getting conflict errors
CREATE PROCEDURE Book_Book_Creation
#Book_id_arg DECIMAL(12),
#author_id_arg DECIMAL(12),
#type_id_arg DECIMAL(12),
#title_arg VARCHAR(64), -
#copyright_arg DECIMAL(4),
#dauthor_id_2_arg DECIMAL(12),
#author_fname_arg VARCHAR (64),
#author_lname_arg VARCHAR (64)
AS
BEGIN
INSERT INTO Book(Book_id, author_id,genre_id, title, copyright)
VALUES (#author_arg, #author_id_arg, #type_id_arg, #title_arg, #copyright_arg);
INSERT INTO Author(author_id, author_fname, author_lname)
VALUES (#director_id_2_arg, #director_fname_arg, #director_lname_arg)
END;
EXECUTE Book_Book_Creation 32,32,1,'Tempting',2013,32,'Taylor','Mendez';
Basically, you just need to do this:
insert into the Director table first
get the newly inserted ID from that table (assuming that the Director_Id column is your primary key and is of type INT IDENTITY)
then insert into the Movie table with that new ID
Something like this:
DECLARE #NewDirectorID INT
INSERT INTO Director (Director_id, Director_fname, director_lname)
VALUES (#director_id_2_arg, #director_fname_arg, #director_lname_arg)
SELECT #NewDirectorID = SCOPE_IDENTITY()
INSERT INTO Movie (Movie_id, director_id,genre_id, title, copyright)
VALUES (#movie_id_arg, #NewDirectorID, #genre_id_arg, #title_arg, #copyright_arg);
I don't see why you would pass in the director's ID as a parameter - twice!
Try this one -
ALTER PROCEDURE dbo.Movie_Movie_Creation12
#movie_id_arg DECIMAL(12),
#director_id_arg DECIMAL(12),
#genre_id_arg DECIMAL(12),
#title_arg VARCHAR(64),
#copyright_arg DECIMAL(4),
#director_fname_arg VARCHAR (64),
#director_lname_arg VARCHAR (64)
AS BEGIN
INSERT INTO dbo.Director (Director_id, Director_fname, director_lname)
SELECT #director_id_arg, #director_fname_arg, #director_lname_arg
INSERT INTO dbo.Movie (Movie_id, director_id,genre_id, title, copyright)
SELECT #movie_id_arg, #director_id_arg, #genre_id_arg, #title_arg, #copyright_arg
END
EXECUTE dbo.Movie_Movie_Creation12
#movie_id_arg = 32
, #director_id_arg = 32
, #genre_id_arg = 1
, #title_arg = 'Argo'
, #copyright_arg = 2012
, #director_fname_arg = 'Ben'
, #director_lname_arg = 'Affleck'
Related
I have a table Age_cal, Vaccine Information and Medical_information table as follows:
I am trying to write a stored procedure where, I will be able to insert the values into a table named vaccine based on the vaccine_information table.
In med_info table we have DOB, and the age is calculated and stored in age_cal table along with the age grp and medicalID (PK). The vaccine information table has name of vaccine and to which age group it needs to be given to.
So while inserting a value into Vaccine table, I want to give the values medicalId, date it was given on , dose no and facility name as parameters. But I want the vaccine name to be automatically selected based on the age group they belong to.
I have written the following stored procedure, but null value is getting inserted into the vaccine_name. Please help me with inserting the vaccine name using the stored procedure.
ALTER PROCEDURE [dbo].[allot_v]
#MedicalID int,
#VaccineDate date,
#DoseNo int,
#facilityName varchar(20)
AS
BEGIN
DECLARE #injectvaccine varchar(20)
SET #injectvaccine = (SELECT dbo.VaccineInformation.VaccineName
FROM dbo.VaccineInformation, dbo.Age_Cal
WHERE dbo.Age_Cal.grp = dbo.VaccineInformation.grp
AND dbo.Age_Cal.MedicalID = #MedicalID)
INSERT INTO [dbo].[Vaccination]
([MedicalID], [VaccineName], [VaccineDate], [DoseNo], [FacilityName])
VALUES (#MedicalID, #injectvaccine, #VaccineDate, #DoseNo, #facilityName)
END
The stored procedure worked with the following code:
ALTER PROCEDURE [dbo].[allot_v]
#MedicalID int,
#VaccineDate date,
#DoseNo int,
#facilityName varchar(20)
AS
BEGIN
DECLARE #injectvaccine varchar(20)
SET #injectvaccine = (SELECT dbo.VaccineInformation.VaccineName
FROM dbo.VaccineInformation, dbo.Age_Cal
WHERE dbo.Age_Cal.grp = dbo.VaccineInformation.grp
AND dbo.Age_Cal.MedicalID = #MedicalID)
-- Insert statements for procedure here
INSERT INTO [dbo].[Vaccination]
([MedicalID], [VaccineName], [VaccineDate], [DoseNo], [FacilityName])
VALUES (#MedicalID, #injectvaccine, #VaccineDate, #DoseNo, #facilityName)
END
I have a SQL Server table with an identity column, set to autoincrement.
Coded in Perl, the insert in the code below works fine, in the while loop the fetchrow_array() call returns no data in the #row array.
How do I best retrieve the identity value for use in subsequent SQL statements?
my $term_sql = "INSERT INTO reminder_term(site, name, description, localization) OUTPUT \#\#IDENTITY VALUES(?,?,?,?)";
my $t_stmt = $dbh->prepare($term_sql);
...
$t_stmt->execute($site, $name, $description, $localizer);
while (#row = $t_stmt->fetchrow_array()) {
$referential_key = $row[0];
}
Avoid using the ##IDENTITY value since it's unreliable in the presence of triggers.
Given the example table schema...
create table [dbo].[reminder_term] (
[id] int not null identity(1,1),
[site] nvarchar(10),
[name] nvarchar(10),
[description] nvarchar(10),
[localization] nvarchar(10)
);
You can rework your OUTPUT clause slightly you can capture the new id value by way of the special inserted row source...
INSERT INTO reminder_term(site, name, description, localization)
OUTPUT inserted.id
VALUES(?,?,?,?)
I'm using database first approach with EF core and trying to figure out a clean solution to the below problem -
Consider a Student attendance table (irrelevant columns removed) below that stores date of class and allows the student to enter his class rating -
create table Student (
Id int Identity(1, 1) not null,
ClassDate smalldatetime not null,
ClassRatingByStudent varchar(250) not null
)
This is a webapp where school attendance system automatically populates the above table at EOD and then the student (let's say a few days later) is required to add class ratings. When the table is populated by the school attendance system, there is nothing in the ClassRatingByStudent column. Then when the student logs in, he must add the rating.
As you see, ClassRatingByStudent must be null when the school attendance system populates the table and must be not-null when the student saves his changes. One obvious solution is make ClassRatingByStudent column nullable ad handle it in the code but I'm wondering if there is a neater database (or maybe EF) level solution exists or some sort of pattern/architecture guidelines for this type of scenarios?
I don't know but maybe CHECK constraint could help you:
CREATE TABLE TestTable(
ID int NOT NULL IDENTITY,
RatingAllowed bit NOT NULL DEFAULT 0, -- switcher
RatingValue varchar(250),
CONSTRAINT PK_TestTable PRIMARY KEY(ID),
CONSTRAINT CK_TestTable_RatingValue CHECK( -- constraint
CASE
WHEN RatingAllowed=0 AND RatingValue IS NULL THEN 1
WHEN RatingAllowed=1 AND RatingValue IS NOT NULL THEN 1
ELSE 0
END=1
)
)
INSERT TestTable(RatingAllowed,RatingValue)VALUES(0,NULL)
INSERT TestTable(RatingAllowed,RatingValue)VALUES(1,'AAA')
-- The INSERT statement conflicted with the CHECK constraint "CK_TestTable_RatingValue"
INSERT TestTable(RatingAllowed,RatingValue)VALUES(0,'AAA')
INSERT TestTable(RatingAllowed,RatingValue)VALUES(1,NULL)
I found a variant how to check using another table as switcher
CREATE TABLE TableA(
ID int NOT NULL IDENTITY PRIMARY KEY,
StudentID int NOT NULL,
Grade int
)
CREATE TABLE TableB(
StudentID int NOT NULL PRIMARY KEY
)
GO
-- auxiliary function
CREATE FUNCTION GradeIsAllowed(#StudentID int)
RETURNS bit
BEGIN
DECLARE #Result bit=CASE WHEN EXISTS(SELECT * FROM TableB WHERE StudentID=#StudentID) THEN 1 ELSE 0 END
RETURN #Result
END
GO
-- constraint to check
ALTER TABLE TableA ADD CONSTRAINT CK_TableA_Grade CHECK(
CASE dbo.GradeIsAllowed(StudentID) -- then we can use the function here
WHEN 1 THEN CASE WHEN Grade IS NOT NULL THEN 1 ELSE 0 END
WHEN 0 THEN CASE WHEN Grade IS NULL THEN 1 ELSE 0 END
END=1)
GO
-- Tests
INSERT TableB(StudentID)VALUES(2) -- allowed student
INSERT TableA(StudentID,Grade)VALUES(1,NULL) -- OK
INSERT TableA(StudentID,Grade)VALUES(2,5) -- OK
INSERT TableA(StudentID,Grade)VALUES(1,4) -- Error
INSERT TableA(StudentID,Grade)VALUES(2,NULL) -- Error
INSERT TableB(StudentID)VALUES(1) -- add 1
UPDATE TableA SET Grade=4 WHERE StudentID=1 -- OK
UPDATE TableA SET Grade=NULL WHERE StudentID=1 -- Error
I want to execute following sql code.
--1
CREATE TABLE User_Type(
User_Type_ID Integer NOT NULL PRIMARY KEY,
User_Type_Name Char(20 ) NOT NULL
);
CREATE SEQUENCE User_Type_seq;
CREATE TRIGGER User_Type_ID_trg
BEFORE INSERT ON User_Type
FOR EACH ROW
BEGIN
SELECT User_Type_seq.NEXTVAL INTO :new.User_Type_ID
FROM DUAL;
END;
/
When I write it by hand to sql developer, it works. But I need a lot of block like this. So, I've edited those with notepad++ and copy/paste to sql developer again. This time; tables and sequences were created but it gives an error about trigger code (I've taken this routine with select from user_errors)
PLS-00103: Encountered the symbol "end-of-file" when expecting one of the following: ERROR 103
;
Here is the full code which doesn't work. create table and sequences codes works, but the trigger code doesn't.
--1
CREATE TABLE User_Type(
User_Type_ID Integer NOT NULL PRIMARY KEY,
User_Type_Name Char(20 ) NOT NULL
);
CREATE SEQUENCE User_Type_seq;
CREATE TRIGGER User_Type_ID_trg
BEFORE INSERT ON User_Type
FOR EACH ROW
BEGIN
SELECT User_Type_seq.NEXTVAL INTO :new.User_Type_ID
FROM DUAL;
END;
/
--2
CREATE TABLE Users(
User_ID Integer NOT NULL PRIMARY KEY,
User_Password Char(8 ) NOT NULL,
User_Name Char(20 ) NOT NULL,
User_Type_ID Integer NOT NULL
);
CREATE SEQUENCE Users_seq;
CREATE TRIGGER User_ID_trg
BEFORE INSERT ON Users
FOR EACH ROW
BEGIN
SELECT Users_seq.NEXTVAL INTO :new.User_ID
FROM DUAL;
END;
/
--3
CREATE TABLE Admin(
Admin_ID Integer NOT NULL PRIMARY KEY,
E_mail Char(20 ) NOT NULL,
Phone_Number Char(20 ) NOT NULL,
User_ID Integer NOT NULL
);
CREATE SEQUENCE Admin_seq;
CREATE TRIGGER Admin_ID_trg
BEFORE INSERT ON Admin
FOR EACH ROW
BEGIN
SELECT Admin_seq.NEXTVAL INTO :new.Admin_ID
FROM DUAL;
END;
/
--4
CREATE TABLE City(
City_ID Integer NOT NULL PRIMARY KEY,
City_Name Char(20 ) NOT NULL
);
CREATE SEQUENCE City_seq;
CREATE TRIGGER City_ID_trg
BEFORE INSERT ON City
FOR EACH ROW
BEGIN
SELECT City_seq.NEXTVAL INTO :new.City_ID
FROM DUAL;
/
I've searched similar questions but no response to solve my problem.
Sorry for English, thank you...
You're just missing an END; in your final trigger:
CREATE TRIGGER City_ID_trg
BEFORE INSERT ON City
FOR EACH ROW
BEGIN
SELECT City_seq.NEXTVAL INTO :new.City_ID
FROM DUAL;
END;
/
Everything else is already created and compiled successfully.
Incidentally, from 11g you can assign a sequence directly in PL/SQL, withouth having to select from dual:
...
BEGIN
:new.City_ID := City_seq.NEXTVAL;
END;
/
It was solved. Unfortunately, a letter mistake I have not understood yet why, but:
it must be
create trigger
instead of
CREATE TRIGGER
I have a stored procedure that looks up an article based on the article's title. But I also need to increment a column in the same table that counts the number of times the article is viewed.
Trying to be as efficient as possible, I see two possible ways to approach this:
Perform one SELECT to obtain the PK on the target row. Then use that PK to increment the number of views and, finally, another SELECT using the PK to return the article data.
Perform one SELECT to return the article data to my application, and then use the returned PK to make another round trip to the database to increment the number of views.
I know #1 would be pretty fast, but it's three lookups. And #2 requires two round trips to the database. Is there no way to optimize this task?
EDIT Based on feedback, I came up with the following. Thanks for any comments or constructive criticism.
DECLARE #Slug VARCHAR(250) -- Stored procedure argument
-- declare #UpdatedArticle table variable
DECLARE #UpdatedArticle TABLE
(
ArtID INT,
ArtUserID UNIQUEIDENTIFIER,
ArtSubcategoryID INT,
ArtTitle VARCHAR(250),
ArtHtml VARCHAR(MAX),
ArtDescription VARCHAR(350),
ArtKeywords VARCHAR(250),
ArtLicenseID VARCHAR(10),
ArtViews BIGINT,
ArtCreated DATETIME2(7),
ArtUpdated DATETIME2(7)
);
UPDATE Article
SET ArtViews = ArtViews + 1
OUTPUT
INSERTED.ArtID,
INSERTED.ArtUserID,
inserted.ArtSubcategoryID,
INSERTED.ArtTitle,
INSERTED.ArtHtml,
INSERTED.ArtDescription,
INSERTED.ArtKeywords,
INSERTED.ArtLicenseID,
INSERTED.ArtViews,
INSERTED.ArtUpdated,
INSERTED.ArtCreated
INTO #UpdatedArticle
WHERE ArtSlugHash = CHECKSUM(#Slug) AND ArtSlug = #Slug AND ArtApproved = 1
SELECT a.ArtID, a.ArtUserID, a.ArtTitle, a.ArtHtml, a.ArtDescription, a.ArtKeywords, a.ArtLicenseID,
l.licTitle, a.ArtViews, a.ArtCreated, a.ArtUpdated, s.SubID, s.SubTitle, c.CatID, c.CatTitle,
sec.SecID, sec.SecTitle, u.UsrDisplayName AS UserName
FROM #UpdatedArticle a
INNER JOIN Subcategory s ON a.ArtSubcategoryID = s.SubID
INNER JOIN Category c ON s.SubCatID = c.CatID
INNER JOIN [Section] sec ON c.CatSectionID = sec.SecID
INNER JOIN [User] u ON a.ArtUserID = u.UsrID
INNER JOIN License l ON a.ArtLicenseID = l.LicID
Here is a way using the OUTPUT statement (SQL Server 2005 onwards), in a single update statement:
IF OBJECT_ID ('Books', 'U') IS NOT NULL
DROP TABLE dbo.Books;
CREATE TABLE dbo.Books
(
BookID int NOT NULL PRIMARY KEY,
BookTitle nvarchar(50) NOT NULL,
ModifiedDate datetime NOT NULL,
NumViews int not null CONSTRAINT DF_Numviews DEFAULT (0)
);
INSERT INTO dbo.Books
(BookID, BookTitle, ModifiedDate)
VALUES
(106, 'abc', GETDATE()),
(107, 'Great Expectations', GETDATE());
-- declare #UpdateOutput1 table variable
DECLARE #UpdateOutput1 table
(
BookID int,
BookTitle nvarchar(50),
ModifiedDate datetime,
NumViews int
);
-- >>>> here is the update of Numviews and the Fetch
-- update Numviews in Books table, and retrive the row
UPDATE Books
SET
NumViews = NumViews + 1
OUTPUT
INSERTED.BookID,
INSERTED.BookTitle,
INSERTED.ModifiedDate,
INSERTED.NumViews
INTO #UpdateOutput1
WHERE BookID = 106
-- view updated row in Books table
SELECT * FROM Books;
-- view output row in #UpdateOutput1 variable
SELECT * FROM #UpdateOutput1;