Related
I'm creating a script that will search the database and look for customers that are in the Realtors latitude and longitude boundary range. If the customer lat and long coordinates is within the range of the realtor's lat and long boundaries then this script will email only the Realtor in that customers range. I'm using a CRON job to run the php script. I got the script to email each person that is in range of the Realtors but when a third Realtor is entered into the database the email goes to the third Realtor even though the lat and long is out of range.
How do I write a better loop where each row gets checked if the client is in range of that Realtor and only email that Realtor only? Thanks.
Here is my SQL code.
CREATE TABLE `realtors` (
`rid` int(11) NOT NULL AUTO_INCREMENT,
`rEmail` varchar(255) NOT NULL,
`rZipCode` int(10) NOT NULL,
`rDist` int(11) NOT NULL,
`rlatitude` numeric(30,15) NOT NULL,
`rlongitude` numeric(30,15) NOT NULL,
PRIMARY KEY (`rid`)
) ENGINE=InnoDB AUTO_INCREMENT=1 DEFAULT CHARSET=utf8;
CREATE TABLE `customers` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`eMail` varchar(255) NOT NULL,
`zipCode` int(11) NOT NULL,
`clatitude` numeric(30,15) NOT NULL,
`clongitude` numeric(30,15) NOT NULL,
PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=1 DEFAULT CHARSET=utf8;
Here is my php code.
<?php
use geocodeloc\GeoLocation as GeoLocation;
require_once 'geocodeloc/GeoLocation.php';
//require_once 'phpmailer/PHPMailerAutoload.php';
$db = getDB();
//database prep for customers
$cust = $db->prepare("SELECT fullName, eMail, clatitude, clongitude FROM customers ORDER BY id DESC");
$cust->bindParam("fullName", $fullName,PDO::PARAM_STR);
$cust->bindParam("zipCode", $zipCode,PDO::PARAM_STR);
$cust->bindParam("eMail", $email,PDO::PARAM_STR);
$cust->bindParam("clatitude", $clatitude,PDO::PARAM_STR);
$cust->bindParam("clongitude", $clongitude,PDO::PARAM_STR);
$cust->execute();
$cust->rowCount();
//database prep for realtors
$realt = $db->prepare("SELECT rEmail, rDist, rlatitude, rlongitude FROM realtors ORDER BY rid DESC");
$realt->bindParam("rZipCode", $rZipCode,PDO::PARAM_STR);
$realt->bindParam("rEmail", $rEmail,PDO::PARAM_STR);
$realt->bindParam("rDist", $rDist,PDO::PARAM_STR);
$realt->bindParam("rlatitude", $rlatitude,PDO::PARAM_STR);
$realt->bindParam("rlongitude", $rlongitude,PDO::PARAM_STR);
$realt->execute();
$realt->rowCount();
$i = -1;
while ($realtor_row = $realt ->fetch(PDO::FETCH_ASSOC) AND $customers_row = $cust ->fetch(PDO::FETCH_ASSOC)) {
$i++;
$realtLatLong = GeoLocation::fromDegrees( $realtor_row['rlatitude'], $realtor_row['rlongitude']);
$coordinates = $realtLatLong->boundingCoordinates($realtor_row['rDist'], 'miles');
//look to see if customers latitude and longitude is within range of the realtors lat and long boundaries.
if($customers_row['clatitude'] && $customers_row['clongitude'] <= $coordinates){
//email the realtor
// the message
$msgBody = "This is a test";
// use wordwrap() if lines are longer than 70 characters
$msgBody = wordwrap($msgBody,70);
$Mailto = $realtor_row['rEmail'];
$FromName = $customers_row['fullName'];
// send email
mail($Mailto, $FromName , $msgBody);
}else{
//send to debug log
}
};
?>
Looping through the entire result set and doing the calculations is going to kill your database very quickly. Looping through one table and then looping through another to do a distance comparison is going to kill your database even faster. Luckily this is a re invention of the wheel. Mysql has built in functionality for this by way of ST_Distance
SELECT * FROM realtors INNER JOIN customers WHERE ST_within(customers.loc, realtors.loc) < 10; /* location in degrees */
Where one degree is approximately 111 kilometer. You whould need to change your table as follows
CREATE TABLE `realtors` (
`rid` int(11) NOT NULL AUTO_INCREMENT,
`rEmail` varchar(255) NOT NULL,
`rZipCode` int(10) NOT NULL,
`rDist` int(11) NOT NULL,
`loc` point NOT NULL,
PRIMARY KEY (`rid`)
) ENGINE=InnoDB AUTO_INCREMENT=1 DEFAULT CHARSET=utf8;
CREATE TABLE `customers` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`eMail` varchar(255) NOT NULL,
`zipCode` int(11) NOT NULL,
`loc` POINT not null,
PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=1 DEFAULT CHARSET=utf8;
of course this requires mysql 5.7
Using a spatial data type means that you can use an index for spatial looksup. In an RDBS if a table contains N rows, having an indes means you do not need to check through all those N number of rows to find a result. Thus using spatial data here + an index you can avoid the NxM time complexity you might have with lat,lng in separate columns.
No matter how fast you can make your code, the complexity will still be NxM.
First thing you should do is to create a relationship between Customer and Realtor, i.e. a table with Customer.id and Realtor.id. Take a hit the first time you populate this table (no need to change your code). After that, you just need to create a relationship everytime a Customer or a Realtor got added.
When it's time to send your email, you just need to look at the relationship table.
I am trying to generate invoice id in each invoice, now i am having thousands of invoices, Now while adding from different ip same time i am getting duplicate invoice ids how to prevent it,
invoice id generating by getting the last inserted invoice id and increment 1 to it.
my function as follows parameters
get_new_tbl_id('table_name','invoice_id_column','string to strip (INV in INV0012)','any conditions');
function get_new_tbl_id($tbl_name,$id_field,$string,$options='')
{
$new_id = 0;
$query_count_rows = "SELECT MAX(CONVERT(replace(replace($id_field,',',''),'$string',''), SIGNED INTEGER)) as $id_field FROM $tbl_name WHERE $id_field LIKE '$string%' $options";
$count_rows = mysql_query($query_count_rows);
$num_rows = mysql_num_rows($count_rows);
if($num_rows >0)
{
$last_row = mysql_fetch_assoc($count_rows);
$last_id = $last_row[$id_field];
$last_inserted_id = intval(str_replace($string,'',$last_id));
$new_id = $last_inserted_id+1;
}
else
$new_id = 1;
$format = '%1$03d';
$new_id=sprintf($format,$new_id,'');
return $string.$new_id;
}
My table as follows
CREATE TABLE IF NOT EXISTS `tbl_invoice` (
`invoice_tbl_id` int(11) NOT NULL AUTO_INCREMENT,
`invoice_time` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
`invoice_ip` varchar(25) NOT NULL,
`invoice_status` tinyint(1) NOT NULL DEFAULT '0',
`invoice_added_by` smallint(6) NOT NULL,
`invoice_edited_by` smallint(6) NOT NULL,
`invoice_date` date NOT NULL,
`invoice_id` varchar(15) NOT NULL,
`customer_id` varchar(11) NOT NULL,
`invoice_credit_date` tinyint(4) NOT NULL,
`invoice_credit_status` tinyint(1) NOT NULL DEFAULT '0',
`total_items_count` smallint(6) NOT NULL,
`invoice_total_amount` varchar(20) NOT NULL,
`invoice_grandtotal_amount` double NOT NULL,
`invoice_discount` double NOT NULL DEFAULT '0',
`invoice_total_card_amount` double NOT NULL,
`invoice_total_cash_amount` double NOT NULL,
`invoice_total_profit` varchar(10) NOT NULL,
`cashier_approval` tinyint(1) NOT NULL DEFAULT '0',
`cashier_approval_id` smallint(6) NOT NULL,
`cashier_approval_time` datetime NOT NULL,
`cashier_approval_ip` varchar(20) NOT NULL,
`invoice_delete_note` text NOT NULL,
PRIMARY KEY (`invoice_tbl_id`),
KEY `invoice_id` (`invoice_id`)
) ENGINE=InnoDB DEFAULT CHARSET=latin1
Use a myisam table to generate the ids for you with 2 fields. The 1st field contains the prefix (this is $string in your function), the second should be an auto increment field. Add a primary key on these 2 fields, but the prefix field must be the 1st one in the index. If you insert a new row into this table with a prefix, then mysql will increment the auto increment value within that group.
See myisam notes section in mysql documentation on auto increment for details and example.
CREATE TABLE animals (
grp ENUM('fish','mammal','bird') NOT NULL,
id MEDIUMINT NOT NULL AUTO_INCREMENT,
name CHAR(30) NOT NULL,
PRIMARY KEY (grp,id)
) ENGINE=MyISAM;
INSERT INTO animals (grp,name) VALUES
('mammal','dog'),('mammal','cat'),
('bird','penguin'),('fish','lax'),('mammal','whale'),
('bird','ostrich');
If your base table is mysql, then just alter it to get this behaviour, if not, then create a separate myisam table, do the inserts into that one first, then obtain the ids fo use in your main table.
May there will be some optimized solution, but for now I can give you this solution
use static variable lock if one person is getting id make $lock=true and keep other requests on waiting for 1 second and check again by goto start; until first request is completed; make $lock=false; at the end to release the function.
public static $lock=false;
function get_new_tbl_id($tbl_name,$id_field,$string,$options='')
{
global $lock;
start:
if($lock==true){
sleep(1);
goto start;
}
if($lock==false){
$lock==true;
}
$new_id = 0;
$query_count_rows = "SELECT MAX(CONVERT(replace(replace($id_field,',',''),'$string',''), SIGNED INTEGER)) as $id_field FROM $tbl_name WHERE $id_field LIKE '$string%' $options";
$count_rows = mysql_query($query_count_rows);
$num_rows = mysql_num_rows($count_rows);
if($num_rows >0)
{
$last_row = mysql_fetch_assoc($count_rows);
$last_id = $last_row[$id_field];
$last_inserted_id = intval(str_replace($string,'',$last_id));
$new_id = $last_inserted_id+1;
}
else
$new_id = 1;
$format = '%1$03d';
$new_id=sprintf($format,$new_id,'');
$lock=false;
return $string.$new_id;
}
So in my database i have two tables. Jokes and Comments. I want the ability to assign the post_id of the comment, to the joke_id of the joke, so it will assign and retrieve the comments relating to that joke. My problem is that i suck at writing SQL statements and haven't the foggiest on how to join two tables to make this happen.
My jokes table looks like this:
CREATE TABLE IF NOT EXISTS `jokes` (
`joke_id` int(11) NOT NULL AUTO_INCREMENT,
`joke` varchar(1024) NOT NULL,
`category_id` int(11) NOT NULL,
`vote` int(255) NOT NULL,
`date_added` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
PRIMARY KEY (`joke_id`)
) ENGINE=InnoDB DEFAULT CHARSET=latin1 AUTO_INCREMENT=1 ;
and my comments table looks like this:
CREATE TABLE IF NOT EXISTS `comments` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`name` varchar(40) NOT NULL,
`comment` text NOT NULL,
`joke_id` int(11) NOT NULL,
`post_id` int(11) NOT NULL,
`date` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP,
PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=latin1 AUTO_INCREMENT=1 ;
and for the moment, I am grabbing the data by assigned the $post_id = "1", but i want to change it to something like $post_id = $joke_id (with the joke id being in the same function, but i have no idea how to do it).
I'm using a MVC with codeigniter if thats any help.
Inside my controller, i have a php file called comments which has a function called insertComment, which looks like this:
public function insertComment(){
//extracts the data from the ajax
extract($_POST);
if($_POST['act'] == 'add-com'){
//assigned the db rows with the actual data which was inputted
$data = array(
'name' => htmlentities($name),
'comment' => htmlentities($comment),
//id_post should correlate to the joke_id
'id_post' => $id_post = "1"
);
$this->comments_m->insertComment($data);
}
and my insertComment function, inside the models of comment_m function looks like this:
function insertComment (){
extract($_POST);
if($_POST['act'] == 'add-com'){
$data = array(
'name' => htmlentities($name),
'comment' => htmlentities($comment),
'id_post' => $id_post = "1"
);
if(strlen($data['name']) <= '1'){
$data['name'] = 'Guest';
}
$this->db->insert('comments', $data);
}
}
To finalise, it would be a great help if someone could help with an SQL statement which joins the two tables together, which the joke_id having the same value as the comment's post_id which will make it unique to that joke.
Thank you
The SQL to join these two tables is -
SELECT `jokes`.*, `comments`.*
FROM `jokes`
LEFT OUTER JOIN `comments`
ON `jokes`.`joke_id` = `comments`.`joke_id`
This will return all of the comments for each joke. You can then filter or limit by adding the WHERE clause(s) -
WHERE `jokes`.`joke_id` = 1
I have two tables like this:
CREATE TABLE `tblFacilityHrs` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`uid` varchar(45) DEFAULT NULL,
`title` varchar(100) DEFAULT NULL,
`description` text,
PRIMARY KEY (`id`),
KEY `key_uid` (`uid`)
) ENGINE=InnoDB AUTO_INCREMENT=9 DEFAULT CHARSET=latin1$$
CREATE TABLE `tblFacilityHrsDateTimes` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`owner_uid` varchar(45) DEFAULT NULL,
`startDate` date DEFAULT NULL,
`endDate` date DEFAULT NULL,
`startTime` time DEFAULT NULL,
`endTime` time DEFAULT NULL,
`days` int(2),
`recurrence` int(1) DEFAULT NULL,
PRIMARY KEY (`id`),
KEY `fk_fh_owneruid` (`owner_uid`),
CONSTRAINT `fk_fh_owneruid` FOREIGN KEY (`owner_uid`) REFERENCES `tblFacilityHrs` (`uid`) ON DELETE CASCADE ON UPDATE CASCADE
) ENGINE=InnoDB AUTO_INCREMENT=52 DEFAULT CHARSET=latin1$$
id uid title description location
8ada0ceabd40d509c3fb38f2822a97de11bc6628 Swim Lessons Parent and Child
Classes CRC 2 543a6ed0005ff6a0a7fc99cc2f9715d86804ecb0 Swim Lessons
Level 1, Session 1 3 7d219b64be6dc706135bdad3e7c2f0d56cb7f353 Swim
Lessons Level 2 4 f7c91e2f1daa9c696c22f5aa5736c167d1ba9f94 Swim
Lessons Level 3 5 262f06fb75645248162aa983f610ec7959a2011b Swim
Lessons Level 4 6 51f9f552ffc5fa4bc8b4e7f914fb22b3b0920c2e Bike 275
Participate in this program and take 4 Fitness Cycling classes and get
a FREEWaterbottle! Participants can only register 1 time. This is a
FREE program! Sign up in the Multipurpose Room. 7
0cca3515ec8ee990c863e474fee634ae94d382c2 Passport to Fitness Take
Norse Fitness Classes, Get your Passport Stamped and win aFree
T-shirt! Take 8 Fitness classes between April 10 and May 1 and get a
Free T-shirt.
id owner_uid startDate endDate startTime endTime days recurrance
8ada0ceabd40d509c3fb38f2822a97de11bc6628 4/13/2012 4/13/2012 0:00:00
NULL NULL None 2 543a6ed0005ff6a0a7fc99cc2f9715d86804ecb0 NULL NULL
12:30:00 2:00:00 2 3 7d219b64be6dc706135bdad3e7c2f0d56cb7f353 NULL
NULL NULL NULL NULL NULL 4 f7c91e2f1daa9c696c22f5aa5736c167d1ba9f94
NULL NULL NULL NULL NULL NULL 5
262f06fb75645248162aa983f610ec7959a2011b NULL NULL NULL NULL NULL NULL
6 51f9f552ffc5fa4bc8b4e7f914fb22b3b0920c2e NULL NULL NULL NULL NULL
NULL 7 0cca3515ec8ee990c863e474fee634ae94d382c2 NULL NULL NULL NULL
NULL NULL
In my controllers folder I have a file named main.php with the following code:
...
function fitnessSchedule()
{
$this->config->set_item('url_suffix', '');
$crud = new grocery_CRUD();
$crud->set_model('schedule_model');
$crud->set_table('tblFitnessClasses');
$crud->join_table('tblFitnessClasses','tblFitnessClassDateTimes');
$crud->columns('title','description','location','startDate','endDate','startTime', 'endTime', 'days', 'recurrance');
$crud->display_as('title','Event')
->display_as('description','Description')
->display_as('location','Location')
->display_as('startDate','Start Date')
->display_as('endDate','End Date')
->display_as('startTime','Start Time')
->display_as('endTime','End Time');
$crud->required_fields('title','location');
$crud->set_subject('Event');
$output = $crud->render();
$this->_example_output($output);
}
function _example_output($output = null)
{
$this->load->view('main_view', $output);
}
...
In my models folder I have this:
<?php
class schedule_model extends grocery_CRUD_Model
{
function join_table($table1, $table2)
{
if($this->$table1 === null)
return false;
$select = "{$this->$table1}.*";
$select .=",$table2.startDate, $table2.endDate, $table2.startTime, $table2.endTime, $table2.days, $table2.recurrence";
if(!empty($this->relation))
foreach($this->relation as $relation)
{
list($field_name , $related_table , $related_field_title) = $relation;
$unique_join_name = $this->_unique_join_name($field_name);
$unique_field_name = $this->_unique_field_name($field_name);
if(strstr($related_field_title,'{'))
$select .= ", CONCAT('".str_replace(array('{','}'),array("',COALESCE({$unique_join_name}.",", ''),'"),str_replace("'","\\'",$related_field_title))."') as $unique_field_name";
else
$select .= ", $unique_join_name.$related_field_title as $unique_field_name";
if($this->field_exists($related_field_title))
$select .= ", {$this->$table1}.$related_field_title as '{$this->$table1}.$related_field_title'";
}
$this->db->select($select, false);
$this->db->join('uid', '$table2.owner_uid = $table1.uid');
$results = $this->db->get($this->$table1)->result();
return $results;
}
/* function join_table($table1, $table2)
{
$this->db->select('$table1.*');
$this->db->join('$table2','$table1.uid = $table2.owner_uid','left');
$this->db->get('$table1');
}*/
}
?>
I'm getting this error:
Fatal error: Call to undefined method grocery_CRUD::join_table() in
C:\xampp\htdocs\codeigniter\application\controllers\main.php on line
234
I'm basically trying to concatenate tblFacilityHrs with tblFacilityHrsDateTimes by way of UID (uid to owner_uid). I want to display both tables at once so that when a user edits the table not only do they edit the event's name/location they also edit its time/date etc.
Reference: http://www.grocerycrud.com/documentation/options_functions/set_model
I had the same question and following is the solution that I did.
Create a view with mySQL join statements for the tables that I want to join
Look for the state ($cur_state=$crud->getState();) and see if it is either "list,ajaxlist,read or success" and then I set the $crud->set_table('VIEW_NAME');
and also set the primary key $crud->set_primary_key('KEY','VIEW_NAME');
for the else part I use the table name itself so that it will help add,edit operation.
this solution working very nicely.
$crud = new grocery_CRUD();
$cur_state=$crud->getState();
$crud->set_subject('Patient Notes');
/* Use the mySQL view to display the data with related tables */
if(($cur_state=="list") || ($cur_state=="ajaxlist") || ($cur_state=="read") || ($cur_state=="success"))
{
$crud->set_table('patientsnotes_vw');
$crud->columns('noteID','Clinic_No','note','noteCreated','username');
$crud->set_primary_key('noteID','patientsnotes_vw');
if($cur_state=="read")
{
$crud->unset_fields('noteCreatedBy','user_id','patientID');
}
}
else
{
/* Use the patient_note table itself for add/edit operation */
$crud->set_table('patient_notes');
$crud->columns('noteID','Clinic_No','note','noteCreated','username');
$crud->required_fields('note');
$crud->field_type('noteCreatedBy', 'hidden', $this->userID);
$crud->field_type('patientID', 'hidden', 1);
$crud->unset_add_fields('noteCreated');
$crud->unset_edit_fields('noteCreated');
}
$crud->display_as('username','Note Created by');
$crud->display_as('noteCreated','Note Created at');
$output = $crud->render();
In this forum thread, web-johnny states that this functionality is not yet possible.
This is the 1-1 relation that grocery CRUD doesn't have this
functionality yet. It depends of how much time I will have in the
future to do that and/or if I have any good donations till then.
This functionality is a big deal and also really complicated to do it,
but I think it will save lot of time for many users when it will come
up.
Perhaps if you provide what web-johnny deems to be a good donation he might implement it.
The class grocery_CRUD ($crud = new grocery_CRUD(); )
Is localed in the folder libraries and you implement join_table in the model.
I have a simple table as below.
CREATE TABLE `stats` (
`id` int(11) NOT NULL auto_increment,
`zones` varchar(100) default NULL,
`date` date default NULL,
`hits` int(100) default NULL,
PRIMARY KEY (`id`)
) ENGINE=MyISAM AUTO_INCREMENT=7 DEFAULT CHARSET=latin1;
So just storing simple hits counter per zone per day.
But I just want to increment the hits value for the same day.
I have tried the MYSQL DUPLICATE KEY UPDATE but this wont work as I may have many zones on different dates so I cant make them unique or dates.
So the only way I can think is first to do a query to see if a date exists then do a simple if() for insert/update
Is their a better way of doing such a task as there maybe be many 1000's hits per day.
Hope this makes sense :-).
And thanks if you can advise.
Declare the tuple (zone, date) as unique in your CREATE statement. This will make INSERT ... ON DUPLICATE UPDATE work as expected:
CREATE TABLE `stats` (
`id` int(11) NOT NULL auto_increment,
`zone` varchar(100) default NULL,
`date` date default NULL,
`hits` int(100) default NULL,
PRIMARY KEY (`id`),
UNIQUE (`zone`, `date`)
) ENGINE=MyISAM AUTO_INCREMENT=7 DEFAULT CHARSET=latin1;
INSERT INTO stats (zone, date, hits) values ('zone1', 'date1', 1) ON DUPLICATE KEY UPDATE hits = hits + 1;
$result = mysql_query("SELECT id FROM stats WHERE zone=$zone AND date=$today LIMIT 1");
if(mysql_num_rows($result)) {
$id = mysql_result($result,0);
mysql_query("UPDATE stats SET hits=hits+1 WHERE id=$id");
} else {
mysql_query("INSERT INTO stats (zone, date, hits) VALUES ($zone, $today, 1)");
}
Something like that, if I've interpreted you correctly... that's completely untested. You can figure out what the variables are.