How to create a view that calls stored functions in MySQL? - mysql

I have created 2 tables. One contains 5 golf courses (name & address). the other is the par and yards for all 18 holes per golf course (foreign key to link to courses). I have created a function getYards to return the number of yards for a course. I have created a 2nd function, getPar, to return the total par for a course. These each work. I now need to create a view to display the course name and the par and yardage using the 2 functions.
getYards function:
DELIMITER $$
CREATE FUNCTION getYards (id INT)
RETURNS INT (5)
BEGIN
DECLARE totalyards INT;
SELECT SUM(hole_yards) INTO totalyards FROM golf_holes WHERE id=golf_id;
RETURN totalyards;
END$$
DELIMITER ;
The function for getPar is very similar using hole_par instead of yards.
So now I want to create a view with course name, par and yardage using the getPar and getYards functions. I have tried searching and I'm just not knowledgeable enough to understand. I know this is vague, but can anyone help me ... keep it simple! thanx in advance.
I've been a cobol programmer for almost 40 years. I am trying to learn MySQL so have very little knowledge with SQL.

If you want to keep it simple, I would not recommend using stored functions in your view definition.
Instead you can just join the two tables.
Something like this should work for you:
create or replace view v_golf_courses as
select golf_courses.id as golf_course_id,
golf_courses.name as golf_course_name,
sum(golf_holes.yards) as course_yardage,
sum(golf_holes.par) as course_par
from golf_courses
inner join golf_holes on golf_holes.golf_course_id = golf_courses.id
group by golf_courses.id, golf_courses.name;

Assuming the id column in your courses table is also named golf_id
SELECT name, getPar(golf_id) as par, getYards(golf_id) as yards
FROM courses
However you don't really need UDF's for this:
SELECT a.name, sum(b.hole_par) as total_par, sum(b.hole_yards) as total_yards
FROM courses a
JOIN golf_holes b
ON a.golf_id = b.golf_id
GROUP BY a.golf_id

Related

How to optimized my code in Mysql?

I hava three tables called t_asset,t_device and t_asset_device.The relationship between t_asset and the t_device is multiple pairs.Each table column is :
t_asset :id , asset_name,asset_code,create_time,creator
t_device:id, device_name,device_code,latitude,longitude,create_time,creator
t_assets_device:id,asset_id,device_id,create_time,creator
Now I want to get all the t_asset and the latitude,longitude of the first device,So I write the code and function like these:
fun_getLatitudeByAssetId(`assetId` varchar(50)){
BEGIN
declare v_latituede DECIMAL(10,5) DEFAULT(-1) ;
select latitude into v_latituede
from t_device tDevice
inner join t_assets_device tAssetsDevice
on tAssetsDevice.asset_id=assetId and
tDevice.id=tAssetsDevice.device_id
and tDevice.latitude!=-1
ORDER BY tDevice.id desc
limit 0,1;
return v_latituede;
END
}
fun_getLongititueByAssetId(`assetId` varchar(50)){
BEGIN
declare v_longititue DECIMAL(10,5) DEFAULT(-1) ;
select longititueinto v_longititue
from t_device tDevice
inner join t_assets_device tAssetsDevice
on tAssetsDevice.asset_id=assetId and
tDevice.id=tAssetsDevice.device_id
and tDevice.latitude!=-1
ORDER BY tDevice.id desc
limit 0,1;
return v_longititue ;
END
}
The final query sql is:
select tAsset.*,fun_getLatitudeByAssetId(tAsset.id) latitude,
fun_getLongititueByAssetId(tAsset.id) longititue from t_asset tAsset
It seems that I have query the latitude and longititue two times,If I want to get the other field from the t_device,I do not want to write another function
like fun_getDeviceCodeByAssetId, How can I optimized my code?
I don't think a function or procedure is the way to go - why not just define a view that has asset_id + all the other fields you want? Then just join to it on asset_id rather than calling functions. In addition to just being cleaner, I'd be concerned about performance with row rather than set processing with the function approach (this is total speculation, I don't have deep enough knowledge of MySQL to know how it's handled)
Is it really necessary to do it with functions?
You can do it with views, for example:
create view latitudeLongitude as
select latitude,longitude,asset_id
from t_device tDevice
inner join t_assets_device tAssetsDevice
on tDevice.id=tAssetsDevice.device_id
and tDevice.latitude!=-1;
Finally your last select should look like this:
select tAsset.*,latitudeLongitude.latitude,
latitudeLongitude.longititue
from t_assettAsset inner join latitudeLongitude
on t_assettAsset.id = latitudeLongitude.asset_id
If you're trying to return several values at once then you should rather declare a stored procedure, not a function. Then you'll be able to write select latitude, longitude from ... inside your procedure and then call it with a command like call getLatAndLong(...)

Check each value have already exceeded the limit in PostgreSQL with trigger and rise a notice

What I want here is to get which subject has already 4 classrooms, if it exceeds raise a notice and return null, and works. But when I insert other subject "2" the code raise the same notice, but I this subject has only 1 classrom. I know I'm using "HAVING COUNT(cod_classroom) = 4" and the code only get what the subject has already 4 classrooms. I tried to use this only: "SELECT DISTINCT cod_subject,COUNT(cod_classroom) AS CountOf FROM registration_subject GROUP BY cod_subject" But I dont know how to check more values.
I hope you all understand what I want, I tried my best =) Thank you all in advance
CREATE OR REPLACE FUNCTION quantas()
RETURNS trigger AS
$BODY$declare qtd record;
begin
SELECT * INTO qtd FROM (SELECT DISTINCT cod_subject,COUNT(cod_classroom) AS CountOf FROM registration_subject
GROUP BY cod_subject HAVING COUNT(cod_classroom) = 4) AS total;
if found then
raise notice 'This subject has already 4 classroom';
return null;
end if;
end;$BODY$
LANGUAGE plpgsql VOLATILE
COST 100;
ALTER FUNCTION qtd()
OWNER TO postgres;
In addition to the issue Craig mentions in his comment (the need to lock IN EXCLUSIVE MODE), I think you have a couple of other problems.
Your query is this and it doesn't make much sense to me.
SELECT * INTO qtd
FROM (SELECT DISTINCT cod_subject,COUNT(cod_classroom) AS CountOf
FROM registration_subject
GROUP BY cod_subject HAVING COUNT(cod_classroom) = 4) AS total;
I am not convinced this is a very helpful way to do this query. Indeed it rather helpfully declines all inserts once any subject at all has 4 classrooms.
I would recommend instead:
PERFORM count(cod_classroom)
FROM registration_subject
WHERE cod_subject = NEW.subject
HAVING count(*) >= 4;
PERFORM is used in plpgsql when you don't care about using the result. Here you only care if it is found or not. Secondly, in the event where something goes wrong and a stray insert is allowed, you will still block subsequent inserts. Your old code will block inserts to all subjects if any subject has exactly four classrooms, but 5 classrooms is ok, which is not what you want either.

How to identify sequenced record gaps by field on MySQL

I can find sequenced record gaps where sequenced weeks with same numbers using following query.
SELECT * FROM pointed_numbers A WHERE EXISTS (
SELECT * FROM pointed_numbers B WHERE A.number = B.number AND (A.week = B.week + 1 XOR A.week = B.week - 1)
) ORDER BY A.number, A.week;
How can I identify each gaps without stored procedure. I have tried with user-defined variable but I had no success.
Take a look at http://www.artfulsoftware.com/infotree/queries.php and look at the stuff under the "sequences" section. This is a super super helpful site with recipes for how to do complicated things in mysql!

MySQL design; View or Stored Procedure with variable

I've to execute a complex query, selecting several columns from 7-8 tables.
We don't want to write that query in programming language (PHP - Symfony 1.4/Propel 1.4 in our case) but to create a view or stored procedure to have very simple select query for developers. I'm confused what will be better approach.
We need query in following format:
SET #PlayerId = 1;
SELECT CASE WHEN mat.player1id = #PlayerId THEN mat.player2id ELSE mat.player1id END as opponent
/*plus many other columns*/
FROM `wzo_matches` as mat /*plus few other tables*/
WHERE (mat.player1id =#PlayerId OR mat.player2id=#PlayerId)
/*plus other join conditions*/
Problem with view is, SET #PlayerId=xx statement. We don't know player id in advance but will be passed through PHP. I hope this is the reason to rule out views; is there any workaround for that?
Other option will be stored procedure. Only issue with that is, it will create a new view for every query so operation will be very heavy for DB.
Can someone suggest best approach so that developers can get required data from above query without writing above complex query in PHP. (Obviously through SP or view & simple select query from there)
Based on reply of Can I create view with parameter in MySQL?, My issue is fixed with following queries:
create function getPlayer() returns INTEGER DETERMINISTIC NO SQL return #getPlayer;
create view getPlay as
SELECT
CASE WHEN play.hiderid = getPlayer() THEN play.seekerid ELSE play.hiderid END AS opponent, play . *
FROM odd_play play, odd_match mat
WHERE (seekerid = getPlayer() OR hiderid = getPlayer())
AND play.id = mat.latestplay;
select play.*
from (select #getPlayer:=1 p) ply, getPlay play;
CREATE PROCEDURE SELECT_PLAYER(p INT) SET #PlayerId = p
SELECT CASE WHEN mat.player1id = #PlayerId THEN mat.player2id ELSE mat.player1id END as opponent
/*plus many other columns*/
FROM `wzo_matches` as mat /*plus few other tables*/
WHERE (mat.player1id =#PlayerId OR mat.player2id=#PlayerId)
/*plus other join conditions*/

design a stored procedure with multiple parameters and HAVING LIKE

I' m trying to write a stored procedure that will search a fairly simple database with
a USER table (user_id,name,...)
a USER_TYPE table (user_id,type_id) - multi to multi
a TYPE table (type_id,type_name)
a USER_GAME (user_id,game_id) -multi to multi
a GAME table (game_id,game_name)
A same user can have several games. Now, I want to be able to get the user according to a particular type and also according to a/some particular game(s), so that for example I can get all the user with, say type1, and with the games, say game2 and game5. I think I can get round the problem of several game names by passing them as a string parameter and do some kind of HAVING LIKE condition (I call get_user_spec('type1' , 'game3,game5') for example).
So far I get to that point:
CREATE DEFINER=`root`#`localhost` PROCEDURE `get_user_spec`(
IN inTypeName VARCHAR(50),
IN inGameName VARCHAR(150)
)
BEGIN
PREPARE statement FROM
"SELECT u.user_id,t.type_name,GROUP_CONCAT(g.game_name) AS game
FROM user u
INNER JOIN user_type ut
ON u.user_id=ut.user_id
INNER JOIN type t
ON ut.type_id=t.type_id
LEFT JOIN user_game ug
ON u.user_id=ug.user_id
LEFT JOIN game g
ON ug.game_id=g.game_id
WHERE t.type_name=?
GROUP BY u.user_id
HAVING game LIKE CONCAT('%',?,'%')
ORDER BY u.user_id";
SET #p1=inTypeName;
SET #p2=inGameName;
EXECUTE statement USING #p1,#p2;
END
But my real problem is that if I don't pass any game name, I then want to get all users with type1 (I then call get_user_spec('type1' , NULL). But I am then not getting anything as the procedure sees
HAVING game LIKE CONCAT('%',NULL,'%').
I hope that was clear enough. If anybody has any suggestions to get around that problem, I would be very grateful.
Thank you very much.
Change this line:
EXECUTE statement USING #p1,#p2;
to
EXECUTE statement USING #p1, ifnull(#p2, '');
This will cause the LIKE expression to be just '%%', which means "match everything"