Referential Insert - Stored Procedure - SQL Server 2008 - sql-server-2008

I working in .Net Application. Here in my aspx page, i am having 3 Tabs (i.e) Tab 1, Tab 2,Tab 3. The first Tab contains some Textbox controls, the Second tab contains some combo box controls and same as Third tab contains some controls. I want to save all these three tab controls to THREE different tables in SQL Database. Only one Stored Procedure should be used for this. The PRIMARY KEY of the FIRST table should be saved in the SECOND and THIRD table. ( LIKE, REFERENTIAL INSERT ). Here is my SP...
ALTER PROCEDURE [dbo].[Insert]
(#Name NVARCHAR(50))
AS
BEGIN
SET NOCOUNT ON
DECLARE #TableOnePrimaryKey INT
BEGIN
INSERT INTO TABLEONE(Name)
VALUES (#Name)
SELECT #TableOnePrimaryKey=##IDENTITY
SELECT CAST(##IDENTITY AS INT)
INSERT INTO TABLETWO(TableTwoIDColumn)
VALUES (#TableOnePrimaryKey)
SELECT #TableOnePrimaryKey=##IDENTITY
SELECT CAST(##IDENTITY AS INT)
INSERT INTO TABLETHREE(TableThreeIDColumn)
VALUES (#TableOnePrimaryKey)
SELECT #TableOnePrimaryKey=##IDENTITY
SELECT CAST(##IDENTITY AS INT)
INSERT INTO TABLEFOUR(TableFourIDColumn)
VALUES (#TableOnePrimaryKey)
END
But, its the TABLE ONE Primary key is not got saved in other tables. How to Fix this..

Use scope_identity() instead of ##identity. And you should not assign the value to #TableOnePrimaryKey more than once. If you have an identity column in the other tables you loose the identity you got from the first insert.
ALTER PROCEDURE [dbo].[Insert]
(#Name NVARCHAR(50))
AS
BEGIN
SET NOCOUNT ON
DECLARE #TableOnePrimaryKey INT
INSERT INTO TABLEONE(Name)
VALUES (#Name)
SET #TableOnePrimaryKey=SCOPE_IDENTITY()
INSERT INTO TABLETWO(TableTwoIDColumn)
VALUES (#TableOnePrimaryKey)
INSERT INTO TABLETHREE(TableThreeIDColumn)
VALUES (#TableOnePrimaryKey)
INSERT INTO TABLEFOUR(TableFourIDColumn)
VALUES (#TableOnePrimaryKey)
END

I'd be using scope_identity over identity.
From http://blog.sqlauthority.com/2007/03/25/sql-server-identity-vs-scope_identity-vs-ident_current-retrieve-last-inserted-identity-of-record/
"To avoid the potential problems associated with adding a trigger
later on, always use SCOPE_IDENTITY() to return the identity of the
recently added row in your T SQL Statement or Stored Procedure."
Try that and see if it fixes your issue.
Edit: I meant to mention that I think you need to set the variable differently. Try the following;
SET #TableOnePrimaryKey = (SELECT SCOPE_IDENTITY())
SELECT CAST(#TableOnePrimaryKey AS INT)
etc etc

Related

Update query is not working in my sql procedure.why?

CREATE PROCEDURE Sp_IU_Group(
GID int,
GroupName nvarchar(200),
UserID int,
Status int
)
BEGIN
IF GID=0 THEN
Insert into tblGroup (GroupName,UserID,Status)
values (GroupName,UserID,Status);
else
update tblGroup set GroupName=GroupName,UserID=UserID,Status=Status WHERE GID=GID;
END IF;
END
This query:
update tblGroup set GroupName=GroupName,UserID=UserID,Status=Status WHERE GID=GID
Will update every record in the table... to itself. This matches every record, because this is always true:
WHERE GID=GID
And this updates a value to itself:
GroupName=GroupName
The problem is that you're using the same names for multiple things. Give things different names. Something as simple as this:
CREATE PROCEDURE Sp_IU_Group(
GIDNew int,
GroupNameNew nvarchar(200),
UserIDNew int,
StatusNew int
)
(Or use any other standard you want to distinguish the variables from the database objects, such as prepending them with a special character like an #.)
Then the query can tell the difference:
update tblGroup set GroupName=GroupNameNew,UserID=UserIDNew,Status=StatusNew WHERE GID=GIDNew
(Modify the rest of the stored procedure for the new variable names accordingly, of course.)
Basically, as a general rule of thumb, never rely on the code to "know what you meant". Always be explicit and unambiguous.

SQL check if existing row, if not insert and return it

I'm having a problem with my sql query. I need to insert a data that needs to be checked first if it is existing or not. If the data is existing the sql query must return it, if not insert and return it. I already google it but the result is not quite suitable to my problem. I already read this.
Check if a row exists, otherwise insert
How to 'insert if not exists' in MySQL?
Here is a query that' I'm thinking.
INSERT INTO #tablename(#field, #conditional_field, #field, #conditional_field)
VALUES(
"value of field"
(SQL QUERY THAT CHECK IF THERE IS AN EXISTING DATA, IF NOT INSERT THE DATA and RETURN IT, IF YES return it),
"value of feild",
(SQL QUERY THAT CHECK IF THERE IS AN EXISTING DATA, IF NOT INSERT THE DATA and RETURN IT, IF YES return it)
);
Please take note that the conditional field is a required field so it can't be NULL.
Your tag set is quite weird, I'm unsure you require all the technologies listed but as long as Firebird is concerned there's UPDATE OR INSERT (link) construction.
The code could be like
UPDATE OR INSERT INTO aTable
VALUES (...)
MATCHING (ID, SomeColumn)
RETURNING ID, SomeColumn
Note that this will only work for PK match, no complex logic available. If that's not an option, you could use EXECUTE BLOCK which has all the power of stored procedures but is executed as usual query. And you'll get into concurrent update error if two clients execute updates at one time.
You could split it out into 2 steps
1. run a select statement to retrieve the rows that match your valus. select count (*) will give you the number of rows
2. If zero rows found, then run the insert to add the new values.
Alternatively, you could create a unique index form all your columns. If you try to insert a row where all the values exist, an error will be returned. You could then run a select statement to get the ID for this existing row. Otherwise, the insert will work.
You can check with if exists(select count(*) from #tablename) to see if there is data, but with insert into you need to insert data for all columns, so if there is only #field missing, you cant insert values with insert into, you will need to update the table and go with a little different method. And im not sure, why do you check every row? You know for every row what is missing? Are you comparing with some other table?
You can achieve it using MySQL stored procedure
Sample MySQL stored procedure
CREATE TABLE MyTable
(`ID` int, `ConditionField` varchar(10))
;
INSERT INTO MyTable
(`ID`, `ConditionField`)
VALUES
(1, 'Condition1'),
(1, 'Condition2')
;
CREATE PROCEDURE simpleproc (IN identifier INT,ConditionData varchar(10))
BEGIN
IF (SELECT ID FROM MyTable WHERE `ConditionField`=ConditionData) THEN
BEGIN
SELECT * FROM MyTable WHERE `ConditionField`=ConditionData;
END;
ELSE
BEGIN
INSERT INTO MyTable VALUES (identifier,ConditionData);
SELECT * FROM MyTable WHERE `ConditionField`=ConditionData;
END;
END IF;
END//
To Call stored procedure
CALL simpleproc(3,'Condition3');
DEMO

Stored procedure used to insert datas in two tables

I am using a stored procedure to insert data into two tables. But when I insert the datas the total number of rows in the first table and the second table is different, so it means that sometimes it only inserted the datas in the first table but failed to insert it in the second table. But this case should not happen in my case as the Id of the two tables is related to each other. How can I solve this problem? So that when it will insert datas in both tables or no table if an error occurs so that the number of datas are the same in both the table. My stored procedure is as follows:
Begin
insert into base_table(imgPath,store,apparelType) values (imgPath,store,apparelType);
insert into data_table(cvID,color) values
(LAST_INSERT_ID(),color);
END
To make sure that the 1st query has been successfully executed, the best way would be to add an Identity column in your base_table, then proceed as follows;
DECLARE #LAST_INSERT_ID INT
DECLARE #EXECUTION_OK char(1)
SET #EXECUTION_OK = 1
insert into base_table(imgPath,store,apparelType) values (imgPath,store,apparelType)
SELECT #LAST_INSERT_ID = SCOPE_IDENTITY()
insert into data_table(cvID,color) values (#LAST_INSERT_ID, color)
GO
If exists( Select cvID from data_table where cvID= #LAST_INSERT_ID)
Begin
#EXECUTION_OK = 0
End
SCOPE_IDENTITY: Returns the last identity value inserted into an identity column in the same scope. A scope is a module: a stored procedure, trigger, function, or batch. Therefore, two statements are in the same scope if they are in the same stored procedure, function, or batch.
You can also use the mysql_affected_rows() function to verify that the query has been successful.

create trigger on update to insert old values into other table in MYSQL

I have two tables. The first is my person table which has id, name, creation_date as values, I have a old_person table (id, name, modified_date) which I want to populate the value of person before it actually changes. How would I go about that? I have tried triggers but failed.
I tried as follows
create trigger Person_Trigger Update on person
before update
as
insert into old_person(id, name, modified)
select id, new.name, getdate()
from person
It's giving me syntax errors...Not many Trigger references out there either, a little push would be greatly appreciated!
Have a look at the following example
SQL Fiddle DEMO
IO hade a bit of trouble myself, but from How to Create Triggers in MySQL there was a line that made me think
The first MySQL command we’ll issue is a little unusual:
DELIMITER $$
Our trigger body requires a number of SQL commands separated by a
semi-colon (;). To create the full trigger code we must change
delimiter to something else — such as $$.
Finally, we set the delimiter back to a semi-colon:
DELIMITER ;
So in the SQL Fiddle I changed the query terminator to GO and that seemed top work.
CREATE TABLE person
(
id INT,
name varchar(20)
)
GO
CREATE TABLE old_person
(
id INT,
name varchar(20),
modified DATETIME
)
GO
CREATE TRIGGER Person_Trigger before update on person
FOR EACH ROW BEGIN
INSERT INTO old_person(id, name, modified)
VALUES (OLD.id, OLD.name, NOW());
END
GO
INSERT INTO person VALUES (1,'TADA')
GO
UPDATE person SET name = 'FOO'
GO
Try this:
DELIMITER \\
CREATE TRIGGER `Person_Trigger`
BEFORE UPDATE ON `Person`
FOR EACH ROW
BEGIN
DECLARE date_modified datetime;
SET date_modified = NOW();
INSERT INTO old_person(id, name, modified)
VALUES (OLD.id, OLD.name, date_modified);
END\\
This syntax works for me on my own projects. You may also need to declare delimiters before you begin the trigger. Also if you want to use the NEW keyword it should be AFTER update. Switch to the OLD keyword if you are going to keep using BEFORE update on your trigger.

Retrieve generated ID in MS SQL 2008

I'm converting a ColdFusion Project from Oracle 11 to MS SQL 2008. I used SSMA to convert the DB including triggers, procedures and functions. Sequences were mapped to IDENTITY columns.
I planned on using INSERT-Statements like
INSERT INTO mytable (col1, col2)
OUTPUT INSERTED.my_id
values('val1', 'val2')
This throws an error since the table has a trigger defined, that AFTER INSERT writes some of the INSERTED data to another table to keep a history of the data.
Microsoft writes:
If the OUTPUT clause is specified without also specifying the INTO
keyword, the target of the DML operation cannot have any enabled
trigger defined on it for the given DML action. For example, if the
OUTPUT clause is defined in an UPDATE statement, the target table
cannot have any enabled UPDATE triggers.
http://msdn.microsoft.com/en-us/library/ms177564.aspx
I'm now wondering what is the best practice fo firstly retrieve the generated id and secondly to "backup" the INSERTED data in a second table.
Is this a good approach for the INSERT? It works because the INSERTED value is not simply returned but written INTO a temporary variable. It works in my tests as Microsoft describes without throwing an error regarding the trigger.
<cfquery>
DECLARE #tab table(id int);
INSERT INTO mytable (col1, col2)
OUTPUT INSERTED.my_id INTO #tab
values('val1', 'val2');
SELECT id FROM #tab;
</cfquery>
Should I use the OUTPUT clause at all? When I have to write multiple clauses in one cfquery-block, shouldn't I better use SELECT SCOPE_DENTITY() ?
Thanks and best,
Bernhard
I think this is what you want to do:
<cfquery name="qryInsert" datasource="db" RESULT="qryResults">
INSERT INTO mytable (col1, col2)
</cfquery>
<cfset id = qryResults.IDENTITYCOL>
This seems to work - the row gets inserted, the instead of trigger returns the result, the after trigger doesn't interfere, and the after trigger logs to the table as expected:
CREATE TABLE dbo.x1(ID INT IDENTITY(1,1), x SYSNAME);
CREATE TABLE dbo.log_after(ID INT, x SYSNAME,
dt DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP);
GO
CREATE TRIGGER dbo.x1_after
ON dbo.x1
AFTER INSERT
AS
BEGIN
SET NOCOUNT ON;
INSERT dbo.log_after(x) SELECT x FROM inserted;
END
GO
CREATE TRIGGER dbo.x1_before
ON dbo.x1
INSTEAD OF INSERT
AS
BEGIN
SET NOCOUNT ON;
DECLARE #tab TABLE(id INT);
INSERT dbo.x1(x)
OUTPUT inserted.ID INTO #tab
SELECT x FROM inserted;
SELECT id FROM #tab;
END
GO
Now, if you write this in your cfquery, you should get a row back in output. I'm not CF-savvy so I'm not sure if it has to see some kind of select to know that it will be returning a result set (but you can try it in Management Studio to confirm I am not pulling your leg):
INSERT dbo.x1(x) SELECT N'foo';
Now you should just move your after insert logic to this trigger as well.
Be aware that right now you will get multiple rows back for (which is slightly different from the single result you would get from SCOPE_IDENTITY()). This is a good thing, I just wanted to point it out.
I have to admit that's the first time I've seen someone use a merged approach like that instead of simply using the built-in PK retrieval and splitting it into separate database requests (example).