Updating SQL Column - php

So I have a table with 6 columns, each column corresponds to a certain product type. Each column holds a number that corresponds to the number of times people have chosen that product type.
A | B | C | D | E | F
---------------------
0 | 0 | 0 | 0 | 0 | 0
So if the user picks type A, then I want to update column A's number from 0 to 1. So here's the SQL code I wrote:
$link = new PDO('***;dbname=***;charset=UTF-8','***','***');
$stmt = $link->prepare("UPDATE table SET :column=:num");
$stmt->bindParam(':column', $column);
$stmt->bindParam(':num', $num);
$stmt->execute();
But it's not updating anything at all. So i'm guessing there is something wrong with the SQL code, most likely having to do with the column placeholder :column. Can anyone tell me the right SQL code?

First make sure, $column is in an accepted list of values. Next, you can't bind :column you will have assign it like so:
$stmt = $link->prepare('UPDATE table SET ' . $column .' = :num');
$stmt->bindParam(':num', $num);
$stmt->execute();
If you were going to check for a valid $column I would use
$valid_column = preg_match('/[a-z0-9_]/i, $column);
or a sufficient replace (preg_replace). Though you would likely wrap it in a try/catch and set exceptions to be thrown in your PDO instance to make sure it's even legit.

Related

How can I get the sums of all the integers in a row of an SQL table with PHP?

I am currently making an attendance website. The data for attendance is stored like this...
+-----------------------------------------------+
| Name | 12/20/16 | 12/21/16 | 12/23/16 |
+-----------------------------------------------+
|Person1 | 1 | 0 | 1 |
|Person2 | 0 | 1 | 0 |
|Person3 | 1 | 1 | 1 |
+-----------------------------------------------+
If a person was there, then the date column for their row is marked as a "1". If they weren't there, then the date column for their row is marked as a "0".
I am trying to make a readout of how many days they were present.
How can I get a sum of all the values in the date columns for that specific person's row in PHP?
EDIT: I understand that it is a bad way of formatting the data. This is per the owners request. They have their mind set on it and won't listen to reason. They are thinking of SQL as an Excel file.
Since you can't refactor the database to work the only way to do this is
SELECT name, `12/20/16`+`12/21/16`+`12/23/16` as days_attended
FROM tablename
and yes every time you add a column you have to change your query.
You could make a dynamic query -- use the above as a template as to what that dynamic query would look like.
But you REALLY should refactor the database and make a view for your user to make them happy.
This is exactly why views exist.
Okay so with the help of some people in the comments, I have put together a working function to accomplish what I needed.
$ppl = mysqli_query($conn, "SELECT * FROM Attendance2016 WHERE name = '" . getSessionVal("Name") . "'");
$row = mysqli_fetch_array($ppl);
$loopMax = count($row);
$currentAtttendance = 0;
for($x = 0; $x < $loopMax; $x++){
if($row[$x] === "0"){
continue;
}else if($row[$x] === "1"){
$currentAtttendance = $currentAtttendance + 1;
}
}
return $currentAtttendance;

Passed-in parameter used in GROUP BY still returns 1 row?

I stumbled on this behavior and now I'm curious what's actually going on. If I try to bind a parameter to a column name in the GROUP BY clause, data is actually returned, but only one row. I know you're not allowed to use table or column names as parameters, but I'm wondering what's causing this to happen behind the scenes. This was buried deep in my (amateur) code and it was hard to troubleshoot, because it didn't throw an error, and it actually returned data. I would love more insight into this!
Sample table:
| artist | album | label |
|-----------------|----------------|-------------|
| John Coltrane | A Love Supreme | MCA Records |
| John McLaughlin | Extrapolation | Marmalade |
| John Coltrane | A Love Supreme | Impulse! |
| John McLaughlin | Extrapolation | Polydor |
Example code:
$field = 'artist';
$artist = '%john%';
$sql = 'SELECT artist, album
FROM record_collection
WHERE artist LIKE :artist
GROUP BY :field';
$stmt = $pdo->prepare($sql);
$stmt->bindParam(':artist', $artist);
$stmt->bindParam(':field', $field);
$stmt->execute();
echo 'Row count: '. $stmt->rowCount();
This returns: "Row count: 1"
I noticed:
Using "GROUP BY artist" instead, and commenting out the bindParam line for :field, I get the expected "Row count: 2". This is the right way to do it.
Using the parameter :field in the WHERE clause as a column name (e.g., "WHERE :field LIKE :artist") gives you "Row count: 0".
So what I'm wondering is:
What is SQL/PDO doing beind the scenes that's causing 1 row to get returned?
If parameters in the GROUP BY aren't supported, why not return nothing, or better yet, throw an error? I assume not, but is there any legitimate use for passing a parameter into GROUP BY?
When you pass :field in, then you are passing in a string value. So, the result is group by <constant>, which returns one row.
You cannot parameterize the name of a column, so you have to put it directly into the SQL statement:
$sql = 'SELECT artist, album
FROM record_collection
WHERE artist LIKE :artist
GROUP BY '.$field'
:artist is fine because it is a value, not an identifier in SQL.

MySQL Where clause misinterpreting data

It is strange for me that for MySQL '1kdasa' AND '1' seem to be the same thing. I ran the following query
SELECT *
FROM `applications`
WHERE user_id = '1dadawdq'
AND uni_id = '3'
LIMIT 0 , 30
And the result was a row where user_id = 1. Well this is really strange. I want to check whether user with particular id exists but if MySQL doesn't understand that '1ada' is not '1' I have to write:
public function user_exists($user_id){
if(!is_numeric($user_id)){
return FALSE;
}
$query = $this->CI->db->get_where('user', array('id'=>$value));
return $query->num_rows() === 1;
}
Is this the only possible solution? I don't think it's very smart to check everytime if the passed variables are numeric or not.
Update
If i don't escape the data that is :
SELECT * FROM `applications` WHERE user_id = eqewq and uni_id = 2
There shows up an error :
#1054 - Unknown column 'eqewq' in 'where clause'
SELECT 1='1';
+-------+
| 1='1' |
+-------+
| 1 |
+-------+
SELECT 1='1a';
+--------+
| 1='1a' |
+--------+
| 1 |
+--------+
SELECT '1'='1a';
+----------+
| '1'='1a' |
+----------+
| 0 |
+----------+
You are trying to pass a string through an int field. This causes MySQL to only parse the first numerical characters. You can try to seacrh for a1dadawdq and you will have zero results because the first character is not numeric.
So it is very important you always check your input and prepare your queries before you send it to MySQL. You can try using MySQLi or PDO for this.
PDO or MySQLi are not directly a solution to your problem but they can help you since you can use their prepare() method. But verifying your input data will definitely help you. If you only want an integer to be passed through to MySQL try something like:
if (!is_int($idUser)) {
$idUser = null;
}
Or:
if (!is_int($idUser)) {
return false;
}
In the case of your update you can use PDO's bindParam() or bindValue() to pass only variables and no query string. By doing so, MySQL will never mistake a value for a column. Read the documentation and see whether you'll prefer PDO or MySQLi.
Personaly I use PDO becasue of the wide support of different database types.

SQL Fetching single scalar using PDO

This question is more on this post. So I want to retrieve the number in a certain column. So let's say the table looks like this:
A | B | C | D | E | F
---------------------
0 | 50| 0 | 0 | 0 | 0
Let's say the user enters in B, I want to go fetch the current number in the table, which is 50. This is my SQL code:
$link = new PDO('**;dbname=**;charset=UTF-8','**','**');
$stmt = $link->prepare("SELECT MAX(:type) as max FROM table");
$stmt->bindParam(':type', $type);
$stmt->execute();
$used = $stmt->fetch(PDO::FETCH_ASSOC);
$used = $used["max"];
But this is just returning B rather than the number. How do I change my code to get the number? Thanks
SELECT MAX(:type) as max FROM table
Assuming :type gets set to "B", this will be executed as:
SELECT MAX("B") as max FROM table
The MAX of something with just one value will be that one value, i.e.:
SELECT "B" as max FROM table
Most likely you want to select the MAX from that column, not the string:
SELECT MAX(B) as max FROM table
Which you can do like this:
$stmt = $link->prepare("SELECT MAX($type) as max FROM table");
Obviously, this is vulnerable to SQL injection if $type comes from a user, but if you're getting a table name from a user, then you're doing something wrong anyway.
It's also possible that your table would make more sense like this:
type | value
------------
A | 0
B | 50
C | 0
And then you could get it with:
SELECT MAX(value) FROM tableName WHERE type = :type

PHP: using REGEX to get the tablename from a mysql query

Consider these three mysql statements:
select * from Users;
select id, title, value from Blogs;
select id, feelURL, feelTitle from Feeds where id = 1;
Now im not very good at REGEX, but i want to get the table name from the mysql query. Could someone possibly create one for me with a little explanation.
Thanks,
You can actually use MySQL as the parser and get the tablenames in your query no matter how complex your SQL syntax.
(Sorry that this is a late response to your question - I just had the same problem today and found this solution.)
Simply prefix your query with the word EXPLAIN and the results set returned to PHP will include id,select_type,table,type,possible_keys,key,key_len,ref,rows,Extra. The third column is the name of each table in your query.
For example, if your query was:
select count(*) from ey_def left join ey_rels on def_id=item_id;
Use:
explain select count(*) from ey_def left join ey_rels on def_id=item_id;
And MySQL will return this to PHP:
+----+-------------+---------+-------+---------------+---------+---------+------+------+-------------+
| id | select_type | table | type | possible_keys | key | key_len | ref | rows | Extra |
+----+-------------+---------+-------+---------------+---------+---------+------+------+-------------+
| 1 | SIMPLE | ey_def | index | NULL | PRIMARY | 4 | NULL | 87 | Using index |
| 1 | SIMPLE | ey_rels | ALL | NULL | NULL | NULL | NULL | 123 | |
+----+-------------+---------+-------+---------------+---------+---------+------+------+-------------+
Now you can simply process the results like any other query.
Try:
preg_match('/\bfrom\b\s*(\w+)/i',$query,$matches)
This will not work if the query has more than 1 table.
Basically the regex searchs for the complete word FROM in the query and picks the following word as the table name.
A naive implementation would be this:
preg_match("/\s+from\s+`?([a-z\d_]+)`?/i", $query, $match);
echo $query . " => " . $match[1] . "\n";
This will break when you have a subquery in your SELECT field list (and probably in a few other cases). Or when your table name contains characters beside a-z, numbers and underscores.
Parsing SQL correctly isn't trivial.
For the query string you gave, the following should do:
preg_match_all('/from (\w+)/', $query, $tables);
print_r($tables[1]);
[0] => Users
[1] => Blogs
[2] => Feeds
But like pointed out in a comment already, creating a full fledged SQL parser is a non-trivial task. Don't expect this to be usable on any and all queries you throw against it.
Wish I would have seen this earlier... Like the people above me stated, it's non-trivial parsing sql statements. To pick out the table names from a sql string, it would be a better idea to get all the table names first, then find matches in the sql (providing you don't have a million tables in your database). I just so happen to have a function on hand that does just that:
/*
Takes a sql statement and attempts to get a table name from it.
This assumes a database is already specified in the connection.
[$sql]: string; SQL statement that was executed
[$conn]: resource; MySQLi connection resource
returns table name string
*/
function get_table_names($sql,$conn){
//declare variables
$table_array = array();
$table_string = "";
//get all the table names in the selected database
$sql2 = "SHOW TABLES";
$result = mysqli_query($conn, $sql2);
//display an error if something went wrong
if (!$result) {
echo "DB Error, could not list tables\n";
echo 'MySQL Error: ' . mysqli_error($conn);
exit;
}
//fetch the rows and push the table names into $table_array
while ($row = mysqli_fetch_row($result)) {
array_push($table_array, $row[0]);
}
//loop through all the tables in the database
foreach($table_array as $table){
if(strpos($sql,$table)){ //if match is found append to string
$table_string .= " $table ";
}
}
//return a string of table name matches
return $table_string;
}
Hope that helps someone...
This should do it:
(SELECT|DELETE|UPDATE|INSERT INTO) (\*|[A-Z0-9_]+)(FROM)?([A-Z0-9_, ]+)
It will works with select delete update and insert. If you use tablename1, tablename2 it will return it as a array

Categories