Apex ORA-01008: not all variables bound on host variables - mysql

I am working with an apex environment where I need a dynamic sql query. Apex provides this api with the help of "Classic Report (based on function)". I would like you to draw your attention to line 8. My appoligies the spacing gets messed up when I paste it into stackoverflow. Now the problem is that I am getting the error 'ORA-01008: not all variables bound' Which is funny because when I change the :TEAM_SELECTOR in line 8 to something along the lines of 'MYSQL' (which is a team name) then this code works without error. Notice that :TEAM_SELECTOR is also used in the returned query without problem. Also I am using APEX 5.
DECLARE
v_time INT;
v_start_time int;
v_end_time int;
v_rownum int := 1;
v_max_shifts int;
v_location INT;
v_P4_team_selector varchar2(30) := :TEAM_SELECTOR;
BEGIN
select extract(hour from CAST(sysdate AS TIMESTAMP)) into v_time from dual;
select count(*) into v_max_shifts from oncall_shift
where team = v_P4_team_selector;
FOR i IN 0..v_max_shifts
LOOP
select start_time into v_start_time from (select * from oncall_shift where team =v_P4_team_selector)
where rownum = v_rownum;
select end_time into v_end_time from (select * from oncall_shift where team =v_P4_team_selector)
where rownum = v_rownum;
v_rownum := v_rownum + 1;
if v_time >= v_start_time and v_time <= v_end_time
then
select location into v_location from oncall_shift
where team = v_P4_team_selector
and start_time = v_start_time
and end_time = v_end_time;
return '
SELECT E.FNAME "First Name",E.LNAME "Last Name",E.OFFICE_NUM "Office Number", E.MOBILE_NUM "Mobile Number",L.NAME Location, o.position "Primary/Secondary"
FROM EMPLOYEE E, LOCATION L, ON_CALL O
WHERE E.ID = O.EMP_ID
AND L.ID = O.LOC_ID
AND O.ONCALL_DATE=TRUNC(SYSDATE)
AND O.TEAM=:TEAM_SELECTOR
AND L.ID = ' || v_location ||
' ORDER BY l.name asc
';
EXIT;
END IF;
END LOOP;
END;

Okay so I believe the issue was that the classic report based on a function executes before the host variables (TEAM_SELECTOR) is able to be bound therefore throwing the error. My solution to this was to change it to a normal classic report based on a sql query which runs after host variables are bound, because I needed one separate variable I created a P1_LOCATION item on that page using the same the query expect changing "v_location" to P1_LOCATION. Now in the P1_LOCATION page item I put the procedure to get the location.
P1_LOCATION code:
DECLARE
v_time INT;
v_start_time int;
v_end_time int;
v_rownum int := 1;
v_max_shifts int;
v_location INT;
v_P4_team_selector varchar2(30) := :TEAM_SELECTOR;
BEGIN
select extract(hour from CAST(sysdate AS TIMESTAMP)) into v_time from dual;
select count(*) into v_max_shifts from oncall_shift
where team = v_P4_team_selector;
FOR i IN 0..v_max_shifts
LOOP
select start_time into v_start_time from (select * from oncall_shift where team =v_P4_team_selector)
where rownum = v_rownum;
select end_time into v_end_time from (select * from oncall_shift where team =v_P4_team_selector)
where rownum = v_rownum;
v_rownum := v_rownum + 1;
if v_time >= v_start_time and v_time <= v_end_time
then
select location into v_location from oncall_shift
where team = v_P4_team_selector
and start_time = v_start_time
and end_time = v_end_time;
APEX_UTIL.set_session_state('P1_LOCATION',v_location);
EXIT;
END IF;
END LOOP;
END;
Classic Report (sql query):
SELECT E.FNAME "First Name",E.LNAME "Last Name",E.OFFICE_NUM "Office Number", E.MOBILE_NUM "Mobile Number",L.NAME Location, o.position "Primary/Secondary"
FROM EMPLOYEE E, LOCATION L, ON_CALL O
WHERE E.ID = O.EMP_ID
AND L.ID = O.LOC_ID
AND O.ONCALL_DATE=TRUNC(SYSDATE)
AND O.TEAM=:TEAM_SELECTOR
AND L.ID = :P1_LOCATION
ORDER BY l.name asc

Related

MySQL stores procedure cannot add values

DELIMITER $$
CREATE PROCEDURE GetCustomerLevel(p_barcode int)
BEGIN
DECLARE q1 int;
DECLARE q2 int;
DECLARE q3 int;
DECLARE total int;
SET total :=0;
SELECT sum(adjustment_quantity) INTO q1 FROM adjustment_inventory WHERE item_barcode = p_barcode group by adjustment_quantity;
SELECT sum(opening_stock) INTO q2 FROM openingstock
WHERE item_barcode = p_barcode group by opening_stock;
SELECT sum(inwardquantity) INTO q3
FROM inwardmaster WHERE item_barcode = p_barcode group by inwardquantity;
IF q1 IS NULL THEN
SET q1 := 0;
END IF;
IF q2 IS NULL THEN
SET q2 := 0;
END IF;
IF q3 IS NULL THEN
SET q3 := 0;
END IF;
SELECT q1;
SELECT q2;
SELECT q3;
SELECT q1+q2+q3;
END$$
It's return wrong answer everytime. For example q1=100 q2=200 q3=100 its return 100
you dont need to use stored procedure for this
set #barcode = '1234';
select
coalesce((
SELECT sum(coalesce(adjustment_quantity,0))
FROM adjustment_inventory
WHERE item_barcode = #p_barcode
),0) +
coalesce((
SELECT sum(opening_stock)
FROM openingstock
WHERE item_barcode = #p_barcode
), 0) +
coalesce((
SELECT
sum(coalesce(inwardquantity,0))
FROM inwardmaster WHERE item_barcode = #p_barcode
), 0) res
From Dual
;
If you really want to use procedure then check the code below
DELIMITER $$
CREATE PROCEDURE GetCustomerLevel(p_barcode int)
BEGIN
DECLARE q1 int;
DECLARE q2 int;
DECLARE q3 int;
DECLARE total int;
SET total :=0;
SELECT coalesce(sum(adjustment_quantity), 0) INTO q1 FROM adjustment_inventory WHERE item_barcode = p_barcode;
SELECT coalesce(sum(opening_stock), 0) INTO q2 FROM openingstockWHERE item_barcode = p_barcode;
SELECT coalesce(sum(inwardquantity), 0) INTO q3 FROM inwardmaster WHERE item_barcode = p_barcode;
SELECT q1;
SELECT q2;
SELECT q3;
set total = q1+q2+q3;
SELECT total;
END$$

Mysql group_concat limit rows in grouping

The next example is my database.
tb_port
id port
1 80
2 22
3 53
4 3128
5 443
tb_dest
id dest
1 network
2 local
tb_rule
id id_port id_dest
1 1 1
2 2 1
3 3 1
4 4 1
5 5 1
Select:
select dest,group_concat(port) from tb_port a, tb_dest b, tb_rule c where a.id=c.id_port and b.id=c.id_dest group by dest
Result:
network 80,22,53,3128,443
but is not the result I'm looking for, the result would be this.
Select ex:
select dest,group_concat(port limit 2) from tb_port a, tb_dest b, tb_rule c where a.id=c.id_port and b.id=c.id_dest group by dest
result I would like
network 80,22
network 53,3128
network 443
how to achieve this result only with SQL?
Sqlfiddle: http://sqlfiddle.com/#!2/d11807
MySQL doesn't make this kind of query easy, but one (admittedly not very pretty) solution is to use a variable to give each row a sequence number per dest and just group by the row number integer divided by 2 to get two numbers in each group;
SELECT dest, GROUP_CONCAT(port ORDER BY rank) ports
FROM (
SELECT dest, port, (
CASE dest WHEN #curDest
THEN #curRow := #curRow + 1
ELSE #curRow := 1 AND #curDest := dest END) rank
FROM tb_port a
JOIN tb_rule c ON a.id = c.id_port
JOIN tb_dest b ON b.id = c.id_dest,
(SELECT #curRow := 0, #curDest := '') r
ORDER BY dest
) z
GROUP BY FLOOR(rank/2),dest
ORDER BY dest, MIN(rank)
An SQLfiddle to test with.
Here is a stored proc,you just put in the delimiter when you call it
DELIMITER $$
DROP PROCEDURE IF EXISTS explode_table $$
CREATE PROCEDURE explode_table(bound VARCHAR(255))
BEGIN
DECLARE id TEXT;
DECLARE value TEXT;
DECLARE occurance INT DEFAULT 0;
DECLARE i INT DEFAULT 0;
DECLARE splitted_value TEXT;
DECLARE done INT DEFAULT 0;
DECLARE cur1 CURSOR FOR
select dest,group_concat(port) from tb_port a, tb_dest b, tb_rule c
where a.id=c.id_port and b.id=c.id_dest and dest != '' group by dest;
DECLARE CONTINUE HANDLER FOR NOT FOUND SET done = 1;
DROP TEMPORARY TABLE IF EXISTS table2;
CREATE TEMPORARY TABLE table2(
`id` VARCHAR(255),
`value` VARCHAR(255) NOT NULL
) ENGINE=Memory;
OPEN cur1;
read_loop: LOOP
FETCH cur1 INTO id, value;
IF done THEN
LEAVE read_loop;
END IF;
SET occurance = (SELECT LENGTH(CONCAT(value,bound))
- LENGTH(REPLACE(CONCAT(value,bound), bound, ''))
+1);
SET i=2;
WHILE i <= occurance DO
SET splitted_value =
SUBSTRING_INDEX(SUBSTRING_INDEX(CONCAT(value,bound),bound,i),bound,-2) ;
INSERT INTO table2 VALUES (id, splitted_value);
SET i = i + 2;
END WHILE;
END LOOP;
SELECT * FROM table2;
CLOSE cur1;
END; $$
CALL explode_table(',')

mysql - updating a table setting a column with a complex condition

I have been working on this for a while now and this seems to be too complex. What I want to do is to update a column (x) with the operation: (p * (100/c) ) / 100.
p corresponds to a value of a date x and c corresponds to a value of date x minus one day.
I tried to create a stored procedure with loop but select statement doesnt work for me in the loop statement.
Here is my procedure which update nothing :
BEGIN
DECLARE firstqDate,date2 date;
DECLARE p, c float;
DECLARE cpt, val int;
SET #val = 0;
SET #cpt = (select count(*)-1 from quotes);
WHILE (val < 3) DO
SET #firstqDate = (select qDate from quotes ORDER BY YEAR(qDate) ASC, MONTH(qDate) ASC, DAY(qDate) ASC limit 1,1);
SET date2 = (select qDate from quotes where qDate like DATE_ADD(#firstqDate, INTERVAL 1 DAY );
SET p = (select qOp from quotes where qDate like date2);
SET c = (select qCl from quotes where qDate like DATE_SUB(date2, INTERVAL val DAY));
update quotes
set qCh = (p * (100/c) ) / 100;
set val = val + 1;
end while;
END
EDIT : I did some updates to the stored procedure but still updating no lines!
BEGIN
DECLARE firstqDate,date2 date;
DECLARE p, c float;
DECLARE cpt, val int;
SET #val = 0;
SET #cpt = (select count(*)-1 from quotes);
SET firstqDate = (select qDate from quotes ORDER BY YEAR(qDate) ASC, MONTH(qDate) ASC, DAY(qDate) ASC limit 1,1);
WHILE (val < 3) DO
SET date2 = (select qDate from quotes where qDate like DATE_ADD(#firstqDate, INTERVAL val DAY ));
SET p = (select qOp from quotes where qDate like date2);
SET c = (select qCl from quotes where qDate like DATE_SUB(date2, INTERVAL val+1 DAY));
set val = val + 1;
update quotes
set qCh = (p * (100/c) ) / 100
where qOp = p AND qCl = c;
end while;
END
I did some updates again to the stored procedures but no changes. i used some functions.
BEGIN
DECLARE p, c float;
DECLARE cpt, val int;
SET #val = 0;
SET #cpt = (select count(*)-1 from quotes);
WHILE (#val < 3) DO
SET p = getp(#val, getd());
SET c = getc(#val+1, getd());
set #val = #val + 1;
update quotes
set qCh = (#p * (100/#c) ) / 100
where qOp = #p AND qCl = #c;
end while;
END
functions :
get p:
BEGIN
declare d date;
select qDate into d from quotes ORDER BY YEAR(qDate) ASC, MONTH(qDate) ASC, DAY(qDate) ASC limit 1,1;
return d;
END
get c:
BEGIN
DECLARE c float;
DECLARE qDa date;
select qDate into qDa from quotes where qDate like DATE_SUB(qD, INTERVAL v DAY );
SELECT qCl INTO c FROM quotes WHERE qDate = qDa;
RETURN c;
END
getd:
BEGIN
declare d date;
select qDate into d from quotes ORDER BY YEAR(qDate) ASC, MONTH(qDate) ASC, DAY(qDate) ASC limit 1,1;
return d;
END
this stored procedure must calculate all qCh from p of qDate and c of the qDate minus one day.
Thank you!
EDIT - Solved
Ouf! I finally managed to write this stored procedure :
BEGIN
DECLARE p, c float;
DECLARE cpt, val int;
SET #val = 0;
SET #cpt = (select count(*)-1 from quotes);
WHILE (#val <= 2) DO
SET p := getp(#val, getd());
SET c := getc(#val+1, getd());
set #val := #val + 1;
update quotes q
set q.qCh = (getp(#val, getd()) * (100/getc(#val-1, getd())) ) / 100
where q.qOp = getp(#val, getd());
end while;
END
new getC
BEGIN
DECLARE c float;
DECLARE qDa date;
select qDate into qDa from quotes where qDate like DATE_ADD(qD, INTERVAL v DAY );
SELECT qCl INTO c FROM quotes WHERE qDate = qDa;
RETURN c;
END
I changed the code of function getC to add (-1) in the first iteration. now it is working!
Thank you everyone for your help!
Your update cycle looks ok, even if i don't understand why you make 3 cycles. Shouldn't you use:
WHILE (#val < 3) DO
Instead of
WHILE (val < 3) DO
? Hope it helps
EDIT:
You need to debug your cycle to know where the problem is.
Try this:
BEGIN
DECLARE #p, #c float;
DECLARE #cpt, #val int;
SET #val = 0;
SET #cpt = (select count(*)-1 from quotes);
SELECT 'Enter Cycle';
WHILE (#val < 3) DO
SELECT 'In Cyle';
SET #p = getp(#val, getd());
SELECT #p;
SET c = getc(#val+1, getd());
set #val = #val + 1;
update quotes
set qCh = (#p * (100/#c) ) / 100
where qOp = #p AND qCl = #c;
end while;
END
Does you SP prints 'Enter Cycle' and 'In Cyle'? Does the value of p Variable prints? It is correct?

Loop through all records in my SQL Server database

I have the following SQL script. As you can see I manually set the #listingid value to 30653.
But this script should be executed for all records in the [listings] table where #listingid is assigned the value of the [listings].id column.
DECLARE #profname nvarchar(150)
DECLARE #furl nvarchar(250)
DECLARE #city nvarchar(250)
DECLARE #listingid int
set #listingid=30653
--select the top 1 professionname
SELECT TOP 1 #profname=REPLACE(LOWER(pn.title),' ','-'),#furl=l.friendlyurl,#city=REPLACE(REPLACE(LOWER(l.city),'''',''),' ','-') FROM healthprof_professionnames hpn
INNER JOIN professionname pn ON pn.id=hpn.professionnameid
INNER JOIN listings l on l.id=hpn.healthprofid
WHERE l.id=#listingid ORDER BY pn.title
--check if current friendlyurl already contains profession
IF NOT CHARINDEX(#profname,#furl)>0
SET #furl = #furl + '-' + #profname
IF NOT CHARINDEX(#city,#furl)>0
SET #furl = #furl + '-' + #city
SET #furl = #furl + '-3'
UPDATE listings set friendlyurl=#furl WHERE id=#listingid
You can use a cursor to loop over every row in a result set:
declare cur cursor for
select distinct id from listings
declare #listingid int
open cur
fetch next from cur into #listingid
while ##FETCH_STATUS = 0
BEGIN
-- your code from above goes here
fetch next from cur into #listingid
END
That being said, I agree with Tim's comment above. Rewrite it to work in one set-based operation if at all possible. I think this will work, but I haven't tested it:
;WITH vars AS (
SELECT id, profname, furl, city
FROM (
SELECT l.id,
REPLACE(LOWER(pn.title),' ','-') as profname,
l.friendlyurl as furl,
REPLACE(REPLACE(LOWER(l.city),'''',''),' ','-') as city,
ROW_NUMBER() OVER (PARTITION BY l.id ORDER BY pn.title) as rnk
FROM healthprof_professionnames hpn
INNER JOIN professionname pn ON pn.id=hpn.professionnameid
INNER JOIN listings l on l.id=hpn.healthprofid
) A
WHERE A.rnk = 1
),
vars2 AS (
SELECT id,
CASE WHEN NOT CHARINDEX(profname, furl) > 0
THEN furl + '-' + profname ELSE furl END as furl,
city
FROM vars
),
vars3 as (
SELECT id,
CASE WHEN NOT CHARINDEX(city, furl) > 0
THEN furl + '-' + city ELSE furl END as furl
FROM vars2
)
UPDATE listings SET friendlyurl = vars3.furl + '-3'
FROM listings INNER JOIN vars3 on vars3.id = listings.id

Query with multiple EXIST

I've got a database of rooms and equipments. I want to query the database and return a list of rooms with e.g. tv, radio, sat and fridge (eq1, eq2, eq3, ...., eqN).
I have the following SELECT statement:
select * from rooms r where
exists (select id from equipments where eq_id='eq1' and room_id=r.id)
and
exists (select id from equipments where eq_id='eq2' and room_id=r.id)
and
exists (select id from equipments where eq_id='eq3' and room_id=r.id)
.......
and
exists (select id from equipments where eq_id='eqN' and room_id=r.id)
Is there any way to optimize or making this shorter?
To shorten you could
select *
from rooms r
where #N = (select count(distinct eq_id)
from equipments
where eq_id IN ('eq1','eq2',...,'eqN') and room_id=r.id)
EDIT
but not sure if it will actually make it faster... quite the opposite, the version with EXISTS AND EXISTS has a chance to prune execution branch on the first false, the above must actually count the distinct values (go through all records) and see what that value is.
So you should think what is faster:
going once through all records related to a room (one correlated subquery) or
running N (worst case) correlated (but highly selective subqueries) for each room
It depends on the statistics of your data (I would think that if most rooms don't have all the sought equipment in them then your initial version should be faster, if most rooms have all equipment in them then the proposed version might perform better; also if the EXISTS version is faster make an effort to first the queries that are most likely to fail i.e. first check for rarest equipment)
You can also try a version with GROUP BY
select r.*
from rooms r join
equipments e on r.id = e.room_id
group by r.id
where eg_id in ('eq1','eq2',...,'eqN')
having count(distinct e.eq_id) = #N
(above SQL not tested)
try this (I don't have any DB available to test it, also consider performance )
select * from
rooms r,
(
select count(distinct id) as cnt, id from equipments where eq_id in ('eq1','eq2') group by id
) as sub
where sub.id = r.id
and sub.cnt >= 2 'Options count
Note: 2 - it is the number of options that you need. In example they are: 'eq1','eq2'
select * from rooms r where
(select count(id) from equipments where eq_id='eq1' and room_id=r.id) > 0
and
...
Use an Stored Procedure.
here is the procedure for mysql:
DELIMITER $$
CREATE DEFINER=`root`#`%` PROCEDURE `GetRooms`(IN roomtable TEXT, IN equipmenttable TEXT, IN equipments TEXT )
BEGIN
DECLARE statement text;
DECLARE Pos int;
DECLARE cond text;
DECLARE element text;
DECLARE tmpTxt text;
set tmpTxt = equipments;
set cond = "";
set Pos = instr(tmpTxt,';');
while Pos <> 0 do
set element = substring(tmpTxt, 1, Pos-1);
if cond <> "" then
set cond = concat(cond,' and ');
end if;
set cond = concat(cond,'exists (select id from ' , equipmenttable ,' where eq_id=''' , element ,''' and room_id=r.id) ');
set tmpTxt = replace(tmpTxt, concat(element,';'), '');
set Pos = instr(tmpTxt,';');
end while;
if tmpTxt <> "" then
if cond <> "" then
set cond = concat(cond,' and ');
end if;
set cond = concat(cond,'exists (select id from ' , equipmenttable ,' where eq_id=''' , tmpTxt ,''' and room_id=r.id) ');
end if;
SET #statement = concat('Select * FROM ' , roomtable , " WHERE " , cond , ";");
PREPARE stmt FROM #statement;
EXECUTE stmt;
END
Execute it with: CALL GetRooms('RoomTableName','EquipmentTableName','EquipmentIDs')
Example:
Call GetRooms('rooms','equipemnts','eq1;eq2;eq3');
Hope this helps.
To Execute Query Faster use Exists
select *
from rooms as r
where exists (
select *
from equipments
where eq_id IN ('eq1','eq2',..,'eqN') and r.id= equipments.room_id);