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

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.

Related

How to create a Trigger within sql

I have been trying to create a Trigger, however my attempts have been unsuccessful. I seem to be getting an error (#1064), which I have no solution for. Can somebody explain or demonstrate any faults in the syntax.
Let me specify:
I have delivery_id as primary key in delivery table,
I also have delivery_id as a foreign key in entry_log table.
By comparing both id's(if true), will return a text referring to the output of the bit (either 0 or 1)
DELIMITER //
DROP TRIGGER IF EXISTS entry_trigger//
CREATE TRIGGER entry_trigger BEFORE INSERT ON entry_log
FOR EACH ROW
BEGIN
DECLARE #xentry VARCHAR(45)
DECLARE #inta bit
SET #inta = SELECT allowed
FROM delivery
WHERE delivery.delivery_id = entry_log.delivery_id;
CASE
when #inta = 0 then #xentry = 'Acces Denied'
when #inta = 1 then #xentry = 'Acces Allowed'
END CASE
INSERT INTO entry_log(entry_time,access_allowed) VALUES(now(),#xentry);
END
//
This is assuming that you use MySQL. In the body of the trigger you use
WHERE delivery.delivery_id = entry_log.delivery_id;
I think you want to compare to the entry_log entry that the trigger is running on, right? In that case you must use this syntax:
WHERE delivery.delivery_id = NEW.delivery_id;
see here for more examples.
UPDATE
I see that also you try to do an INSERT INTO entry_log within the TRIGGER. This will of course not work, because you would create an infinite recursive loop. Within the
body of the trigger you can do unrelated table access, but not into the table you are inserting. You can change the values to be inserted by the trigger by setting NEW.xyz = whatever
UPDATE 2
I doubt, that your CASE statement is correct. At least it must end with END CASE. You can use IF here, since you don't have many cases to address. If you must use CASE this post might help you: MYSQL Trigger set datetime value using case statement
UPDATE 3
I am not sure, but I think you need brackets around the variable setting statement. try this trigger definition:
DELIMITER //
DROP TRIGGER IF EXISTS entry_trigger//
CREATE TRIGGER entry_trigger BEFORE INSERT ON entry_log
FOR EACH ROW
BEGIN
SET #inta = (SELECT allowed
FROM delivery
WHERE delivery.delivery_id = NEW.delivery_id);
SET NEW.access_allowed = #inta;
SET NEW.entry_time = NOW();
END
//
Note, that this is written out of my head, so beware of syntax errors in my script.

In MySql Stored Procedure updating more than one time

In MySql
UPDATE `inventoryentry` SET `Status` = 1 WHERE `InventoryID`=92 AND `ItemID`=28;
It successfully update only one row , where inventoryID = 92 and itemID=28 , the following message displayed.
1 row(s) affected
when I put this on stored procedure, as follow
CREATE DEFINER=`root`#`localhost` PROCEDURE `Sample`(IN itemId INT, IN itemQnty
DOUBLE, IN invID INT)
BEGIN
DECLARE crntQnty DOUBLE;
DECLARE nwQnty DOUBLE;
SET crntQnty=(SELECT `QuantityOnHand` FROM `item` WHERE id=itemId);
SET nwQnty=itemQnty+crntQnty;
UPDATE `item` SET `QuantityOnHand`=nwQnty WHERE `Id`=itemId;
UPDATE `inventoryentry` SET `Status` = 1 WHERE `InventoryID`=invID AND
`ItemID`=itemId;
END$$
calling stored procedures
CALL Sample(28,10,92)
It update all the status = 1 in inventoryentry against InventoryID (i.e. 92) ignoring ItemID, instead of updating only one row. The following message displayed!
5 row(s) affected
Why Stored procedure ignoring itemID in update statement ? or Why Stored procedure updating more than one time? But without Stored procedure it working fine.
You need to use different variable names apart from your field name, also use the table name with the columns for better understanding like i used in following:
CREATE DEFINER=`root`#`localhost` PROCEDURE `Sample`(IN itemID INT, IN itemQnty
DOUBLE, IN invID INT)
BEGIN
DECLARE crntQnty DOUBLE;
DECLARE nwQnty DOUBLE;
SET crntQnty=(SELECT `QuantityOnHand` FROM `item` WHERE id=itemID);
SET nwQnty=itemQnty+crntQnty;
UPDATE `item` SET `QuantityOnHand`=nwQnty WHERE `QuantityOnHand`.`Id`=itemID;
UPDATE `inventoryentry` SET `Status` = 1 WHERE `InventoryID`=invID AND
`inventoryentry`.`ItemID`=itemID;
END$$
because of
update inventoryentry ... WHERE ... AND `ItemID`=itemId
You are saying that column itemid should be the same as column itemid which is always true
Try renaming your parameter to a name that differs from your column name
Using same names for columns and variable names has some issues.
Semantics of Stored procedure code is not checked at CREATE time. At runtime, undeclared variables are detected, and an error message is generated for each reference to an undeclared variable. However, SP's seem to believe any reference denotes a column, even though the syntactic context excludes that. This leads to a very confusing error message in case the procedure.
Your column name ItemID matches with input variable name itemId, and hence is the issue.
Please look at my answer to a similar query here.

Referential Insert - Stored Procedure - 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

Split table into two tables with foreign keys

I have one table: drupal.comments, with amongst others, the columns:
cid: primary key
uid: foreign key to users table, optional
name: varchar, optional
email: varchar, optional
The description says: UID is optional, if 0, comment made by anonymous; in that case the name/email is set.
I want to split this out into two tables rails.comments and rails.users, where there is always a user:
id: primary key
users_id: foreign key, always set.
So, for each drupal.comment, I need to create either a new user from the drupal.comments.name/drupal.comments.email and a rails.comment where the rails.comment.users_id is the ID of the just created user.
Or, if username/email already exists for a rails.user, I need to fetch that users_id and use that on the new comment record as foreign key.
Or, if drupal.comment.uid is set, I need to use that as users_id.
Is this possible in SQL? Are queries that fetch from one source, but fill multiple tables possible in SQL? Or is there some (My)SQL trick to achieve this? Or should I simply script this in Ruby, PHP or some other language instead?
You could do this with a TRIGGER.
Here's some pseudo-code to illustrate this technique:
DELIMITER $$
DROP TRIGGER IF EXISTS tr_b_ins_comments $$
CREATE TRIGGER tr_b_ins_comments BEFORE INSERT ON comments FOR EACH ROW BEGIN
DECLARE v_uid INT DEFAULT NULL;
/* BEGIN pseudo-code */
IF (new.uid IS NULL)
THEN
-- check for existing user with matching name and email address
select user_id
into v_uid
from your_user_table
where name = new.name
and email = new.email;
-- if no match, create a new user and get the id
IF (v_uid IS NULL)
THEN
-- insert a new user into the user table
insert into your_user_table ...
-- get the new user's id (assuming it's auto-increment)
set v_uid := LAST_INSERT_ID();
END IF;
-- set the uid column
SET new.uid = v_uid;
END IF;
/* END pseudo-code */
END $$
DELIMITER ;
I searched further and found that, apparently, it is not possible to update/insert more then one table in a single query in MySQL.
The solution would, therefore have to be scripted/programmed outside of SQL.

MySQL Trigger - Storing a SELECT in a variable

I have a trigger in which I want to have a variable that holds an INT I get from a SELECT, so I can use it in two IF statements instead of calling the SELECT twice. How do you declare/use variables in MySQL triggers?
You can declare local variables in MySQL triggers, with the DECLARE syntax.
Here's an example:
DROP TABLE IF EXISTS foo;
CREATE TABLE FOO (
i SERIAL PRIMARY KEY
);
DELIMITER //
DROP TRIGGER IF EXISTS bar //
CREATE TRIGGER bar AFTER INSERT ON foo
FOR EACH ROW BEGIN
DECLARE x INT;
SET x = NEW.i;
SET #a = x; -- set user variable outside trigger
END//
DELIMITER ;
SET #a = 0;
SELECT #a; -- returns 0
INSERT INTO foo () VALUES ();
SELECT #a; -- returns 1, the value it got during the trigger
When you assign a value to a variable, you must ensure that the query returns only a single value, not a set of rows or a set of columns. For instance, if your query returns a single value in practice, it's okay but as soon as it returns more than one row, you get "ERROR 1242: Subquery returns more than 1 row".
You can use LIMIT or MAX() to make sure that the local variable is set to a single value.
CREATE TRIGGER bar AFTER INSERT ON foo
FOR EACH ROW BEGIN
DECLARE x INT;
SET x = (SELECT age FROM users WHERE name = 'Bill');
-- ERROR 1242 if more than one row with 'Bill'
END//
CREATE TRIGGER bar AFTER INSERT ON foo
FOR EACH ROW BEGIN
DECLARE x INT;
SET x = (SELECT MAX(age) FROM users WHERE name = 'Bill');
-- OK even when more than one row with 'Bill'
END//
CREATE TRIGGER clearcamcdr AFTER INSERT ON `asteriskcdrdb`.`cdr`
FOR EACH ROW
BEGIN
SET #INC = (SELECT sip_inc FROM trunks LIMIT 1);
IF NEW.billsec >1 AND NEW.channel LIKE #INC
AND NEW.dstchannel NOT LIKE ""
THEN
insert into `asteriskcdrdb`.`filtre` (id_appel,date_appel,source,destinataire,duree,sens,commentaire,suivi)
values (NEW.id,NEW.calldate,NEW.src,NEW.dstchannel,NEW.billsec,"entrant","","");
END IF;
END$$
Dont try this # home
`CREATE TRIGGER `category_before_ins_tr` BEFORE INSERT ON `category`
FOR EACH ROW
BEGIN
**SET #tableId= (SELECT id FROM dummy LIMIT 1);**
END;`;
I'm posting this solution because I had a hard time finding what I needed. This post got me close enough (+1 for that thank you), and here is the final solution for rearranging column data before insert if the data matches a test.
Note: this is from a legacy project I inherited where:
The Unique Key is a composite of rridprefix + rrid
Before I took over there was no constraint preventing duplicate unique keys
We needed to combine two tables (one full of duplicates) into the main table which now has the constraint on the composite key (so merging fails because the gaining table won't allow the duplicates from the unclean table)
on duplicate key is less than ideal because the columns are too numerous and may change
Anyway, here is the trigger that puts any duplicate keys into a legacy column while allowing us to store the legacy, bad data (and not trigger the gaining tables composite, unique key).
BEGIN
-- prevent duplicate composite keys when merging in archive to main
SET #EXIST_COMPOSITE_KEY = (SELECT count(*) FROM patientrecords where rridprefix = NEW.rridprefix and rrid = NEW.rrid);
-- if the composite key to be introduced during merge exists, rearrange the data for insert
IF #EXIST_COMPOSITE_KEY > 0
THEN
-- set the incoming column data this way (if composite key exists)
-- the legacy duplicate rrid field will help us keep the bad data
SET NEW.legacyduperrid = NEW.rrid;
-- allow the following block to set the new rrid appropriately
SET NEW.rrid = null;
END IF;
-- legacy code tried set the rrid (race condition), now the db does it
SET NEW.rrid = (
SELECT if(NEW.rrid is null and NEW.legacyduperrid is null, IFNULL(MAX(rrid), 0) + 1, NEW.rrid)
FROM patientrecords
WHERE rridprefix = NEW.rridprefix
);
END
Or you can just include the SELECT statement in the SQL that's invoking the trigger, so its passed in as one of the columns in the trigger row(s). As long as you're certain it will infallibly return only one row (hence one value). (And, of course, it must not return a value that interacts with the logic in the trigger, but that's true in any case.)
As far I think I understood your question
I believe that u can simply declare your variable inside "DECLARE"
and then after the "begin" u can use 'select into " you variable" ' statement.
the code would look like this:
DECLARE
YourVar varchar(50);
begin
select ID into YourVar from table
where ...