Databases:
listings
| ID | TITLE | COMPANY_ID |
| 1 | One | 1 |
| 2 | One | 1 |
| 3 | One | 1 |
Companies
| ID | NAME |
| 1 | One |
| 2 | Two |
| 3 | Three |
I have the PDO MYSQL statement:
"SELECT
listings.ID,
listings.TITLE,
listings.COMPANY_ID,
companies.ID,
companies.NAME
FROM listings INNER JOIN companies ON (listings.COMPANY_ID = companies.ID ) WHERE (listings.TITLE = :p1 AND companies.NAME = :p2 )LIMIT 10"
The associative array output:
Array
(
[ID] => 1
[TITLE] => analyst
[COMPANY_ID] => 1
[NAME] => one
)
Array
(
[ID] => 1
[TITLE] => analyst
[COMPANY_ID] => 1
[NAME] => one
)
Array
(
[ID] => 1
[TITLE] => analyst
[COMPANY_ID] => 1
[NAME] => one
)
When executing this query, my ID field that is being outputted to the PHP fetch associative array is being returned as 1.
With the exception to the ID, the output works as expected.
Why is my ID value changing?
______________________
METHOD:
protected function search_joined($parameters, $func){
$field = "*";
if(isset($parameters["fields"])){
if($parameters["fields"] != "*" && gettype($parameters["fields"]) == gettype(array())){
if(count($parameters["fields"]) == 2){
$field = "";
foreach($parameters["fields"] as $key=>$v){
foreach($v as $v_){
$field.= $parameters["tables"][$key] . "." . $v_ . ",";
}
}
$field = rtrim($field, ",");
}
}
}
$cond_ = "";
$values = array();
if(gettype($parameters["condArry"]) == gettype(array())){
$COND_TYPE = " AND ";
foreach($parameters["condArry"] as $v){
$operator = " = ";
if($v[1][0] == "%" || substr($v[1], -1) == "%"){
$operator=" LIKE ";
}
if(substr($v[1], 5) == "L::_>"){
$operator=" > ";
$v[1] = str_replace($v[1], "L::_>", "");
}
if($v[1][0] == "!"){
$operator=" != ";
//$v[1] = str_replace($v[1], "!", "");
$v[1] = substr($v[1], 1);
}
$COND_TYPE = (
(isset($v[2]))?
(
($v[2] == "&")? " AND " :
(
(($v[2]=="||")? " OR ": "")
)
): " AND "
);
$unique = md5($v[0] . $v[1]);
$cond_.= $v[0] . $operator . ":".substr($v[0], strpos($v[0], ".") + 1).$unique. " " . $COND_TYPE . " ";
$values[':'.substr($v[0], strpos($v[0], ".") + 1).$unique] = $v[1];
}
$cond_ = "WHERE (" . substr($cond_, 0, strlen($COND_TYPE)*(-1)) . ")";
}
//$cond_ = rtrim($cond_, ",");
$joiner = $parameters["tables"][0] . "." . $parameters["joiner"][0] . "=" . $parameters["tables"][1] . "." . $parameters["joiner"][1] . " ";
$sql = "SELECT ". $field . " FROM " . $parameters["tables"][0] . " INNER JOIN " . $parameters["tables"][1] . " ON (" . $joiner . ") " .$cond_ . (isset($parameters["LIMIT"])? "LIMIT " . $parameters["LIMIT"]: "");
echo $sql;
if(isset($parameters["test"])){
if($parameters["test"] == true){
echo "<br>". $sql . "<br>";
}
}
//echo "<br>". $sql . "<br>";
$q = $parameters["connection"]->prepare($sql);
foreach($values as $key => $v){
$q->bindvalue($key, $v);
echo "<br>" . $key . " : " . $v . "<br>";
}
$q->execute();
while($row = $q->fetch(PDO::FETCH_ASSOC)) {
if(!$func($row)){
break;
}
}
$conTableArry = null;
}
CALLER:
$params_= array(
"connection"=>$this->connection_,
"tables"=>array("listings", "companies"),
"joiner"=>array("COMPANY_ID","ID"),
"fields"=> array(
array(
"ID",
"TITLE",
"PAYMIN",
"PAYMAX",
"GEO_LOCATIONS_ID",
"CRIMINAL_HISTORY_REQ",
"EVAL_EXAM_REQ",
"AGE_REQ",
"EX_REQ",
"DRIVERS_LICENSE_REQ",
"CERTIFICATE_REQ",
"EDUCATION_REQ",
"RESUME_REQ" ,
"POSITIONS",
"COMPANY_ID"
),
array(
"ID",
"NAME"
)
),
"condArry"=>array(array("listings.TITLE", "analyst"), array("companies.NAME", "suitespec")),
"LIMIT"=>$LIMIT,
);
parent::search_joined($params_, function($r){
out($r);
return true;
});
You've got some confusing syntax in your SQL
SELECT
listings.ID,
listings.TITLE,
listings.COMPANY_ID,
companies.ID,
companies.NAME
FROM listings
INNER JOIN companies ON (listings.COMPANY_ID = companies.ID )
WHERE (listings.TITLE = :p1 AND companies.NAME = :p2 )
LIMIT 10
You have two ID columns and no aliasing. So you're likely getting the second ID back. Try aliasing the second ID column and trying again
SELECT
listings.ID,
listings.TITLE,
listings.COMPANY_ID,
companies.ID AS comp_id,
companies.NAME
You should be able to solve this by using unique column names for your result set (you currently use ID twice).
Change your SQL to something like this:
"SELECT
listings.ID,
listings.TITLE,
listings.COMPANY_ID,
companies.ID AS CID,
companies.NAME
FROM listings
INNER JOIN companies ON (listings.COMPANY_ID = companies.ID )
WHERE (listings.TITLE = :p1
AND companies.NAME = :p2 )LIMIT 10"
Notice the 5th line of that. I've changed it from companies.ID to companies.ID AS CID.
Related
I have a client that requires a new function adding to an existing solution.
They store financial data in a MySQL database with the below columns. These are all
outstanding payments, some with penalties, and some without.
ID | money_id | principal | interest | penalty | paid_pri | paid_int | paid_pen
1 1 50 60 5 0 0 0
2 1 55 55 5 0 0 0
3 1 60 50 0 0 0 0
In the case above, the client has two penalties to pay on the ID 1 & 2, if the
customer pays $200, then the interest and penalties need to be paid first before
paying the principal payments. once all outstanding interest & penalties are paid
the principal payments can be paid off.
The new table, after the 200 is paid should look like this.
ID | money_id | principal | interest | penalty | paid_pri | paid_int | paid_pen
1 1 50 60 5 25 60 5
2 1 55 55 5 0 25 5
3 1 60 50 0 0 50 0
This is because the allocation of 200 pays, 60 + 5 + 2 5 + 5 + 50
then 25 off the first principal.
Edit For MARC B's point:
how can i "Reduce values in several columns and rows of a database using a php loop"
I have to work with an object, returned from mysql.
Array
(
[0] => Array
(
[id] => 1
[interest] => 60
[interestPaid] => 0
[penalty] => 5.00
[penaltyPaid] => 0
[principal] => 50
[principalPaid] => 0.00
)
[1] => Array
(
[id] => 2
[interest] => 55
[interestPaid] => 739.10
[penalty] => 5
[penaltyPaid] => 739.10
[principal] => 55
[principalPaid] => 0.00
)
)
I have to use this to loop through the database, its called $payment_array.
My code so far, bearing in mind they have a class which extends mysql ($this)
$amount_to_pay = 200;
foreach ($payment_array as $ordered_repayment) {
if ($amount_to_pay > 0) {
$interest_balance = $ordered_repayment['interest'] - $ordered_repayment['interestPaid'];
if ($interest_balance > 0) {
$sql = "update schedules set paid_int = paid_int + '" . $interest_balance . "' where id = '" . $ordered_repayment['id'] . "'";
$amount_to_pay -= $interest_balance;
$this->query($sql);
}
$penalty_balance = $ordered_repayment['penalty'] - $ordered_repayment['penaltyPaid'];
if ($penalty_balance > 0) {
$sql = "update schedules set paid_pen = paid_pen + '" . $penalty_balance . "' where id = '" . $ordered_repayment['id'] . "'";
$amount_to_pay -= $penalty_balance;
$this->query($sql);
}
}
}
foreach ($payment_array as $ordered_repayment) {
if ($amount_to_pay > 0) {
$capital_balance = $ordered_repayment['principal'] - $ordered_repayment['principalPaid'];
if ($capital_balance > 0) {
$sql = "update schedules set paid_pri = paid_pri + '" . $capital_balance . "' where id = '" . $ordered_repayment['id'] . "'";
$amount_to_pay -= $capital_balance;
$this->query($sql);
}
}
}
Hope this is a bit more verbose ;)
I would slightly amend things to avoid the need for 2 updates on some rows:-
<?php
$amount_to_pay = 200;
foreach ($payment_array as $ordered_repayment)
{
switch (true)
{
case $amount_to_pay <= 0 :
break;
case $amount_to_pay > ($ordered_repayment['interest'] + $ordered_repayment['penalty']) :
$sql = "UPDATE schedules
SET paid_int = paid_int + " . $ordered_repayment['interest'] . ",
paid_pen = paid_pen + " . $ordered_repayment['penalty'] . ",
interest = 0,
penalty = 0
WHERE id = " . $ordered_repayment['id'] . "";
$amount_to_pay -= ($ordered_repayment['interest'] + $ordered_repayment['penalty']);
break
case $amount_to_pay > $ordered_repayment['penalty']) :
$sql = "UPDATE schedules
SET paid_int = paid_int + " . ($amount_to_pay - $ordered_repayment['penalty']) . ",
paid_pen = paid_pen + " . $ordered_repayment['penalty'] . ",
interest = " . ($ordered_repayment['interest'] - ($amount_to_pay - $ordered_repayment['penalty'])) . ",
penalty = 0
WHERE id = " . $ordered_repayment['id'] . "";
$amount_to_pay = 0;
break
default :
$sql = "UPDATE schedules
SET paid_pen = paid_pen + " . $amount_to_pay . ",
penalty = " . ($ordered_repayment['penalty'] - $amount_to_pay) . "
WHERE id = " . $ordered_repayment['id'] . "";
$amount_to_pay = 0;
break
}
}
foreach ($payment_array as $ordered_repayment)
{
if ($amount_to_pay > 0)
{
$capital_balance = $ordered_repayment['principal'] - $ordered_repayment['principalPaid'];
if ($capital_balance > 0)
{
$sql = "UPDATE schedules
SET paid_pri = paid_pri + " . $capital_balance . "
WHERE id ='" . $ordered_repayment['id'] . "";
$amount_to_pay -= $capital_balance;
$this->query($sql);
}
}
}
I'm currently trying to put together a fairly simple search form - all checkboxes - but running into issues putting together the query. I'm able to return results if 1 location is selected, 1 experience or only 1 language. If I select a combination of any my results are spotty at best. My intention is to return all results for users with:
experience a OR b OR c AND location a OR b OR b OR d AND languages a OR b AND approved
Right now, if I only select a bunch of locations, no other criteria, I get no results.
What type of query should I be looking at when trying to search through 20+ languages, 50+ locations, and a few other requirements? And how should I go about building it? Am I on the right track?
$adv_search_query = "SELECT
users.*, general.*, languages.*, experience.*
FROM users
LEFT OUTER JOIN languages ON users.user_id = languages.user_id
LEFT OUTER JOIN general ON users.user_id = general.user_id
LEFT OUTER JOIN experience ON users.user_id = experience.user_id
WHERE (";
if(!empty($_POST['location'])) {
foreach($_POST['location'] as $location) {
$location_input = " general.neighborhood LIKE '%" . $location . "%' OR";
}
$adv_search_query .= trim($location_input, 'OR');
$adv_search_query .= ") ";
}
if(!empty($_POST['languages']) && !empty($_POST['location'])) {
$adv_search_query .= "AND (";
}
if(!empty($_POST['languages'])) {
foreach($_POST['languages'] as $language) {
$language_input = " languages." . $language . " = 1 OR";
}
$adv_search_query .= trim($language_input, 'OR');
$adv_search_query .= ") ";
}
if(!empty($_POST['experience']) && !empty($_POST['location'])) {
$adv_search_query .= "AND (";
}
if(!empty($_POST['experience'])) {
foreach($_POST['experience'] as $exp) {
$exp_input = " experience." . $exp . " = 1 OR";
}
$adv_search_query .= trim($exp_input, 'OR');
$adv_search_query .= ") ";
}
if (isset($_POST["approved"])) {
$approved = " users.approved = 1 OR";
} else { $approved = ""; }
if (isset($_POST["pending"])) {
$pending = " users.approved = 2 OR";
} else { $pending = ""; }
if (isset($_POST["incomplete"])) {
$incomplete = " users.approved = 0 OR";
} else { $incomplete = ""; }
if(isset($_POST['approved']) || isset($_POST['pending']) || isset($_POST['incomplete'])) {
$status_input = "AND (" . $approved . " " . $pending . " " . $incomplete . "";
$adv_search_query .= trim($status_input, 'OR');
$adv_search_query .= ") ";
}
$adv_search_query .= "AND users.admin_level = 0";
Tables
table.users
user_id first_name last_name admin_level user_approved
1 nick jones 0 1
2 johnny rocket 0 1
3 sally fields 0 2
table.general
user_id city state zip neighborhood
1 baltimore maryland 00125 hamsterdam
2 lakeland maine 11542 treemont
3 sonic new york 11763 highville
table.languages
user_id french german italian spanish
1 0 1 0 1
2 0 0 1 1
3 1 1 1 1
table.experience
user_id waldorf kumon homeschooling
1 0 1 0
2 0 0 1
3 1 1 1
First, be aware that your code is susceptible to SQL injection in the:
general.neighborhood LIKE
part.
In this type of SQL query building, "array()" and "implode" are your friends:
$experience_valid_values = array('exp1', 'exp2');
$experience_conditions = array();
if(!empty($_POST['experience'])) {
foreach($_POST['experience'] as $exp) {
if (in_array($exp, $experience_valid_values)) {
$experience_conditions[] = 'experience.' . $exp . '=1';
}
}
}
$language_valid_values = array('english', 'japanese', 'spanish', 'chinese');
$language_conditions = array();
if(!empty($_POST['language'])) {
foreach($_POST['languages'] as $language) {
if (in_array($language, $language_valid_values)) {
$language_conditions[] = 'language.' . $language . '=1';
}
}
}
$conditions = array();
if (!empty($experience_conditions)) {
$conditions[] = '(' . implode(' OR ', $experience_conditions) . ')';
}
if (!empty($language_conditions)) {
$conditions[] = '(' . implode(' OR ', $language_conditions) . ')';
}
$sql = 'SELECT *
FROM users
LEFT OUTER JOIN experience ON users.user_id = experience.user_id
LEFT OUTER JOIN languages ON users.user_id = languages.user_id
WHERE
';
$sql .= implode(' AND ', $conditions);
Using "array()" and "implode" will make your code shorter and easier to read.
This is just a short example I am hoping will give you the idea.
i fetch id and name from categories table like this :
|id|name| tag |desc|order|status|
|1 |test| NULL|NULL| 1 | 1 |
$i = 0;
$allcats = Access::FETCH("SELECT * FROM " . CATS . "");
$cats = array();
foreach($allcats AS $row2){
$cats[$i] = array("name" => $row2['name'], "id" => $row2['id']);
$i++;
}
i fetch category id for each news :
|id|catid|storyid|
|1 | 1 | 5 |
$groupcats = Access::FETCH("SELECT * FROM " . GROUPCATS . " WHERE storyid = ?", $row['postid']);
Now, i need to print name of cat for storyid. i,e : i need to print test(catname) for story id 5.
How do can i print this?
Easier to do with with a join rather than in php. Something like,
$joinedcats = Access::FETCH("SELECT name FROM " . CATS .
" JOIN " . GROUPCATS . " ON catid = id");
foreach($joinedcats as $row) {
echo $row['name'];
}
See https://dev.mysql.com/doc/refman/5.5/en/join.html
You can use join to get needed rows for each story:
"SELECT * FROM " . GROUPCATS . " LEFT JOIN " . CATS . " USING(categoryid) WHERE storyid = ?"
I am trying to parse all the column fields and data of a single row of any selected mysql table.
The reason behind this is to make a 'universal'-like Table parser of any given single row.
For example I have this table 'tbl1':
+----+---------------------+---------+---------+--+
| id | date | amounta | amountb | |
+----+---------------------+---------+---------+--+
| 1 | 2014-02-28 05:58:41 | 148 | 220 | |
+----+---------------------+---------+---------+--+
| 2 | 2014-01-20 05:58:41 | 50 | 285 | |
+----+---------------------+---------+---------+--+
| 3 | 2014-03-30 05:58:41 | 501 | 582 | |
+----+---------------------+---------+---------+--+
and I want to be able to select table tbl1 and id = 1 to export into:
<label>id <input type="text" value="1"/></label>
<label>date <input type="text" value="2014-02-28 05:58:41"/></label>
<label>amounta <input type="text" value="148"/></label>
<label>amountb <input type="text" value="220"/></label>
This is what I have thus far:
if ($_GET['p'] && $_GET['table']) {
include ("con.php");
$query = "SELECT * FROM `" . $_GET['table'] . "` WHERE id = '" . $_GET['p'] . "'";
$result = mysql_query($query);
while ($row = mysql_fetch_array($result)) {
$fields[] = $row['0'];
$p = $row;
}
$fields = array();
$res = mysql_query("SHOW COLUMNS FROM `" . $_GET['table'] . "`");
while ($x = mysql_fetch_assoc($res)) {
$fields[] = $x['Field'];
}
foreach($fields as $f) {
foreach($p as $obj) {
echo '<label>' . $f . ' <input type="text" value="' . $p[$f] . '"></label>';
};
}
mysql_close();
}
The problem I'm sure is somewhere between the foreach looping. I know its totally wrong but im not quite sure how to solve this problem.
Basically the idea is to select all column names from $_GET['table'] and for each column name find its value where id = $_GET['p'];
if $p is a single-level array like
Array (
'field1' => 'value1',
'field2' => 'value2',
...
)
and $fields is an array like this
Array (
0 => 'field1',
1 => 'field2',
...
)
Then this should work
foreach($fields as $f) {
echo '<label>' . $f . ' <input type="text" value="' . $p[$f] . '"></label>';
}
Use mysql_fetch_field.
$fields = array();
while($f = mysql_fetch_field($query)) $fields[] = $f->name;
In this way you can get all field names, works for any kind of query rather than just SELECT *
I bought an iPhone wallpaper gallery PHP script a year ago, and it has been running great. I've been slowly learning PHP and MySQL by doing little tweaks here and there.
To view a category, the URL is www.example.com/index.php?catid=27, and I want to change it to www.example.com/index.php?catid=apple for SEO reasons. I know I can futher tidy up the URL with .htaccess but I just want to get this word slug to begin with.
This is the code (inside index.php) that I think generates the category pages:
if (($_REQUEST['catid']) && ($_REQUEST['catid'] > 1)) {
$catid = $_REQUEST['catid'];
if (is_numeric($catid)) {
$tempitemarray = $myitems->getItemsByCategory($catid);
$catnav = "&catid=" . $catid;
$tempcat = new Category();
$tempcat->getCategory($catid);
$maintitle = "<h1>" . $tempcat->getCategoryName() . " " . $itemplural . "</h1>";
}
That code calls another PHP page called itemList.php, and inside that page I found this code:
public function getItemsByCategory($catid) {
$this->retrieveSQL = "select * from items i, item_category_lookup l where livedate < '" . $this->currentdate . "' and i.active = " . $this->showApproved . " and l.categoryid = $catid and i.id = l.itemid order by i.livedate desc limit " . $this->startRow . "," . $this->numRows;
$this->countSQL = "select * from items i, item_category_lookup l where livedate < '" . $this->currentdate . "' and i.active = " . $this->showApproved . " and l.categoryid = $catid and i.id = l.itemid";
$this->retrieveItems();
return $this->items;
}
This is the 'items' table:
id | itemname | itemdec | itemkeywords | createdate | viewcount | active | rating | livedate | sourcedesc | sourceurl
-----------------------------------
64 | Apple Logo
This is the 'item_category_lookup' table:
categoryid | itemid
-------------------------
27 | 64
This is the 'category' table below. It's not called in the query above but does have the actual category names. How would I implement it?
id | categoryid
------------------
27 | Apple
You might change the SQL of $this->retrieveSQL = ... into
if (!is_number($catid)) {
$this->retrieveSQL = "select * from items i, item_category_lookup l, category j where livedate < '" . $this->currentdate . "' and i.active = " . $this->showApproved . " and i.id = l.itemid and j.id = l.categroyid and j.categoryid = "$catid" order by i.livedate desc limit " . $this->startRow . "," . $this->numRows;
}
Try this:
URL example: www.example.com/index.php?catid=27-apple
if ( isset( $_REQUEST['catid'] ) ) {
$catSEO = explode( '-', $_REQUEST['catid'] );
}
$catid = is_numeric( $catSEO[0] ) ? $catSEO:0;