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.
I use SQL Server 2012. I have a sample table named 'Table1' with seven columns.
CREATE TABLE TABLE1
(
Field1 INT ,
Field2 INT ,
Field3 INT ,
Field4 INT ,
Field5 INT ,
Field6 INT ,
Field7 INT
)
GO
INSERT INTO TABLE1 VALUES (1,2,9,5,1,5,85)
INSERT INTO TABLE1 VALUES (2,6,8,4,1,4,45)
INSERT INTO TABLE1 VALUES (3,5,7,3,5,6,1)
INSERT INTO TABLE1 VALUES (4,4,6,1,51,4,1)
INSERT INTO TABLE1 VALUES (5,5,5,4,7,2,7)
INSERT INTO TABLE1 VALUES (6,5,4,6,4,7,8)
INSERT INTO TABLE1 VALUES (7,12,5,3,2,5,3)
INSERT INTO TABLE1 VALUES (8,1,6,5,9,5,1)
INSERT INTO TABLE1 VALUES (9,1,13,2,1,7,3)
INSERT INTO TABLE1 VALUES (10,6,9,3,6,2,6)
INSERT INTO TABLE1 VALUES (11,2,1,2,8,7,7)
INSERT INTO TABLE1 VALUES (12,7,6,1,3,3,2)
INSERT INTO TABLE1 VALUES (13,7,2,6,4,7,1)
GO
I have created the below Stored Procedure, This SP is able to query data considering the asked order by column.
I can query my table with each possibility of single column and kind of order by (ASC or Desc).
CREATE Procedure ProceName
(
#OrderByField INT = 1,
#OrderDirection INT = 0 -- 0 = Asc , 1 = Desc
)
As
Begin
SELECT
*
FROM Table1
ORDER BY
CASE WHEN #OrderDirection=0 AND #OrderByField=1 THEN Field1 END ASC,
CASE WHEN #OrderDirection=0 AND #OrderByField=2 THEN Field2 END ASC,
CASE WHEN #OrderDirection=0 AND #OrderByField=3 THEN Field3 END ASC,
CASE WHEN #OrderDirection=0 AND #OrderByField=4 THEN Field4 END ASC,
CASE WHEN #OrderDirection=0 AND #OrderByField=5 THEN Field5 END ASC,
CASE WHEN #OrderDirection=0 AND #OrderByField=6 THEN Field6 END ASC,
CASE WHEN #OrderDirection=0 AND #OrderByField=7 THEN Field7 END ASC,
CASE WHEN #OrderDirection=1 AND #OrderByField=1 THEN Field1 END DESC,
CASE WHEN #OrderDirection=1 AND #OrderByField=2 THEN Field2 END DESC,
CASE WHEN #OrderDirection=1 AND #OrderByField=3 THEN Field3 END DESC,
CASE WHEN #OrderDirection=1 AND #OrderByField=4 THEN Field4 END DESC,
CASE WHEN #OrderDirection=1 AND #OrderByField=5 THEN Field5 END DESC,
CASE WHEN #OrderDirection=1 AND #OrderByField=6 THEN Field6 END DESC,
CASE WHEN #OrderDirection=1 AND #OrderByField=7 THEN Field7 END DESC End
GO
EXECUTE ProceName #OrderByField=1, #OrderDirection=0
EXECUTE ProceName #OrderByField=6, #OrderDirection=1
Now I need to change this sp for accepting multi columns as order by series columns. They can be pass by name or by order of columns.
In this case i should be able to execute my SP like below command:
EXECUTE ProceName #OrderByField='6,7,2', #OrderDirection='0,1,1'
How can I achive this gool with out using the sp_executesql (Dynamic Query)?
Well, your ascending and descending wouldn't matter in your case unless you were trying to choose asc and desc for each column. Here is using the last column.
DECLARE #OrderByField VARCHAR(64) = '1,3,7'
DECLARE #DynamicColumns VARCHAR(256) = REPLACE(REPLACE(REPLACE(REPLACE(REPLACE(REPLACE(REPLACE(#OrderByField,1,'FIELD1'),2,'FIELD2'),3,'FIELD3'),4,'FIELD4'),5,'FIELD5'),6,'FIELD6'),7,'FIELD7')
DECLARE #OrderDirection INT = 1
DECLARE #order varchar(4)
SET #order = CASE WHEN #OrderDirection = 1 THEN 'DESC' ELSE 'ASC' END
--uncomment this line of code to add the sort to every column which only matters for DESC since ASC is default
--SET #DynamicColumns = REPLACE(#DynamicColumns,',', ' ' + #order + ',')
DECLARE #sql VARCHAR(MAX) = (
'SELECT
*
FROM Table1
ORDER BY ' + #DynamicColumns + ' ' + #order)
SELECT #sql
EXEC(#sql)
CREATE Procedure ProceName
(
#OrderByField VARCHAR(100),
#OrderDirection VARCHAR(100),
)
As
Begin
Declare #SQL VARCHAR(MAX)
if OBJECT_ID('Example1') is not null
begin
drop table Example
end
if OBJECT_ID('Example2') is not null
begin
drop table Example2
end
Create table Example1
(
id int identity(1,1),
Field varchar(20)
)
Create table Example2
(
id int identity(1,1),
OrderNumber varchar(10)
)
--iterate each element for both #OrderByField and #OrderDirection
Declare #separator char(1)=','
Declare #position int = 0
Declare #name varchar(20)
Set #OrderByField = #OrderByField + #separator
---------------------------------------------------------------------------
/*iterate each for #OrderByField */
---------------------------------------------------------------------------
While CHARINDEX (#separator,#OrderByField,#position) != 0
BEGIN
SET #name = SUBSTRING (#OrderByField,#position,CHARINDEX (#separator,#OrderByField,#position)-#position)
SET #SQL = 'Insert into Example1([Field]) Values(' + char(39) + #name + char(39) + ')'
EXEC(#SQL)
SET #position = CHARINDEX(#separator,#OrderByField,#position)+1
END
---------------------------------------------------------------------------
/*iterate each for #OrderDirection */
---------------------------------------------------------------------------
SET #position = 0 --do not forget to reset the position number
Set #OrderDirection = #OrderDirection + #separator
While CHARINDEX (#separator,#OrderDirection,#position) != 0
BEGIN
SET #name = SUBSTRING (#OrderDirection,#position,CHARINDEX (#separator,#OrderDirection,#position)-#position)
SET #SQL = 'Insert into Example2([OrderNumber]) Values(' + char(39) + #name + char(39)+ ')'
EXEC(#SQL)
SET #position = CHARINDEX(#separator,#OrderDirection,#position)+1
END
Set #name = '' --reset the #name for the use of Cursor
declare #NewName varchar(500) =''
Declare row_cursor CURSOR
FOR
select 'Field'+a.Field + ' '+ case when b.OrderNumber = 0 then 'ASC' else 'DESC' end +',' as command
from Example1 as a
inner join Example2 as b
on b.id = a.id
OPEN row_cursor
FETCH NEXT FROM row_cursor into #name
WHILE (##FETCH_STATUS =0)
begin
Set #NewName = #NewName + #name
FETCH NEXT FROM row_cursor into #name
end
close row_cursor
deallocate row_cursor
SET #NewName = REVERSE(STUFF(REVERSE(#NewName),1,1,''))
SET #SQL = 'Select * From Table1 Order by ' + #NewName
--Print(#SQL)
EXEC (#SQL)
END
I am newbie to IBM db2.Need to convert the below mentioned SP to db2 syntax. But i am stuck with many equivalents used or available in Db2. Even google research doesn't show how exactly we can compare object id of tables in db2 as I am doing in SQL Server stored procedure. Could anyone suggest me with right way to proceed?
EDIT: I have updated with equivalent DB2 syntax, but facing below error while deploying at the particular line, Can anyone identify and help me understand what is wrong with this syntax or the problem lies anywhere else in the procedure.
line no 25 : DECLARE v_sqlstate CHAR(5);
BACKUPTABLE: 25: An unexpected token "<variable declaration> was found following "". Expected tokens may include: "".. SQLCODE=-104, SQLSTATE=42601, DRIVER=4.18.60
An unexpected token variable declaration was found following "". Expected tokens may include: "".. SQLCODE=-104, SQLSTATE=42601, DRIVER=4.18.60
SQL Server Stored procedure syntax:
CREATE PROCEDURE [dbo].[BackUpTable]
#TableName sysname
AS
BEGIN
SET nocount ON
DECLARE #sql VARCHAR(500)
IF EXISTS (SELECT *
FROM sys.objects
WHERE object_id = Object_id(N'[dbo].[' + #TableName+'_EST' + ']')
AND TYPE IN ( N'U' ))
BEGIN
SET #sql = 'declare #Done bit
set #Done = 0
while #Done = 0
begin
delete top (100000)
from ' + #TableName + '_Bak' +
' if ##rowcount = 0
set #Done = 1
end;'
SET #sql = #sql + 'insert into ' + #TableName + '_Bak select * from ' +
#TableName +'_EST'
EXEC(#sql)
END
ELSE
BEGIN
DECLARE #err_message VARCHAR(300)
SELECT #err_message = 'The table "' + Isnull(#TableName, 'null') +
'" does not exist'
RAISERROR (#err_message, 16, 1)
END
END
DB2 SYNTAX CREATED SO FAR:
CREATE OR REPLACE PROCEDURE BackUpTable (IN TableName VARCHAR(128))
DYNAMIC RESULT SETS 1
BEGIN
DECLARE dynamicSql VARCHAR(500);
IF(EXISTS(
SELECT * FROM SYSIBM.SYSTABLES
WHERE NAME = TableName||'_EST'
)
)
THEN
SET dynamicSql = 'DELETE FROM '||TableName ||'_BAK';
SET dynamicSql = dynamicSql ||'insert into ' || TableName || '_BAK select * from ' ||
TableName || '_EST';
EXECUTE IMMEDIATE dynamicSql;
ELSE
DECLARE v_sqlstate CHAR(5);
DECLARE v_sqlcode INT;
DECLARE SQLSTATE CHAR(5) DEFAULT '00000';
DECLARE SQLCODE INT DEFAULT 0;
DECLARE EXIT HANDLER FOR SQLEXCEPTION
BEGIN
SELECT SQLSTATE, SQLCODE
INTO v_sqlstate, v_sqlcode
FROM sysibm.sysdummy1;
SET O_Error_Msg = 'TABLE IS NOT AVAILABLE:: SQLState : '||v_sqlstate||' SQLCode : '||v_sqlcode ;
END;
END IF;
END
on z/os you can do it:
IF( EXISTS( SELECT 1 FROM QSYS2.SYSTABLES WHERE TABLE_SCHEMA = 'YOURLIB' AND TABLE_NAME = 'YOURTABLENAME')) THEN
DROP TABLE YOURLIB.YOURTABLENAME;
END IF;
I am having a problem with the procedure below as whenever I pass a date of month and year, the IF condition loops on the number of records that exists in my table- only two rows existed- and also it shows the two possibilities. I also included the result to show what I mean!
PRODUCT_NUM ORDER_NUM QUANTITY PRICE MONTHLY_DATE
-------------- ----------- ------------- ------------ -------------
12345 106 3 19.99 21-DEC-15
67894 107 1 19.99 21-DEC-15
CREATE OR REPLACE PROCEDURE Proce_Name (Pram_Date DATE )
AS
BGIN
FOR Y IN
(SELECT SUM(PRICE) AS TOTAL_AMOUNT, SUM(NVL(QUANTITY,0) * NVL (
(PRICE,0)) AS TOTAL, MONTHLY_DATE
FROM PRODUCTS
GROUP BY MONTHLY_DATE
ORDER BY MONTHLY_DATE DESC) LOOP
IF TO_CHAR( Y.MONTHLY_DATE, 'mm-yyyy')= TO_CHAR(Pram_Date,'mm-yyyy') THEN
DBMS_OUTPUT.PUT_LINE('TOTAL AMOUNT: '|| ' '|| Y.TOTAL_AMOUNT);
DBMS_OUTPUT.PUT_LINE('');
DBMS_OUTPUT.PUT_LINE('TOTAL: '|| ' '|| Y.TOTAL);
ELSE
DBMS_OUTPUT.PUT_LINE('No Data for this month');
END IF;
END LOOP;
END Proce_name;
calling block:
DECLARE
MONTHLY_D DATE := TO_DATE('01-2012','mm-yyyy');
BEGIN
Proce_Name(MONTHLY_D);
END;
/
This is when the date I pass matches the records in the table:
No Data for this month
TOTAL AMOUNT: 4
TOTAL: 99.96
The result here when I pass a date that is not existed in the table:
No Data for this month
No Data for this month
How can I alter my procedure to examine the above date format accurately ?
Here is the code, you can consider exception handling as well:
Few observations, you an consider prem_date as char it self instead of passing date. Also if data set is huge and monthly_date is indexed, you should avoid applying to_char condition on monthly_date.
CREATE OR REPLACE PROCEDURE Proce_Name (Pram_Date DATE )
AS
l_total_amount float;
l_total float;
BEGIN
SELECT SUM(PRICE) AS TOTAL_AMOUNT,
SUM(NVL(QUANTITY,0) * NVL(PRICE,0)) AS TOTAL
INTO l_total_amount, l_total
FROM PRODUCTS
WHERE to_char(monthly_date, 'mm-yyyy') = to_char(pram_date, 'mm-yyyy');
if(l_total_amount is not null) then
DBMS_OUTPUT.PUT_LINE('TOTAL AMOUNT: '|| ' '|| L_TOTAL_AMOUNT);
DBMS_OUTPUT.PUT_LINE('');
DBMS_OUTPUT.PUT_LINE('TOTAL: '|| ' '|| L_TOTAL);
else
DBMS_OUTPUT.PUT_LINE('No Data for this month');
end if;
END Proce_name;
/
I have update procedure as following
CREATE OR REPLACE PROCEDURE get_sales_report(c_date date) AS
l_amount number(18, 2);
l_total number(18, 2);
BEGIN
begin
SELECT SUM(PRICE), SUM(NVL(QUANTITY, 0) * NVL(PRICE, 0))
into l_amount, l_total
FROM product
where TO_CHAR(MONTHLY_DATE, 'mm-yyyy') = to_char(c_date, 'mm-yyyy')
GROUP BY TO_CHAR(MONTHLY_DATE, 'mm-yyyy')
ORDER BY TO_CHAR(MONTHLY_DATE, 'mm-yyyy') DESC;
DBMS_OUTPUT.PUT_LINE('TOTAL AMOUNT: ' || ' ' || l_amount);
DBMS_OUTPUT.PUT_LINE('');
DBMS_OUTPUT.PUT_LINE('TOTAL: ' || ' ' || l_total);
exception
when no_data_found then
DBMS_OUTPUT.PUT_LINE('No Data for this month');
END; -- end of exception
-- add your code here
-- add your code here
-- add your code here
end;
Also call following command for dbms_output echoing text.
set serveroutput on size 30000;
Call your procedure as
declare
c_date2 DATE := TO_DATE('12-2015','mm-yyyy');
begin
get_sales_report(c_date2);
end;
Dates are fixed sized types. So, if you want to group by a whole month, you must query your date column as char. Meaning, you should use TO_CHAR instead of TO_DATE.
Here's (the simpler) way to do it (My changes are lowercase) -
CREATE OR REPLACE PROCEDURE Proce_Name (Pram_Date date) AS
l_month char(7);
l_total_amount number;
l_total number;
BEGIN
SELECT to_char(monthly_date,'mm-yyyy') as month,
SUM(PRICE) AS TOTAL_AMOUNT,
SUM(NVL(QUANTITY,0)*NVL(PRICE,0)) AS TOTAL
INTO l_month, l_total_amount, l_total
FROM PRODUCTS
where to_char(monthly_date,'mm-yyyy')=to_char(Pram_Date, 'mm-yyyy')
GROUP BY to_char(monthly_date,'mm-yyyy');
if l_month is not null then
DBMS_OUTPUT.PUT_LINE('TOTAL AMOUNT: '|| ' '|| Y.TOTAL_AMOUNT);
DBMS_OUTPUT.PUT_LINE('');
DBMS_OUTPUT.PUT_LINE('TOTAL: '|| ' '|| Y.TOTAL);
else
DBMS_OUTPUT.PUT_LINE('No Data for this month');
end if;
END Proce_name;
/
Here's some testing code - http://sqlfiddle.com/#!4/b1068/2
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