DB2 Cast return value of cursor - stored-procedures

the line with fetch will return a VARCHAR, but the values that can be taken are 1 or nothing(NULL). So how can I realize it when I want to do without SET ANS = (ANSV IS NOT NULL);
DECLARE C1 CURSOR FOR S1;
DECLARE ANSV VARCHAR;
DECLARE ANS BOOLEAN;
SET STMT = "SELECT 1 FROM ..."
PREPARE S1 FROM STMT;
OPEN C1;
FETCH FROM C1 INTO ANSV;
CLOSE C1;
SET ANS = (ANSV IS NOT NULL);

Related

OUTPUT variables to sp_executesql

Background: SQL Server 2008 R2
Having issues with the following. Been given a usp to "finish off", author is unavailable. It extracts data from source table, copies to target table of same name + datetime stamp in an archive db then truncates source table. Want to confirm that no errors have occurred & row counts are the same before I truncate source. As I'm using DDL a TRY CATCH combo won't work.
Following code works:
DECLARE #HostName VARCHAR(30) -- Name of server running script
DECLARE #dbName VARCHAR(30) -- Database currently connected to
DECLARE #LogText VARCHAR(255) -- Text to be logged to ArchLog table
DECLARE #NewUTMetaData VARCHAR(255) -- New tablename for UTMetaData
DECLARE #NewOutboundMessagePending VARCHAR(255) -- New tablename for OutboundMessagePending
DECLARE #NewOutboundMessageStatus VARCHAR(255) -- New tablename for OutboundMessageStatus
DECLARE #NewOutboundMessageResult VARCHAR(255) -- New tablename for OutboundMessageResult
DECLARE #NewFileMessageNonSequence VARCHAR(255) -- New tablename for FileMessageNonSequence
DECLARE #NewOutboundMessageRequest VARCHAR(255) -- New tablename for OutboundMessageRequest
DECLARE #NewOutboundMessage VARCHAR(255) -- New tablename for OutboundMessage
DECLARE #SQLQuery NVARCHAR(500)
DECLARE #return_value INT
DECLARE #Err INT = 0
DECLARE #ErrFin INT = 0
DECLARE #SQLCount NVARCHAR(255) = 'SELECT #TargetCountOUT = COUNT(*) FROM '
DECLARE #ParmDefinition nvarchar(50) = N'#TargetCountOUT int OUTPUT';;
DECLARE #SourceCount INT = 0
DECLARE #TargetCount INT = 0
-- Log message that delete is starting
SELECT #HostName = host_name()
SELECT #dbName = db_name()
SELECT #LogText = 'Procedure ArchiveMuleDBMetrix_NEW starting database= ' + #dbname + ' host= ' + #HostName
EXEC xp_logevent 50001, #LogText, 'INFORMATIONAL'
INSERT INTO [MuleDBArch].[dbo].[ArchLog]
([LogEntryDateTime]
,[LogEntry])
VALUES (GETDATE()
,#LogText)
BEGIN
SELECT #NewUTMetaData = (SELECT '[MuleDBArch].[dbo].UTMetaDataA' + convert(varchar(50),GetDate(),112) + replace(convert(varchar, GetDate(),108),':',''))
SET #err = ##error;
SET #SQLQuery = 'select * into ' + #NewUTMetaData + ' from [MuleDB].[dbo].[SASITUTMetaData]'
EXECUTE sp_executesql #SQLQuery
SET #SourceCount = ##ROWCOUNT
SELECT #LogText = 'Rows archived into ' + #NewUTMetaData + ' by Procedure ArchiveMuleDBMetrix_NEW = ' + CAST(#SourceCount as VARCHAR(6))
EXEC xp_logevent 50002, #LogText, 'INFORMATIONAL'
INSERT INTO [MuleDBArch].[dbo].[ArchLog]
([LogEntryDateTime]
,[LogEntry])
VALUES (GETDATE()
,#LogText)
IF #err = 0
BEGIN
SET #sqlcount = #sqlcount + #NewUTMetaData
EXECUTE sp_executesql #sqlcount, #ParmDefinition, #TargetCountOUT=#TargetCount OUTPUT;
IF #SourceCount = #TargetCount
TRUNCATE TABLE [MuleDB].[dbo].[SASITUTMetaData]
ELSE
SELECT #LogText = 'Post archive, row counts differ between [MuleDB].[dbo].[SASITUTMetaData] and' + #NewUTMetaData
EXEC xp_logevent 50003, #LogText, 'Error'
Set #errfin = 1
SET #err = 0
END
ELSE
BEGIN
SELECT #LogText = 'Procedure ArchiveMuleDBMetrix_NEW failed archiving into ' + #NewUTMetaData
EXEC xp_logevent 50004, #LogText, 'Error'
Set #errfin = 1
SET #err = 0
END
END
However, if I repeat the block to work on another table (or indeed the same one) query completes with error and I get the message:
Msg 102, Level 15, State 1, Line 1
Incorrect syntax near '.'.
I think I've narrowed it down to the line:
EXECUTE sp_executesql #sqlcount, #ParmDefinition, #TargetCountOUT=#TargetCount OUTPUT;
Any help appreciated
In the below code
EXECUTE sp_executesql #sqlcount, #ParmDefinition, #TargetCountOUT=#TargetCount OUTPUT;
You are trying to pass parameter values without specifying the parameter names except the output parameter while calling the stored procedure. Either specify the parameter name for all or none. Try to execute the code as follows
EXECUTE sp_executesql #sqlcount, #ParmDefinition, #TargetCount OUTPUT;

Stored procedure not compiling with 'Set'

I have a Stored procedure which I am not able to compile.
CREATE PROCEDURE FINGOODSCH(IN STRDATE DATE,
IN prodln Char(5))
LANGUAGE SQL
RESULT SETS 1
SET OPTION DBGVIEW =*SOURCE
BEGIN
Declare IN_DATE NUMERIC(7,0);
SET IN_DATE = 0;
/* SET IN_DATE = (DECIMAL(CHAR(SUBSTR(CHAR(STRDATE),1,4); */
/* SUBSTR(CHAR(STRDATE),6,2) CONCAT */
/* SUBSTR(CHAR(STRDATE),9,2))) - 19000000) ; */
Declare FinGoodSCH Cursor for
Select TRIM(ORDNO) as OrderNumber,
( '20' || SUBSTR(CHAR(ODUDT),2,2) || '-' ||
SUBSTR(CHAR(ODUDT),4,2) || '-' ||
SUBSTR(CHAR(ODUDT),6,2)) as OrderDueDate,
TRIM(FITEM) as ModelNumber,
TRIM(DPTNO) as ProductionLine
From ORMAST
Where
DPTNO = prodln
OPEN FinGoodSCH ;
END
THe issue is withthe statement 'SET IN_DATE = 0; (I know I can use Default to set it to 0, but thats not what I am looking for)'. If I remove this statement, it will compile. Compilation error is:
SQL0104 30 3 Position 33 Token FINGOODSCH was not valid. Valid
tokens: GLOBAL.
Also, I tried declaring it with decimal but it did not work
You have to do the declarations in SQL before any executable code....
CREATE PROCEDURE FINGOODSCH(IN STRDATE DATE,
IN prodln Char(5))
LANGUAGE SQL
RESULT SETS 1
SET OPTION DBGVIEW =*SOURCE
BEGIN
Declare IN_DATE NUMERIC(7,0);
Declare FinGoodSCH Cursor for
Select TRIM(ORDNO) as OrderNumber,
( '20' || SUBSTR(CHAR(ODUDT),2,2) || '-' ||
SUBSTR(CHAR(ODUDT),4,2) || '-' ||
SUBSTR(CHAR(ODUDT),6,2)) as OrderDueDate,
TRIM(FITEM) as ModelNumber,
TRIM(DPTNO) as ProductionLine
From ORMAST
Where
DPTNO = prodln;
-- Executable code starts here....
SET IN_DATE = (DECIMAL(CHAR(SUBSTR(CHAR(STRDATE),1,4);
SUBSTR(CHAR(STRDATE),6,2) CONCAT
SUBSTR(CHAR(STRDATE),9,2))) - 19000000) ;
OPEN FinGoodSCH ;
END
Note that if you happened to want to use IN_DATE in your cursor, you'd still do it like the above. The value of any variables used in the DECLARE CURSOR statement are not evaluated until the cursor is opened in DB2.

KanBan Database architecture

I'm trying to implement a kanban agile board like Trello in my application. I'm wondering how to do it the best way. I'm considering these entities:
Board
has many Lists
List
has many Cards
Card
contains some content
However I'm stuck with ordering of Cards. Each Card should have a sort position to sort Cards in particular order in a List. When dragging a Card, I should change it's position and save it in the database. What is the most efficient way of doing this?
Adding a position field to each Card seems redundant since I would have to recalculate positions of all Cards in a List (or two) after I drag a Card to a different position (there will be hundreds of Cards). I was thinking of storing an array of all Card ids in a List and sort Cards by this array. What are pros/cons of this solution? And are there any better solutions?
I'm using Ruby on Rails and PostgreSQL.
UPDATE
Using #cske answer I came up with the following solution:
CREATE OR REPLACE FUNCTION move_buyer_card(
new_list_id INT
, param_id INT
, new_position INT
) RETURNS FLOAT4
LANGUAGE plpgsql SECURITY DEFINER
AS $$
DECLARE
var_lower_bound FLOAT4;
var_upper_bound FLOAT4;
var_new_weight FLOAT4; /*between 0 and 1*/
BEGIN
IF new_position < 2 THEN /*first position*/
var_lower_bound := 0;
SELECT MIN(weight) FROM Buyers
WHERE board_list_id = new_list_id
INTO var_upper_bound;
IF var_upper_bound IS NULL THEN /*empty list*/
var_upper_bound := 1;
END IF;
ELSE /*not first position*/
WITH ordered_cards AS (
SELECT id, RANK() OVER (ORDER BY weight ASC) AS rank, weight
FROM Buyers WHERE board_list_id = new_list_id
)
SELECT cards0.weight, cards1.weight from ordered_cards cards0
JOIN ordered_cards cards1
ON cards0.rank = cards1.rank - 1
WHERE cards1.rank = new_position
INTO var_lower_bound, var_upper_bound;
IF NOT FOUND THEN /*only 1 item in list OR last position*/
SELECT MAX(weight) FROM Buyers WHERE board_list_id = new_list_id
INTO var_lower_bound;
IF var_lower_bound IS NULL THEN /*empty list*/
var_lower_bound := 0;
END IF;
var_upper_bound := 1;
END IF;
END IF;
var_new_weight := var_lower_bound + (var_upper_bound - var_lower_bound) / 2;
UPDATE Buyers
SET weight = var_new_weight,
board_list_id = new_list_id
WHERE id = param_id;
RETURN var_new_weight;
END;
$$;
Consider this, trick is not storing the position, but a weight so you can insert between any two elements
create table listOfCards (
listId INTEGER
,cardId INTEGER
,weigth FLOAT4
,PRIMARY KEY (listId,cardId)
);
CREATE OR REPLACE FUNCTION addCard(
plistId INT
, pcardId INT
, ppos INT
) RETURNS FLOAT4
LANGUAGE plpgsql SECURITY DEFINER
AS $$
DECLARE
vlb FLOAT4;
vub FLOAT4;
vnw FLOAT4;
BEGIN
IF 2 > ppos THEN
vlb := 0;
SELECT min(weigth) FROM listOfCards WHERE listId = plistId INTO vub;
IF vub IS NULL THEN /*empty list*/
vub := 2;
END IF;
ELSE
with corder as (select cardId,RANK() OVER (order by weigth asc) as r,weigth FROM listOfCards WHERE listId=1)
select c0.weigth,c1.weigth from corder c0 JOIN corder c1 ON c1.r = c0.r + 1 where c1.r = ppos INTO vlb,vub;
IF NOT FOUND THEN
SELECT max(weigth) FROM listOfCards WHERE listId = plistId INTO vlb;
IF vlb IS NULL THEN /*empty list*/
vlb := 0;
END IF;
vub := (vlb+1) * 2;
END IF;
END IF;
vnw := vlb + (vub-vlb) /2;
INSERT INTO listOfCards(listId, cardId, weigth)
VALUES (plistId,pcardId,vnw )
ON CONFLICT ON CONSTRAINT listofcards_pkey DO UPDATE SET weigth = vnw ;
RETURN vnw;
END;
$$;
Usage:
select addCard(1,1,1);
select addCard(1,2,1);
select addCard(1,3,2);
select addCard(1,4,2);
select addCard(1,5,5);
select addCard(1,5,2);
select * from listOfCards ORDER BY weigth;
Result:
1,2,0.5
1,5,0.5625
1,4,0.625
1,3,0.75
1,1,1

Use MERGE statement in a stored procedure with Informix

I have a stored procedure like this : http://tinyurl.com/pbk8qfb
CREATE PROCEDURE PS_TAB_TSEN(NOMFIC VARCHAR(70), LIBSEN VARCHAR(35),
CODAPPFOF VARCHAR(3))
DEFINE NUMINTNAV VARCHAR(10);
BEGIN;
LET NUMINTNAV = '0';
SELECT TO_CHAR(NUM_INT_NAV) INTO NUMINTNAV
FROM TAB_INAV WHERE NOM_FIC = NOMFIC;
MERGE INTO TAB_TSEN T
USING (SELECT COD_SEN, LIBSEN AS LIB_SEN, CODAPPFOF AS COD_APP_FOF
FROM TAB_TSEN
WHERE COD_SEN = NUMINTNAV) AS S
ON (T.COD_SEN = S.COD_SEN)
WHEN MATCHED THEN
UPDATE SET T.COD_SEN = S.COD_SEN, T.LIB_SEN = S.LIB_SEN,
T.COD_APP_FOF = S.COD_APP_FOF
WHEN NOT MATCHED THEN
INSERT (T.COD_SEN, T.LIB_SEN, T.COD_APP_FOF)
VALUES (S.COD_SEN, S.LIB_SEN, S.COD_APP_FOF);
COMMIT;
END PROCEDURE;
When I run it all is good, but when I check in the table I see no result, for the insert statement and for the update statement too.
I call it like this:
CALL PS_TAB_TSEN('~/PSS/Order/OrderList.aspx', 'Liste des commandes', 'NET');
I don't understand what is going on...
Problem solved, I replace my stored procedure by this one :
CREATE PROCEDURE PS_TAB_TSEN(NOMFIC VARCHAR(70), LIBSEN VARCHAR(35), CODAPPFOF VARCHAR(3))
DEFINE ISPRESENT INTEGER;
DEFINE NUMINTNAV VARCHAR(10);
BEGIN;
LET NUMINTNAV = '0';
SELECT TO_CHAR(NUM_INT_NAV) INTO NUMINTNAV FROM TAB_INAV WHERE NOM_FIC = NOMFIC;
LET ISPRESENT = 0;
SELECT COUNT(*) INTO ISPRESENT FROM TAB_TSEN WHERE COD_SEN = NUMINTNAV;
IF(ISPRESENT > 0) THEN
UPDATE TAB_TSEN SET T.COD_SEN = S.COD_SEN, T.LIB_SEN = S.LIB_SEN, T.COD_APP_FOF = S.COD_APP_FOF
ELSE
INSERT INTO TAB_TSEN (T.COD_SEN, T.LIB_SEN, T.COD_APP_FOF) VALUES (S.COD_SEN, S.LIB_SEN, S.COD_APP_FOF);
END IF;
COMMIT;
END PROCEDURE;

How to return ref_cursor from for loop from pl/sql procedure

i want to get back ref cursor for the below sp. But it's not working.. not getting any records.. So please help me for this.
DATA_INPUTLIST - it's collection type for two variables (mtn, mtnEffDate)
DATA_RESULTLIST - It's also a collection type for 3 variables (id, effDate, mtn)
CREATE OR REPLACE PROCEDURE proc1 (
pCustId IN NUMBER,
pAcctNo IN NUMBER,
pSearchCriteria IN DATA_INPUTLIST,
pRecordSet OUT SYS_REFCURSOR,
out_error_code OUT NUMBER,
out_error_message OUT VARCHAR2
) AS
--Variables
v_SUN_DATE DATE := TO_DATE('01/15/2011', 'mm/dd/yyyy');
vCount NUMBER := 0;
v_mtn NUM
BER;
v_mtn_eff_date DATE;
cMtnPricePlanInfo DATA_RESULTLIST;
BEGIN
SELECT COUNT(*) INTO vCount FROM TABLE (pSearchCriteria);
FOR i IN 1..vCount LOOP
SELECT MTN, TO_DATE(MTN_EFF_DATE, 'mm/dd/yyyy') into v_mtn, v_mtn_eff_date
FROM TABLE (pSearchCriteria)
WHERE
ROWNUM = i;
SELECT
A.PPLAN_ID, A.EFF_DATE, A.MTN INTO cMtnPricePlanInfo(i).PPLAN_ID, cMtnPricePlanInfo(i).EFF_DATE, cMtnPricePlanInfo(i).MTN
FROM CUST_ACCT_LINE_PPLAN A, CUST_ACCT_LINE_PPLAN_HIST B
WHERE
A.CUST_ID = pCustId
AND A.ACCT_NO = pAcctNo
AND A.MTN = v_mtn
AND A.MTN_EFF_DATE = v_mtn_eff_date
AND A.EFF_DATE >=
(SELECT MAX(EFF_DATE) FROM CUST_ACCT_LINE_PPLAN_HIST C
WHERE
C.CUST_ID = pCustId
AND C.ACCT_NO = pAcctNo
AND C.MTN = v_mtn
AND C.MTN_EFF_DATE = v_mtn_eff_date
AND C.EFF_DATE <= v_SUN_DATE)
AND A.CUST_ID = B.CUST_ID
AND A.ACCT_NO = B.ACCT_NO
AND A.MTN = B.MTN
AND A.MTN_EFF_DATE = B.MTN_EFF_DATE
AND A.PPLAN_ID = B.PPLAN_ID;
END LOOP;
OPEN pRecordSet FOR
SELECT * FROM TABLE (cMtnPricePlanInfo);
END;
/
The first things that stands out to me is the following:
SELECT MTN, TO_DATE(MTN_EFF_DATE, 'mm/dd/yyyy') into v_mtn, v_mtn_eff_date
FROM TABLE (pSearchCriteria)
WHERE
ROWNUM = i;
For i greater than 1 this will not return anything. For the reason why, see this question. In fact, because you're using SELECT ... INTO ..., you'll get a no data found error if i is greater than or equal to 2.
You don't need an SQL query just to fetch some values out of PL/SQL collections. Try replacing this query with
v_mtn := pSearchCriteria(i).MTN;
v_mtn_eff_date := TO_DATE(pSearchCriteria(i).MTN_EFF_DATE, 'mm/dd/yyyy');
I don't have your tables nor the data in them so I can't be sure why you're getting no data. I can only hazard at some obvious suggestions: is vCount zero? If you put suitable values for v_mtn and v_mtn_eff_date and run your query on its own, separate from this stored procedure, does it return any rows?

Resources