MySQL IN CLAUSE duplicates WANTED - php

$sql = 'SELECT * FROM `courses` WHERE `id` IN ('. implode(",", $course[0]) .')';
This is the code I just had help with, I have used it before but the problem with it is, when something for example 1,1,2,3 is passed into the IN clause I will not get all values returned.
1x1
2x1
3x1
I want
1x2
2x1
3x1
All values must be returned otherwise it will mess up my table, any suggestions?

You won't be able to do that in SQL. Query by id and then use whatever language your are using to to do the 'multiplication'.

Related

How to get max of an ID as a PHP variable and insert it into another table where the ID is also max

I have two tables, Requests & Accounting_Fundscenter_Request
I'm creating a SQL query in PHP that updates
Request_ID from Accounting_Fundscenter_Request WHERE ID is max
to
the max Request_ID from Requests
So far I have gotten the max(Request_ID) rom Requests, but I don't know how to take that value in php & sql and update the other Request_ID to equal that value.
Also, I cannot use the syntax "max(id)" because the "max" function will not work in my first query and I don't know why.
Here's what I have so far:
/* GET MAX ID FROM REQUESTS */
$selectMaxID = 'SELECT Request_ID FROM Requests ORDER BY Request_ID DESC LIMIT 1';
$maxIdResult = mysqli_query($conn, $selectMaxID); //run query
if (mysqli_num_rows($maxIdResult) > 0) {
while($maxid = mysqli_fetch_assoc($maxIdResult)) {
echo "Max Request ID: " . $maxid["Request_ID"]. "<br>";
} //echo result of
}
$insertFundsCenterMaxId = "INSERT INTO `Accounting_Fundscenter_Request` (
`Request_ID`,
VALUES (
$maxid["Request_ID"],
)
WHERE MAX(`ID`);";
/* RUN THE QUERY */
$insertFundsCenterMaxId = mysqli_query($conn, $insertFundsCenterMaxId);
This does not work. Is there a way to fix this or maybe do it in one query?
EDIT: with your help I found the solution:
You have many options here:
You can fix the syntax error you have in you insert query execution like this:
$insertFundsCenterMaxIdQuery = sprintf('INSERT INTO Accounting_Fundscenter_Request (Request_ID) VALUES (%d)', $maxid["Request_ID"]);
/* RUN THE QUERY */
$insertFundsCenterMaxId = mysqli_query($conn, $insertFundsCenterMaxIdQuery);
This way you use string formatting to replace the variable instead of directly using $maxid["Request_ID"] in a string.
Please replace %d with %s in case the Request_ID is supposed to be string/varchar.
Or you can follow another approach and just use one query to do the work like this:
INSERT INTO Accounting_Fundscenter_Request (Request_ID)
SELECT MAX(Request_ID) FROM Requests
And just execute this query
You're facing a syntax error in the update query:
$insertFundsCenterMaxId = "INSERT INTO `Accounting_Fundscenter_Request` (
`Request_ID`,
VALUES (
$maxid["Request_ID"],
)
WHERE MAX(`ID`);";
Using the double quotes in that variable hiding in the VALUES part, you are ending the string contained in insertFundsCenterMaxId. Following it is a raw string containing Request_ID which cannot be parsed by PHP. That's simply invalid code.
To solve it, you could start using prepared statements. They will also help you to secure your application against SQL injection.
There is also a solution to the syntax error problem alone - but that will leave your application vulnerable. That's why I haven't included a fix for that, but by checking how to build strings you might find it on your own. But please, please do not use it for this problem. Please.

Double quotes in PHP

I don't know PHP at all, so I am struggling through this. I need to add an or section to a MySQL query, but the values I'm searching have double quotes. I need to figure out how to add them in PHP so they are passed in to MySQL. The current query looks like:
$query = 'SELECT * FROM ' .$tableName.' WHERE allowed_countries LIKE "%'.$regionId.'%" and skurules REGEXP "i:'.$secondlastdigit.';" and status = 1 ORDER BY id DESC LIMIT 1';
But I need to add an or statement to search for string values that looks like:
$query = 'SELECT * FROM ' .$tableName.' WHERE allowed_countries LIKE "%'.$regionId.'%" and skurules REGEXP "i:'.$secondlastdigit.';" or skurules REGEXP "s:1:'.$secondlastdigit.';" and status = 1 ORDER BY id DESC LIMIT 1';
with double quotes surrounding the second instance of '.$secondlastdigit.'; when passed into MySQL.
My JSON string I'm searching looks like this:
a:12:{i:1;s:2:"15";i:2;s:2:"10";i:3;s:2:"30";i:4;s:2:"50";i:5;s:3:"120";i:6;s:3:"240";i:7;s:3:"480";i:8;s:3:"960";i:9;s:4:"3786";s:1:"A";s:3:"100";s:1:"C";s:2:"60";s:1:"B";s:5:"18930";}
First of all: DON'T.
If you still want to, then...REALLY DO NOT.
Making SQL queries on serialized arrays is just hell. You should try to avoid it at all costs.
Either:
Convert the serialized column into a standard SQL table
or select the column into a PHP variable, unserialize it and search through it.
Example:
$properPhpArray = unserialize($sqlResult['column_name']);
Agreed, searching serialized string is not the best solution and what the developer did despite having a bottle_size table available. I needed a quick fix and no time/skill to rewrite a tax calculation magento extension so I used replace in the query to solve my problem for now.
Since "s:1:X" will always be just one alpha character after the 1 and will not match anything else. I change the query to:
$query = 'SELECT * FROM ' .$tableName.' WHERE allowed_countries LIKE "%'.$regionId.'%" and skurules REGEXP "i:'.$secondlastdigit.';" or replace(skurules,char(34),0) REGEXP "s:1:0'.$secondlastdigit.'0;" and status = 1 ORDER BY id DESC LIMIT 1';
Very hackish fix but gets me out of a bind for now..
Mark

MySQL and PDO, speed up query and get result/output from MySQL function (routine)?

Getting the Value:
I've got the levenshtein_ratio function, from here, queued up in my MySQL database. I run it in the following way:
$stmt = $db->prepare("SELECT r_id, val FROM table WHERE levenshtein_ratio(:input, someval) > 70");
$stmt->execute(array('input' => $input));
$result = $stmt->fetchAll();
if(count($result)) {
foreach($result as $row) {
$out .= $row['r_id'] . ', ' . $row['val'];
}
}
And it works a treat, exactly as expected. But I was wondering, is there a nice way to also get the value that levenshtein_ratio() calculates?
I've tried:
$stmt = $db->prepare("SELECT levenshtein_ratio(:input, someval), r_id, val FROM table WHERE levenshtein_ratio(:input, someval) > 70");
$stmt->execute(array('input' => $input));
$result = $stmt->fetchAll();
if(count($result)) {
foreach($result as $row) {
$out .= $row['r_id'] . ', ' . $row['val'] . ', ' . $row[0];
}
}
and it does technically work (I get the percentage from the $row[0]), but the query is a bit ugly, and I can't use a proper key to get the value, like I can for the other two items.
Is there a way to somehow get a nice reference for it?
I tried:
$stmt = $db->prepare("SELECT r_id, val SET output=levenshtein_ratio(:input, someval) FROM table WHERE levenshtein_ratio(:input, someval) > 70");
modelling it after something I found online, but it didn't work, and ends up ruining the whole query.
Speeding It Up:
I'm running this query for an array of values:
foreach($parent as $input){
$stmt = ...
$stmt->execute...
$result = $stmt->fetchAll();
... etc
}
But it ends up being remarkably slow. Like 20s slow, for an array of only 14 inputs and a DB with about 350 rows, which is expected to be in the 10,000's soon. I know that putting queries inside loops is naughty business, but I'm not sure how else to get around it.
EDIT 1
When I use
$stmt = $db->prepare("SELECT r_id, val SET output=levenshtein_ratio(:input, someval) FROM table WHERE levenshtein_ratio(:input, someval) > 70");
surely that's costing twice the time as if I only calculated it once? Similar to having $i < sizeof($arr); in a for loop?
To clean up the column names you can use "as" to rename the column of the function. At the same time you can speed things up by using that column name in your where clause so the function is only executed once.
$stmt = $db->prepare("SELECT r_id, levenshtein_ratio(:input, someval) AS val FROM table HAVING val > 70");
If it is still too slow you might consider a c library like https://github.com/juanmirocks/Levenshtein-MySQL-UDF
doh - forgot to switch "where" to "having", as spencer7593 noted.
I'm assuming that `someval` is an unqalified reference to a column in the table. While you may understand that without looking at the table definition, someone else reading the SQL statement can't tell. As an aid to future readers, consider qualifying your column references with the name of the table or (preferably) a short alias assigned to the table in the statement.
SELECT t.r_id
, t.val
FROM `table` t
WHERE levenshtein_ratio(:input, t.someval) > 70
That function in the WHERE clause has to be evaluated for every row in the table. There's no way to get MySQL to build an index on that. So there's no way to get MySQL to perform an index range scan operation.
It might be possible to get MySQL to use an index for the query, for example, if the query had an ORDER BY t.val clause, or if there is a "covering index" available.
But that doesn't get around the issue of needing to evaluate the function for every row. (If the query had other predicates that excluded rows, then the function wouldn't necessarily need be evaluated for the excluded rows.)
Adding the expression to the SELECT list really shouldn't be too expensive if the function is declared to be DETERMINISTIC. A second call to a DETERMINISTIC function with the same arguments can reuse the value returned for the previous execution. (Declaring a function DETERMINISTIC essentially means that the function is guaranteed to return the same result when given the same argument values. Repeated calls will return the same value. That is, the return value depends only the argument values, and doesn't depend on anything else.
SELECT t.r_id
, t.val
, levenshtein_ratio(:input, t.someval) AS lev_ratio
FROM `table` t
WHERE levenshtein_ratio(:input2, t.someval) > 70
(Note: I used a distinct bind placeholder name for the second reference because PDO doesn't handle "duplicate" bind placeholder names as we'd expect. (It's possible that this has been corrected in more recent versions of PDO. The first "fix" for the issue was an update to the documentation noting that bind placeholder names should appear only once in statement, if you needed two references to the same value, use two different placeholder names and bind the same value to both.)
If you don't want to repeat the expression, you could move the condition from the WHERE clause to the HAVING, and refer to the expression in the SELECT list by the alias assigned to the column.
SELECT t.r_id
, t.val
, levenshtein_ratio(:input, t.someval) AS lev_ratio
FROM `table` t
HAVING lev_ratio > 70
The big difference between WHERE and HAVING is that the predicates in the WHERE clause are evaluated when the rows are accessed. The HAVING clause is evaluated much later, after the rows have been accessed. (That's a brief explanation of why the HAVING clause can reference columns in the SELECT list by their alias, but the WHERE clause can't do that.)
If that's a large table, and a large number of rows are being excluded, there might be a significant performance difference using the HAVING clause.. there may be a much larger intermediate set created.
To get an "index used" for the query, a covering index is the only option I see.
ON `table` (r_id, val, someval)
With that, MySQL can satisfy the query from the index, without needing to lookup pages in the underlying table. All of the column values the query needs are available from the index.
FOLLOWUP
To get an index created, we would need to create a column, e.g.
lev_ratio_foo FLOAT
and pre-populate with the result from the function
UPDATE `table` t
SET t.lev_ratio_foo = levenshtein_ratio('foo', t.someval)
;
Then we could create an index, e.g.
... ON `table` (lev_ratio_foo, val, r_id)
And re-write the query
SELECT t.r_id
, t.val
, t.lev_ratio_foo
FROM `table` t
WHERE t.lev_ratio_foo > 70
With that query, MySQL can make use of an index range scan operation on an index with lev_ratio_foo as the leading column.
Likely, we would want to add BEFORE INSERT and BEFORE UPDATE triggers to maintain the value, when a new row is added to the table, or the value of the someval column is modified.
That pattern could be extended, additional columns could be added for values other than 'foo'. e.g. 'bar'
UPDATE `table` t
SET t.lev_ratio_bar = levenshtein_ratio('bar', t.someval)
Obviously that approach isn't going to be scalable for a broad range of input values.

Using a bound parameter in Select portion of statement

I'm trying to pass a variable in the select portion of one of my mysql statements here but am not getting the desired result. Heres a snippet of my code:
if(isset($_GET['send'])) {
$send='ra_dccr.'.$_GET['send'];
}
$query = $link->prepare("SELECT locale.id, locale.provider_num, locale.provider_name, :var as ccr
FROM `ra_dccr`
INNER JOIN `locale`
ON ra_dccr.id = locale.id
WHERE locale.report_record_num LIKE concat ('%',:recordnum,'%')
$query->bindParam(':var', $send, PDO::PARAM_STR);
$query->execute();
My issue is that ccr is displaying as ra_dccr.{value of $send}
instead of the actual value that should be pulled from the database when I fetch the result. Am I actually allowed to use variables in this way in a select statement? How can I get sql to look for the appropriate column name this way. For instance if send is ct_scan, it should look for ra_dccr.ct_scan and then pull the val.
Thanks in advance
Heres an image of what is happening
No, this is not possible. Parameters are for passing values to a query. And that's what happens here: the value you pass to :var is returned literally.
If you want to use a dynamic field name, build the query using the actual field name without using bind parameters:
"SELECT locale.id, locale.provider_num, locale.provider_name, $send as ccr
The best way to do this is to verify the value of $_GET['send'] first. Maybe you can even check it against a whitelist of allowed fields.
if (!in_array($_GET['send'], array('field1', 'field3', 'field30'))) {
die('invalid field!');
}

php multiple AND Select Query

Hi There I'm trying to get some data with this SELECT statement and when I just select two items it gives me result but when I place third item it doesn't give any result.
$Query="SELECT * from tableName WHERE status='true' AND gid='".$gid."' AND section='".$cid."'";
Plz any solution.
this one works fine, but when I add third item status='true'. doesn't work.
$Query="SELECT * from tableName WHERE gid='".$gid."' AND section='".$cid."'";
First, let me say this: Double-quoted strings can parse your variables, so this line can work, too:
$Query="SELECT * from tableName WHERE gid='$gid' AND SECTION='$cid'";
Try to learn PHP basics about using single ' and double " quotes here: What is the difference between single-quoted and double-quoted strings in PHP?
Related to the database query, is status field is present in your database table? If not, it should NOT be included within the database, or it will return FALSE boolean value. Instead, use IF if you want to be 'selectively' filtering the status of the table.
if('your conditions here'){
$query = "SELECT * FROM tableName WHERE gid='$gid' AND section='$cid'";
}
I think your mistake is the status='true'
probable the database control its field with 1 or 0 value.

Categories