to insert into one table if record is null - sql-server

Hi I have a problem with my stored procedure, I want to insert into three tables which I created a stored procdure to do that, yet I have these two tables called wishlist and general. I want to insert into the wishlist table if the dateaquired row is null but the script I created inserts into the the table regardless, could someone please improve my script so that it does not insert into my wishlist table if my dateaquired row from my general table is not null.
USE [MediaPlayer]
GO
/****** Object: StoredProcedure [dbo].[CreateBooks] Script Date: 12/03/2013 19:05:29 ******/
SET ANSI_NULLS ON
GO
SET QUOTED_IDENTIFIER ON
GO
ALTER PROCEDURE [dbo].[CreateBooks]
-- Add the parameters for the stored procedure here
#Name nvarchar (250),
#FileName nvarchar (250),
#FilePath nvarchar (50),
#FileSize float,
#DateAdded date,
#MediaLength nvarchar (50),
#MediaSubType nvarchar(50),
#MediaType nvarchar(50),
#Thumbnail image,
#DateAquired nvarchar(50),
#BooksName nvarchar (50),
#Publisher nvarchar(50),
#Author nvarchar(50),
#YearOfPublication date,
#Genre nvarchar(50),
#ISBN nvarchar (50),
#Synoposis nvarchar(50),
#SeriesTitle nvarchar(50),
#SeriesNumber nvarchar(50),
#GeneralID int output,
#BookID int output,
#WishListID int output
AS
BEGIN
Insert into dbo.General
(Name, FileName, FilePath, FileSize, DateAdded, MediaLength,
MediaSubType, MediaType, Thumbnail, DateAquired)
values (#Name, #FileName, #FilePath, #FileSize, #DateAdded, #MediaLength,
#MediaSubType, #MediaType, #Thumbnail, #DateAquired)
SET #GeneralID = ##IDENTITY
insert into dbo.Book
(GeneralID, BooksName, Publisher, Author, [Year of publication], Genre,
ISBN, Synoposis,[Series Title],[Series Number])
Values (IDENT_CURRENT('dbo.General'), #BooksName, #Publisher, #Author, #YearOfPublication, #Genre,
#ISBN, #Synoposis, #SeriesTitle, #SeriesNumber)
SET #BookID = ##IDENTITY
Select GeneralID, Name, FileName, FilePath,FileSize,DateAdded,MediaLength,MediaSubType,MediaType, Thumbnail,DateAquired As Wishlist
From General where NULLIF(DateAquired,'')IS Null
Select * from WishLists
select GeneralID, MediaSubType, Name
From General where NOT EXISTS (Select Name from WishLists Where Name =#Name);
insert into Wishlists (generalID ,MediaType, Name)
values ((IDENT_CURRENT('dbo.General')),#MediaSubType, #Name)
SET #WishListID = ##IDENTITY
select * from wishlists
END

I could be wrong but to me it seems you are missing some logic cases.
Your code:
Select GeneralID, Name, FileName, FilePath, FileSize, DateAdded,
MediaLength,MediaSubType,MediaType, Thumbnail, DateAquired As Wishlist
From General where NULLIF(DateAquired,'')IS Null
What this does is selects the named fields from General where DateAcquired is Null. On a side note, I believe (I may be wrong) that if you want all those fields AS Wishlist, you should have them all in parentheses like:
Select (GeneralID, Name, FileName, FilePath, FileSize, DateAdded,
MediaLength,MediaSubType,MediaType, Thumbnail, DateAquired) As Wishlist
From General where NULLIF(DateAquired,'')IS Null
Regardless, once you have this data you aren't doing anything with it, you are moving into a new select query, selecting * from Wishlists. Then you are selecting from General again where the the name is not in the table already. Then you are inserting into Wishlists the current Identity value of dbo.General, #MediaSUbType, #Name.
So all this code is really doing is running some queries, then regardless of those results, is inserting the Identity value of dbo.General, and the status values #MediaSubType and #Name into the table wishlist. In order for all the queries you run before to have an affect on what gets inserted, you need to use cases.
I don't know enough about the tables and things to write up the code you will need, but this answer should give you an idea of how to use CASE WHEN to do what you need.
Hope this helps some and isn't just useless ramblings from me.

If you're just trying to skip the WishList insert based on the #DateAquired being null you can modify your script as below. Please reply if this is not what you want.
BEGIN
Insert into dbo.General(Name, FileName, FilePath, FileSize, DateAdded, MediaLength, MediaSubType, MediaType, Thumbnail, DateAquired)
values (#Name, #FileName, #FilePath, #FileSize, #DateAdded, #MediaLength, #MediaSubType, #MediaType, #Thumbnail, #DateAquired);
SET #GeneralID = scope_identity(); --use this instead of ##IDENTITY
insert into dbo.Book(GeneralID, BooksName, Publisher, Author, [Year of publication], Genre, ISBN, Synoposis,[Series Title],[Series Number])
Values (#GeneralID, #BooksName, #Publisher, #Author, #YearOfPublication, #Genre, #ISBN, #Synoposis, #SeriesTitle, #SeriesNumber)
SET #BookID = scope_identity(); --use this instead of ##IDENTITY
-- you already have the DateAquired from the insert into dbo.General above, so just use the param #DateAquired
if(#DateAquired is null)
begin
insert into Wishlists (generalID, MediaType, Name)
values (#GeneralId, #MediaSubType, #Name)
SET #WishListID = scope_identity(); --use this instead of ##IDENTITY
end
select [InsertedGeneralId] = #GeneralID, [InsertedBookID] = #BookID, [InsertedWishListID] = #WishListID
END

AS
BEGIN
Insert into dbo.General
(Name, FileName, FilePath, FileSize, DateAdded, MediaLength,
MediaSubType, MediaType, Thumbnail, DateAquired)
values (#Name, #FileName, #FilePath, #FileSize, #DateAdded, #MediaLength,
#MediaSubType, #MediaType, #Thumbnail, #DateAquired)
SET #GeneralID = SCOPE_IDENTITY() --<--
insert into dbo.Book
(GeneralID, BooksName, Publisher, Author, [Year of publication], Genre,
ISBN, Synoposis,[Series Title],[Series Number])
Values (IDENT_CURRENT('dbo.General'), #BooksName, #Publisher, #Author,
#YearOfPublication, #Genre, #ISBN, #Synoposis, #SeriesTitle, #SeriesNumber)
SET #BookID = SCOPE_IDENTITY()
Select GeneralID, Name, FileName, FilePath,FileSize,DateAdded,MediaLength
,MediaSubType,MediaType, Thumbnail,DateAquired As Wishlist
From General where NULLIF(DateAquired,'')IS Null
Select * from WishLists
select GeneralID, MediaSubType, Name
From General where NOT EXISTS (Select Name from WishLists Where Name =#Name);
IF (#DateAquired IS NULL)
BEGIN
insert into Wishlists (generalID ,MediaType, Name)
values ((IDENT_CURRENT('dbo.General')),#MediaSubType, #Name)
SET #WishListID = SCOPE_IDENTITY()
END
select * from wishlists
END
Also use SCOPE_IDENTITY() instead of ##IDENTITY as it will return last generate Identity in that column , even if it was created by a concurrent connection.

Related

Stored Procedure with two raiserrors

I use SQL Server.
I want to write a stored procedure that looks if a questionid and employeeid exists (questionid is in the table question, same for employeeid, is in the table employee) AND looks if they not already existing in the table (you don't get a duplicate in the table contentment). I want raiserror's for the user if so.
In my case it is possible to have a duplicate but not on the same DATE!
Contentment table has columns:
employeeid, questionid, date, score
employeeid, questionid, date make up the primary key.
So I want something like this:
1,1, 18-11-2018, null
1,1, 19-11-2018, null
and not something like this:
1,1, 18-11-2018, null
1,1, 18-11-2018, null
I already made something but it is not working (1-1-1900 is a standard date, because it is primary key it needs to be inserted, score is not inserted because the user needs to do this):
#employeeid int,
#questionid int
as
begin
if exists (select * from question where questionid = #questionid)
and exists (select * from employee where employeeid= #employeeid)
begin
insert into contentment (employeeid, questionid, date, score)
values (#employeeid, #questionid, '1-1-1900', null)
end
if (select count(*)
from contentment
where employeeid = #employeeid
and questionid = #questionid
and date = date) = 0
raiserror ('#employeeid or #questionid already existing', 16, 1)
else
raiserror ('#employeeid or #questionid are not existing', 16, 1, null)
end
If you want to date validation you need to provide #date as well. I have created a sample stored procedure with detail that you provide:
DROP PROCEDURE P_ContentmentInsert
GO
CREATE PROCEDURE P_ContentmentInsert
#employeeid int,
#questionid int,
#date DATE
AS
BEGIN
--Check if exists Employee, Question and Contentment
DECLARE #IsEmployeeExists INT=ISNULL((SELECT COUNT(1) FROM employee WHERE employeeid= #employeeid),0)
DECLARE #IsQuestionExists INT=ISNULL((SELECT COUNT(1) FROM question WHERE questionid = #questionid),0)
DECLARE #IsContentmentExists INT=ISNULL((SELECT COUNT(1) FROM contentment WHERE questionid = #questionid and employeeid= #employeeid and [date]=#date),0)
DECLARE #ErrorMessage VARCHAR(1000)=''
--If one of the validation not passed give error message
IF (#IsEmployeeExists=0 OR #IsQuestionExists=0 OR #IsContentmentExists=0)
BEGIN
IF #IsEmployeeExists=0
SET #ErrorMessage='-EmployeeId Not exists'
IF #IsQuestionExists=0
SET #ErrorMessage=#ErrorMessage+'-QuesitonId Not exists'
IF #IsContentmentExists=0
SET #ErrorMessage=#ErrorMessage+'-Contentment already exists'
RETURN
END
--If there is no problem insert it.
IF #IsEmployeeExists>0 and #IsQuestionExists>0 and #IsContentmentExists>0
BEGIN
INSERT INTO contentment (employeeid, questionid, date, score)
VALUES (#employeeid, #questionid, #date, null)
END
END

INSERT INTO View, INSTEAD OF Trigger, Identity, multiple tables?

Here as my tables (Entier = Integer // Caractère long variable = Varchar) :
http://i.stack.imgur.com/lNjyy.jpg
I created a view V_Enterprise(idContact, phoneNumber, email, name, city, adress)
I tried to create a Trigger on that View to allow users to update the view :
CREATE TRIGGER test
ON V_Entreprise
INSTEAD OF INSERT
AS
DECLARE #T_ContactId INT
BEGIN
INSERT INTO T_Contact
SELECT i.phoneNumber, i.email
FROM Inserted i
SELECT #T_ContactId = ##IDENTITY
INSERT INTO T_Entreprise
SELECT #T_ContactId, i.name, i.city, i.adress
FROM Inserted i
END ;
As I expected, it work on simple inserts, but when I add couples of rows at once, it fails because #T_ContactId only contains the first id. Can someone help me to fix it ? I feel like I should use INNER JOIN inserts but I can't figure out how to deal with it.
OK you should never set scalar variables to a value in inserted or delted in a trigger.
Use the OUTPUT clause instead to get your id values back.
This trigger uses a loop over a cursor and won't require any particular uniqueness in the tables;
CREATE TRIGGER test
ON V_Enterprise
INSTEAD OF INSERT
AS
BEGIN
DECLARE #name VARCHAR(32)
DECLARE #city VARCHAR(32)
DECLARE #address VARCHAR(32)
DECLARE #pn VARCHAR(32)
DECLARE #email VARCHAR(32)
DECLARE cursor1 CURSOR FOR
SELECT name,city,address,phoneNumber,email FROM inserted;
OPEN cursor1;
FETCH NEXT FROM cursor1 INTO #name, #city, #address, #pn, #email;
WHILE ##FETCH_STATUS = 0
BEGIN
INSERT INTO T_Contact (phoneNumber,email) VALUES (#pn, #email);
INSERT INTO T_Enterprise (idcontact,name,city,address) VALUES
(##IDENTITY,#name,#city,#address);
FETCH NEXT FROM cursor1 INTO #name, #city, #address, #pn, #email;
END
CLOSE cursor1;
DEALLOCATE cursor1;
END
GO
I don't know if this is a good way to do it, but you can do this without relying on unique columns or using a cursor using the OUTPUT clause for INSERT. This approach does make use of an in-memory temporary table that could get big with large inserts.
DECLARE #Table table( NewID BIGINT);
INSERT INTO T_Contact (PhoneNumber)
OUTPUT Inserted.ID
INTO #Table
SELECT PhoneNumber FROM inserted WHERE
;
INSERT INTO T_Enterprise (Contact_ID)
SELECT NewID FROM #Table;
If phoneNumber and email are a unique key in T_Contact then you could do this:
CREATE TRIGGER test
ON V_Entreprise
INSTEAD OF INSERT
AS
DECLARE #T_ContactId INT
BEGIN
INSERT INTO T_Contact
SELECT i.phoneNumber, i.email
FROM Inserted i
SELECT #T_ContactId = ##IDENTITY
INSERT INTO T_Entreprise
SELECT
(SELECT idContact FROM T_Contact
WHERE phoneNumber = i.phoneNumber AND email = i.email),
i.name, i.city, i.adress
FROM Inserted i
END ;

SQL Server unique row insertion from stored procedure does not work

I have a page on our intranet that submits requests to a perl CGI script. That script in turn calls a stored procedure on a SQL Server DB that check whether an object with certain attributes exists. If it does, the storproc returns the instrument's id, if it doesn't, it creates a new instrument and returns that new instrument's id. The stored procedure creates a transaction, and also uses with (TABLOCKX) in the insert statement. For user-friendliness, when said user submits a bunch of requests simultaneously, the web pages submits the requests to the perl script asynchronously. I thought that when several requests are submitted that all require a new instrument, the first one to hit the storproc would run, lock the table, create the new instrument, release the lock, and then the subsequent calls to the storproc would be aware of the new instrument and use that. What I saw in practice was that there would be a couple of requests that create the new instrument, and the rest would use the most recent one. I tried using a setTimeout on the client side to space out the requests, but that doesn't seem to make a difference. Any ideas as to what I may be doing wrong?
Here is the code of the stored procedure:
CREATE PROCEDURE [dbo].[CreateFutures]
#code varchar(5),
#month int,
#year int,
#currency varchar(3)
AS
BEGIN
SET NOCOUNT ON;
BEGIN TRANSACTION
declare #ticker varchar(7)
declare #yearCode char(1)
declare #res as Table (id int)
declare #n as int
set #yearCode = convert(char(1), #year % 10)
set #ticker = (
select #code + futures + #yearCode
from FuturesMonthCodes
where month = #month
)
insert into #res
select top 1 instrument
from InstrumentFutures // This is a view that joins InstrumentText and InstrumentNumber data
where ticker = #ticker
and code = #code
and month = #month
and year = #year
and currency = #currency
order by instrument
set #n = (select COUNT(id) from #res)
if #n = 0
begin
print 'Creating Future'
declare #id int
declare #stamp datetime
set #stamp = CURRENT_TIMESTAMP
insert into Instrument with (TABLOCKX) (insertTime) values (#stamp)
set #id = (select SCOPE_IDENTITY());
insert into InstrumentText (instrumentId, name, value) values (#id, 'type', 'futures')
insert into InstrumentText (instrumentId, name, value) values (#id, 'ticker', #ticker)
insert into InstrumentText (instrumentId, name, value) values (#id, 'code', #code)
insert into InstrumentText (instrumentId, name, value) values (#id, 'currency',#currency)
insert into InstrumentNumber (instrumentId, name, value) values (#id, 'month', #month)
insert into InstrumentNumber (instrumentId, name, value) values (#id, 'year', #year)
insert into #res (id) values (#id)
end
commit transaction
if #n = 0 --instrument created
select top 1 id, 1 from #res order by id
else --returning existing instrument
select top 1 id, 0 from #res order by id
END
It is more sql problem than perl.
Let say 3 scripts try to run this stored proc in the same time.
The first execute and it is locked the table. The others waited for the table to unlock but they not reread the data when the locking is over so they used old data.
If your stored proc make a select you have to rerun it after the locking are gone.
Regards,

Error when using INSERT INTO with SELECT

I'm trying to compile this stored procedure on MSSQL:
ALTER PROCEDURE [dbo].[sp_Notice_insert]
#type text,
#message text
AS
BEGIN
SET NOCOUNT ON;
INSERT INTO tbl_Notices (id, type, message)
VALUES (NewID(), #type, #message);
DECLARE #noticeid uniqueidentifier;
SET #noticeid = SCOPE_IDENTITY();
INSERT INTO tbl_NoticesInbox (userid, noticeid)
(SELECT id, #noticeid FROM tbl_User WHERE role='Staff');
END
GO
It should insert a "notice" into one table then insert a "notification" into the inbox table, for every 'Staff' user. But when compiling I get this error:
Operand type clash: numeric is incompatible with uniqueidentifier
The 'role' field is nbarchar(10) so I tried N'Staff' as the value too but I get the same error. It doesn't say which types are actually clashing. What am I doing wrong?
The problem is that SCOPE_IDENTITY() returns the last value entered into an IDENTITY column within the current scope, and therefore by default returns a numeric value.
It looks to me like you want to create a NEWID() and use that both to insert the header and related records:
DECLARE #noticeid uniqueidentifier;
SET #noticeid = NEWID();
INSERT INTO tbl_Notices (id, type, message)
VALUES (#noticeid , #type, #message);
INSERT INTO tbl_NoticesInbox (userid, noticeid)
(SELECT id, #noticeid FROM tbl_User WHERE role='Staff');

Inserting batch of rows into two tables in SQL Server 2008

I have a requirement to insert multiple rows into table1 and at the same time insert a row into table2 with a pkID from table1 and a value that comes from a SP parameter.
I created a stored procedure that performs a batch insert with a table valued parameter which contains the rows to be inserted into table1. But I have a problem with inserting the row into table2 with the corresponding Id (identity) from table1, along with parameter value that I have passed.
Is there anyone who implemented this, or what is the good solution for this?
CREATE PROCEDURE [dbo].[oSP_TV_Insert]
#uID int
,#IsActive int
,#Type int -- i need to insert this in table 2
,#dTableGroup table1 READONLY -- this one is a table valued
AS
DECLARE #SQL varchar(2000)
DECLARE #table1Id int
BEGIN
INSERT INTO dbo.table1
(uID
,Name
,Contact
,Address
,City
,State
,Zip
,Phone
,Active)
SELECT
#uID
,Name
,Contact
,Address
,City
,State
,Zip
,Phone
,Active
,#G_Active
FROM #dTableGroup
--the above query will perform batch insert using the records from dTableGroup which is table valued
SET #table1ID = SCOPE_IDENTITY()
-- this below will perform inserting records to table2 with every Id inserted in table1.
Insert into table2(#table1ID , #type)
You need to temporarily store the inserted identity values and then create a second INSERT statement - using the OUTPUT clause.
Something like:
-- declare table variable to hold the ID's that are being inserted
DECLARE #InsertedIDs TABLE (ID INT)
-- insert values into table1 - output the inserted ID's into #InsertedIDs
INSERT INTO dbo.table1(ID, Name, Contact, Address, City, State, Zip, Phone, Active)
OUTPUT INSERTED.ID INTO #InsertedIDs
SELECT
#ID, Name, Contact, Address, City, State, Zip, Phone, Active, #G_Active
FROM #dTableGroup
and then you can have your second INSERT statement:
INSERT INTO dbo.table2(Table1ID, Type)
SELECT ID, #type FROM #InsertedIDs
See the MSDN docs on the OUTPUT clause for more details on what you can do with the OUTPUT clause - one of the most underused and most "unknown" features of SQL Server these days!
Another approach using OUTPUT clause and only one statement for inserting data in both destination tables:
--Parameters
DECLARE #TableGroup TABLE
(
Name NVARCHAR(100) NOT NULL
,Phone VARCHAR(10) NOT NULL
);
DECLARE #Type INT;
--End Of parameters
--Destination tables
DECLARE #FirstDestinationTable TABLE
(
FirstDestinationTableID INT IDENTITY(1,1) PRIMARY KEY
,Name NVARCHAR(100) NOT NULL
,Phone VARCHAR(10) NOT NULL
);
DECLARE #SecondDestinationTable TABLE
(
SecondDestinationTable INT IDENTITY(2,2) PRIMARY KEY
,FirstDestinationTableID INT NOT NULL
,[Type] INT NOT NULL
,CHECK([Type] > 0)
);
--End of destination tables
--Test1
--initialization
INSERT #TableGroup
VALUES ('Bogdan SAHLEAN', '0721200300')
,('Ion Ionescu', '0211002003')
,('Vasile Vasilescu', '0745600800');
SET #Type = 9;
--execution
INSERT #SecondDestinationTable (FirstDestinationTableID, [Type])
SELECT FirstINS.FirstDestinationTableID, #Type
FROM
(
INSERT #FirstDestinationTable (Name, Phone)
OUTPUT inserted.FirstDestinationTableID
SELECT tg.Name, tg.Phone
FROM #TableGroup tg
) FirstINS
--check records
SELECT *
FROM #FirstDestinationTable;
SELECT *
FROM #SecondDestinationTable;
--End of test1
--Test2
--initialization
DELETE #TableGroup;
DELETE #FirstDestinationTable;
DELETE #SecondDestinationTable;
INSERT #TableGroup
VALUES ('Ion Ionescu', '0210000000')
,('Vasile Vasilescu', '0745000000');
SET #Type = 0; --Wrong value
--execution
INSERT #SecondDestinationTable (FirstDestinationTableID, [Type])
SELECT FirstINS.FirstDestinationTableID, #Type
FROM
(
INSERT #FirstDestinationTable (Name, Phone)
OUTPUT inserted.FirstDestinationTableID
SELECT tg.Name, tg.Phone
FROM #TableGroup tg
) FirstINS
--check records
DECLARE #rc1 INT, #rc2 INT;
SELECT *
FROM #FirstDestinationTable;
SET #rc1 = ##ROWCOUNT;
SELECT *
FROM #SecondDestinationTable;
SET #rc2 = ##ROWCOUNT;
RAISERROR('[Test2 results] #FirstDestinationTable: %d rows; ##SecondDestinationTable: %d rows;',1,1,#rc1,#rc2);
--End of test1
Since you need all inserted identity values, look at the output clause of the insert statement: http://msdn.microsoft.com/en-us/library/ms177564.aspx

Resources