Mysql function not returning response to Hibernate - java

I have a mysql function that is being called from a service class of a web application using HibernateCallback class of Spring. This function selects some rows using a cursor, then does some update and insert. It then returns a varchar.
When the rows selected by cursor are few hundreds, everything is fine. But when number of rows was 12000, the calling java code did not receive any response, even for 1 hour.
When we check in mysql slow query log and error log, it shows that mysql function has run in 8 seconds and lock time was 0.7 seconds. But java program is not getting any response. Also, whenever this function is called, mysql process list goes very high.
What could be the possible reason of no response, when as per mysql logs, function has run?
The function is as follows:
CREATE FUNCTION fun_test(
param1 varchar(15),
param2 varchar(20),
v_startTime timestamp,
v_endTime timestamp)
RETURNS varchar(50) DETERMINISTIC
begin
declare v_no_more_rows BOOLEAN;
declare v_id int ;
declare v_count int default 0;
declare v_amount double ;
declare v_totalAmount double default 0;
DECLARE cur1 CURSOR FOR
SELECT id,amount
from table1
where col1=param1
and updatedat>= v_startTime
and updatedat< v_endTime
and amount>0;
DECLARE CONTINUE HANDLER FOR NOT FOUND
SET v_no_more_rows = TRUE;
OPEN cur1;
loop_cur1: LOOP
FETCH cur1
INTO v_id,v_amount;
IF v_no_more_rows THEN
CLOSE cur1;
LEAVE loop_cur1;
END IF;
insert into table2 (parentid,createdAt) values(v_id,current_timestamp());
update table1
set updatedat = current_timestamp()
where
id = v_id;
set v_totalAmount=v_totalAmount+v_amount;
set v_count=v_count+1;
END LOOP loop_cur1;
return concat("SUCCESS:",v_totalAmount,":",v_count);
end ;
Edit: Adding mysql slow query log results for this function:
Time: 140116 11:22:39
User#Host: host # [x.x.x.x]
Query_time: 7.868044 Lock_time: 0.852301 Rows_sent: 1 Rows_examined: 0
SET timestamp=1389851559;
select fun_test('a','b','2014-01-01 11:00:00','2014-01-01 12:00:00');

Related

How to make a Mysql insert statement conditionally occur depending on the result of a 1 Select Statement

Consider what my Ajax call looks like (below the code I explain the issue):
$.ajax({
type : "GET",
url : "servlet",
data: {course_name_param:selectedOption},
dataType:"json",
success : function(data) {
$('#startDate').empty();
$('#startDate').append('<option value="">Select Start Date</option>');
$.each(data, function(index, jsonData) {
/*Here below is where I check availability*/
if(jsonData.students_registered < jsonData.max_size){
/*Display available courses*/
}
}
I have a MySQL table that holds information on courses available for educational sessions. 1 of these fields is available slots(maximum capacity).
Registration is an insert statement. I need to control this by checking the slots actually available when inserting.
Currently, I perform the check with Ajax calls to this table. The check is useless when multiple users load the page relatively close in timing(less than a minute).
Question:
How can I accomplish this away from the front-end?
Is this something that should be included into the class that holds the logic for my
Insert statements?
I don't see this scenario being considered in SO questions, only in the case of avoiding duplicate entries, this is not my case.
Any suggestions would be greatly appreciated.
You could simply structure your query in such a way that you only pull back the courses with open slots. Something like this might give you an idea:
SELECT courses.title
FROM courses
WHERE students_registered < max_size
If you want to ensure validation on the database, you can use a conditional insert:
INSERT INTO enrollments(course_id, student_id)
SELECT <your course id>, <your student id> FROM dual
WHERE EXISTS (
SELECT * FROM courses
WHERE id = <your course id>
AND students_registered < max_size
)
A "before insert" trigger on the table where the user inserts. I went with this approach. Then handling the SQLException in the application:
CREATE DEFINER=`root`#`localhost` TRIGGER before_insert_registration
BEFORE INSERT ON registration
FOR EACH ROW
BEGIN
DECLARE num_rows INTEGER;
Declare x INTEGER;
SET x = NEW.course_id;
SELECT
COUNT(*)
INTO num_rows FROM courses
WHERE
course_id = NEW.course_id
AND maxsize > students_registered;
IF num_rows < 1 THEN
SIGNAL SQLSTATE '45000' SET MESSAGE_TEXT = 'Testing Custom Fail-Over';
END IF;
END

How to upsert(update if exists, else insert) into a table using jdbcTemplate [duplicate]

The UPSERT operation either updates or inserts a row in a table, depending if the table already has a row that matches the data:
if table t has a row exists that has key X:
update t set mystuff... where mykey=X
else
insert into t mystuff...
Since Oracle doesn't have a specific UPSERT statement, what's the best way to do this?
The MERGE statement merges data between two tables. Using DUAL
allows us to use this command. Note that this is not protected against concurrent access.
create or replace
procedure ups(xa number)
as
begin
merge into mergetest m using dual on (a = xa)
when not matched then insert (a,b) values (xa,1)
when matched then update set b = b+1;
end ups;
/
drop table mergetest;
create table mergetest(a number, b number);
call ups(10);
call ups(10);
call ups(20);
select * from mergetest;
A B
---------------------- ----------------------
10 2
20 1
The dual example above which is in PL/SQL was great becuase I wanted to do something similar, but I wanted it client side...so here is the SQL I used to send a similar statement direct from some C#
MERGE INTO Employee USING dual ON ( "id"=2097153 )
WHEN MATCHED THEN UPDATE SET "last"="smith" , "name"="john"
WHEN NOT MATCHED THEN INSERT ("id","last","name")
VALUES ( 2097153,"smith", "john" )
However from a C# perspective this provide to be slower than doing the update and seeing if the rows affected was 0 and doing the insert if it was.
An alternative to MERGE (the "old fashioned way"):
begin
insert into t (mykey, mystuff)
values ('X', 123);
exception
when dup_val_on_index then
update t
set mystuff = 123
where mykey = 'X';
end;
Another alternative without the exception check:
UPDATE tablename
SET val1 = in_val1,
val2 = in_val2
WHERE val3 = in_val3;
IF ( sql%rowcount = 0 )
THEN
INSERT INTO tablename
VALUES (in_val1, in_val2, in_val3);
END IF;
insert if not exists
update:
INSERT INTO mytable (id1, t1)
SELECT 11, 'x1' FROM DUAL
WHERE NOT EXISTS (SELECT id1 FROM mytble WHERE id1 = 11);
UPDATE mytable SET t1 = 'x1' WHERE id1 = 11;
None of the answers given so far is safe in the face of concurrent accesses, as pointed out in Tim Sylvester's comment, and will raise exceptions in case of races. To fix that, the insert/update combo must be wrapped in some kind of loop statement, so that in case of an exception the whole thing is retried.
As an example, here's how Grommit's code can be wrapped in a loop to make it safe when run concurrently:
PROCEDURE MyProc (
...
) IS
BEGIN
LOOP
BEGIN
MERGE INTO Employee USING dual ON ( "id"=2097153 )
WHEN MATCHED THEN UPDATE SET "last"="smith" , "name"="john"
WHEN NOT MATCHED THEN INSERT ("id","last","name")
VALUES ( 2097153,"smith", "john" );
EXIT; -- success? -> exit loop
EXCEPTION
WHEN NO_DATA_FOUND THEN -- the entry was concurrently deleted
NULL; -- exception? -> no op, i.e. continue looping
WHEN DUP_VAL_ON_INDEX THEN -- an entry was concurrently inserted
NULL; -- exception? -> no op, i.e. continue looping
END;
END LOOP;
END;
N.B. In transaction mode SERIALIZABLE, which I don't recommend btw, you might run into
ORA-08177: can't serialize access for this transaction exceptions instead.
I'd like Grommit answer, except it require dupe values. I found solution where it may appear once: http://forums.devshed.com/showpost.php?p=1182653&postcount=2
MERGE INTO KBS.NUFUS_MUHTARLIK B
USING (
SELECT '028-01' CILT, '25' SAYFA, '6' KUTUK, '46603404838' MERNIS_NO
FROM DUAL
) E
ON (B.MERNIS_NO = E.MERNIS_NO)
WHEN MATCHED THEN
UPDATE SET B.CILT = E.CILT, B.SAYFA = E.SAYFA, B.KUTUK = E.KUTUK
WHEN NOT MATCHED THEN
INSERT ( CILT, SAYFA, KUTUK, MERNIS_NO)
VALUES (E.CILT, E.SAYFA, E.KUTUK, E.MERNIS_NO);
I've been using the first code sample for years. Notice notfound rather than count.
UPDATE tablename SET val1 = in_val1, val2 = in_val2
WHERE val3 = in_val3;
IF ( sql%notfound ) THEN
INSERT INTO tablename
VALUES (in_val1, in_val2, in_val3);
END IF;
The code below is the possibly new and improved code
MERGE INTO tablename USING dual ON ( val3 = in_val3 )
WHEN MATCHED THEN UPDATE SET val1 = in_val1, val2 = in_val2
WHEN NOT MATCHED THEN INSERT
VALUES (in_val1, in_val2, in_val3)
In the first example the update does an index lookup. It has to, in order to update the right row. Oracle opens an implicit cursor, and we use it to wrap a corresponding insert so we know that the insert will only happen when the key does not exist. But the insert is an independent command and it has to do a second lookup. I don't know the inner workings of the merge command but since the command is a single unit, Oracle could execute the correct insert or update with a single index lookup.
I think merge is better when you do have some processing to be done that means taking data from some tables and updating a table, possibly inserting or deleting rows. But for the single row case, you may consider the first case since the syntax is more common.
A note regarding the two solutions that suggest:
1) Insert, if exception then update,
or
2) Update, if sql%rowcount = 0 then insert
The question of whether to insert or update first is also application dependent. Are you expecting more inserts or more updates? The one that is most likely to succeed should go first.
If you pick the wrong one you will get a bunch of unnecessary index reads. Not a huge deal but still something to consider.
Try this,
insert into b_building_property (
select
'AREA_IN_COMMON_USE_DOUBLE','Area in Common Use','DOUBLE', null, 9000, 9
from dual
)
minus
(
select * from b_building_property where id = 9
)
;
From http://www.praetoriate.com/oracle_tips_upserts.htm:
"In Oracle9i, an UPSERT can accomplish this task in a single statement:"
INSERT
FIRST WHEN
credit_limit >=100000
THEN INTO
rich_customers
VALUES(cust_id,cust_credit_limit)
INTO customers
ELSE
INTO customers SELECT * FROM new_customers;

Before insert trigger in mysql giving issue when insert is fired from Java code

DELIMITER $$
CREATE TRIGGER `classdost`.`tr_xyz_media`
BEFORE INSERT ON classdost.xyz_media
FOR EACH ROW
BEGIN
DECLARE t_id INT(20);
IF NEW.id < 5000000 THEN
INSERT INTO xyz_media_temp (insert_date) VALUES(CURDATE());
SELECT MAX(xyz_media_temp.id) INTO t_id FROM xyz_media_temp;
SET New.id = t_id;
END IF;
END$$
DELIMITER ;
I have this trigger in MySQL this is running fine when I fire any Insert query in PHPMyadmin but when same Insert is fired from java code it is not executing. I tried changing the code made all request as Ajax with async: false and then I found out that that the earlier requests are still running in background and my later request is giving exception as no ID is returned from database yet.
What can be done to avoid this issue?
If you say requests are still running in background, then it is possible that your table is locked (MyISAM tables), and you cannot modify it.
See if table is locked using this query -
SHOW OPEN TABLES FROM database_name;
This query will show you open tables, whicj cannot be edited (INSERT, UPDATE or etc.). Then check you Java application, find code that locks tables.

Detect, delete empty columns and update database in sql, oracle

I have 100 of columns and some of the doesn't have any values inside(they are empty) how can I search for empty columns and delete from table and update database? I tried this query but it doesnt work. It shows 0 rows selected. After selecting how can I update the database?
select table_name, column_name
from all_tab_columns
where table_name='some_table'
and column_name is NULL;
Thanks,
You are querying a data dictionary view. It shows meta-data, in formation about the database. This view, ALL_TAB_COLUMNS, shows information for every column of every table (you have privileges on). Necessarily COLUMN_NAME cannot be null, hence your query returns no rows.
Now what you want to do is query every table and find which columns have no data in them. This requires dynamic SQL. You will need to query ALL_TAB_COLUMNS, so you weren't completely off-base.
Because of dynamic SQL this is a programmatic solution, so the results are displayed with DBMS_OUTPUT.
set serveroutput on size unlimited
Here is an anonymous block: it might take some time to run. The join to USER_TABLES is necessary because columns from views are included in TAB_COLUMNS and we don't want those in the result set.
declare
dsp varchar2(32767);
stmt varchar2(32767);
begin
<< tab_loop >>
for trec in ( select t.table_name
from user_tables t )
loop
stmt := 'select ';
dbms_output.put_line('table name = '|| trec.table_name);
<< col_loop >>
for crec in ( select c.column_name
, row_number() over (order by c.column_id) as rn
from user_tab_columns c
where c.table_name = trec.table_name
and c.nullable = 'Y'
order by c.column_id )
loop
if rn > 1 then stmt := concat(stmt, '||'); end if;
stmt := stmt||''''||crec.column_name||'=''||'
||'to_char(count('||crec.column_name||')) ';
end loop col_loop;
stmt := stmt || ' from '||trec.table_name;
execute immediate stmt into dsp;
dbms_output.put_line(dsp);
end loop tab_loop;
end;
sample output:
table name = MY_PROFILER_RUN_EVENTS
TOT_EXECS=0TOT_TIME=0MIN_TIME=0MAX_TIME=0
table name = LOG_TABLE
PKG_NAME=0MODULE_NAME=0CLIENT_ID=0
PL/SQL procedure successfully completed.
SQL>
Any column where the COUNT=0 has no values in it.
Now whether you actually want to drop such columns is a different matter. You might break programs which depend on them. So you need an impact analysis first. This is why I have not produced a program which automatically drops the empty columns. I think that would be dangerous practice.
It is crucial that changes to our database structure are considered and audited. So if I were ever to undertake an exercise like this I would alter the output from the program above so it produced a script of drop column statements which I could review, edit and keep under source control.

concurrency with h2 database

I have a table xxx with id (id_xxx int AUTO_INCREMENT ) and name (name_xxx varchar (50)),
When I insert a new row in the table I made​​:
INSERT INTO xxx VALUES ​​("name for test");
and the result (int=1) of insertion is returned, then I display in my java interface a message "succseed!", until now it's a very basic and simple operation...
BUT,
when I want to return the inserted id_xxx,I have to do another query to the database:
INSERT INTO xxx VALUES ​​("name for test");
//after the insert response I made:
SELECT MAX (id_xxx) FROM xxx;
and I display in my java interface "succseed $$$ is your id_xxx "....
the second version can easily cause a serious error during concurrent access to multiple users:
imagine a case when a user1 makes an insert... and then H2DB interrupt operations of this user then executes the insert of user2.
when user1 executes a select max (id_xxx) the H2DB return A FALSE id_xxx...
(I hope that my example is clear otherwise I will schematize this problem).
how to solve this problem?
You should be able to retrieve keys generated by insert query, see 5.1.4 Retrieving Automatically Generated Keys.

Categories

Resources