calculation within table row by row - php

I have a table in database as followings:
locationname postcode locationlatitude locationlongitude
--------------------------------------------------------
1. gsfs 2322. 352353. 35235235
2. gsfs 2322. 352443. 352353
3. gsfs 2322. 352353. 35235235
.
.
I want to calculate the distance between each location to all locations in the table(many to many). I have been able to compute one-to-many and one-to-one calculation but I couldn't figure out how to calculate distance from every location to every location. Do I have to use nested loop or not? Should I have to fetch all the data into an array first? Can you please help me out with this. I don't know whether my question is clear or not.
I have tried but I couldn't execute it due to errors and stupid mistakes.
$sql = "SELECT id, locationname, locationlatitude, locationlongitude, postcode FROM distancetable";
$result = $conn->query($sql);
$i=0;
$j=0;
while($row = mysql_fetch_assoc($result)) {
for ($row=0; $row <num_rows ; $row++) {
$lat1=$row[$i]["locationlatitude"];
$long1=$row[$i]["locationlongitude"];
$suburb1=$row[$i]["locationname"];
$lat2=$row[$j]["locationlatitude"];
$long2=$row[$j]["locationlongitude"];
$suburb2=$row[$j]["locationname"];
$distance=Round(ACOS(SIN(PI()*$long1/180)*SIN(PI()*$long2/180)+COS(PI()*$long1/180)*COS(PI()*$long2/180)*COS(PI()*$lat1/180-PI()*$lat2/180))*6371,3);
echo nl2br (" \n ");
echo $row[id]. ". Distance between " .$suburb1. " to " .$suburb2. "=" .$distance;
$i++;
$j++;

So I made a table like this:
create table latlongdata (
id int auto_increment primary key not null,
locationname varchar(35) not null,
postcode char(5) not null,
locationlatitude numeric(6, 3) not null,
locationlongitude numeric(6,3) not null
);
then I added some sample lat/long values like so:
insert into latlongdata (locationname, postcode, locationlatitude, locationlongitude)
VALUES
('Fenwick, MI', '48834',43.142,-85.049),
('Andover, MN', '55304', 45.255, -93.287),
('Minneapolis, MN', '55422', 45.015, -93.340),
('Lydia, SC', '29079', 34.296, -81.113);
then I made a query like so:
select
a.postcode as FromPostCode,
b.postcode as ToPostCode,
round(acos(sin(pi()*a.locationlongitude/180)*sin(pi()*b.locationlongitude/180)+cos(pi()*b.locationlongitude/180)*cos(pi()*a.locationlatitude/180-pi()*b.locationlatitude/180))*6731, 3) as distance
from latlongdata a
inner join latlongdata b
where b.id > a.id;
...which should be close to what you want, but I think there might be something wrong with your math since the results didn't make a lot of sense. But that should be how you do it.
Note the self-join with the inequality operator on the SELECT statement. This will prevent you from calculating B -> A after you've already calculated A -> B. Fair warning: With a large table, you should expect the result set for this to be... large. It might be better to directly insert the results into a new table (assuming you have sufficient storage space) than try to select out the results.

Related

Copy added info from one mysql table to another in PHP

I have two tables in different mysql databases.
I would like to copy from table A to table B. Only one way.
I need to read last datetime from table B and then check if there any data added to table A after this readed datetime. If there is some data added, then copy it.
I tried this:
It writes one row if I refresh page, but I need it to write everything in one load!
do
{
#TABLE A
$querylastA = "SELECT * FROM `stock` ORDER BY `jrk` DESC LIMIT 1";
$resultlastA = mysql_query($querylastA) or die(mysql_error());
while($rows=mysql_fetch_array($resultlastA)){
$lastcodeA = $rows['datetime'];
}
#TABLE B
$querylastB = "SELECT * FROM `stockcopy` ORDER BY `jrk` DESC LIMIT 1";
$resultlastB = mysql_query($querylastB) or die(mysql_error());
while($rows=mysql_fetch_array($resultlastB)){
$lastcodeB = $rows['datetime'];
}
#TABLE A - NEXT DATE AFTER LAST DATE IN TABLE B
$querynextA = "SELECT datetime FROM stock WHERE datetime > '$lastcodeB' ORDER BY datetime ASC LIMIT 1";
$resultnextA = mysql_query($querynextA) or die(mysql_error());
while($rows=mysql_fetch_array($resultnextA)){
$nextcodeA = $rows['datetime'];
}
mysql_query("INSERT INTO stockcopy(datetime, data1, data2) SELECT datetime, data1, data2 FROM stock WHERE datetime = '$nextcodeA'");
echo "Date from table A " . $lastcodeA . "<br>";
echo "Date from table B " . $lastcodeB . "<br>";
}
while ('$lastcodeA' == '$lastcodeB');
You can insert data on a table using a SELECT statement, so all you need to do is put the condition in the SELECT.
For instance:
INSERT INTO table_example(foo, bar)
SELECT foo, bar FROM table_example2
WHERE time_condition > {SOME DATE}
This will insert into table_example the rows that the SELECT statement returns, which are the ones that satisfy the time condition. You will need to replace {SOME DATE} with an actual date (without brackets).
Also, please see: INSERT with SELECT
And also: Select columns across different databases
You can do this with little to no knowledge of SQL, but with a bit of creativity :)
Create a temporary table (table_c), and copy the contents of table_a there.
Then manually (this means by running delete queries in phpmyadmim) delete the information that doesn't matter anymore ( DELETE FROM table_c WHERE id < 1000 for example ).
Then, export the info from table_c and import it into table_b (and delete table_c
Problem was in: while ('$lastcodeA' == '$lastcodeB');
Changed it to: while ($lastcodeA != $lastcodeB); now it works! Thank you all :)

PHP MySQL - Find Row ID

I have a table called "participants" that has 3 fields:
prt_id
prt_event_id
prt_participant_id
What I have is a select query with a where condition on event_id. The query returns let's say 20 rows (20 different participants). What I would like to do is to be able to figure out the row number for a given participant (prt_id).
SELECT *
FROM participants
WHERE prt_id = someinteger
While you can't specifically find a row ID using MySQL, you could do something like the following:
$conn = new mysqli(/*dbinfo*/);
$res = $conn->query("SELECT prt_id FROM participants");
$rowids = array(); $currid = 1;
while ($row = $res->fetch_object()) { // this is using the mysqli library
$rowids[$row->prt_id] = $currid;
$currid++;
}
This would give you an array of ids associated with prt_id.
You could do something like:
<?php
$counter = 1; // Start at one for first entry
$res = mysql_query("SELECT * FROM participants WHERE prt_id = 12");
while( $array = mysql_fetch_assoc($res) )
{
// Do something with the counter, store it into array with details
$counter++;
}
?>
This should do what you want inside MySQL (ie assign a rownum in the order of prt_id), but the performance will be dependent on the number of rows in the table so it's not optimal.
SELECT * FROM (
SELECT #tmp:=#tmp+1 rownum, p.*
FROM (SELECT #tmp:=0) z, participants p
ORDER BY prt_id
) participants
WHERE prt_id = 36;
Demo here.
Edit: This "doh level" rewrite uses an simple index range instead of a table scan, so should be much faster (provided prt_id is a PRIMARY KEY)
SELECT *, COUNT(p2.prt_id) ROWNUM
FROM participants p1
JOIN participants p2
ON p1.prt_id >= p2.prt_id
WHERE p1.prt_id=36;
Demo here.
you could just add an index column in your database, set it as int, primary key and auto increment. then when retrieving the row you retrieve the index number.
RowID is a feature of Oracle: http://docs.oracle.com/cd/B19306_01/server.102/b14200/pseudocolumns008.htm.
MySQL does not have something like that, you can basically emulate that by assign number to an array inside php as you retrieve each row, but that doesn't guarantee you the same number next time you retrieve that results. You probably have to settle for using one of the primary IDs

Problem with mysql_fetch_assoc

I'm having a little trouble with a mysql_fetch_assoc script i hope you can help with.
Background:
I'm retrieving my data for part of my site with a typical MySQL query and echoing out the results from various fields etc etc from my main table whose structure is not important but which has a unique id which is 'job_id'
In order to have multiple catagories associated with that 'job_id' i have employed a toxi solution which associates catgories to each 'job_id'.
TABLE `tags` (
`tag_id` INT NOT NULL AUTO_INCREMENT,
`tag_name` VARCHAR(20) NOT NULL,
PRIMARY KEY (`tag_id`)
)
CREATE TABLE `tag_relational` (
`job_id` INT NOT NULL,
`tag_id` INT NOT NULL
)
When i echo out the info from the main table (using 'job_id') i also want to echo all the catagories which that job_id is matched against, which id using:
$query = "SELECT * FROM tags t
JOIN tag_relational r
ON t.tag_id=r.tag_id
WHERE r.job_id = $job_id";
$result=mysql_query($query) or die(mysql_error());
$cats=mysql_fetch_assoc($result);
In my code i'm using this to echo out the matched catagories:
<p>Job Catagories | <?php while ($cats=mysql_fetch_assoc($result)) { echo $cats['tag_name'];}?></p>
I have two problems:
The above echo seems to be ignoring the first tag name all together, so if a job is tagged with six catagories, it only echos five. If a job is only tagged with one, I get nothing. The query works in the SQL shell, so I'm assuming the problem lie in the PHP.
When I do get multiple echos, I want to be able to seperate them with a comma or a |, but I'm unfamiliar with using group_concat in queries, so I could also use a little help there.
Because mysql_fetch_assoc is already called once before the while loop, the first row is always discarded.
This should solve your problems:
$query = "SELECT * FROM tags t
JOIN tag_relational r
ON t.tag_id=r.tag_id
WHERE r.job_id = $job_id";
$result=mysql_query($query) or die(mysql_error());
<p>Job Catagories |
<?php
$first = true;
while ($cats=mysql_fetch_assoc($result)) {
if($first){
$first = false;
} else{
echo ", ";
}
echo $cats['tag_name'];
}
?>
</p>
use SEPARATOR in group_concat. see example below
mysql> SELECT student_name,
-> GROUP_CONCAT(DISTINCT test_score
-> ORDER BY test_score DESC SEPARATOR ' ')
-> FROM student
-> GROUP BY student_name;

MySQL query searching (advanced)

I have a course page, this page is setup to display the details (easy), then who is teaching it first, second, third, and forth period of each semester. The problem with that is, my teachers data is handled in one giant chain.
Bobby: 1-1-1-1-1-1-1-1
Tina: 20-20-20-10-1-1-1-1
Joey: 20-1-1-1-49-432-10-19
What I want to do is find a course: 20, when all of the information is shown a search through teachers would be made to find out who is teaching course 20 and what period?
Ex.
Course id: 20
Period 1: Joey, Tina
Period 2: Tina
Period 3: Tina
I want to get every teacher teaching that course in one search but given the obstacle of the capact data which may be a problem.
Teacher Table:
id / name / link / course (1-1-1-1-1-1-1-1)
Course Table:
id / name / code / grade / level
Teachers Course contains the id's from course Table
While re-structuring your db might be the best answer, I thought I'd post a straight php solution that works with your current structure and presumptively the rest of your code.
//I set up variables to contain your expected search results so I could test
$search_course = '20';
$search_results = array(
'Bobby' => '1-1-1-1-1-1-1-1',
'Tina' => '20-20-20-10-1-1-1-1',
'Joey' => '20-1-1-1-49-432-10-19'
);
//explode the course strings into arrays and store with teacher names so
//you can loop through them later
foreach($search_results as $teacher=>$string_courses){
$array_courses = explode('-',$string_courses);
$search_results[$teacher] = $array_courses;
}
//Match course you are searching for to the elements in your array
//Create a result array with period and matching teachers
foreach($search_results as $teacher=>$courses){
foreach($courses as $period => $course){
if($course == $search_course){
$results[$period][] = $teacher;
}
}
}
//Loop through your result array and show the results
//I expect you'll have different html for this
foreach($results as $period => $teachers){
echo 'Period: ';
echo $period+1;
echo implode(',',$teachers);
echo '<br>';
}
The printed results match the list you wanted in your OP
Create a new table
Something like this:
CREATE TABLE `TeacherToPeriod` (
`id` INT NOT NULL AUTO_INCREMENT PRIMARY KEY ,
`TeacherID` INT NOT NULL ,
`CourseID` INT NOT NULL ,
`Period` INT NOT NULL
) ENGINE = MYISAM ;
Insert the data
Here is some PHP-Code:
foreach($lines as $line){
$line_data = split($line, ': ');
$teacher = $line_data[0];
// SELECT your $teacher_id from the database
$courses = split($line_data[0], '-');
$i = 0;
foreach($courses as $course_id){
$i++;
$sql = "INSERT INTO `TeacherToPeriod` (`TeacherID` ,`CourseID` ,`Period`) ";
$sql.= "VALUES ($teacher_id, $course_id, $i);"
mysql_query($sql);
}
}
Select the data you want
SELECT * FROM `TeacherToPeriod` WHERE `CourseID` = 20 ORDER BY `Period` ASC;
You should change the structure of your db, instead of storing a string of all the periods, you should have an additional table with three columns: teacher,course,period and have a separate row in this table for each course that a teacher is teaching. Then determining who is teaching what course would simply be a matter of querying that table by course id and then sorting by period. e.g:
SELECT teacher_id, course_id, period FROM course_info WHERE course_id = 20
ORDER BY period;

Summing a field from all tables in a database

I have a MySQL database called "bookfeather." It contains 56 tables. Each table has the following structure:
id site votes_up votes_down
The value for "site" is a book title. The value for "votes_up" is an integer. Sometimes a unique value for "site" appears in more than one table.
For each unique value "site" in the entire database, I would like to sum "votes_up" from all 56 tables. Then I would like to print the top 25 values for "site" ranked by total "votes_up".
How can I do this in PHP?
Thanks in advance,
John
You can do something like this (warning: Extremely poor SQL ahead)
select site, sum(votes_up) votes_up
from (
select site, votes_up from table_1
UNION
select site, votes_up from table_2
UNION
...
UNION
select site, votes_up from table_56
) group by site order by sum(votes_up) desc limit 25
But, as Dav asked, does your data have to be like this? There are much more efficient ways of storing this kind of data.
Edit: You just mentioned in a comment that you expect there to be more than 56 tables in the future -- I would look into MySQL limits on how many tables you can UNION before going forward with this kind of SQL.
Here's a PHP code snip that should get it done.
I have not tested it so it might have some typos and stuff, make sure you replace DB_NAME
$result = mysql_query("SHOW TABLES");
$tables = array();
while ($row = mysql_fetch_assoc($result)) {
$tables[] = '`'.$row["Tables_in_DB_NAME"].'`';
}
$subQuery = "SELECT site, votes_up FROM ".implode(" UNION ALL SELECT site, votes_up FROM ",$tables);
// Create one query that gets the data you need
$sqlStr = "SELECT site, sum(votes_up) sumVotesUp
FROM (
".$subQuery." ) subQuery
GROUP BY site ORDER BY sum(votes_up) DESC LIMIT 25";
$result = mysql_query($sqlStr);
$arr = array();
while ($row = mysql_fetch_assoc($result)) {
$arr[] = $row["site"]." - ".$row["sumVotesUp"];
}
print_r($arr)
The UNION part of Ian Clelland answer can be generated using a statement like the following. The table INFORMATION_SCHEMA.COLUMNS has a column TABLE_NAME to get all tables.
select * from information_schema.columns
where table_schema not like 'informat%'
and column_name like 'VOTES_UP'
Join all inner SELECT with UNION ALL instead of UNION. UNION is doing an implicit DISTINCT (on oracle).
The basic idea would be to iterate over all your tables (using a SQL SHOW TABLES statement or similar) in PHP, then for every table, iterate over the rows (SELECT site,votes_up FROM $table). Then, for every row, check the site against an array that you're building with sites as keys and votes up as values. If the site is already in the array, increment its votes appropriately; otherwise, add it.
Vaguely PHP-like pseudocode:
// Build an empty array for use later
$votes_array = empty_array();
// Get all the tables and iterate over them
$tables = query("SHOW TABLES");
for($table in $tables) {
$rows = query("SELECT site,votes_up FROM $table");
// Iterate over the rows in each table
for($row in $rows) {
$site = $row['site'];
$votes = $row['votes_up'];
// If the site is already in the array, increment votes; otherwise, add it
if(exists_in_array($site, $votes_array)) {
$votes_array[$site] += $votes;
} else {
insert_into_array($site => $votes);
}
}
}
// Get the sites and votes as lists, and print out the top 25
$sorted_sites = array_keys($votes_array);
$sorted_votes = array_values($votes_array);
for($i = 0; $i < 25; $i++) {
print "Site " . $sorted_sites[$i] . " has " . $sorted_votes[$i] . " votes";
}
"I allow users to add tables to the database." - I hope all your users are benevolent and trustworthy and capable. Do you worry about people dropping or truncating tables, creating incorrect new tables that break your code, or other things like that? What kind of security do you have when users can log right into your database and change the schema?
Here's a tutorial on relational database normalization. Maybe it'll help.
Just in case someone else that comes after you wants to find what this could have looked like, here's a single table that could do what you want:
create database bookfeather;
create user bookfeather identified by 'bookfeather';
grant all on bookfeather.* to 'bookfeather'#'%';
use bookfeather;
create table if not exists book
(
id int not null auto_increment,
title varchar(255) not null default '',
upvotes integer not null default 0,
downvotes integer not null default 0,
primary key(id),
unique(title)
);
You'd vote a title up or down with an UPDATE:
update book set upvotes = upvotes + 1 where id = ?
Adding a new book is as easy as adding another row:
insert into book(title) values('grails in action')
I'd strongly urge that you reconsider.

Categories