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

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

Related

insert new row with auto incremental id is not added as last entry in the database

I have a SQL - MyIsam table called links with the following structure
| field | type | null | predefined | extra |
|------------|--------------|------|------------------|----------------|
| id | int(11) | no | none | auto_increment |
| link | varcar(2083) | no | none | none |
| created_at | timestamp | no | curret_timestamp | none |
| origin | varcar(100) | no | none | none |
my insert statement is
public function createLink($user_id, $link, $origin) {
$stmt = $this->conn->prepare("INSERT INTO links(link, origin) VALUES(?,?)");
$stmt->bind_param("ss", $link, $origin);
$result = $stmt->execute();
$stmt->close();
if ($result) {
// link row created
// now assign the link to user
$new_link_id = $this->conn->insert_id;
$res = $this->createUserLink($user_id, $new_link_id);
if ($res) {
// link created successfully
return $new_link_id;
} else {
// user_link failed to create
return NULL;
}
} else {
// link failed to create
return NULL;
}
}
unfortunately sometimes i get an unexpected behavior which i'm not totally able to reproduce.
as you can see the rows with id 301 and 304 has been inserted between 286 and 293 as well as row with id 300 is between 298 and 296.
Since id is auto incremental, i would have expected to find all of them ordered from the smaller to the higher value.
This is bad for my application because i need that the chronological order of creation is respected and followed whenever i query the db to get all the links
public function getAllUserLinks($user_id) {
$stmt = $this->conn->prepare("SELECT l.* FROM links l, users_links ul WHERE l.id = ul.link_id AND ul.user_id = ?");
$stmt->bind_param("i", $user_id);
$stmt->execute();
$links = $stmt->get_result();
$stmt->close();
return $links;
}
i know i could add an ORDER BY... to my query but this would further slow down things.
Besides, new links should be added as last row of the table, not in the middle , right?!?
So the physical order you see will be down to your clustered index, if you haven't defined one then MySQL will do it for you, from the InnoDB docs here:
If the table has no PRIMARY KEY or suitable UNIQUE index, InnoDB internally generates a hidden clustered index on a synthetic column containing row ID values. The rows are ordered by the ID that InnoDB assigns to the rows in such a table. The row ID is a 6-byte field that increases monotonically as new rows are inserted. Thus, the rows ordered by the row ID are physically in insertion order.
I'm assuming you're using InnoDB but MyISAM would also use the clustered index to determine physical order. Some more info here about clustered indexes.
Edit
The comment below is correct to say clustered indexes are how data is stored and strictly speaking not how data is retrieved. In practice there's a good chance you'll find data returned in physical order if you don't specify an ORDER BY.
I was attempting to explain why the questioner might be seeing the ordering he was but I'm not saying this should be relied upon, no guarantees are given about the order of data without an ORDER BY. From the SQL 92 spec (in various places):
If an is not specified, then the ordering of the rows of Q is implementation-dependent

sum all row from 2 column and use select to display two column

example
|like |comment|
|user1|asd |
|user2|awe |
|user3|aqw |
|user4|atr |
|user5|axc |
|user6|azw |
|user7| |
i have two column and what i wanted to do is to sum all rows inside those column and fetch the two column but i dont have any idea on how to do it in a query in sql.
result
|like |comment|
| 7 | 6 |
i tried queering it but i can only fetch 1 column i dont know how to do it with two column..
function countpostcomment($puid){
$sql = "SELECT COUNT(pc_comment) FROM pcomment WHERE post_uid = '$puid'";
$stmt = $this->dbh->prepare($sql);
$stmt->execute(array("0"));
$active_data = $stmt->fetchAll(PDO::FETCH_ASSOC);
return $active_data;
}
here is my sample query i haven't tested the whole function only the select cause i dont know if i need to use array or not..
I think you simply want:
SELECT COUNT(*) as likes, COUNT(pc_comment) as comments
FROM pcomment
WHERE post_uid = '$puid';
COUNT(*) counts the number of rows (7 in this case). COUNT(<column name>) counts the number of non-NULL values in the column (6 in this case).

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.

How to speed up PHP to MySQL query with large query data

Before code :
for loop should run at least 143,792,640,000 times and create the table at least produce 563,760 rows without duplicated
I want to know how to speed up or something parallel computing like Hadoop that could accelerate between php and MySQL.
Code below:
MySQL connection
$link=mysql_connect($servername,$username,$password);
mysql_select_db($dbname);
$sql= "INSERT INTO EM (source,target) VALUES ";
for loop read data into MySQL check function if duplicate not insert and update count=count+1
for($i=0;$i<$combine_arr_size;$i++){
for($j=0;$j<$combine_arr_size;$j++){
//below check if find the duplicated like a,b we recognize b,a is same thing
if(check($combine_words_array[$i],$combine_words_array[$j])) {
$update_query="UPDATE EM SET count = count+1 where (source='$combine_words_array[$i]' AND target='$combine_words_array[$j]') OR (source='$combine_words_array[$j]' AND target='$combine_words_array[$i]');";
mysql_query($update_query);
} else {
if (!$link) {
die("Connection failed: " . mysql_error());
}
//else using insert into table () value to concatenate the string
$sql.="('$combine_words_array[$i]','$combine_words_array[$j]'),";
mysql_query(substr($sql,0,-1));
$sql= "INSERT INTO EM (source,target) VALUES ";
}
}
}
read the all vector align from comebine_word_array[] to combine_word_array[]
below is check function , check if find the pair return value
function check($src, $trg) {
$query = mysql_query("SELECT * FROM EM WHERE (source='$src' AND target='$trg') OR (source='$trg' AND target='$src');");
if (mysql_num_rows($query) > 0) {
return 1;
} else {
return 0;
}
}
table
+--------+--------------+------+-----+---------+-------+
| Field | Type | Null | Key | Default | Extra |
+--------+--------------+------+-----+---------+-------+
| source | varchar(255) | YES | | NULL | |
| target | varchar(255) | YES | | NULL | |
| count | int(11) | NO | | 0 | |
| prob | double | NO | | 0 | |
+--------+--------------+------+-----+---------+-------+
now the php code just influence the source ,target and count
It is difficult to know exactly what you want to do with duplicate combinations. For example you are getting every combination of the array, which is going to get lots of duplicates which you will then count twice.
However I would be tempted to load the words into an table (possibly a temp table) and then do a cross join of the table against itself to get every combination, and use this to do an INSERT with an on duplicate key clause.
Very crudely, something like this:-
<?php
$sql = "CREATE TEMPORARY TABLE words
(
word varchar(255),
PRIMARY KEY (`word`),
)";
$link = mysql_connect($servername,$username,$password);
mysql_select_db($dbname);
$sql = "INSERT INTO words (word) VALUES ";
$sql_parm = array();
foreach($combine_words_array AS $combine_word)
{
$sql_parm[] = "('".mysql_real_escape_string($combine_word)."')";
if (count($sql_parm) > 500)
{
mysql_query($sql.implode(',', $sql_parm));
$sql_parm = array();
}
}
if (count($sql_parm) > 0)
{
mysql_query($sql.implode(',', $sql_parm));
$sql_parm = array();
}
$sql = "INSERT INTO EM(source, target)
SELECT w1.word, w2.word
FROM words w1
CROSS JOIN words w2
ON DUPLICATE KEY UPDATE `count` = `count` + 1
";
mysql_query($sql);
This does rely on having a unique key covering both the source and target columns.
But whether this is an option depends on the details of the records. For example with your current code if there were 2 words (say A and B) you would find the combination A / B and the combination B / A. But both combinations would update the same records
Put a better processor on your server and increase the RAM, then go to your php.ini settings and raise the maximum allocated memory for all the various memory/processor relative configurations.
This will empower the server further and improve the running efficiency.
If you cannot find your php.ini file. Create a new php file with the following contents and open it in the browser:
<?php phpinfo(); ?>
Make sure you delete this file after finding out where php.ini is... as an unwanted user (hacker) could find this file and it would give them detailed information leading to vulnerabilities in your server configuration.
Once you've found php.ini, do some looks online to determine settings that are not obvious and increase the memory allocations in various areas.

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.

Categories