SQL procedure to add and edit data - sql

Im busy with an old exam paper one of the questions read as follows
Study the following tables and answer the questions below:
CREATE TABLE CARDHOLDERS(
CH_ID INTEGER IDENTITY,
CH_NAME VARCHAR(50),
CH_SURNAME VARCHAR(50),
CH_IDNUMBER CHAR(13),
CH_CARDNUMBER CHAR(13),
CH_STATUS CHAR(2),
CH_CREATE_DATE DATETIME,
CH_LAST_CHANGE_DATE DATETIME)
Write a store procedure to add or edit the cardholders information. Do the neccecary validation checks to ensure data is correct.
My Answer
Create Procedure add_ch (#CH_NAME, #CH_SURNAME...)
AS
BEGIN
INSERT INTO CARDHOLDERS VALUES (#CH_NAME, #CH_SURNAME...)
END
TO RUN PROCEDURE
EXECUTE add_ch ('Peter', 'Kemp')
My Question
Will the above procedure to add cardholer give the correct results?
The Question asks 'Write a store procedure to add or edit the
cardholders information' how do I combine the add procedure with
the edit cardholder procedure or am I correct in assuming that I can
have to different procedure?

Are you looking for something like this?
CREATE PROCEDURE add_ch (#CH_NAME , #CH_SURNAME...)
AS
BEGIN
DECLARE #count INT
SET #count =
(SELECT count (*)
FROM CARDHOLDERS
WHERE CH_NAME = #CH_NAME AND CH_SURNAME = #CH_SURNAME)
IF #count = 0
INSERT INTO CARDHOLDERS
VALUES (#CH_NAME, #CH_SURNAME...)
else Print'This user already exsit.'
END

Try something like this. Using decode and setting defaults for the parameters helps.
create or replace procedure add_ch
(
CHID INTEGER := -1,
CHNAME VARCHAR := '#',
CHSURNAME VARCHAR := '#',
CHIDNUMBER CHAR := '#',
CH_CARDNUMBER CHAR := '#',
CHSTATUS CHAR := '#',
CHCREATE_DATE DATETIME := '01-Jan-1900',
CHLAST_CHANGE_DATE DATETIME:= '01-Jan-1900'
)
as
begin
update cardholders
set CH_NAME = decode( CHNAME,'#',CH_NAME,chname ),
CH_SURNAME = decode( CHSURNAME,'#',CH_SURNAME, CHSURNAME),....
where CH_ID = CHID;
if sql%notfound
then
insert into cardholders
(
CH_ID,
CH_NAME,
CH_SURNAME,
CH_IDNUMBER,
CH_CARDNUMBER,
CH_STATUS,
CH_CREATE_DATE,
CH_LAST_CHANGE_DATE
)
values
(
CHID,
CHNAME,
CHSURNAME,
CHIDNUMBER,
CH_CARDNUMBER,
CHSTATUS CHAR,
CHCREATE_DATE,
CHLAST_CHANGE_DATE
);
end;

first of all You Have to find The primary key data in your database table
if exist data in DBtable(with current primary key value)
then execute your update sql query
else
execute your insert query.
Done your database checking using the previous answer to your question.

Related

stored procedure, handle possible null value

I have a very simple stored procedure which currently works perfectly when both parameters are sent values from form inputs.
However, I need to figure out what to do for IN_NUMBER if the value is empty because that column in the destination table is set to be nullable. It seems like the procedure itself is simply failing because it's waiting for a value.
What should I change?
IN parameters:
IN_NAME
IN_NUMBER
Routine:
P1 : BEGIN ATOMIC
INSERT INTO schema . postings
( name
, postNumber)
VALUES
( IN_NAME
, IN_NUMBER) ;
END P1
Example:
create table postings (name varchar(100), postNumber int) in userspace1#
create or replace procedure postings (
in_name varchar(100)
, in_number int
)
P1 : BEGIN ATOMIC
INSERT INTO postings
( name
, postNumber)
VALUES
( IN_NAME
, IN_NUMBER) ;
END P1#
call postings('myname', null)#
select * from postings#
NAME POSTNUMBER
---- ----------
myname <null>
There is no any problem here as you see.
What db2 error do you have exactly on a case similar to this?
If you want to handle NULL and replace it with some other value, use NVL(IN_NUMBER, 0) - you can exchange 0 for any other number of course (I'm assuming this is an integer).

Nvarchar value overflowing an int column where there is no int column?

How is my nvarchar value overflowing an int column when I'm not defining any int values?
I'm getting the following error when I run my stored procedure:
The conversion of the nvarchar value '17191925814' overflowed an int column.
The statement I was trying to execute:
EXECUTE [dbo].[updateUser] #status = 'active', ..... ,#srcID = '17191925814'
The problem is that I never define that value as an int. It is always handled as a string, as far as I can tell. In my stored procedure the #srcID parameter is defined as an nvarchar(255):
CREATE PROCEDURE [dbo].[updateUser] ... , #srcID nvarchar(255), ...
And within that stored procedure, I try to UPDATE a value in a column that is also defined as nvarchar(255):
IF #srcID NOT IN ('', '0') AND #srcID IS NOT NULL
UPDATE [dbo].[Users] SET [Source System ID] = #srcID WHERE ...
ELSE IF #srcID = '0'
UPDATE [dbo].[Users] SET [Source System ID] = '' WHERE ...
The target table:
CREATE TABLE [dbo].[Users] ( ..., [Source System ID] [nvarchar](255) NULL, ...)
Where, or why, is SQL Server trying to convert '17191925814' to an int, and how can I prevent it?
Edit: I've included the full code of the stored procedure where the parameter #srcID appears.
This doesn't want to be specific answer (I agree with Gordon, Zohar and Damien), but should be difficult to write in a comment.
Follow a "reduced" procedure to show the case.
I hope it can help you as a method when you have similar problems (always try to reduce the scope), and serve as a confirmation of what Gordon, Zohar and other (I know, there is no need of confirmation as they have great reputation :-) already said:
CREATE TABLE X ( SourceSystemID nvarchar(255) NULL);
INSERT INTO X VALUES ('17191925814');
GO
CREATE PROCEDURE Upd_X #srcID NVARCHAR(255)
AS
BEGIN
IF #srcID NOT IN ('', '0') AND #srcID IS NOT NULL UPDATE X SET SourceSystemID = #srcID
ELSE IF #srcID = '0' UPDATE X SET SourceSystemID = ''
END
;
GO
SELECT * FROM X
EXEC Upd_X '17191925814';
SELECT * FROM X;
Output:
SourceSystemID
---------------
17191925814
SourceSystemID
-----------------------------------------
17191925814
The problem, as pointed out by Damian in the comments, was that I had a trigger active on the database, that was copying that data into an integer column.
So if you have this same issue: check triggers on your database!

DB2 stored procedure w/ parameters

I'm having a hard time producing the correct results from my stored procedure. I'm using a db2 database and I have 3 input parameters division, department, project. My call statement looks like this.
CALL schema.stored_procedure ('IT', 'MARKETING', 'ONLINE FULFILLMENT')
I need to produce results that will display the row of data when the third parameter is specified or has a value for the Project Name (as from the example above 'Online_fulfillment') and to display all the results when the third parameter has a value 'ALL' for Project Name (per the example below 'ALL').
CALL schema.stored_procedure ('IT', 'MARKETING', 'ALL')
My query below currently is returning just the column header names with no results and I'm having trouble debugging it. Here is my current stored procedure.
CREATE PROCEDURE schema.stored_procedure
(IN in_DIVISION_NAME VARCHAR(200)
,IN in_DEPARTMENT_NAME VARCHAR(20)
,IN in_PROJECT_NAME VARCHAR(400)
)
DYNAMIC RESULT SETS 1
BEGIN
IF (in_PROJECT_NAME = 'ALL') THEN
BEGIN
DECLARE GLOBAL TEMPORARY TABLE TEMP_DW_1
(DIM_PROJECT_ID INT
,PROJECT_NAME VARCHAR (400)
,DIM_DEPARTMENT_ID INT
,DEPARTMENT_NAME VARCHAR(100)
,DIVISION_NAME VARCHAR(100)
) ON COMMIT DELETE ROWS NOT LOGGED WITH REPLACE;
END;
INSERT INTO SESSION.TEMP_DW_1 (DIM_PROJECT_ID, PROJECT_NAME, DIM_DEPARTMENT_ID,
DEPARTMENT_NAME,DIVISION_NAME)
SELECT DISTINCT DJ.DIM_PROJECT_ID
,PROJECT_NAME
,DIM_DEPARTMENT_ID
,DEPARTMENT_NAME
,DIVISION_NAME
FROM SCHEMA.FACT_TABLE
WHERE DEPARTMENT_NAME = in_DEPARTMENT_NAME
AND DIVISION_NAME = in_DIVISION_NAME;
BEGIN
DECLARE exitCursor CURSOR WITH RETURN FOR
SELECT *
FROM SESSION.TEMP_DW_1;
OPEN exitCursor;
END;
END
EXPECTED RESULTS:
CALL schema.stored_procedure ('IT', 'MARKETING', 'ONLINE FULFILLMENT')
EXPECTED RESULTS:
CALL schema.stored_procedure ('IT', 'MARKETING', 'ALL')
I believe I have solved this by adding an additional IF statement setting the in_PROJECT_NAME <> 'ALL' and adding an additional filter to the second query that sets the PROJECT_NAME = in_PROJECT_NAME. Could be an easier way to solve this but it works:
IF (in_PROJECT_NAME <> 'ALL') THEN
BEGIN
DECLARE GLOBAL TEMPORARY TABLE TEMP_DW_1
(DIM_PROJECT_ID INT
,PROJECT_NAME VARCHAR (400)
,DIM_DEPARTMENT_ID INT
,DEPARTMENT_NAME VARCHAR(100)
,DIVISION_NAME VARCHAR(100)
) ON COMMIT DELETE ROWS NOT LOGGED WITH REPLACE;
END;
INSERT INTO SESSION.TEMP_DW_1 (DIM_PROJECT_ID, PROJECT_NAME, DIM_DEPARTMENT_ID,
DEPARTMENT_NAME ,DIVISION_NAME)
SELECT DISTINCT DJ.DIM_PROJECT_ID
,PROJECT_NAME
,DIM_DEPARTMENT_ID
,DEPARTMENT_NAME
,DIVISION_NAME
FROM SCHEMA.FACT_TABLE
WHERE DEPARTMENT_NAME = in_DEPARTMENT_NAME
AND DIVISION_NAME = in_DIVISION_NAME;
AND PROJECT_NAME = in_PROJECT_NAME

How to split comma separated text in MySQL stored procedure

How to split comma separated text (list of IDs) in MySQL stored procedure to use result in SQL "IN" statement.
SELECT * FROM table WHERE table.id IN (splitStringFunction(commaSeparatedData, ','));
This is simple as hell for MySQL:
SELECT * FROM table WHERE FIND_IN_SET(table.id, commaSeparatedData);
Reference: http://dev.mysql.com/doc/refman/5.0/en/string-functions.html#function_find-in-set
You could use a prepared statement inside the stored procedure to achieve this. You can create the whole select query as a string inside a variable and then concatenate in the comma delimited string into its IN clause. Then you can make a prepared statement from the query string variable and execute it.
DELIMITER ;;
create procedure testProc(in listString varchar(255))
BEGIN
set #query = concat('select * from testTable where id in (',listString,');');
prepare sql_query from #query;
execute sql_query;
END
;;
DELIMITER ;
call testProc("1,2,3");
You could try this MySql example. Before you use it, put some type safety checks in there (i.e. check id is integer, or match against regular expression before insert).
# BEGIN split statements ids
DECLARE current_pos INT DEFAULT 1;
DECLARE delim CHAR DEFAULT ',';
DECLARE current CHAR DEFAULT '';
DECLARE current_id VARCHAR(100) DEFAULT '';;
CREATE TEMPORARY TABLE ids (`id` VARCHAR(100));
split_ids: LOOP
SET current = MID(statement_ids, current_pos, 1);
IF (current_pos = LENGTH(statement_ids)) THEN
IF current != delim THEN SET current_id = CONCAT(current_id,current); END IF;
INSERT INTO ids(id) VALUES (current_id);
LEAVE split_ids;
END IF;
IF current = delim THEN
INSERT INTO ids(id) VALUES (current_id);
SET current_id = '';
ELSE
SET current_id = CONCAT(current_id,current);
END IF;
SET current_pos = current_pos+1;
END LOOP split_ids;
# END split statement ids
# to check ids are correct
SELECT * FROM ids;
# to use the ids:
SELECT * FROM statements WHERE id IN (SELECT id FROM ids);
OK, slightly "easier" but less geeky way for people like me:
say you have one table 'combined_city_state' which looks like:
'Chicago, Illinois'
copy that to 2 other tables:
CREATE TABLE city LIKE combined_city_state;
INSERT city SELECT * FROM combined_city_state;
CREATE TABLE state LIKE combined_city_state;
INSERT state SELECT * FROM combined_city_state;
You now have 3 tables with the same data as 'combined_city_state'.
Install this function:
CREATE FUNCTION SPLIT_STR(
x VARCHAR(255),
delim VARCHAR(12),
pos INT
)
RETURNS VARCHAR(255)
RETURN REPLACE(SUBSTRING(SUBSTRING_INDEX(x, delim, pos),
LENGTH(SUBSTRING_INDEX(x, delim, pos -1)) + 1),
delim, '');
Then apply this to each table to remove the extra index of data:
UPDATE firms
SET city = (SELECT SPLIT_STR((city), ',', 1));
UPDATE firms
SET state = (SELECT SPLIT_STR((state), ',', 2));
This leaves you with one column of just cities, one of just states. You can now remove the original 'combined_city_state' column if you don't need anymore.
You can do it two ways:
SQL Library
Natively with REGEXP
I'm surprised this one-liner isn't properly mentioned here:
SELECT * FROM table
WHERE id in (SELECT convert(int,Value) FROM dbo.Split(#list_string,',')
All you need is a Split SQL function like the one below which will come in handy in other ways as well:
CREATE FUNCTION dbo.Split
(
#List nvarchar(2000),
#SplitOn nvarchar(5)
)
RETURNS #RtnValue table
(
Id int identity(1,1),
Value nvarchar(100)
)
AS
BEGIN
While (Charindex(#SplitOn,#List)>0)
Begin
Insert Into #RtnValue (value)
Select
Value = ltrim(rtrim(Substring(#List,1,Charindex(#SplitOn,#List)-1)))
Set #List = Substring(#List,Charindex(#SplitOn,#List)+len(#SplitOn),len(#List))
End
Insert Into #RtnValue (Value)
Select Value = ltrim(rtrim(#List))
Return
END
You can use find_in_set() function for collection filter
how-to-split-and-search-in-comma-separated-values-in-mysql
SELECT * FROM table WHERE find_in_set(table.id,commaSeparatedData) > 0;
I have parsed data with hyphens in it. The example below uses a fixed text string to demonstrate, just change the references to relevant column names in the table. I played for ages with a way to ensure it worked on codes with varying numbers of components and in the end decided to add the where clause. Most data you are trying to parse would have a fixed number of columns.
select
SUBSTRING_INDEX(TS,"-",1) as "1",
reverse(left(reverse(SUBSTRING_INDEX(TS,"-",2)),locate("-",reverse(SUBSTRING_INDEX(TS,"-",2)))-1)) as "2",
reverse(left(reverse(SUBSTRING_INDEX(TS,"-",3)),locate("-",reverse(SUBSTRING_INDEX(TS,"-",3)))-1)) as "3",
reverse(left(reverse(SUBSTRING_INDEX(TS,"-",4)),locate("-",reverse(SUBSTRING_INDEX(TS,"-",4)))-1)) as "4",
reverse(left(reverse(SUBSTRING_INDEX(TS,"-",5)),locate("-",reverse(SUBSTRING_INDEX(TS,"-",5)))-1)) as "5",
reverse(left(reverse(SUBSTRING_INDEX(TS,"-",6)),locate("-",reverse(SUBSTRING_INDEX(TS,"-",6)))-1)) as "6",reverse(left(reverse(SUBSTRING_INDEX(TS,"-",7)),locate("-",reverse(SUBSTRING_INDEX(TS,"-",7)))-1)) as "7",
reverse(left(reverse(SUBSTRING_INDEX(TS,"-",8)),locate("-",reverse(SUBSTRING_INDEX(TS,"-",8)))-1)) as "8",
reverse(left(reverse(SUBSTRING_INDEX(TS,"-",9)),locate("-",reverse(SUBSTRING_INDEX(TS,"-",9)))-1)) as "9",
reverse(left(reverse(SUBSTRING_INDEX(TS,"-",10)),locate("-",reverse(SUBSTRING_INDEX(TS,"-",10)))-1)) as "10"
from (select "aaa-bbb-ccc-ddd-eee-fff-ggg-hhh-iii-jjj" as TS) as S
where (LENGTH(TS)-LENGTH(REPLACE(TS,'-',''))) =9
A bit strange but:
SET #i = 1;
set #str = 'a,b,c,d,e,f,g,h';
select temp.length into #length from
(select
ROUND(
(
LENGTH(dt.data)
- LENGTH( REPLACE (dt.data, ",", "") )
) / LENGTH(",")
)+1 AS length
from (select #str as data) dt
) temp;
SET #query = CONCAT('select substring_index(
substring_index(#str, '','', seq),
'','',
-1
) as letter from seq_', #i, '_to_',#length);
PREPARE q FROM #query;
EXECUTE q;

STORED PROCEDURE Calculations & performance improvements

I currently have the following stored procedure;
CREATE PROCEDURE web.insertNewCampaign
(
#tmp_Id BIGINT,
#tmp_Title VARCHAR(100),
#tmp_Content VARCHAR(8000),
#tmp_Pledge DECIMAL(7,2),
--#tmp_Recipients BIGINT,
#tmp_Date DATETIME,
#tmp_Private BIT,
#tmp_Template BIGINT,
#tmp_AddyBook BIGINT
)
AS
declare #recipients BIGINT
declare #tmp_IDENTITY BIGINT
declare #fave BIGINT
declare #allocation VARCHAR(50)
--insert campaign data
BEGIN TRAN
SELECT #recipients = addMaster_NoRecipients FROM tbl_AddressBookMaster
WHERE addMaster_UserId = #tmp_Id AND addMaster_Key = #tmp_AddyBook;
INSERT INTO TBL_CAMPAIGNS ([campaign_MemberId], [campaign_Title], [campaign_Content], [campaign_Pledge], [campaign_Date], [campaign_Private], [campaign_Template], [campaign_AddressBook], [campaign_Recipients])
VALUES (#tmp_Id, #tmp_Title, #tmp_Content, #tmp_Pledge, #tmp_Date, #tmp_Private, #tmp_Template, #tmp_AddyBook, #recipients)
SELECT #tmp_IDENTITY = SCOPE_IDENTITY() --this returns the newly added IDENTITY ID
COMMIT
......
So i have 2 questions:
1) How do i divide #tmp_Pledge by #recipients to give #allocation eg:(#allocation = #tmp_Pledge / #recipients)
2) Is it possible to compound these statements into a more efficient statement(s) with #allocation effectively being inserted as a value into the column [campaign_RecipShare], and reducing the need for these declared variables?
Many Thanks for any help you can offer for either question.
;-)
After the first select, you can do this to set #allocation:
set #allocation = #tmp_pledge / #recepients
As for making it more efficient, it's already fairly efficient--you won't go through any less steps, but you can condense the code a bit:
INSERT INTO TBL_CAMPAIGNS (
[campaign_MemberId], [campaign_Title], [campaign_Content],
[campaign_Pledge], [campaign_Date], [campaign_Private],
[campaign_Template], [campaign_AddressBook], [campaign_Recipients],
[capmain_RecipShare])
SELECT
#tmp_Id, #tmp_Title, #tmp_Content,
#tmp_Pledge, #tmp_Date, #tmp_Private,
#tmp_Template, #tmp_AddyBook, addMaster_NoRecipients,
#tmp_Pledge / addMaster_NoReceipients as Allocation
FROM
tbl_AddressBookMaster
WHERE
addMaster_UserId = #tmp_Id
AND addMaster_Key = #tmp_AddyBook
SELECT #tmp_IDENTITY = SCOPE_IDENTITY() --this returns the newly added IDENTITY ID
This also removes the need for you calculating the #allocation member outside of the insert statement.
1) #tmp_pledge / #recepients - I'll assume allocation is a numeric field of some form in TBL_CAMPAIGNS holding a number in varchar is not a good idea.
2) You just need to build a select that returns all the values from the other table and the parameters matching the columns to insert into.
insert into TBL_CAMPAIGNS ([campaign_MemberId], [campaign_Title], [campaign_Content], [campaign_Pledge], [campaign_Date], [campaign_Private], [campaign_Template], [campaign_AddressBook], [campaign_Recipients], [campaign_allocation)
select #tmp_Id, #tmp_Title, #tmp_Content, #tmp_Pledge, #tmp_Date, #tmp_Private, #tmp_Template, #tmp_AddyBook, addMaster_NoRecipients, #tmp_pledge / addMaster_NoRecipients
FROM FROM tbl_AddressBookMaster
WHERE addMaster_UserId = #tmp_Id AND addMaster_Key = #tmp_AddyBook;
SELECT #tmp_IDENTITY = SCOPE_IDENTITY() --this returns the newly added IDENTITY ID
set #allocation = #tmp_pledge / (#recepients* 1.0)
You want to do that because othewise you will run into integer math and the result will round to an integer.