Selecting next related row in MySQL - mysql

I have a spatial dataset in MySQL 5.7 where I have the columns: id, deviceid, latlng_point, time. latlng_point is a geospatial Point.
What I'm trying to achieve is calculating distance from points. I'm unsure on how to approach this.
SELECT
ST_DISTANCE_SPHERE(latlng_point, i want the next latlng_point here) AS distance
FROM points
WHERE deviceid = 1
ORDER BY time DESC;
In PHP I would do something like this:
<?php
$conn = new mysqli($host,$user,$pass,$db);
$query = "SELECT latlng_point FROM points WHERE deviceid = 1...";
$latlng_array = array();
if ($result = $conn->query($query)) {
while ($row = $result->fetch_assoc()) {
$latlng_array[] = $row;
}
}
$distance = 0;
for ($i = 0; $i < count($latlng_array) - 1; $i++) {
$pt1 = $latlng_array[$i]['latlng_point'];
$pt2 = $latlng_array[$i+1]['latlng_point'];
$distance += haversine_function($pt1,$pt2);
}
echo "Distance: {$distance}";
?>
I'm trying to achieve something similar purely in MySQL.

Try this one:
SELECT SUM(ST_DISTANCE_SPHERE(p1.latlng_point, p2.latlng_point)) AS total_distance
FROM points p1
JOIN points p2 ON p2.id = (
SELECT p.id
FROM points p
WHERE p.deviceid = p1.deviceid
AND p.time > p1.time
ORDER BY p.time ASC
LIMIT 1
)
WHERE p1.deviceid = 1
The (correlated) subquery should return the id of the next point (sorted by time).
I can't tell you if it is really efficient or if it even works at all (can't test it).
However you should have an index on (deviceid, time) - Assuming that id is the primary key.

Related

Get maximum value of row in mysql ,using codeigniter

Get maximum value of row in mysql using codeigniter.
i have following mysql data
I need to get the details based on total_payment of each student.
like this
i tried code below
$student_id_dis = $this->db->query('SELECT DISTINCT(student_id) FROM student_fees')->result_array();
$fee_cat_id_dis = $this->db->query('SELECT DISTINCT(fee_category_id) FROM student_fees')->result_array();
$this->db->select(['student_fees.*', 'fee_categories.fee_category_name as fee_name', 'fee_categories.amount as fee_amount']);
$this->db->join('student', 'student_fees.student_id = student.student_id');
$this->db->join('fee_categories', 'student_fees.fee_category_id = fee_categories.fee_categories_id');
$where = '';
for ($i = 0; $i < count($student_id_dis); $i++) {
if (isset($fee_cat_id_dis[$i]['fee_category_id'])) {
$where .='total_paid = (SELECT max(stdp.total_paid)
FROM student_fees stdp
WHERE stdp.fee_category_id = ' . $fee_cat_id_dis[$i]['fee_category_id'] . ')';
}
$this->db->where($where);
$this->db->get('student_fees')->result_array();
}
try this
select * from student_fees where student_fees_id in
(select student_fees_id from
where total_paid =max(total_paid) group by fee_category_id)

Which one is Faster, SUB-Query or IN-Query?

If I want to select whether i am following the threads or not.
I have two approaches to do so... but I don't know which one would be more better in terms of performance and speed. Can anyone help me out?
Approach 1
$cui = $_SESSION['user_id'];
$data = mysqli_query($con,"
SELECT t.*,( select count(follow_id) from follows where object_id =
t.thread_id AND object_type='thread' AND user_id = $cui) as me_follow FROM threads t
");
while($row = mysqli_fetch_assoc($data)){
/*
$row['me_follow'] = 0 if i aint following
$row['me_follow'] = 1 if i am following
*/
}
Approach 2
$cui = $_SESSION['user_id'];
$data = mysqli_query($con,"SELECT * FROM threads");
$ids = array();
while($row = mysqli_fetch_assoc($data)){
$ids[] = $row['thread_id'];
}
$ids = join($ids,",");
$data = mysqli_query($con,"SELECT COUNT(*) FROM follows WHERE object_id IN($ids) AND user_id = $cui");
One round-trip wins over two. This is because there is some overhead in sending SQL to the server, having it parse it, execute it and send the results back. It is (usually) better to do everything in one round-trip.

Database query for desired results

I am using some filters to display the products. Filters like colors, price and stuff.
Link : http://www.applechain.com/products/iPod.php
I use this query
$sql = "Select *
from tbl_product
where device='iPhone'
and (color='$c1'
or color='$c2'
or color='$c3'
or color='$c4'
or color='$c5'
or color='$c6'
or color='$c7'
or color='$c8'
or color='$c9'
or color='$c10'
) and (storage='$cp1'
or storage='$cp2'
or storage='$cp3'
or storage='$cp4'
or storage='$cp5'
) and (f_unlock='$factory')
and (warranty='$warranty')
and (price >= '$price1'
and price <= '$price2'
)
order by product_id desc";
I am using the AND condition for main parameters. Now how do I display the result if my only two parameters gets satisfied. How to achieve that if color and storage parameters gets satisfied,it shows result based on them only irrespective of whether the others are selected or not.
Simple. Remove the other conditions from the query.
BTW, you can rewrite that query this way...
SELECT *
FROM tbl_product
WHERE device = 'iPhone'
AND color IN ('$c1','$c2','$c3','$c4','$c5','$c6','$c7','$c8','$c9','$c10')
AND storage IN ('$cp1','$cp2','$cp3','$cp4','$cp5')
AND f_unlock = '$factory'
AND warranty = '$warranty'
AND price BETWEEN '$price1' and '$price2'
ORDER
BY product_id DESC;
You could build your query based on what is set, normally an ORM tool would do this for you.
As it's probably too late to start using a PHP framework, you should do something like:
if(isset($colours))
{
$sub_query = concatenateOrClauses($colours, 'color');
$main_query .= $sub_query;
}
private function concatenateOrClauses($values, $name)
{
$query_string = "";
for($i = 0; $i < sizeof($values); $i++)
{
if($i == 0)
{
$query_string = $name."=".$values[$i];
}
else
{
$query_string = $query_string." OR ".$name."=".$values[$i];
}
}
return $query_string;
}

Perl with mysql, terribly slow, how to accelerate

unit
id fir_name sec_name
author
id name unit_id
author_paper
id author_id paper_id
I want to unify authors['same author' means the names are the same and their units' fir_names are the same], and I have to change author_paper table at the same time.
Here is what i do:
$conn->do('create index author_name on author (name)');
my $sqr = $conn->prepare("select name from author group by name having count(*) > 1");
$sqr->execute();
while(my #row = $sqr->fetchrow_array()) {
my $dup_name = $row[0];
$dup_name = formatHtml($dup_name);
my $sqr2 = $conn->prepare("select id, unit_id from author where name = '$dup_name'");
$sqr2->execute();
my %fir_name_hash = ();
while(my #row2 = $sqr2->fetchrow_array()) {
my $author_id = $row2[0];
my $unit_id = $row2[1];
my $fir_name = getFirNameInUnit($conn, $unit_id);
if (not exists $fir_name_hash{$fir_name}) {
$fir_name_hash{$fir_name} = []; #anonymous arr reference
}
$x = $fir_name_hash{$fir_name};
push #$x, $author_id;
}
while(my ($fir_name, $author_id_arr) = each(%fir_name_hash)) {
my $count = scalar #$author_id_arr;
if ($count == 1) {next;}
my $author_id = $author_id_arr->[0];
for ($i = 1; $i < $count; $i++) {
#print "$author_id_arr->[$i] => $author_id\n";
unifyAuthorAndAuthorPaperTable($conn, $author_id, $author_id_arr->[$i]); #just delete in author table, and update in author_paper table
}
}
}
select count(*) from author; #240,000
select count(distinct(name)) from author; #7,7000
It is terribly slow!!I've runned it for 5hours, it just removed about 4,0000 dup names.
How to make it run faster.I am eager for your advice
You should not prepare the second sql statement within the loop and you can make real use of the preparation when you use the ? placeholder:
$conn->do('create index author_name on author (name)');
my $sqr = $conn->prepare('select name from author group by name having count(*) > 1');
# ? is the placeholder and the database driver knows if its an integer or a string and
# quotes the input if needed.
my $sqr2 = $conn->prepare('select id, unit_id from author where name = ?');
$sqr->execute();
while(my #row = $sqr->fetchrow_array()) {
my $dup_name = $row[0];
$dup_name = formatHtml($dup_name);
# Now you can reuse the prepared handle with different input
$sqr2->execute( $dup_name );
my %fir_name_hash = ();
while(my #row2 = $sqr2->fetchrow_array()) {
my $author_id = $row2[0];
my $unit_id = $row2[1];
my $fir_name = getFirNameInUnit($conn, $unit_id);
if (not exists $fir_name_hash{$fir_name}) {
$fir_name_hash{$fir_name} = []; #anonymous arr reference
}
$x = $fir_name_hash{$fir_name};
push #$x, $author_id;
}
while(my ($fir_name, $author_id_arr) = each(%fir_name_hash)) {
my $count = scalar #$author_id_arr;
if ($count == 1) {next;}
my $author_id = $author_id_arr->[0];
for ($i = 1; $i < $count; $i++) {
#print "$author_id_arr->[$i] => $author_id\n";
unifyAuthorAndAuthorPaperTable($conn, $author_id, $author_id_arr->[$i]); #just delete in author table, and update in author_paper table
}
}
}
This should speed up things as well.
The moment I see a query and a loop I think that you have a latency problem: you query to get a set of values and then iterate over the set to do something else. That's a LOT of latency if it means a network round trip to the database for each row in the set.
It'd be better if you could do it in a single query using an UPDATE and a sub-select OR if you could batch those requests and perform all of them in one round trip.
You'll get an additional speed up if you use indexes wisely. Every column in a WHERE clause should have an index. Every foreign key should have an index.
I'd run EXPLAIN PLAN on your queries and see if there are any TABLE SCAN going on. If there is, you've got to index properly.
I wonder if a properly designed JOIN would come to your rescue?
240,000 rows in one table and 77,000 in another isn't that large a database.

Dynamic mysql query LIMIT not working

I'm creating the parameters to LIMIT a mysql query using PHP variables. I'm outputting the query to make sure it's right, and it is. However the results completely ignore the offset parameter. However if I manually type in the LIMIT, it works fine.
$page_rows = 5;
$max = ($pagenum - 1) * $page_rows .',' .$page_rows;
$qry = "SELECT * FROM ".$wpdb->prefix."nslikethis_votes WHERE user_id = '$uid' AND active = 1 ORDER BY time DESC LIMIT " . $max;
$rs = mysql_query($qry);
$result = array();
if($rs && $rows>0){
while($lc = mysql_fetch_object($rs, "LikedContent")){
$result[] = $lc;
}
}
return $result;
Outputting $qry gives me this whether I use $max or manually enter '5,5':
SELECT * FROM wp_nslikethis_votes WHERE user_id = '1' AND active = 1 ORDER BY time DESC LIMIT 5,5
Try doing this for check:
$page_rows = "5";
$max = ($pagenum - 1) * $page_rows .',' .$page_rows;
$qry = "SELECT * FROM ".$wpdb->prefix."nslikethis_votes WHERE user_id = '$uid' AND `active` = 1 ORDER BY `time` DESC LIMIT " . $max;
//check for variables in query like $wpdb->prefix, $uid: are they fine, and try adding tilded sign as above
Hope this works for you