I'm trying to update a mysql database with data I fetched. (btw I need to do this for specific individual items, but that's not the problem.) When it comes to creating separate statements for fetching or updating I can do that. Separately, I'm able to fetch data like this:
$query = "SELECT starting_amount FROM comp ORDER BY item DESC LIMIT 3, 1";
$result = $conn->query($query);
$row = mysqli_fetch_assoc($result);
and I'm able to update data like this:
$sql = "UPDATE comp SET final_amount=25 WHERE item='Y'";
but I can't put the two together (I tried several ways and failed). In other words, I am able to update a table record with data that I manually type, e.g. I type "25" manually in the update statement, which in this example is the data from 'staring_amount', but I don't know how to update with a statement that will automatically use data I fetch from the table. Again in other words, how do I write the update statement so that "SET final_amount=" is followed by fetched data? Thanks in advance for any help!
So, you just need to pass your fetched data into the query
$starting_amount = $row['starting_amount'];
$sql = "UPDATE comp SET final_amount=$starting_amount WHERE item='Y'";
Firstly, I highly recommend looking into prepared statements - using a prepared statement to insert data is an easy way to prevent SQL injection attacks and also will make what you want to do a little easier.
Here's an example of a prepared update statement using mysqli based on your example:
$statement = $conn->prepare("UPDATE comp SET final_amount=? WHERE item='Y'")
$statement->bind_param(25);
I'll assume for this answer that you want to use just the first row of the resultset.
Using your example above, you can replace the value in bind_param with a value from your row.
$statement->bind_param($row['starting_amount']);
There's no need to do them as separate statements, since you can join queries in an UPDATE.
UPDATE comp AS c1
JOIN (SELECT starting_amount
FROM comp
ORDER BY item DESC
LIMIT 3, 1) AS c2
SET c1.final_amount = c2.starting_amount
WHERE c1.item = 'Y'
Related
Okay, basically I have a table that contains statements like:
incident.client_category = 1
incident.client_category = 8
incident.severity = 1
etc.
I would like to use the contents from this table to generate other tables that fulfill the conditions expressed in this one. So I would need to make it something like
SELECT * FROM incident WHERE incident.client_category = 1
But the last part of the where has to come from the first table. Right now what I'm trying to do is something like
SELECT * FROM incident WHERE (SELECT condition FROM condition WHERE id = 1)
id = 1 stands for the condition's id. Right now I only want to work with ONE condition for testing purposes. Is there a way to achieve this? Because if there isn't, I might have to just parse the first query's results through PHP into my incident query.
Table schemas:
Engineering Suggestion - Normalize the DB
Storing a WHERE clause, like id = 10, in a field in a MySQL table, is not a good idea. I recommend taking a look at MySQL Normalization. You shouldn't store id = 10 as a varchar, but rather, you should store something like OtherTableid. This allows you to use indices, to optimize your DB, and to get a ton of other features that you are deprived of by using fields as WHERE clauses.
But sometimes we need a solution asap, and we can't re-engineer everything! So let's take a look at making one...
Solution
Here is a solution that will work even on very old, v. 5.0 versions of MySQL. Set the variable using SET, prepare a statement using PREPARE, and execute it using EXECUTE. Let's set our query into a variable...
SET #query = CONCAT(
"SELECT * FROM incident WHERE ",
(SELECT condition FROM condition WHERE id = 1)
);
I know for a fact that this should work, because the following definitely works for me on my system (which doesn't require building any new tables or schema changes)...
SET #query = CONCAT("SELECT id FROM myTable WHERE id = ", (SELECT MAX(id) FROM myTable));
If I SELECT #query;, I get: SELECT id FROM myTable WHERE id = 1737901. Now, all we need to do is run this query!
PREPARE stmt1 FROM #query;
EXECUTE stmt1;
DEALLOCATE PREPARE stmt1;
Here we use a prepare to build the query, execute to execute it, and deallocate to be ready for the next prepared statement. On my own example above, which can be tested by anyone without DB schema changes, I got good, positive results: EXECUTE stmt1; gives me...
| id | 1737901 | .
here is one way to achieve your goal by using what is called dynamic sql, be ware that this works only select from condition table returns only one record.
declare #SQLSTRING varchar(4000)
, #condition VARCHAR(500) -- change the size to whatever condition column size is
SELECT #condition = condition
FROM
condition
WHERE
id = 1
SET #SQLSTRING= 'SELECT * FROM incident WHERE ' + #condition
exec sp_executesql(#SQLSTRING)
Since you have also tagged the question with PHP, I would suggest using that. Simply select the string from the condition table and use the result to build up a SQL query (as a string in PHP) including it. Then run the second query. Psudo-code (skipping over what library/framework you re using to call the db):
$query = "select condition from condition where id = :id";
$condition = callDbAndReturnString($query, $id);
$query = "select * from incident where " . $condition;
$result = callDb($query);
However, be very careful. Where and how are you populating the possible values in the condition table? Even how is your user choosing which one to use? You run the risk of opening yourself up to a secondary SQL injection attack if you allow the user to generate values and store them there. Since you are using the value from the condition table as a string, you cannot parametrise the query using it as you (hopefully!) normally would. Depending on the queries you run and the possible values there as conditions, there might also be risk even if you just let them pick from a pre-built list. I would seriously ask myself if this (saving parts of SQL queries as strings in another table) is the best approach. But, if you decide it is, this should work.
I am trying to refer to a column name to order a query in an application communicating with an Oracle database. I want to use a bind variable so that I can dynamically change what to order the query by.
The problem that I am having is that the database seems to be ignoring the order by column.
Does anyone know if there is a particular way to refer to a database column via a bind variable or if it is even possible?
e.g my query is
SELECT * FROM PERSON ORDER BY :1
(where :1 will be bound to PERSON.NAME)
The query is not returning results in alphabetical order, I am worried that the database is interpreting this as:-
SELECT * FROM PERSON ORDER BY 'PERSON.NAME'
which will obviously not work.
Any suggestions are much appreciated.
No. You cannot use bind variables for table or column names.
This information is needed to create the execution plan. Without knowing what you want to order by, it would be impossible to figure out what index to use, for example.
Instead of bind variables, you have to directly interpolate the column name into the SQL statement when your program creates it. Assuming that you take precautions against SQL injection, there is no downside to that.
Update: If you really wanted to jump through hoops, you could probably do something like
order by decode(?, 'colA', colA, 'colB', colB)
but that is just silly. And slow. Don't.
As you are using JDBC. You can rewrite your code, to something without bind variables. This way you can also dynamically change the order-by e.g.:
String query = "SELECT * FROM PERS ";
if (condition1){
query = query+ " order by name ";
// insert more if/else or case statements
} else {
query = query+ " order by other_column ";
}
Statement select = conn.createStatement();
ResultSet result = select.executeQuery(query);
Or even:
String columnName = getColumnName(input);
Statement select = conn.createStatement();
ResultSet result = select.executeQuery("SELECT * FROM PERS ORDER BY "+columnName);
ResultSet result = select.executeQuery(
"SELECT * FROM PERS ORDER BY " + columnName
);
will always be a new statement to the database.
That means it is, like Thilo already explained, impossible to "reorder" an already bound, calculated, prepared, parsed statement. When using this result set over and over in your application and the only thing, which changes over time is the order of the presentation, try to order the set in your client code.
Otherwise, dynamic SQL is fine, but comes with a huge footprint.
I am trying to update table and add data if it doesnt exist in the table row.
$data = "red flowers";
$id = "12";
mysql_query("update shares set data = data + '".$data."' WHERE id = '".$id."' LIMIT 1")
But it doesnt work. What is the correct way to do it ?
Use the REPLACE statement instead of UPDATE.
It works exactly the same as a INSERT statement, but it will replace the data if data with the same primary keys exists.
Ex:
mysqli_query("REPLACE INTO shares (id,data) values (".$id.",'".$data."')");
You should sanitize your data to avoid SQL Injection.
You need DELETE privileges for this statement to work
First of all mysql_query is deprecated use mysqli_query or PDO.
Secondly don't use simple sql statements while constructing query. Use prepared statement , thereby preventing your code from mysql injections.
Thirdly, use http://dev.mysql.com/doc/refman/5.7/en/insert-on-duplicate.html for insert if doesn't exist else update case.
I am trying to display the data from 'table' if a key inputted by the user is found in the database. Currently I have it set up so that the database checks if the key exists, like so:
//Select all from table if a key entry that matches the user specified key exists
$sql = 'SELECT * FROM `table` WHERE EXISTS(SELECT * FROM `keys` WHERE `key` = :key)';
//Prepare the SQL query
$query = $db->prepare($sql);
//Substitute the :key placeholder for the $key variable specified by the user
$query->execute(array(':key' => $key));
//While fetched data from the query exists. While $r is true
while($r = $query->fetch(PDO::FETCH_ASSOC)) {
//Debug: Display the data
echo $r['data'] . '<br>';
}
These aren't the only SQL statements in the program that are required. Later, an INSERT query along with possibly another SELECT query need to be made.
Now, to my understanding, using WHERE EXISTS isn't always efficient. However, would it be more efficient to split the query into two separate statements and just have PHP check if any rows are returned when looking for a matching key?
I took a look at a similar question, however it compares multiple statements on a much larger scale, as opposed to a single statement vs a single condition.
#MarkBaker Join doesn't have to be faster than exists statement. Query optymalizer is able to rewrite the query live if it sees better way to accomplish query. Exists statement is more readable than join.
Fetching all the data and making filtering directly in PHP is always bad idea. What if your table grow up to milions of records? MySQL is going to find the best execute plan for you. It will automaticaly cache the query if it is going to improve performance.
In other words, your made everything correctly as far as we can see your code now. For futher analyse show us all of your queries.
I have a feature in my web app where a table is "quick-editable", that is, its cells can be edited directly. When the user saves his changes, the client sends to the server the changed rows, with their changed columns (excluding non-changed columns, just to clarify), and their corresponding IDs.
In order to do UPDATE queries efficiently, I am using PDO's prepared statement feature. Here is an equivalent statement what I currently came up:
UPDATE table
SET
col1 = :arg_col1,
col2 = :arg_col2,
col3 = :arg_col3
WHERE
ID = :arg_ID
Then I came up with this problem in which I cannot set a column into its current value. Because only the edited column(s) in a row is/are submitted, I only need to bind the data to their respective column(s). For example, if only col1 and col2 are changed, the resulting statement should be
UPDATE table
SET
col1 = 'new data',
col2 = 'an edit',
col3 = col3 /* Use the current value of the column */
WHERE
ID = 153454
Modifying the statement directly would definitely nullify the performance improvement of using the same prepared statement for updating multiple rows. Sadly, PDO doesn't seem to have an option to bind a column to its current value.
How should I solve this problem?
ADDITIONAL: I do not wish to send all the columns, for performance reasons.
Unfortunately, an approach you are aiming to, won't actually work. You just can't prepare a statement in one call and then use it in all subsequent calls - you'll have to prepare it every time again.
So, there is no point in creating a generic query. Thus, you can create a custom query for the very data set. And this latter task can be automated: just create your UPDATE statement dynamically.
A solution, based on the tag wiki (scroll to the very bottom):
// first, have your update data in array (you can omit this line though)
$update = $_POST;
// next, list all fields a user allowed to
$allowed = array("col1","col2","col3");
// finally, create a SET statement query dynamically
$set = pdoSet($fields,$values, $update);
// voila - your query contains only fields were POSTed:
$sql = "UPDATE users SET $set WHERE id = :id";
$stm = $dbh->prepare($sql);
// just add an ID and execute
$values["id"] = $_POST['id'];
$stm->execute($values);
You actually don't want the col3 in the sql, what you need to do is to build the sql dynamically, only add the changed columns to the sql.