Joining Two Woocommerce Tables - mysql

I am trying to join two standard woocommerce tables and get the results.
My ultimate goal being to get out the zone_name based off a location_code.
My current query is as follows but with no results:
<?php
function prntPage() {
global $wpdb;
// The SQL query
$results = $wpdb-> get_results("SELECT location_code, zone_id, zone_name ".
"FROM {$wpdb->prefix} woocommerce_shipping_zone_locations ".
"JOIN {$wpdb->prefix} woocommerce_shipping_zones ".
"ON (woocommerce_shipping_zone_locations.zone_id = woocommerce_shipping_zones.zone_id)");
// Loop though rows data
foreach( $results as $row ){
echo $row ."<br>";
}
}
prntPage();
?>

Did you not enabled error reporting? this should have given you an error
{$wpdb->prefix}has to be direct in front of the table names at any time
$results = $wpdb-> get_results("SELECT location_code, zone_id, zone_name ".
"FROM {$wpdb->prefix}woocommerce_shipping_zone_locations ".
"JOIN {$wpdb->prefix}woocommerce_shipping_zones ".
"ON ({$wpdb->prefix}woocommerce_shipping_zone_locations.zone_id = {$wpdb->prefix}woocommerce_shipping_zones.zone_id)");
but it seems more logical
{$wpdb->prefix}has to be direct in front of the table names at any time
$results = $wpdb-> get_results("SELECT location_code, zone_id, zone_name ".
"FROM {$wpdb->prefix}shipping_zone_locations ".
"JOIN {$wpdb->prefix}shipping_zones ".
"ON ({$wpdb->prefix}shipping_zone_locations.zone_id = {$wpdb->prefix}shipping_zones.zone_id)");
Because woocommerce_ is already your prefix.

Related

Writing mysql query with two variable conditions with prepare statement and bind param [duplicate]

I need to change this query to use a prepared statement. Is it possible?
The query:
$sql = "SELECT id, title, content, priority, date, delivery FROM tasks " . $op . " " . $title . " " . $content . " " . $priority . " " . $date . " " . $delivery . " ORDER BY " . $orderField . " " . $order . " " . $pagination . "";
Before the query, there's code to check the POST variables and change the content of variables in the query.
//For $op makes an INNER JOIN with or without IN clause depending on the content of a $_POST variable
$op = "INNER JOIN ... WHERE opID IN ('"$.opID."')";
//Or
$op = "INNER JOIN ... ";
//For $title (depends of $op):
$title = "WHERE title LIKE'%".$_POST["title"]."%'";
//Or
$title = "AND title LIKE'%".$_POST["title"]."%'";
//For $content:
$content = "AND content LIKE '%".$_POST["content"]."%'";
//For $priority just a switch:
$priority = "AND priority = DEPENDING_CASE";
//For $date and $delivery another switch
$d = date("Y-m-d", strtotime($_POST["date"]));
$date = "AND date >= '$d' 00:00:00 AND date <= '$d' 23:59:59";
//Or $date = "AND date >= '$d' 00:00:00";
//Or $date = "AND date <= '$d' 23:59:59";
//For $orderField
$orderField = $_POST["column"];
//For $order
$order= $_POST["order"];
//For $pagination
$pagination = "LIMIT ".$offset.",". $recordsPerPage;
How I could do this query using prepared statement?
The query could be more static but this means to make different prepared statements and execute it depending of $_POST checks.
It depends on many variables because this query show results in a table that contains search fields and column to order.
A full example of query would be like this (depending of $_POST checks):
SELECT id, title, content, priority, date, delivery FROM tasks INNER JOIN op ON task.op = op.opId WHERE op IN (4851,8965,78562) AND title LIKE '%PHT%' AND content LIKE '%%' AND priority = '2' ORDER BY date DESC LIMIT 0, 10
An excellent question. And thank you for moving to prepared statements. It seems that after all those years of struggle, the idea finally is starting to take over.
Disclaimer: there will be links to my own site because I am helping people with PHP for 20+ years and got an obsession with writing articles about most common issues.
Yes, it's perfectly possible. Check out my article, How to create a search filter for mysqli for the fully functional example.
For the WHERE part, all you need is to create two separate arrays - one containing query conditions with placeholders and one containing actual values for these placeholders, i.e:
WHERE clause
$conditions = [];
$parameters = [];
if (!empty($_POST["content"])) {
$conditions[] = 'content LIKE ?';
$parameters[] = '%'.$_POST['content ']."%";
}
and so on, for all search conditions.
Then you could implode all the conditions using AND string as a glue, and get a first-class WHERE clause:
if ($conditions)
{
$where .= " WHERE ".implode(" AND ", $conditions);
}
The routine is the same for all search conditions, but it will be a bit different for the IN() clause.
IN() clause
is a bit different as you will need more placeholders and more values to be added:
if (!empty($_POST["opID"])) {
$in = str_repeat('?,', count($array) - 1) . '?';
$conditions[] = "opID IN ($in)";
$parameters = array_merge($parameters, $_POST["opID"]);
}
this code will add as many ? placeholders to the IN() clause as many elements in the $_POST["opID"] and will add all those values to the $parameters array. The explanation can be found in the adjacent article in the same section on my site.
After you are done with WHERE clause, you can move to the rest of your query
ORDER BY clause
You cannot parameterize the order by clause, because field names and SQL keywords cannot be represented by a placeholder. And to tackle with this problem I beg you to use a whitelisting function I wrote for this exact purpose. With it you can make your ORDER BY clause 100% safe but perfectly flexible. All you need is to predefine an array with field names allowed in the order by clause:
$sortColumns = ["title","content","priority"]; // add your own
and then get safe values using this handy function:
$orderField = white_list($_POST["column"], $sortColumns, "Invalid column name");
$order = white_list($_POST["order"], ["ASC","DESC"], "Invalid ORDER BY direction");
this is a smart function, that covers three different scenarios
in case no values were provided (i.e. $_POST["column"] is empty) the first value from the white list will be used, so it serves as a default value
in case a correct value provided, it will be used in the query
in case an incorrect value is provided, then an error will be thrown.
LIMIT clause
LIMIT values are perfectly parameterized so you can just add them to the $parameters array:
$limit = "LIMIT ?, ?";
$parameters[] = $offset;
$parameters[] = $recordsPerPage;
The final assembly
In the end, your query will be something like this
$sql = "SELECT id, title, content, priority, date, delivery
FROM tasks INNER JOIN ... $where ORDER BY `$orderField` $order $limit";
And it can be executed using the following code
$stmt = $mysqli->prepare($sql);
$stmt->bind_param(str_repeat("s", count($parameters)), ...$parameters);
$stmt->execute();
$data = $stmt->get_result()->fetch_all(MYSQLI_ASSOC);
where $data is a conventional array contains all the rows returned by the query.

Displaying Foreign Key data in MySQL

I have a BusinessID in both my staff and business table and I'm wanting to display the staff members for everyone in a particular business. The query below gives me this error.
ERROR: Could not able to execute SELECT * FROM business b inner join BusinessID b ON b.BusinessID = s.BusinessID WHERE b.BusinessID = 1. Not unique table/alias: 'b'
This is my foreign key file
<html>
<body>
<?php
include_once("connect.php");
$BusinessID = $_GET['BusinessID'];
$sql= "SELECT *
FROM business b
inner join BusinessID b
ON b.BusinessID = s.BusinessID
WHERE b.BusinessID = $BusinessID";
if($result = $conn->query($sql)){
if($result->num_rows > 0){
echo "<table>";
echo "<tr>";
echo "<th>Name</th>";
echo "<th>BusinessID<th>";
echo "</tr>";
while($row = $result->fetch_array()){
echo "<tr>";
echo "<td>" . $row['Name'] . "</td>";
echo "<td>" . $row['BusinessID'] . "</td>";
echo "</tr>";
}
echo "</table>";
// Free result set
$result->free();
} else{
echo "No records matching your query were found.";
}
} else{
echo "ERROR: Could not able to execute $sql. " . $conn->error;
}
// Close connection
$conn->close();
?>
</body>
</html>
Below is the fix
$sql= "SELECT *
FROM staff
WHERE BusinessID = $BusinessID";
You need to use a join statement
Select *
From staff as S
Join business as B
on s.businessID=b.businessID
--Where clause <--- If you want to filter by anything.
You say you "have a BusinessID". If it were in $BusinessID, the form of query you want seems to be:
query select * from staff where s.BusinessID = $BusinessID
However be warned of SQL code injection as addressed at How can I prevent SQL injection in PHP? (See also the coincidental meta post from today Should the answer be the simplest ever possible, even at the expense of quality/security?) The correct way to deal with this in a PHP context is to query via prepared statements.

MySQL Multiple Simultaneous Queries From The Same Connection

I'm trying to run multiple queries on a MySQL server at the same time from PHP with the same database connection. I have run the SQL statement on the server itself but the response is as expected. I'm getting no SQL exceptions but just no SQL query response at all (the result is FALSE).
Is this because you can't query when another query is already active on the same connection?
The code loads data from the database table with people's names and last paid date information. This is displayed in a form and then the user can select members to update payments for. Then, the part I'm stuck on is the second query where the selected names are trying to find family_id for families.
Code snippet:
mysql_select_db($database_W3OITesting, $W3OITesting);
$yearnow = date("Y",strtotime('+1 year')).'-12-31';
$yearrecent = date("Y",strtotime('-1 year')).'-12-31';
$query_Recordset1 = "SELECT DISTINCT lname, fname, suffix, fcccall, members.member_id, MaxDateTime " .
"FROM members " .
"INNER JOIN " .
"(SELECT paid.member_id, MAX(paid.year) AS MaxDateTime " .
"FROM paid " .
"GROUP BY paid.member_id) groupedpaid ".
"ON members.member_id = groupedpaid.member_id " .
"Where (MaxDateTime < '$yearnow') AND ".
"(MaxDateTime >= '$yearrecent')" .
"ORDER BY lname, fname, suffix, fcccall";
$Recordset1 = mysql_query($query_Recordset1, $W3OITesting) or die(mysql_error());
$row_Recordset1 = mysql_fetch_assoc($Recordset1);
$totalRows_Recordset1 = mysql_num_rows($Recordset1);
//if a post is received handle it here
function getPostArray($array){
foreach ($array as $key => $value){
//search here for if this member is part of a family
//if not part of a family just update the payment
//record for the member with the value
try {
$query_Recordset3 = "SELECT lname, fname, suffix, `members`.member_id , `family`.member_id " .
"FROM members, family " .
"WHERE (`members`.member_id = `family`.member_id " .
"AND `members`.member_id = $key)";
echo $query_Recordset3. "<br>";
$Recordset3 = mysql_query($query_Recordset3, $W3OITesting);
$row_Recordset3 = mysql_fetch_assoc($Recordset3);
$totalRows_Recordset3 = mysql_num_rows($Recordset3);
echo $totalRows_Recordset3 . "<br>";
echo "$key => $value";
if($totalRows_Recordset3==FALSE) {//Recordset3 is always FALSE
echo " Error - " . $row_Recordset3['lname'];
}
if($totalRows_Recordset3!=0) {
echo " - A Family";
} else {
echo " - Individual";
}
echo "<br>";
}
catch(Exception $e)
{
echo "Exception: $e";
die();
}
if(is_array($value)){ //If $value is an array, get it also
getPostArray($value);
}
}
}

SQL Like Statement with multiple WHERE clauses

I am having an issue getting this to work. I have multiple WHERE statements that need to happen based on conditional information from the search query. Within there I can't seem to get the LIKE statements to work.
In the database the STREET_NUM & STREET_NAME are in different rows. I am using one input field to check against called $address
I am also struggling with getting the MIN & MAX to work.
Here is the Query:
$sql = "SELECT * FROM arc_property_res WHERE ( arc_property_res.STATUS = 'Active'";
if(!empty($_GET['city'])){
// City only query!
$sql .= "AND arc_property_res.CITY = '{$_GET['city']}'";
}
if(!empty($_GET['neighborhood'])){
// Hood only query!
$sql .= "AND arc_property_res.SUBDIVISION = '{$_GET['neighborhood']}'";
}
if(!empty($_GET['mls-number'])){
// MLS only query!
$sql .= "AND arc_property_res.MLS_ACCT = '{$_GET['mls-number']}'";
}
if(!empty($_GET['min-price']) && !empty($_GET['max-price'])){
// MIN AND MAX only query!
$sql .= "AND arc_property_res.LIST_PRICE = MIN('{$_GET['min-price']}') MAX('{$_GET['max-price']}')";
}
if(!empty($_GET['num-of-beds'])){
// BEDS only query!
$sql .= "AND arc_property_res.BEDROOMS = '{$_GET['num-of-beds']}'";
}
if(!empty($_GET['num-of-baths'])){
// BATHS only query!
$sql .= "AND arc_property_res.BATHS_FULL = '{$_GET['num-of-baths']}'";
}
if(!empty($_GET['mls-number'])){
// BATHS only query!
$sql .= "AND arc_property_res.MLS_ACCT = '{$_GET['mls-number']}'";
}
if(!empty($_GET['address'])){
$sql .= "AND arc_property_res.STREET_NUM LIKE '%{$_GET['address']}'";
$sql .= "OR arc_property_res.STREET_NAME LIKE '{$_GET['address']}%'";
}
$sql .= ") ORDER BY {$orderby}{$price_order}{$comma}{$list_date}";
I think all you need are some parentheses around the arc_property_res.STREET_NUM. Further, I would recommend you add some spaces around each line in your entire code so that you don't get syntax errors.
if(!empty($_GET['address'])){
$sql .= " AND (arc_property_res.STREET_NUM LIKE '%{$_GET['address']}' ";
$sql .= " OR arc_property_res.STREET_NAME LIKE '{$_GET['address']}%') ";
}
In addition to the obvious "Bobby Tables" issue that your query has, the problem at hand is that you do not insert a space in front of AND. This results in queries that look like this:
AND arc_property_res.BEDROOMS =3AND arc_property_res.BATHS_FULL =2
Note that there is no space between 3 and AND - a syntax error.
You should look into parametrizing your queries, and modifying it in a way that ignores the parameters that have been set to NULL.
SELECT * FROM arc_property_res WHERE ( arc_property_res.STATUS = 'Active'
AND (arc_property_res.CITY = #cityParam OR #cityParam is NULL)
AND (arc_property_res.SUBDIVISION = #subdiv OR #subdiv is NULL)
...
)
This modification would let you keep the query the same regardless of the number of parameters that were actually set, get you the same results, taking pretty much the same time.
$sql .= "AND arc_property_res.LIST_PRICE = MIN('{$_GET['min-price']}') MAX('{$_GET['max-price']}')";
The min and max functions are for when you want to get the min and max of a field in your database.
What you want is to compare the list price to see if it falls in between the min and max values supplied by the user.
$sql .= " AND arc_property_res.LIST_PRICE >= '{$_GET['min-price']}' AND arc_property_res.LIST_PRICE <= '{$_GET['max-price']}'";

Correcting an UPDATE statement (and making it more secure!)

I'm trying to a single value in my DB...When I run it through the console, it works correctly (as I'm replacing the variables with numbers and text).. However, My query is not returning a value for book ID when I insert the PHP variable for it.. It's because the book_id is unpopulated...
$query = "UPDATE books "
. "SET readstatus='".$readstatus."' "
. "WHERE book_id=".$book_id;
echo $query
The echoed query states:
UPDATE books SET readstatus='half' WHERE book_id=0
The book ID is stored in the URI as bookstatusupdate.php?book_id=
Just cannot figure this one out!
It would help to know the error. Firstly, echo out the query:
$query = "UPDATE books "
. "SET readstatus='".$readstatus."' "
. "WHERE book_id=".$book_id;
echo $query;
I would guess that $book_id is unpopulated, so the query fails. What you should really be doing to make it secure is casting integers with (int) and wrapping strings in mysqli_real_escape_string().
$query = "UPDATE books "
."SET readstatus='". mysqli_real_escape_string( $readstatus )."' "
."WHERE book_id=". (int) $book_id;
If you're trying to get data from the URL, do it like so:
$book_id = (int) $_GET['book_id'];
$query = "UPDATE books "
."SET readstatus='". mysqli_real_escape_string( $readstatus )."' "
."WHERE book_id=". (int) $book_id;
echo $query;