I have a table that links 2 other tables together.
I have a list of checkboxes, where when one is checked, it sends the value to the server via ajax.
What I want to do is if the values are not in the database, insert them (checked) or if they are there, delete them (unchecked)
Is there a way to do it without writing several queries? I know its not to hard with an insert/update, but what about delete?
You can allways delete and, if affected rows is 0, then insert. Easy, simple and it works.
INSERT IF NOT EXISTS ... is not available in MySQL (it is specific to T-SQL afaik). Use a transaction instead but keep in mind that not every MySQL engine supports transactions although no error is raised when using transactional statements.
This might help you out depending on your key structure http://bogdan.org.ua/2007/10/18/mysql-insert-if-not-exists-syntax.html
Otherwise you might be able to handle what you're doing with a subquery?
Not 100% what you are looking for, but it does the insert if not exists:
http://remy.supertext.ch/2010/11/mysqlupdate-and-insert-if-not-exists/
INSERT INTO wp_postmeta (post_id, meta_key) SELECT ?id, ‘page_title’
FROM DUAL WHERE NOT EXISTS (
SELECT meta_id FROM wp_postmeta WHERE post_id = ?id AND meta_key = ‘page_title’);
UPDATE wp_postmeta SET meta_value = ?page_title WHERE post_id = ?id AND meta_key = ‘page_title’;
There is no such thing as ON DUPLICATE DELETE or a correct use of IF ELSE with subqueries to do that in one query (which would be handy since this one query will either succeed or fail). Think you'd have to roll over several queries in achieving your goal, like I did. But keeping track on whether or not the subsequent queries keep succeeding.
Start out with a return value ('state' in this case) which is "Unknown" or "NoChange". If this returns to you ajax call, you know the checkbox needs to return to its previous state before clicking, and possible popup a message (which you can carry along with the json).
run your queries in a correct if/then code structure, and keep track of succeeding queries, and update the 'state' when a series succeeded accordingly.
This should do exactly what you need, keep it from having black holes.
(I use some wrappers for running queries, that return true/false/null/errorinformation - which allows you to check each query result)
I assume you have your ajax call in place already. The code example starts where your ajax call starts processing from the server side:
ob_end_clean();
$json = array("state" => "Unknown");
$module_id = (isset($_POST['module_id']) ? $_POST['module_id'] : null);
$group_id = (isset($_POST['group_id']) ? $_POST['group_id'] : null);
$action = (isset($_POST['action']) ? $_POST['action'] : null);
if (!empty($module_id) && !empty($group_id) && !empty($action)) {
$query = "
SELECT
1
FROM
config_module_actions_groups
WHERE
1
AND module_id = '{$module_id}'
AND group_id = '{$group_id}'
AND action = '{$action}'
";
if (getQueryAsArraySingleValues($query)) {
$query = "
DELETE FROM
config_module_actions_groups
WHERE
1
AND module_id = '{$module_id}'
AND group_id = '{$group_id}'
AND action = '{$action}'
";
if (executeQuery($query)) {
$json["state"] = "Off";
} else {
$json["message"] = "Not correctly deactivated";
}
} else {
$query = "
REPLACE INTO
config_module_actions_groups
SET
module_id = '{$module_id}',
group_id = '{$group_id}',
action = '{$action}'
";
if (executeQuery($query)) {
$json["state"] = "On";
} else {
$json["message"] = "Not correctly activated";
}
}
}
// output
echo json_encode($json);
exit;
Related
how to display count() sql function using php
$results = "SELECT count(votesnumber) FROM `votes` WHERE `candidate_id` = '$candidate_id'";
$queryresults = mysqli_query($connect, $results);
if($queryresults) {
$rowresults = mysqli_fetch_assoc($queryresults);
echo $rowresults['votesnumber'];
} else {
echo "error";
}
i want to display the results of sql count() function using php. am counting specific columns WHERE ID = "some value" in phpmyadmin its working but with php its giving me headache . any ideas on how to solve this?
Try this:
$results = "SELECT count(votesnumber) AS VoteNum FROM `votes` WHERE `candidate_id` = '$candidate_id'";
$queryresults = mysqli_query($connect, $results);
if($queryresults) {
$rowresults = mysqli_fetch_assoc($queryresults);
echo $rowresults['VoteNum'];
} else {
echo "error";
}
First, if you want to refer to the column name by a reference, then you need to give it a better name using an alias:
SELECT COUNT(votesnumber) as votesnumber
ROM `votes`
WHERE `candidate_id` = '$candidate_id';
Second, you should not be munging query strings with parameter values. Instead of '$candidate_id', learn to use parameters. This prevents unexpected syntax errors and SQL injection accounts.
Third, if votesnumber is actually a number of votes, then you probably want SUM() rather than COUNT().
You need to add "AS" instruction to your SQL if you want to get this data as a specific index from array (like $rowresults['votes']):
$results = "SELECT count(votesnumber) AS votes FROM `votes` WHERE `candidate_id` = '$candidate_id'";
Remember that you can always print_r() (for arrays) or var_dump() your variable to check if it contains data you want to have.
I'm using PHP and MySQL and
I have a table with 3 fields ((ID, Username, PID)).
I want the PID field to contain strings of 8 unique characters.
My solution is to generate the random string in PHP and check if it exists. If it exists then it will generate another string.
Is there any better solution that will save processing time, like a MySQL trigger or something like that?
This will give you a random 8 character string:
substr(str_pad(dechex(mt_rand()), 8, '0', STR_PAD_LEFT), -8);
Found here: http://www.richardlord.net/blog/php-password-security
Or if the username field is unique you could also use:
substr(md5('username value'), 0, 8);
Though it's extremely unlikely, particularly for the md5, neither case guarantees a unique string, so I would probably do something like this:
// Handle user registration or whatever...
function generatePID($sUsername) {
return substr(md5($sUsername), 0, 8);
}
$bUnique = false;
$iAttempts = 0;
while (!$bUnique && $iAttempts < 10) {
$aCheck = $oDB->findByPID(generatePID("username value")); // Query the database for a PID matching whats generated
if (!$aCheck) { // If nothing is found, exit the loop
$bUnique = true;
} else {
$iAttempts++;
}
}
// Save PID and such...
... which would probably only yield 1 'check' query, maybe 2 in unique cases, and would ensure a unique string.
Do the characters need to be random? Or just unique? If they only need to be unique, you could use a timestamp. Basing the value on time will ensure a uniqueness.
If you go another route, you'll have to check your generated value against the database until you end up with a unique value.
Why not do this the correct way and use UUIDs (aka GUIDs), which are always unique, no need to check if they are or not. It may be 36 chars, but you get the benefit of storing them as HEX which saves disk space and increase speed over standard CHAR data.
You can read the comments on the PHP doc for functions that do this.
You can create 8 chars unique string in Mysql in such a way
CAST(MD5(RAND()) as CHAR(8))
My solution is to generate the random string in PHP and check if it exists. If it exists then it will generate another string.
This is the wrong way to do it. The web server will run multiple instances of your code concurrently, and sooner or later, two instances will store the same PID in your database.
The correct way to solve this problem is to make the PID column UNIQUE, and don't bother with any pre-checks. Just run the INSERT query, and check the result.
If the result is a 1062 (ER_DUP_ENTRY) error, generate a new PID and try again.
Any other database error should be dealt with like you normally would.
Perhaps something like this (untested):
<?php
/* $link = MySQLi connection */
if (!($stmt = mysqli_prepare ('INSERT `t` (`ID`, `Username`, `PID`) VALUES (?, ?, ?)'))) {
/* Prepare error */
}
if (!mysqli_bind_param ('iss', $id, $user, $pid) {
/* Bind error */
}
$e = 0;
for ($i = 0; $i < 10; $i++) {
$pid = /* generate random string */;
if (mysqli_stmt_execute ($stmt))
break; /* success */
$e = mysqli_stmt_errno ($stmt);
if ($e !== 1062)
break; /* other error */
}
mysqli_stmt_close ($stmt);
if ($e) {
if ($e === 1062) {
/* Failed to generate unique PID */
} else {
/* Other database error */
}
} else {
/* success */
}
If you're set on 8 characters for the PID value then you'll need something to generate the string and check that it doesn't already exist.
$alphabet = range('A','Z');
// get all the PIDs from the database
$sql = "select PID from mytable";
// save those all to an array
$pid_array = results of query saved to array
shuffle($alphabet);
$pid_offer = array_slice($alphabet,0,8);
while(in_array($pid_offer, $pid_array)){
shuffle($alphabet);
$pid_offer = array_slice($alphabet,0,8);
}
// found uniuqe $pid_offer...
race conditions still exist.
If the string doesn't need to be random, then use the ID value, which is probably an auto-increment integer and start the count for that at 10000000.
Then just do a simple A=1, B=2, C=3 etc replacement on the digits in that number to generate your string.
Your mileage may vary.
--Mark
I am updating rows of a MySQL database with groovy and with the method I am using things are very slow. I was hoping you could improve on the performance of my example:
sql.resultSetConcurrency = ResultSet.CONCUR_UPDATABLE
sql.eachRow("SELECT * FROM email) { bt ->
bt.extendedDesc = update(bt.name, bt.direction)
}
sql.resultSetConcurrency = ResultSet.CONCUR_READ_ONLY
Then there is the update method:
def update(name, direction) {
if (direction == 'Outgoing') {
result = 'FROM: '+name
} else {
result = 'TO: '+name
}
if(result.size() > 75) {
result = result.substring(0, 72) + "..."
}
return result
}
So it updates one field of each entry in email (extendedDesc in this example) using a method that needs 2 other fields passed to it as parameters.
It is very slow, around 600 entries updated per minute, and email has 200000+ entries =/
Is there a better method to accomplish this? Should use Groovy if possible, as it needs to run with all my other Groovy scripts.
You are doing your update as a cursor based, updatable query, which has to read every record and conditionally write something back. You're doing all the heavy lifting in the code, rather than letting the database do it.
Try using an update query to only update the records matching your criteria. You won't need eachRow to do this.
I have a database table that I need to import a bunch of records into. I don't want to overwrite existing records if they're already in the database, so I've set things up to do a select query first to check if there's a value, but apparently the rows are importing too quickly for the index to keep up, as I'm getting duplicates created for every single row that I'm inserting.
I'm importing a CSV file.
Here's what I'm doing (this is inside a Joomla system, so some of the code and objects are joomla-specific):
$fp = fopen(JPATH_ROOT.DS."tmp".DS.$filename, 'r');
//run insert query on each line of file
if(JRequest::getVar('importType')=="activated") {
while(!feof($fp)) {
while (($data = fgetcsv($fp, 1000, ",")) !== FALSE) {
if($this->checkUnique($data[0])) {
$this->runInsert2($data[0], $data[1], $data[2], $data[3]);
error_log("there is not already a code for ".$data[0]);
}
else {
error_log("there is already a code for ".$data[0]);
}
$row++;
}
}
}
fclose($fp);
Here's checkUnique:
function checkUnique($vouchNum) {
$db =& JFactory::getDBO();
$query = "select COUNT(*) from arrc_Voucher where VoucherNbr=".$db->quote($vouchNum);
if(!$db->query()) error_log("error running unique check on ".$vouchNum." - " . $db->stderr());
$db->setQuery($query);
$count = $db->loadResult();
if($count>0) {
return false;
}
else {
return true;
}
}
And here's runInsert2:
function runInsert2($vouchNum,$BalanceInit,$BalanceCurrent,$ActivatedDT) {
$rightNow = date('Y-m-d H:i:s');
$db =& JFactory::getDBO();
if($ActivatedDT <> "NULL") {
$activatedDTtmp = strtotime($ActivatedDT);
$activatedDT = date('Y-m-d H:i:s',$activatedDTtmp);
}
else {
$activatedDT = $rightNow;
}
$query = "insert into arrc_Voucher (VoucherNbr,BalanceInit, BalanceCurrent, ActivatedDT)
values (". $db->quote($vouchNum). ", ".$db->quote($BalanceInit).",".$db->quote($BalanceCurrent).",".$db->quote($activatedDT).")";
error_log("query: ".$query);
$db->setQuery($query);
if (!$db->query()) error_log("error inserting voucher number ". $vouchNum . "-" . $db->stderr());
}
I have no clue where I'm going wrong here, but if anyone can help me out (or point me in a better direction for avoiding duplicates) I'd be very grateful. FYI, the field that we're considering to be "unique" (VoucherNbr) is not actually a primary key or in any way marked as unique in the table structure, and cannot be. This is something we need to work around on the coding end right now.
Put an unique constraint and use insert ignore, this way you'll never have duplicates.
That is if it's ok for the duplicate rows to be ignored.
What is the reason you can't set an unique key on a column than needs to keep unique values?
Another solution would be to import data in a separate table with the same structure.
create table arrc_buffer like arrc_Voucher .
You truncate this table before each import.
Then you can insert into your arrc_Voucher table from this buffer.
1.
Remove from the buffer all rows that are already in arrc_Voucher.
delete arrc_buffer b
from arrc_buffer b
inner join arrc_Voucher v on b.VoucherNbr = v.VoucherNbr;
Then insert the rest in arrc_Voucher.
insert into arrc_Voucher
select * from arrc_buffer
Besides these imports, is there any other routine inserting data in arrc_Voucher ?
If you really can't change the tables, you might have to either check for duplicates and remove them after your INSERT, or lock the table before your check for existing rows. You can't guarantee an INSERT doesn't happen between your SELECT and INSERT statements.
Follow up to this question. I have the following code:
string[] names = new[] { "Bob", "bob", "BoB" };
using (MyDataContext dataContext = new MyDataContext())
{
foreach (var name in names)
{
string s = name;
if (dataContext.Users.SingleOrDefault(u => u.Name.ToUpper() == s.ToUpper()) == null)
dataContext.Users.InsertOnSubmit(new User { Name = name });
}
dataContext.SubmitChanges();
}
...and it inserts all three names ("Bob", "bob" and "BoB"). If this was Linq-to-Objects, it wouldn't.
Can I make it look at the pending changes as well as what's already in the table?
I don't think that would be possible in general. Imagine you made a query like this:
dataContext.Users.InsertOnSubmit(new User { GroupId = 1 });
var groups = dataContext.Groups.Where(grp => grp.Users.Any());
The database knows nothing about the new user (yet) because the insert wasn't commited yet, so the generated SQL query might not return the Group with Id = 1. The only way the DataContext could take into account the not-yet-submitted insert in cases like this would be to get the whole Groups-Table (and possibly more tables, if they are affected by the query) and perform the query on the client, which is of course undesirable. I guess the L2S designers decided that it would be counterintuitive if some queries took not-yet-committed inserts into account while others wouldn't, so they chose to never take them into account.
Why don't you use something like
foreach (var name in names.Distinct(StringComparer.InvariantCultureIgnoreCase))
to filter out duplicate names before hitting the database?
Why dont you try something like this
foreach (var name in names)
{
string s = name;
if (dataContext.Users.SingleOrDefault(u => u.Name.ToUpper() == s.ToUpper()) == null)
{
dataContext.Users.InsertOnSubmit(new User { Name = name });
break;
}
}
I am sorry, I don't understand LINQ to SQL as much.
But, when I look at the code, it seems you are telling it to insert all the records at once (similar to a transaction) using SubmitChanges and you are trying to check the existence of it from the DB, when the records are not inserted at all.
EDIT: Try putting the SubmitChanges inside the loop and see that the code will run as per your expectation.
You can query the appropriate ChangeSet collection, such as
if(
dataContext.Users.
Union(dataContext.GetChangeSet().Inserts).
Except(dataContext.GetChangeSet().Deletes).
SingleOrDefault(u => u.Name.ToUpper() == s.ToUpper()) == null)
This will create a union of the values in the Users table and the pending Inserts, and will exclude pending deletes.
Of course, you might want to create a changeSet variable to prevent multiple calls to the GetChangeSet function, and you may need to appropriately cast the object in the collection to the appropriate type. In the Inserts and Deletes collections, you may want to filter it with something like
...GetChangeSet().Inserts.Where(o => o.GetType() == typeof(User)).OfType<User>()...