Pad array values with parentheses to perform insert - php

I want to have each array value inside a paranthesis
$id = $_POST['id'];
$test2 = array($id);
$id_list = implode(',', $test2);
$sql .= "INSERT INTO tmp (id) VALUES ({$id_list});";
for example: I'm performing an insert so the output of the list should be (5),(10),(15) not '5','10','15'
Any suggestions on how I can insert using an array?

MySQL's extended insert syntax is
INSERT INTO sometable (...fields...) VALUES (value set #1), (value set #2), (value set #3), etc...
Note how each value set is in its own (). You're not doing that. you're just passing a bunch of values in a SINGLE () set, which means you're providing values for fields that are NOT present in the field list.
You need to do
$id_list = '(' . implode('),(', $_POST['id']) . ')';
producing
(5),(10),(15)
instead of
(5, 10, 15)
You also need to realize that you're vulnerable to sql injection attacks.

Related

PHP dynamically add new column and value in existing Query

My application performs INSERT queries like this:
INSERT INTO table (`col1`, `col2`, `col3`) VALUES ('oneVal', 'twoVal', 'threeVal')
Now I want to rebuild my application so it will ALWAYS SELECT, INSERT, DELETE and UPDATE with a specific id.
Let's say the unique id is called: companyId
I don't want to rewrite all my queries manually, so I am trying to write a function that rewrites the existing SQL queries with PHP so it will include the companyId inside the query.
Desired outcome if companyId would be '1' (companyId IS NOT ALWAYS '1'!):
INSERT INTO table (`col1`, `col2`, `col3`, `companyId`) VALUES ('oneVal', 'twoVal', 'threeVal', '1')
My question(s) is/are:
Is there a way in PHP so I can dynamically rewrite the query so
it would include the companyId column and the matching id value?
Is there a better way to do this? Like some trick setting MySQL
server to ALWAYS use an extra value (in this case companyId='1'
?
I've tried option (1) by searching for the string
) VALUES
Once I found that string, I add companyId before the ).
Now get to the end of the query, get the most right ) and add the value before that.
But is this for a generic case? I think there might be a better way to solve this.
Thanks in advance community!
EDIT 1 with more clarification
Currently I've already built a function that modifies my SELECT statements.
Function code:
//If current query = SELECT query
if (containsString($sql, 'select')) {
//Check if contains WHERE
if (containsString($sql, 'where')) {
//Yes
//Add companyId after WHERE
$sql = substr_replace($sql, '(companyId=?) AND ', strpos($sql, 'WHERE') + 6, 0);
//Explanation:
//SELECT * FROM table WHERE deleted='No'; becomes -->
//SELECT * FROM table WHERE (companyId=?) AND deleted='No';
}else{
//No
//Get table , and after that INSERT WHERE companyId=?
$tableName = explode(' from ', strtolower($sql))[1]; //Get part AFTER 'from'
//First word after $tableName = tablename
$tableName = explode(' ', $tableName)[0]; //First word after 'from' = tablename
$sql = substr_replace($sql, 'WHERE (companyId=?) ', strpos($sql, $tableName) + strlen($tableName) + 1, 0);
//Explanation:
//SELECT * FROM table ORDER BY id; becomes -->
//SELECT * FROM table WHERE (companyId=?) ORDER BY id;
}
}
So this code dynamically adds an extra condition to the query statement.
This is also easily possible with DELETE and UPDATE statements (same as SELECT)
But Iam trying to come up with something like this for INSERT INTO queries.
How can I modify the original query using the new companyId?
I guess If you have an associative array with the column names and values then you easily can make it more dynamic for future also. Let's say you've an array of column names with value of it e.g
$data = ['col1'=>'val1','col2'=>'val2','col3'=>'val3','companyId'=>1];
$query = "INSERT INTO `yourtable` ( ".implode(' , ', array_keys($data)).") VALUES ('".implode("' , '", array_values($data))."')";
echo $query;
DEMO: https://3v4l.org/udt1i
Then you can do with regex replace way globally to add column and value to all of your 100 query.
<?php
$re = '/\) VALUES (.+?(?=\)))/m';
$str = 'INSERT INTO table (`col1`, `col2`, `col3`) VALUES (\'oneVal\', \'twoVal\', \'threeVal\')';
$subst = ',`col4`) VALUES $1 , \'1\'';
$result = preg_replace($re, $subst, $str);
echo $result;
?>
DEMO: https://3v4l.org/rOQDG

Insert an array to the database like a separate rows

Sorry if this is a duplicate, but I can't reached the result using answers in similar questions.
Here is my situation:
I have a table (users_temp) with 1 column - Users_id;
And I have an array of values, for example - $usersIds = [1,2,3,4,5];
I want to Insert this array to my table and create 5 new rows.
I was trying smth like that, but it doesn't work:
$newdata = "'" . implode("','", $usersIds) . "'";
db_query("INSERT INTO db.users_temp (user_id) VALUES ($newdata)");
Can you help me, please?
Assuming that the $userIds field is safe (all INTs) then just amend your implode a touch.
$newdata = implode("),(", $usersIds);
db_query("INSERT INTO db.users_temp (user_id) VALUES ($newdata)");
You might want to add a check that the array has more than 0 elements.
You can do this
$usersIds = [1,2,3,4,5];
foreach ($usersIds as $user_id) {
db_query("INSERT INTO db.users_temp (user_id) VALUES ($user_id)");
}
To insert multiple rows in a single statement:
sql:
insert into users_temp (user_id)
values (?), (?), (?) [...]
php:
$userIds = array(1, 2, 3, 4, 5);
$placeholders = rtrim(str_repeat('(?),', count($userIds)), ',');
$sql = "insert into users_temp (user_id) values ($placeholders)";
$stmt = $pdo->prepare($sql);
$stmt->execute($userIds);
Your code can build up the placeholders string as above.

PHP SQLSRV: passing a string of values to IN clause

I need a way to perform the following query:
SELECT *
FROM myTable
WHERE column NOT IN ('val1', 'val2')
Now, val1 and val2 are inside an array that I'm imploding into a string like this:
$inClause = "'" . implode("','", $inClauseArr) . "'";
If I put the string in the query declaration like this it works:
$sql = "[...]WHERE column NOT IN ($inClause)";
But if I pass it as a sqlsrv_query parameter like this, the query is not working:
$stmt = sqlsrv_query($conn, $sql, array($inClause));
I'm not getting any error. $stmt is true but while cycle is not returning anything.
I absolutely need to pass it through sqlsrv_query, how can i do this?
UPDATE
Here's the $sql value:
SELECT *
FROM orders(NOLOCK)
WHERE order_id NOT IN (?)
Count of question marks in your original SQL query must match with the count of parameters you have passed into the query.
Supposing $inClauseArr is like:
$inClauseArr = array('val1', 'val2');
There are two elements. Actually, it is not important how much element it has. As long as $inClauseArr is an array, there will be no problem.
So, the query should be constructed as ([...] is the beginning of the query):
$sql = "[...]WHERE column NOT IN ("
. implode(',', array_fill(0, count($inClauseArr), '?'))
. ")";
// For $inClauseArr = array('val1', 'val2');
// Output should be [...]WHERE column NOT IN (?, ?)
And execution should be like:
$stmt = sqlsrv_query($conn, $sql, $inClauseArr);
References:
sqlsrv_query
implode
array_fill
Pass it in a a comma delimited string (varchar) then use a split function to create a table:
declare #vars varchar(14) = 'G' + ',' + 'H'
select * from syscode_detail
where code in (select value from dbo.Split(#vars, ','))
using this function:
CREATE function [dbo].[Split]
(
#List nvarchar(2000),
#SplitOn nvarchar(5)
)
returns #tblReturn table
(
id int identity(1,1),
value nvarchar(100)
)
as
Begin
While (Charindex(#SplitOn, #List) > 0)
Begin
Insert Into #tblReturn (value)
Select value = ltrim(rtrim(Substring(#List, 1, Charindex(#SplitOn,
#List) - 1)))
Set #List = Substring(#List, Charindex(#SplitOn, #List) +
len(#SplitOn), len(#List))
End
Insert Into #tblReturn (value)
Select Value = ltrim(rtrim(#List))
Return
End

PDO json insert multiple tags

I've been trying to insert some tags into a table using pdo but to no avail.
I have a php array called Tag.
Sample data in tag array is as follows
tag[] = [[a,b,c,d,e],[f,g,h,i,j]]
using a for loop below I'm able to convert it to (1,'a','b','c','e',0), (1,'f','g','h','i',0)
$value="";
$value .= "($postid,";
for($i=0;$i<sizeof($tag);$i++)
{
$value .="'$tag[$i]'";
if($i + 1 == $sizeof($tag){
$value .=")";
}else{
$value .="),";
}
}
And prepare and insert into the table as follows
$inserttagquery = "insert Into tagtable ( postid, desc, b, u, toppos,leftpos ,ver) values :value";
$queryinserttag = $conn->prepare($inserttagquery);
$queryinserttag->execute(array('value'=>$value));
$insertedtag = $queryinserttag->rowCount();
However, this does not seem to work. $insertedtag does not return any value.
Your SQL is outright wrong. You're lising 6 fields AND a constant value in your field list, then providing only a SINGLE placeholder to provide values for those fields.
you cannot use numbers as a field name. 0 is a flat out syntax error and an invalid field name.
Placeholders have a 1:1 relation between a field and a value. You CANNOT shove multiple values into a single variable and try to use that value with a placeholder to fill in OTHER fields.
Your query should be:
INSERT INTO tagtable (postid, desc, b, u, etc...)
VALUES (:postid, :desc, :b, :u, etc...)
and then you provide INDIVIDUAL values for each placehodler:
$stmt->execute(array($postid, $desc, $b, $u, etc...));
As written, and ignoring all the other problems, your query would try shove your (1, 'f', 'g', etc..) string into JUST the postid field.

Using WHERE IN (...) with PDO doesn't work when the string is bound

I have a query that looks like this:
UPDATE table SET column = UNIX_TIMESTAMP()
WHERE id IN (:idString)
Where idString is a string of comma separated ids and is passed to execute() in an array. To my surprise, when this query is executed, only the row with the first id in idString is updated.
After banging my head against the wall for a while, I finally decided to try it like this:
UPDATE table SET column = UNIX_TIMESTAMP()
WHERE id IN (' . $idString . ')
The second query works as expected.
Why won't the query work when I bind the string of ids using PDO?
In SQL, the string
'1,2,3,5,12'
Is a single value, and casting it in a numeric context, it will just have the value of the leading digits, so just the value 1.
This is much different from the set of multiple values:
'1', '2', '3', '5', '12'
Any time you use bound parameters, whatever you pass as the parameter value becomes just one single value, even if you pass a string of comma-separated values.
If you want to pass a set of multiple values to parameters in your SQL query, you must have multiple parameter placeholders:
UPDATE table SET column = UNIX_TIMESTAMP()
WHERE id IN (:id1, :id2, :id3, :id4, :id5)
Then explode your string of values and pass them as an array:
$idlist = array('id1' => 1, 'id2' => 2, 'id3' => 3, 'id4' => 5, 'id5' => 12);
$pdoStmt->execute($idlist);
For cases like this, I would recommend using positional parameters instead of named parameters, because you can pass a simple array instead of an associative array:
$pdoStmt = $pdo->prepare("UPDATE table SET column = UNIX_TIMESTAMP()
WHERE id IN (?, ?, ?, ?, ?)");
$idlist = explode(",", "1,2,3,5,12");
$pdoStmt->execute($idlist);
#mario adds a comment that you can use FIND_IN_SET(). That query would look allow you to pass one string formatted as a comma-separated string of values:
$pdoStmt = $pdo->prepare("UPDATE table SET column = UNIX_TIMESTAMP()
WHERE FIND_IN_SET(id, :idString)");
$pdoStmt->execute(["idString" => "1,2,3,5,12"]);
However, I usually don't recommend that function because it spoils any chance of using an index to narrow down the search. It will literally have to examine every row in the table, and during an UPDATE that means it has to lock every row in the table.
Your working solution is not good as it's subject to SQL INJECTION.
The reason it's not working is because you are allocating an array, instead of plain comma separated values.
You have to use implode to separate the values of the array, and then assign the comma separated values to a variable wich can be used by pdo.
Otherwise you can use instead of : $idString, ? in the select statement, and executing the prepared statement from and array which holds the $idString.
$query=$db->prepare("Select a From table where b =? order by 1;");
$query->execute(array($idString));
You are trying to pass a string as a set to a prepared statement. MySQL is trying to execute the query
-- assuming idString is "1,2,3,4,5"
UPDATE table SET column = UNIX_TIMESTAMP() WHERE id IN ("1,2,3,4,5");
instead of
UPDATE table SET column = UNIX_TIMESTAMP() WHERE id IN (1,2,3,4,5);
you'll have to either use the statement
UPDATE table SET column = UNIX_TIMESTAMP() WHERE id == ?
and execute it for however many id's you have or prepare the statement by injecting id string into the query
When binding, PDO expects a single value. Something like this will work, given your $idString above (though if you have the source array, even better!):
$ids = explode(',', $idString);
$placeholders = implode(', id', array_keys($ids));
if($placeholders) {
$placeholders = 'id' . $placeholders;
$sql = "UPDATE table SET column = UNIX_TIMESTAMP()
WHERE id IN ({$placeholders})";
// prepare your statement, yielding "$st", a \PDOStatement
$st = $pdo->prepare($sql);
// bind every placeholder
foreach($ids as $key => $id) {
$st->bindValue("id{$key}", $id);
}
// execute
$st->execute();
}

Categories