php mysqli fetch_assoc ignores null values for left join - php

I have a problem and don't know why my mysqli query does not work correctly in PHP.
I have two tables:
table 1 "reviews"
id | dateCreated | reviewText | ...
table 2 "issues"
id | reviews_id | issueText | ...
Table 1 contains reviews from users. Table 2 contains issues (complaints) for certain reviews. However one review does not necesarrily need to have a issue (if nobody reported it). The query is:
SELECT reviews.*, issues.*
FROM reviews
LEFT JOIN issues
ON reviews.id=issues.reviews_id
WHERE (reviews.idVenue='6438' OR reviews.idVenue='6444' OR reviews.idVenue='7590' )
ORDER BY 'reviews.updated' DESC
This works perfectly if one issue exists for a review (ON clause is true).
However if there is no issue existing for the review (the ON clause is not true), then reviews.id and issues.reviews_id are missing in the $row that was fetched with
if($result = $this -> mysqli -> query($qstr)){
while($row = $result->fetch_assoc()){
$temp[] = $row;
}
}
Also all the other colums from issues are missing in $row because they are NULL. With missing I mean, that they are not there if I print $row or check $row as a watch variable in the netbeans debugger. It just doesn't show reviews.id and issues.reviews_id.
If I run the query above directly in phpmyadmin, then it returns all columns correctly with the value NULL for the cases where no related issue was found for a review.
So my question is, why does PHP ignore the NULL values and does not return them in $row but phpmyadmin does?
Thanks!

Here's my comment in more descriptive information in case someone runs into this in the future.
When you call fetch_assoc php assign columns names as keys & their values as the values of the array, As the keys of an array are unique any entry that holds the same id will be overwritten.
In your example both reviews & issues have a column named 'id' therefore you'll always get the value from the last 'id' column you used in your select statement.
An easy fix for this (without having to permanently change your columns names) is to use aliases in your query.
e.g: SELECT reviews.*, issues.id as `issue_id`, issues.issueText

Related

get a row from a many-to-many table on MySql from both primary keys

I have two main tables:
Products
Category
Then I have a 3rd many-to-many table called Prices with the rows:
IdProducts (Primary key and Foreign Key from Products)
IdCategory (Primary key and Foreign Key from Category)
Price
The 3rd table doesn't have an individual ID for each row, just those 3 rows.
That said, I've already been able to insert data with no problems on the 3rd table using the data from Products and Category. Now I'm trying to edit this data in a small program but I just can't get if there is a coincidence to pre-fill the edit form. I'm using the following code:
$sql1 = mysqli_query($con,"SELECT * FROM table3 WHERE (idProducts='$variable1' AND idCategory='$variable2') LIMIT 1") or die(mysql_error());
$PriceCount = mysqli_num_rows($sql1);
if($productCount>0){
while($row = mysqli_fetch_array($sql1)){
$priceshow= $row["price"];
$idCategoryshow= $row["idCategory"];
$idProductshow= $row["idProduct"];
}
}
So with that I could get the price, category id, and product id ready to show in html.
I don't know if I'm doing something really wrong but my best bet is that the error is in the MySql query:
"SELECT * FROM table3 WHERE (idProduct='$variable1' AND idCategory='$variable2') LIMIT 1"
This returns no values even if I send data that I know it should be using.
Is that how I'm supposed to try to get all the rows? Or maybe something like this can't be done and I need an individual ID for the prices?
EDIT: I found a dirty work around by sending the variables on a href Link... anyway Im facing the almost very same problem when trying to update the desired row, mysql code for update is this:
"UPDATE table3 SET (price1='$var1' AND price2='$var2') WHERE (price.idProduct='$idProduct' AND price.idCategory='$idCategory') "
and i recieve the following error
You have an error in your SQL syntax; check the manual that corresponds to your MySQL server version for the right syntax to use near 'SET (price='123231211.00' AND price='1212654.00') WHERE (table3.idProduct='QEW21' AND ta' at line 1
Finally I can go to sleep xd
"UPDATE table3 SET price1='$var1', price2='$var2' WHERE
price.idProduct='$idProduct' AND price.idCategory='$idCategory'"
I was using "SET(X AND Y)" and "WHERE(X AND Y)" and checking the right uses of AND ends up it was actually like: "SET X, Y" and "WHERE X AND Y"

CodeIgniter SQL query missing columns

I have this SQL query which is something like this in normal sql syntax
SELECT *
FROM question
LEFT JOIN abcd_selection ON question.questionID = abcd_selection.questionID
WHERE question.SurveyID =21
This works perfectly fine I get what i wanted. However when I switch this over to CI, it does not work strangely. The questionID column disappears for rows that do not have a match with abcd_selection.
$this->db->select('*');
$this->db->from('question');
$this->db->join('abcd_selection','question.QuestionID = abcd_selection.QuestionID', 'left');
$this->db->where('question.SurveyID', $input_qid);
$query = $this->db->get();
Can anyone solve this??
When joining 2 tables, which have the same column name, you will get a NULL value when there is not a row that matches in the second table.
question.SurveyID question.QuestionID abcd_selection.QuestionID
1 2 2 //matching row in abcd_selection
2 3 NULL //no matching row in abcd_selection
Since the column names are the same, php will select the last instance of QuestionID, which will be NULL when a matching row does not exist.
One way to work around this is to select the column as an alias
$this->db->select('*,question.QuestionID as QID');
Now you can select $query['QID'] even when a abcd_selection.QuestionID does not exist.

Resetting column ID's [duplicate]

This question already has answers here:
How to handle fragmentation of auto_increment ID column in MySQL
(5 answers)
how to reindex mysql table
(7 answers)
Closed 9 years ago.
I currently have a database which looks a lot like the one below:
1 citrus
2 pear
4 apple
5 melon
8 mango
The numbers represent a column that hold the row numbers but because rows are often deleted they get messed up quite often. With what MySQL query in PHP could I recount these rows so they would make sense again?
You should never re-arrange ID's in a relational database. At least not if they are to be used as a foreign key. (Which I bet they would be, otherwise what's the sense of that ID?)
Your fruits table: 1=>citrus, 2=>pear, 4=>apple, 5=>melon, 8=>mango
Consider having another table, colors holding 1=>red, 2=>yellow, 3=>green.
And now consider having a table fruit_color holding 1=>2 , 2=>3, 4=>3, 5=>2, 8=>1.
Now what would happen if I were to just go rearrange your fruits table?... The relations would get messed up.
The ID associated with each row is a primary key, and is generally auto_increment'd on each insert. This id is used as the unique identification for each row, so that a query can be used to select it, and it alone.
As you delete data, the rows will remain lined up in order from lowest id to highest id, the highest id being the last row inserted.
It is normal for the database to have gaps, but you can also manually assign the id, granted you know that it does not already exist in the database.
If you want to keep your data in a specific order, you could assign an index to each object, representing its rank, and using an order by command in the query.
SELECT id, fruit, rank FROM fruits ORDER BY rank ASC;
What you are trying to do is going to be (A) complicated code, and (B) a nightmare to update, which means that (C) odds are good it is going to screw up your database. As nl-x suggests, your best bet is to leave your PRIMARY KEYs as is. I would recommend, however, if you want to have a set ID, to assign it when you pull it with PHP.
function get_fruits() {
//SQL query
$query = ...
//SQL result
$i = 0;
while($row = $query->fetch_assoc()) {
$rows[$i] = $row;
}
return $rows;
}
Now when you cycle through your rows you'll be able to treat each array key as the rank, and the end user will be none the wiser, all while keeping your database's integrity up to snuff.
** Pulling Data by Order**
If for whatever reason you needed to be able to pull by the record number rather than the ID, you can also get the 5th record from the following table with the following query.
ID | fruit_name
----------------
1 | Apple
2 | Banana
4 | Kiwi
7 | Coconut
9 | Strawberry
The Query in PHP:
$sql = "SELECT fruit_name FROM fruit_table LIMIT $i,1"
That will pull the $i+1 record. Meaning if you want to get the first record, $i=0. This is the way most for loops are executed, and MySQL auto increments by default will start with 1.

Select on empty table but still get column names

I want to do a SELECT on an empty table, but i still want to get a single record back with all the column names. I know there are other ways to get the column names from a table, but i want to know if it's possible with some sort of SELECT query.
I know this one works when i run it directly in MySQL:
SELECT * FROM cf_pagetree_elements WHERE 1=0;
But i'm using PHP + PDO (FETCH_CLASS). This just gives me an empty object back instead of an row with all the column names (with empty values). So for some reason that query doesn't work with PDO FETCH_CLASS.
$stmt = $this->db->prepare ( $sql );
$stmt->execute ( $bindings );
$result = $stmt->fetchAll ( \PDO::FETCH_CLASS, $class );
print_r($result); // Empty object... I need an object with column names
Anyone any idea if there's another method that i can try?
Adding on to what w00 answered, there's a solution that doesn't even need a dummy table
SELECT tbl.*
FROM (SELECT 1) AS ignore_me
LEFT JOIN your_table AS tbl
ON 1 = 1
LIMIT 1
In MySQL you can change WHERE 1 = 1 to just WHERE 1
To the other answers who posted about SHOW COLUMNS and the information scheme.
The OP clearly said: "I know there are other ways to get the column names from a table, but i want to know if it's possible with some sort of SELECT query."
Learn to read.
Anyway, to answer your question; No you can't. You cannot select a row from an empty table. Not even a row with empty values, from an empty table.
There is however a trick you can apply to do this.
Create an additional table called 'dummy' with just one column and one row in it:
Table: dummy
dummy_id: 1
That's all. Now you can do a select statement like this:
SELECT * FROM dummy LEFT OUTER JOIN your_table ON 1=1
This will always return one row. It does however contain the 'dummy_id' column too. You can however just ignore that ofcourse and do with the (empty) data what ever you like.
So again, this is just a trick to do it with a SELECT statement. There's no default way to get this done.
SHOW COLUMNS FROM cf_pagetree_elements;
This will give a result set explaining the table structure. You can quite easily parse the result with PHP.
Another method is to query the infomrmation schema table:
SELECT column_name FROM information_schema.columns WHERE table_name='cf_pagetree_elements';
Not really recommended though!
You could try:
SELECT * FROM information_schema.columns
WHERE table_name = "cf_pagetree_elements"
Not sure about your specific PHP+PDO approach (there may be complications), but that's the standard way to fetch column headings (field names).
this will list the columns of ANY query for PDO drivers that support getColumMeta. I am using this with SQL server and works fine even on very complex queries with aliased tables, sub-queries and unions. Gives me columns even when results are zero
<?php
// just an example of an empty query.
$query =$PDOdb->query("SELECT * from something where 1=0; ");
for ($i=0; $i<$query->columnCount(); $i++) {
echo $query->getColumnMeta($i)['name']."<br />";
}
?>
Even without PDO in the way, the database won't return the structure without at least one row. You could do this and ignore the data row:
SELECT * FROM cf_pagetree_elements LIMIT 1;
Or you could simply
DESC cf_pagetree_elements;
and deal with one row per field.
WHERE 1=0 does not work for me. It always returns empty set.
The latest PDO for SQLSVR definitely works with get column meta.
Simply set up your statement and use this to get an array of useful information:
$stmt->execute();
$meta= array();
foreach(range(0, $stmt->columnCount() - 1) as $column_index)
{
array_push($meta,$stmt->getColumnMeta($column_index));
}
Complete solution for Oracle or MySQL
for any or some columns (my goal is to get arbitrary columns exactly as they are in DB regardless of case)
for any table (w or w/o rows)
$qr = <<<SQL
SELECT $cols
FROM (SELECT NULL FROM DUAL)
LEFT JOIN $able t ON 1 = 0
SQL;
$columns = array_keys($con->query($qr)->fetchAll(PDO::FETCH_ASSOC)[0]);
if($cols === "*") {
array_shift($columns);
}
YOu could use MetaData with;
$cols = mysql_query("SHOW COLUMNS FROM $tableName", $conn);

PHP mysql ordering rows

For example, I have a table which looks like this :
id | name
1 | Mike
2 | Adam
3 | John
4 | Sarah
...
Now, when I execute query select * from table order by id desc it will output something like this:
4 | Sarah
3 | John
2 | Adam
1 | Mike
Now what do I do if I want to move John's row up or down, or move Adam's row up or down ( with a MySQL query ( I need basic one, just to know from where to start )).
My solution :
First of all, I created another column named orderID which has the same value as id.
Here is an example which moves up a user:
$query = "
SELECT (
SELECT orderID
FROM test WHERE id = 'user id that i want to move up'
) AS user_order,
(
SELECT orderID
FROM test WHERE orderID > user_order
ORDER BY orderID
LIMIT 0,1
) AS nextUser_order
";
$result = mysql_query($query);
$data = mysql_fetch_assoc($result);
$query = "
UPDATE test SET orderID = IF(orderID='{$data[nextUser_order]}',
'{$data[user_order]}', '{$data[nextUser_order]}')
WHERE orderID IN ('{$data[nextUser_order]}', '{$data[user_order]}');
";
$result = mysql_query($query);
Is there a better way to do that?
You have to switch IDs, or to order it by another column. That's the only way.
Changing the id is not what you want to do. You never want to mess with your primary key especially because later down the road it would be easier (and take up much less space, one is an int the other a varchar) to reference your users using their id rather than their name from other tables, it is nice to have a field that you know will never change.
Make another field such as order as a floating point number.
When you move foo between bar and foobar, set foo's order to the average of bar and foobar's order.
You can put arbitrary values into an order by clause in a query, but none will work easily for a simple "move up/down a row" type things. You can force certain values to sort first or last, but not "put this value after that value, but let that value go into its natural place". You'd need to have an extra field to specify sorting order.
SQL tables aren't inherently ordered - they effectively behave like a "bag of rows". If you want the results in a specific order, you will need to sort them (using ORDER BY ...) when you pull them out of the bag -- otherwise, the SQL server will return them in whatever order it feels is easiest. (In this case, they're coming out in the reverse order you inserted them, but that's not guaranteed at all.)
You should def be using another column which holds the order of the display. id is just a unique identifier. On a relational database moving up and down rows might result in a lot of queries because of the updates on the related tables so I stick with the idea of defining a special row for this purpose.

Categories