How to optimized my code in Mysql? - 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(...)

Related

Use NEWID() without losing distinct?

I am trying to create a new data extract from a (badly designed) sql database. The customer requires that I add a distinctidentifier which I am attempting to do using the NEWID() function. Unfortunately this leads to multiple duplicate records being returned.
After a bit of research I have found that the NEWID() function does indeed 'undo' the use of the distinct keyword, but I cannot work out why or how to overcome this.
An example of the query I am trying to write is as follows:
select distinct
NEWID() as UUID
,Histo_Results_File.ISRN
,Histo_Results_File.Internal_Patient_No
,Histo_Results_File.Date_of_Birth
,Histo_Result_freetext.histo_report
,Histo_Report.Date_Report_Updated as [Investigation_Result_Date]
from apex.Histo_Results_File
inner join apex.Histo_Report on (Histo_Report.Histo_Results_File = Histo_Results_File.ID)
If I miss out the NEWID() line in the select block, I get 569 records returned, which is correct, but if I include that line then I get in excess of 30,000 which are all duplicates of the original 569 but with different IDs. Can anyone suggest a way around this problem?
Thanks in advance
Use a sub query would be the easiest way to do it.
SELECT NEWID() as UUID
, * -- this is everything from below
FROM (
select distinct
Histo_Results_File.ISRN
,Histo_Results_File.Internal_Patient_No
,Histo_Results_File.Date_of_Birth
,Histo_Result_freetext.histo_report
,Histo_Report.Date_Report_Updated as [Investigation_Result_Date]
from apex.Histo_Results_File
inner join apex.Histo_Report on (Histo_Report.Histo_Results_File = Histo_Results_File.ID)) as mySub
select NEWID() as UUID
,ISRN
,Internal_Patient_No
,Date_of_Birth
,histo_report
,Investigation_Result_Date
from (
select distinct
,Histo_Results_File.ISRN
,Histo_Results_File.Internal_Patient_No
,Histo_Results_File.Date_of_Birth
,Histo_Result_freetext.histo_report
,Histo_Report.Date_Report_Updated as [Investigation_Result_Date]
from apex.Histo_Results_File
inner join apex.Histo_Report on (Histo_Report.Histo_Results_File = Histo_Results_File.ID)) t
You can use a sub-query to get around the issue, something like.....
SELECT NEWID() as UUID
,*
FROM (
select distinct
Histo_Results_File.ISRN
,Histo_Results_File.Internal_Patient_No
,Histo_Results_File.Date_of_Birth
,Histo_Result_freetext.histo_report
,Histo_Report.Date_Report_Updated as [Investigation_Result_Date]
from apex.Histo_Results_File
inner join apex.Histo_Report
on (Histo_Report.Histo_Results_File = Histo_Results_File.ID)
) t

Record does not appear in view yet appears in result of query for view

In MySQL, I have defined a view on two tables as follows:
delimiter $$
CREATE ALGORITHM=UNDEFINED DEFINER=`root`#`localhost`<br/>
SQL SECURITY DEFINER VIEW `test`.`viewinschrijvingen` AS<br/>
select `i`.`student` AS `student`,<br/>
`i`.`opleidingscode` AS `opleidingscode`,<br/>
`i`.`inschrijvingsvorm` AS `inschrijvingsvorm`,<br/>
`i`.`brin` AS `brin`,<br/>
`i`.`brinvolgnummer` AS `brinvolgnummer`,<br/>
`o`.`onderwijsvorm` AS `onderwijsvorm`,<br/>
`o`.`opleidingniveau` AS `opleidingniveau`,<br/>
`o`.`naamopleidingkort` AS `naamopleidingkort`,<br/>
`o`.`instelling` AS `instelling`,<br/>
`o`.`studielast` AS `studielast`,<br/>
date_format(max(str_to_date(`i`.`datuminschrijving`,'%Y-%m-%d')),'%Y-%m-%d') AS `datuminschrijving`,<br/>
`o`.`gemeentenaam` AS `gemeentenaam` from<br/>
(`test`.`inschrijvingen` `i` left outer join `test`.`opleidingen` `o`<br/>
on((`i`.`opleidingscode` = `o`.`opleidingscode`)))<br/>
group by `i`.`opleidingscode`,`i`.`brin`,`i`.`brinvolgnummer`$$<br/>
When I query this view for the information on a specific student:
SELECT * FROM test.viewinschrijvingen WHERE student = '310018717'
the result is empty (no records returned). When I browse through the records in the view, there is no record for student 310018717 (obviously).
However, when I execute the query I used to create the view directly:
select `i`.`student` AS `student`,<br/>
`i`.`opleidingscode` AS `opleidingscode`,<br/>
`i`.`inschrijvingsvorm` AS `inschrijvingsvorm`,<br/>
`i`.`brin` AS `brin`,<br/>
`i`.`brinvolgnummer` AS `brinvolgnummer`,<br/>
`o`.`onderwijsvorm` AS `onderwijsvorm`,<br/>
`o`.`opleidingniveau` AS `opleidingniveau`,<br/>
`o`.`naamopleidingkort` AS `naamopleidingkort`,<br/>
`o`.`instelling` AS `instelling`,<br/>
`o`.`studielast` AS `studielast`,<br/>
date_format(max(str_to_date(`i`.`datuminschrijving`,'%Y-%m-%d')),'%Y-%m-%d') AS `datuminschrijving`,<br/>
`o`.`gemeentenaam` AS `gemeentenaam` from<br/>
(`test`.`inschrijvingen` `i` left outer join `test`.`opleidingen` `o`
on((`i`.`opleidingscode` = `o`.`opleidingscode`)))<br/>
WHERE student = '310018717'<br/>
group by `i`.`opleidingscode`,`i`.`brin`,`i`.`brinvolgnummer`<br/>
I do get a result (1 record, which is the result I expected). Can anybody help me to find what is causing this behaviour?
It probably has to do with your use of MySQL's GROUP BY extension versus the ANSI GROUP BY format. MySQL does not require you to group on every column that is not an aggregate function. For columns that you are not GROUPing on, MySQL can choose whatever value it wants for the column. In your case, you are not using the student field to group and thus it may not be choosing the value you are searching for.
You may want to try this query which uses the ANSI GROUP BY and see if you get the results you want.
delimiter $$
CREATE ALGORITHM=UNDEFINED DEFINER=`root`#`localhost`
SQL SECURITY DEFINER VIEW `test`.`viewinschrijvingen` AS
select
`i`.`student` AS `student`,
`i`.`opleidingscode` AS `opleidingscode`,
`i`.`inschrijvingsvorm` AS `inschrijvingsvorm`,
`i`.`brin` AS `brin`,
`i`.`brinvolgnummer` AS `brinvolgnummer`,
`o`.`onderwijsvorm` AS `onderwijsvorm`,
`o`.`opleidingniveau` AS `opleidingniveau`,
`o`.`naamopleidingkort` AS `naamopleidingkort`,
`o`.`instelling` AS `instelling`,
`o`.`studielast` AS `studielast`,
date_format(max(str_to_date(`i`.`datuminschrijving`,'%Y-%m-%d')),'%Y-%m-%d') AS `datuminschrijving`,
`o`.`gemeentenaam` AS `gemeentenaam`
from `test`.`inschrijvingen` `i`
left outer join `test`.`opleidingen` `o`
on `i`.`opleidingscode` = `o`.`opleidingscode`
group by
`i`.`student`,
`i`.`opleidingscode`,
`i`.`inschrijvingsvorm`,
`i`.`brin` AS `brin`,
`i`.`brinvolgnummer`,
`o`.`onderwijsvorm`,
`o`.`opleidingniveau`,
`o`.`naamopleidingkort`,
`o`.`instelling`,
`o`.`studielast`,
`o`.`gemeentenaam`$$

update row based on order in resultant table

I'm writing an online math testing program, and currently working on the scripts to calculate the rank that each user got. The following code works, but I cringe every time I see it.
get_set() puts the result of the query into $users
function rank_users_in_test($tid){
$GLOBALS['DB']->get_set($users,"select user,test from user_results where test=$tid order by points desc,time");
// $users are already in order by rank thanks to ORDER BY
$rank = 1;
foreach ($users as $u){
$GLOBALS['DB']->query("update user_results set world_rank=$rank where user={$u['user']} and test={$u['test']}");
$rank++;
}
}
The query in the loop makes me cry a bit. My question is, is there a way that MySQL can automatically update each user's rank based on the order they appeared in the result on the first query? There is a related question here, but it does not use UPDATE.
I'm using MySQL 5.
Thanks to ring0 above, the following reduced the running time from minutes to mere seconds :D
create table temp (
rank int auto_increment,
user int,
test int,
primary key(rank)
);
insert into temp(user,test) (select user,test from user_results where test=$tid order by points desc,time);
update user_results ur, temp t set ur.world_rank=t.rank where ur.user=t.user and ur.test=t.test;
drop table temp;

How to use a SQL Function with INNER JOIN in MySQL?

I have a function "fnc_FindIssueId" which accepts an object id and return its assigned issue Id.
When I call the function using pure select statements, it works fine:
select fnc_FindIssueId(150083); // returns 1 as issueId for objectId of 150083
select fnc_FindIssueId(150072); // returns 2 as issueId for objectId of 150072
But when I use it within an Inner Join, it goes into a never-ending loop:
select so.id, si.id
from smart_objects as so
LEFT OUTER join smart_issues as si
on si.id = fnc_FindIssueId(so.id)
where so.id in (150083, 150072);
What's the reason and how to resolve it?
It does not perform never-ending loop.
The reason for that is because the server performs FULL TABLE SCAN which is very slow. This condition si.id = fnc_FindIssueId(so.id) doesn't use an index even if you have define one on si.id and so.id.
The best ways you can do are:
to alter the table smart_objects
another column for the assigned issue Id
define an index on the new column
The workaround was to create a new view with ObjectId and IssueId columns then calling that function from within that view! but it has become very slow now.
CREATE ALGORITHM=UNDEFINED DEFINER=`mysql`#`%` SQL SECURITY DEFINER VIEW `vw_smart_objectissue` AS select `so`.`id` AS `objectid`,`fnc_FindIssueId`(`so`.`id`) AS `issueid` from `smart_objects` `so` order by `so`.`id`$$

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*/