I am new to DB2 queries.
Here, I am passing a comma separated value as an IN parameter in a Stored Procedure. I want to search on the basis of those values.
Select * from USER where user_id in (IN_User);
Here, IN_User will have values of the kind ('val1','val2','val3')
It should return all the rows which has val1 or val2 or val3 as the User_id. As much as I know this can be done using UDF but I want to know is there any other way to do it without UDF.
please create a function to split the comma separated string
Please see the below function
CREATE FUNCTION StringToRows(
cString1 CLOB (10 M) ,
cStringSplitting1 VARCHAR(10) )
RETURNS TABLE (Lines VARCHAR(500))
SPECIFIC StringToRows_Big
DETERMINISTIC
NO EXTERNAL ACTION
CONTAINS SQL
BEGIN ATOMIC
DECLARE cStringSplitting VARCHAR(10);
DECLARE LenSplit SMALLINT;
SET cStringSplitting = cStringSplitting1;
SET LenSplit = LENGTH(cStringSplitting);
IF LENGTH(TRIM(cStringSplitting)) = 0 THEN
SET cStringSplitting = ' ', LenSplit = 1 ;
END IF ;
RETURN WITH
TEMP1 ( STRING) as (values (cString1) ),
TEMP2 ( Lines, STRING_left) as
(SELECT
SUBSTR(STRING,1, CASE WHEN LOCATE(cStringSplitting, STRING) = 0 THEN LENGTH(STRING) ELSE LOCATE(cStringSplitting,STRING) - 1 END),
(CASE WHEN (LOCATE(cStringSplitting, STRING) = 0) THEN '' ELSE SUBSTR(STRING, LOCATE(cStringSplitting,STRING) + LenSplit) END)
FROM TEMP1 WHERE LENGTH(STRING) > 0
UNION ALL
SELECT
SUBSTR(STRING_left,1, CASE LOCATE(cStringSplitting,STRING_left) WHEN 0 THEN LENGTH(STRING_left) ELSE LOCATE(cStringSplitting,STRING_left) - 1 END),
(CASE WHEN LOCATE(cStringSplitting,STRING_left) = 0 THEN '' ELSE SUBSTR(STRING_left, LOCATE(cStringSplitting,STRING_left) + LenSplit) END)
FROM TEMP2 WHERE LENGTH(STRING_left) > 0 )
SELECT Lines FROM TEMP2;
END
please see the sample stored procedure to call the function
CREATE PROCEDURE TEST_USR(IN #inputParam CLOB (10 M))
SPECIFIC TEST_USR
DYNAMIC RESULT SETS 1
P1: BEGIN
DECLARE CURSOR1 CURSOR WITH RETURN FOR
Select * from USER where user_id IN (SELECT * FROM TABLE(StringToRows(#inputParam, ',')) AS test);
OPEN CURSOR1;
END P1
Related
We have a view which contains 2 columns: pattern_start_time, pattern_end_time.
The select query in the function will convert it to minutes and using that result we are processing to get the shift unused coverage.The function is getting created but the processing is not happening and getting the below error:
SQLError[IX000]:Routine (my_list) cant be resolved.
Also please enter image description heresuggest to loop till the length of the result.
CREATE function myshifttesting(orgid int) returning int;
DEFINE my_list LIST( INTEGER not null );
DEFINE my_list1 LIST( INTEGER not null );
define i, j, sub, sub1 int;
define total int;
TRACE ON;
TRACE 'my testing starts';
INSERT INTO TABLE( my_list )
select
((extend(current, year to second) + (dots.v_shift_coverage.pattern_start_time - datetime(00:00) hour to minute) - current)::interval minute(9) to minute)::char(10)::INTEGER
from
dots.v_shift_coverage
where
org_guid = orgid;
INSERT INTO TABLE( my_list1 )
select
((extend(current, year to second) + (dots.v_shift_coverage.pattern_end_time - datetime(00:00) hour to minute) - current)::interval minute(9) to minute)::char(10)::INTEGER
from
dots.v_shift_coverage
where
org_guid = orgid;
let sub = 0;
let sub1 = 0;
let total = 0;
for j = 0 to 4
if (my_list(j) < my_list1(j))
then
if (my_list(j + 1) > my_list1(j))
then
let sub = sub + my_list(j + 1) - my_list1(j);
end if;
end if;
end for
if (my_list(0) > my_list1(4))
then
let sub1 = my_list(0) - my_list1(4);
end if;
let total = sub + sub1;
return total;
end function;
The error that you are receiving is because my_list(j) is not valid Informix syntax to access a LIST element. Informix is interpreting my_list(j) as a call to a function named mylist.
You can use a temporary table to "emulate" an array with your logic, something like this:
CREATE TABLE somedata
(
letter1 CHAR( 2 ),
letter2 CHAR( 2 )
);
INSERT INTO somedata VALUES ( 'a1', 'a2' );
INSERT INTO somedata VALUES ( 'b1', 'b2' );
INSERT INTO somedata VALUES ( 'c1', 'c2' );
INSERT INTO somedata VALUES ( 'd1', 'd2' );
INSERT INTO somedata VALUES ( 'e1', 'e2' );
DROP FUNCTION IF EXISTS forloop;
CREATE FUNCTION forloop()
RETURNING CHAR( 2 ) AS letter1, CHAR( 2 ) AS letter2;
DEFINE number_of_rows INTEGER;
DEFINE iterator INTEGER;
DEFINE my_letter1 CHAR( 2 );
DEFINE my_letter2 CHAR( 2 );
-- Drop temp table if it already exists in the session
DROP TABLE IF EXISTS tmp_data;
CREATE TEMP TABLE tmp_data
(
tmp_id SERIAL,
tmp_letter1 CHAR( 2 ),
tmp_letter2 CHAR( 2 )
);
-- Insert rows into the temp table, serial column will be the access key
INSERT INTO tmp_data
SELECT 0,
d.letter1,
d.letter2
FROM somedata AS d
ORDER BY d.letter1;
-- Get total rows of temp table
SELECT COUNT( * )
INTO number_of_rows
FROM tmp_data;
FOR iterator = 1 TO number_of_rows
SELECT d.tmp_letter1
INTO my_letter1
FROM tmp_data AS d
WHERE d.tmp_id = iterator;
-- Check if not going "out of range"
IF iterator < number_of_rows THEN
SELECT d.tmp_letter2
INTO my_letter2
FROM tmp_data AS d
WHERE d.tmp_id = iterator + 1;
ELSE
-- iterator + 1 is "out of range", return to the beginning
SELECT d.tmp_letter2
INTO my_letter2
FROM tmp_data AS d
WHERE d.tmp_id = 1;
END IF;
RETURN my_letter1, my_letter2 WITH RESUME;
END FOR;
END FUNCTION;
-- Running the function
EXECUTE FUNCTION forloop();
-- Results
letter1 letter2
a1 b2
b1 c2
c1 d2
d1 e2
e1 a2
5 row(s) retrieved.
Good morning,
I'm having troubles to integrate a working query into a stored procedure.
My main issue, is that I'm using a WHILE loop with an integer, and that the stored procedure is having troubles with it.
My working query/code is the following:
CREATE TABLE #tempScaffStandingByTime
(
TotalStanding INT,
MonthsAgo INTEGER
)
DECLARE #StartDate DATETIME = null
DECLARE #months INTEGER = 12
use [Safetrak-BradyTechUK]
WHILE #months >= 0
BEGIN
SET #StartDate = DATEADD(mm, -12 + #months, DATEADD(mm, 0, DATEADD(mm,
DATEDIFF(mm,0,GETDATE()-1), 1)))
INSERT INTO #tempScaffStandingByTime
select TOP 1 COUNT(*) OVER () AS TotalRecords, #months
from asset a
join wshhistory h on h.assetid = a.uid
where a.deleted = 0 and h.assetstate <> 6
AND (dbo.getdecommissiondate(a.uid) > #StartDate)
group by a.uid
SET #months -= 3
END
SELECT * FROM #tempScaffStandingByTime
DROP TABLE #tempScaffStandingByTime
This results in the input which I want:
I then tried to import this code into my stored procedure
DECLARE #Query varchar (8000)
, #Account varchar (100) = 'BradyTechUK'
SET #Account = 'USE [Safetrak-' + #Account + ']'
/************************************************************************************/
/********** Create Table to hold data ***********************************************/
CREATE TABLE #tempScaffStandingByTime
(
TotalStanding INT,
MonthsAgo INTEGER
)
/************************************************************************************/
/********** Populate temp table with data *******************************************/
DECLARE #StartDate DATETIME = null
DECLARE #months INTEGER = 12
SET #Query= +#Account+ '
WHILE '+#months+' >= 0
BEGIN
SET '+#StartDate+' = DATEADD(mm, -12 + ('+#months+', DATEADD(mm, 0, DATEADD(mm, DATEDIFF(mm,0,GETDATE()-1), 1)))
INSERT INTO #tempScaffStandingByTime
select TOP 1 COUNT(*) OVER () AS TotalRecords, '+#months+'
from asset a
join wshhistory h on h.assetid = a.uid
where a.deleted = 0 and h.assetstate <> 6
AND (dbo.getdecommissiondate(a.uid) > '+#StartDate+')
group by a.uid
SET '+#months+' -= 3
END'
EXEC (#Query)
/************************************************************************************/
/********** Select Statement to return data to sp ***********************************/
Select TotalStanding
, MonthsAgo
FROM #tempScaffStandingByTime
/************************************************************************************/
DROP TABLE #tempScaffStandingByTime
But when loading the stored procedure in SSRS I get a conversion error.
I can convert my parameter to a string, but then it won't count anymore.
I searched far and wide on the internet, tried various things, but can't get it to work.
Any help is appreciated, thanks!
It looks like your problem original comes from the lack of conversion between the INT in #months and your VARCHAR in #Accounts and #Query.
You need to cast the months to a string to append it, otherwise the SQL Server thinks you're trying to do addition.
However you'll also have problems with the usage of your #StartDate variable as you try to set it via your dynamic SQL which is not going to work like that - and as with #months it'll have an incorrect datatype, but I'm also unsure why it's written as is, and what you're tying to achieve with it
I think - from what you're writing - that this might be what you're looking for?
SET #Query= #Account + '
DECLARE #StartDate DATETIME = null
DECLARE #months VARCHAR(2) = ''12''
WHILE #months >= 0
BEGIN
SET #StartDate = DATEADD(mm, -12 + #months, DATEADD(mm, 0, DATEADD(mm, DATEDIFF(mm,0,GETDATE()-1), 1)))
INSERT INTO #tempScaffStandingByTime
select TOP 1 COUNT(*) OVER () AS TotalRecords, #months, #StartDate
from asset a
join wshhistory h on h.assetid = a.uid
where a.deleted = 0 and h.assetstate <> 6
AND (dbo.getdecommissiondate(a.uid) > #StartDate)
group by a.uid
SET #months -= 3
END'
EXEC (#Query)
I've been trying to resolve this since a while, still couldn't figure out how to apply a while value like:
while ....(having values in (X1, X2, X3, ........)
(
execute 'package....'
Param = X
)
passing every time the one of the values of X, X1 then X2 and so on.
You can pass all params to temporary table and then loop them using WHILE:
CREATE TABLE #tab(id INT IDENTITY(1,1), param_value NVARCHAR(MAX));
INSERT INTO #tab(param_value)
VALUES (#X1), (#X2), (#X3); -- ...
DECLARE #counter INT = 1,
#param NVARCHAR(MAX);
WHILE #counter <= (SELECT MAX(id) FROM #tab)
BEGIN
SELECT #param = param_value
FROM #tab
WHERE id = #counter;
EXEC [dbo].[my_stored_proc]
#param;
SET #counter += 1;
END
SqlFiddleDemo
CREATE OR REPLACE PROCEDURE STATUS_MAIL(FROM_MAIL IN VARCHAR2, TO_MAIL IN VARCHAR2)
is
v_From VARCHAR2(80) := FROM_MAIL;
v_Recipient VARCHAR2(80) := TO_MAIL;
v_Subject VARCHAR2(80) := 'EMPLOYEE STATUS';
v_Mail_Host VARCHAR2(30) := 'xx.xx.xxx.xxx';
v_Mail_Conn utl_smtp.Connection;
v_msg_body VARCHAR2(5000);
v_output VARCHAR2(5000);
BEGIN
/*Result always returns 42 rows*/
v_output := 'select empid,ename,mobile,dept from employee';
EXECUTE IMMEDIATE v_output into v_msg_body;
v_Mail_Conn := utl_smtp.Open_Connection(v_Mail_Host, xx);
utl_smtp.Helo(v_Mail_Conn, v_Mail_Host);
utl_smtp.Mail(v_Mail_Conn, v_From);
utl_smtp.Rcpt(v_Mail_Conn, v_Recipient);
utl_smtp.Data(v_Mail_Conn,
'Date: ' || to_char(sysdate, 'Dy, DD Mon YYYY hh24:mi:ss') || UTL_TCP.crlf ||
'From: ' || v_From || UTL_TCP.crlf ||
'Subject: '|| v_Subject || UTL_TCP.crlf ||
'To: ' || v_Recipient || UTL_TCP.crlf ||
UTL_TCP.crlf || v_msg_body );
utl_smtp.Quit(v_mail_conn);
EXCEPTION
WHEN utl_smtp.Transient_Error OR utl_smtp.Permanent_Error then
raise_application_error(-20000, 'Unable to send mail: '||sqlerrm);
END;
Getting an error inconsistent datatypes
# EXECUTE IMMEDIATE v_output into v_msg_body
while executing the above procedure, please help me....
So in fact your real question is: "how do I aggregate multiple rows into a single string ?"
The answer is to use aggregate functions. Oracle has introduced listagg-function in 11gR2 that solves this problem nicely but in earlier releases one has to do a bit more work.
When you know the right keywords Google finds a plenty of great resources, e.g.
String Aggregation Techniques
listagg function in 11g release 2
the collect function in 10g
I have collected the following examples from the above mentioned resources. Hope this gives you a good starting point:
The table that will be queried:
create table foo (d1 number, d2 varchar2(10));
insert all
into foo values(1, 'a')
into foo values(2, 'b')
into foo values(3, 'c')
select 1 from dual;
commit;
Oracle 11gR2:
declare
v_str varchar2(32767);
begin
select listagg('key = ' || d1 || ' value = ' || d2, chr(10))
within group (order by d1)
into v_str
from foo;
dbms_output.put_line(v_str);
exception
when value_error then
dbms_output.put_line('Exception: trying to insert too many characters to a varchar2 variable.');
end;
/
Oracle 10g:
create or replace type str_list_t as table of varchar2(32676);
/
create function to_string (
nt_in in str_list_t,
delimiter_in in varchar2 default ','
) return varchar2 is
v_idx pls_integer;
v_str varchar2(32767);
v_dlm varchar2(10);
begin
v_idx := nt_in.first;
while v_idx is not null loop
v_str := v_str || v_dlm || nt_in(v_idx);
v_dlm := delimiter_in;
v_idx := nt_in.next(v_idx);
end loop;
return v_str;
end;
/
declare
v_str varchar2(32676);
begin
select to_string(cast(collect('key = ' || d1 || ' value = ' || d2) as str_list_t), chr(10))
into v_str
from foo;
dbms_output.put_line(v_str);
exception
when value_error then
dbms_output.put_line('Exception: trying to insert too many characters to a varchar2 variable.');
end;
/
Note how I'll catch value_error exception that will be raised if the aggregated string won't fit into the reserved varchar2 capacity.
Output of both examples:
key = 1 value = a
key = 2 value = b
key = 3 value = c
I'm having trouble creating this stored procedure on IBM Informix Dynamic Server Version 10.00.FC9 (see Jonathan Leffler's answer to this post here) using the 'isql' command from Informix SQL.
I get an error on the ( char for each of his two examples near RETURNING CHAR(8)
ex. 1:
CREATE PROCEDURE ampm_time(tm SMALLINT) RETURNING CHAR(8);
DEFINE hh SMALLINT;
DEFINE mm SMALLINT;
DEFINE am SMALLINT;
DEFINE m3 CHAR(3);
DEFINE a3 CHAR(3);
LET hh = MOD(tm / 100 + 11, 12) + 1;
LET mm = MOD(tm, 100) + 100;
LET am = MOD(tm / 1200, 2);
LET m3 = mm;
IF am = 0
THEN LET a3 = ' am';
ELSE LET a3 = ' pm';
END IF;
RETURN (hh || ':' || m3[2,3] || a3);
END PROCEDURE;
ex. 2:
CREATE PROCEDURE ampm_time(tm SMALLINT) RETURNING CHAR(8);
DEFINE i2 SMALLINT;
DEFINE hh SMALLINT;
DEFINE mm SMALLINT;
DEFINE am SMALLINT;
DEFINE m3 CHAR(3);
DEFINE a3 CHAR(3);
LET i2 = tm / 100;
LET hh = MOD(i2 + 11, 12) + 1;
LET mm = MOD(tm, 100) + 100;
LET i2 = tm / 1200;
LET am = MOD(i2, 2);
LET m3 = mm;
IF am = 0
THEN LET a3 = ' am';
ELSE LET a3 = ' pm';
END IF;
RETURN (hh || ':' || m3[2,3] || a3);
END PROCEDURE;
They were designed by Jonathan Leffler (Informix Magician extraordinaire) in an effort to resolve the question I asked in the linked post. The intended return is to convert an INT datatype holding military time into a 2:30 pm type format.
This problem is DB-Access vs ISQL.
ISQL has a warped sense of humour and thinks that the syntax of SQL still matches what was current with Informix OnLine 4.10 (or, in those days, INFORMIX-OnLine 4.10). Specifically, it doesn't know that stored procedures are made up of multiple statements separated by semi-colons and mis-assumes that SQL statements end at the first semi-colon not in a string or comment.
Workarounds:
Use DB-Access instead of ISQL to create stored procedures.
Obtain SQLCMD from the IIUG Software Archive and use that instead.
Use 'mkproc' from the SQLCMD software to create stored procedures.
Of these, the easiest is to use DB-Access (aka dbaccess - found in $INFORMIXDIR/bin where the server software lives).