I have an update operation that I perform on multiple users at once (the value stays the same). Is it possible to join tables to an update statement for reasons of validation?
For instance, here is my statement at the moment :
$user_set = array(1, 5, 10, 15, .....)
//update 'changed' status for ALL selected users at once
$stmt = $db->prepare("
UPDATE users
SET changed = ?
WHERE user_id IN(". implode(', ', array_fill(1,count($user_set),'?')) .")
");
array_unshift($user_set, 1);
$stmt->execute($user_set);
In a perfect scenario I would like to join one table (computers) to the users table to validate account ownership and if this update is 'valid' and should occur or not.
I found out earlier I can do EXACTLY that with DELETE, but can it be done with UPDATE as well? Example delete using validation I want :
$selected = array(1, 5, 10, 15, .....)
$stmt = $db->prepare("
DELETE del_table.*
FROM some_table as del_table
LEFT JOIN
users
on users.user_id = del_table.user_id
LEFT JOIN
computers
on computers.computer_id = users.computer_id
WHERE computers.account_id = ? AND del_table.activity_id IN(". implode(', ', array_fill(1,count($selected),'?')) .")
");
// use selected array and prepend other data into the array so binding and execute work in order
array_unshift($selected, $_SESSION['user']['account_id']);
$stmt->execute($selected);
EDIT (SOLUTION) :
Thanks Alex... it works!
$selected = array(5,10,12,13);
$stmt = $db->prepare("
UPDATE users
INNER JOIN computers
on computers.computer_id = users.computer_id
SET changed = ?
WHERE computers.account_id = ? AND users.user_id IN(". implode(', ', array_fill(1,count($selected),'?')) .")
");
array_unshift($selected, 1, $_SESSION['user']['account_id']);
$stmt->execute($selected);
Yes, you can, as documented here under the multi-table syntax section.
UPDATE [LOW_PRIORITY] [IGNORE] table_references
SET col_name1={expr1|DEFAULT} [, col_name2={expr2|DEFAULT}] ...
[WHERE where_condition]
You just need to make sure you order the statements correctly.
UPDATE my_table
INNER JOIN other_table
ON my_table.col2 = othertable.col2
SET my_table.col = 'foo'
WHERE other_table.col = 'bar'
try this
$stmt = $db->prepare("
UPDATE users
SET changed = ?
from users
JOIN computers on computers.computer_id = users.computer_id
WHERE user_id IN(". implode(', ', array_fill(1,count($user_set),'?')) .")
");
Related
I am trying to leftjoin off of the entity_id, but just receiving a error without details.
Is there a trick to leftjoin off of the entity_id in Drupal?
$query = db_query('SELECT COUNT(n.field_feed_vehicle_code_value) FROM {field_data_field_feed_vehicle_code} n LEFT JOIN {field_data_field_feed_vehicle_date_used} du ON n.entity_id = du.entity_id WHERE n.field_feed_vehicle_code_value = :utilization AND du.field_feed_vehicle_date_used = :utilization_date', array(':utilization' => $fieldVehicleCode, ':utilization_date' => $fieldDVIRDate))->fetchField();
I found other questions talking about leftjoins, but nothing really on entity_id. I also found this Drupal 7 select query with joins but this solution did not work either.
$query = db_select('node', 'n');
$query->leftJoin('field_data_field_feed_vehicle_code', 'vc', 'n.nid = vc.entity_id');
$query->leftJoin('field_data_field_feed_vehicle_date_used', 'du', 'n.nid = du.entity_id');
$query
->fields('n', array('nid'))
->fields('vc.field_feed_vehicle_code_value', $fieldVehicleCode , '=')
->fields('du.field_feed_vehicle_date_used', $fieldDVIRDate , '=')
->condition('type', 'dvir_utilization_feed')
->condition('status', 1)
->execute();
$num = $query->rowCount();
I believe that you want to use
->condition('vc.field_feed_vehicle_code_value', $fieldVehicleCode , '=')
->condition('du.field_feed_vehicle_date_used', $fieldDVIRDate , '=')
as that is not the correct syntax for ->fields
I ended up having the wrong field name and had to do db_query, although I would have preferred db_select.
$query = db_query('SELECT COUNT(vc.field_feed_vehicle_code_value)
FROM {node} n
LEFT JOIN {field_data_field_feed_vehicle_code} vc ON n.nid = vc.entity_id
LEFT JOIN {field_data_field_feed_vehicle_date_used} du ON n.nid = du.entity_id
WHERE vc.field_feed_vehicle_code_value = :utilization
AND du.field_feed_vehicle_date_used_value = :utilization_date',
array(':utilization' => $fieldVehicleCode, ':utilization_date' => $fieldDVIRDate))->fetchField();
$utilization = $query;
I want to update members_roosevelt table ACCOUNT column starting with 3000+ value I also want to update ACCOUNT column on loan_roosevelt table that is related to my member_roosevelt. What's wrong with my query? Thank you!
$query1 = "SELECT ACCOUNT
FROM
`members_roosevelt`";
$result_q1 = $link->query($query1) or die($link->error);
while ($obj = $result_q1->fetch_object()) {
$members[] = $obj->ACCOUNT;
}
$ids = implode(',', $members);
$sql = "UPDATE `members_roosevelt` as `memb`
JOIN `loan_roosevelt` as `loan`
ON `memb`.`ACCOUNT` = `loan`.`ACCOUNT`
SET
(`memb`.`ACCOUNT`,
`loan`.`ACCOUNT`) = CASE ACCOUNT";
foreach ($members as $id => $ordinal) {
$sql .= sprintf("WHEN %d THEN %d ", $ordinal, (3000+$id));
}
$sql .= "END WHERE memb.ACCOUNT IN ($ids)";
$link->query($sql) or die($link->error);
SET (`memb`.`ACCOUNT`, `loan`.`ACCOUNT`) = CASE ACCOUNT...
This is simply not part of SQL syntax. You can't set two columns at a time like this. The left side of an assignment operator must be one column.
A better solution is to use a session variable.
SET #acct = 3000;
UPDATE members_roosevelt as memb
JOIN loan_roosevelt as loan
ON memb.ACCOUNT = loan.ACCOUNT
SET memb.ACCOUNT = (#acct:=#acct+1),
loan.ACCOUNT = (#acct);
This way you don't have to run the SELECT query at all, and you don't have to create a huge UPDATE statement with potentially thousands of WHEN clauses.
Demo: SQLFiddle
This is the only way I could think of doing this and I am getting an error about the limit. I am trying to delete in chunks within the while loop as the result total might be quite large.
The first count statement works fine. It is the second, delete statement, which does not. I am guessing because of the joins and/or using limit. How can I limit the delete while still joining?
//find total count to delete
$stmt = $db->prepare("
SELECT
COUNT(*)
FROM app_logs
INNER JOIN users
ON users.user_id = app_logs.user_id
INNER JOIN computers
ON computers.computer_id = users.computer_id AND computers.account_id != :account
WHERE app_logs.timestamp < :cutoff_time
");
$binding = array(
'account' => 2,
'cutoff_time' => strtotime('-3 months')
);
$stmt->execute($binding);
//get total results count from above
$found_count = $stmt->fetch(PDO::FETCH_COLUMN, 0);
echo $found_count; //ex. 15324
//delete rows
$stmt = $db->prepare("
DELETE
FROM app_logs
INNER JOIN users
ON users.user_id = spc_app_logs.user_id
INNER JOIN computers
ON computers.computer_id = users.computer_id AND computers.account_id != :account
WHERE app_logs.timestamp < :cutoff_time
LIMIT :limit
");
$binding = array(
'account' => 2,
'cutoff_time' => strtotime('-3 months'),
'limit' => 2000
);
while($found_count > 0)
{
$stmt->execute($binding);
$found_count = $found_count - $binding['limit'];
}
As mentioned in the docs and also in this answer, LIMIT cannot be used along DELETE + JOIN.
If you need to somehow limit deleting, just create conditions for the DELETE statement, that will emulate your LIMIT section. For example you could follow these steps:
Take minimum and maximum rows' ids from the query you want to delete values from (lets say: 10 and 200)
Then choose bordering ids imitating your limit (so for limit 50, the bordering ids could be 50, 100, 150, 200)
Then, for each bordering id, query a DELETE statement with WHERE having AND id <= ?, and put every bordering id in the place of ?.
So you end up with 4 similar queries, each of them deleting ids from a certain range (10-50], (50, 100] etc..
Hope this helps.
Basically, I need to order a list of WordPress users by the city they live in. I've got a custom user meta field for city, and I've got the query working properly, but the query lists everyone who hasn't filled out a city at the beginning since it places blank fields at the beginning of the order.
What I need is to figure out how to only select and display users who have given a value other than blank in the city field. Unfortunately, I've found myself stumped.
Any thoughts on how to do this? Also, if anyone knows a way to orderby a custom user meta field using wp_user_query as opposed to this mess, I'm all ears.
$paged = (get_query_var('paged')) ? get_query_var('paged') : 1;
$limit = 10;
$offset = ($paged - 1) * $limit;
$key = 'city';
$sql = "SELECT SQL_CALC_FOUND_ROWS {$wpdb->users}.* FROM {$wpdb->users}
INNER JOIN {$wpdb->usermeta} wp_usermeta ON ({$wpdb->users}.ID = wp_usermeta.user_id)
INNER JOIN {$wpdb->usermeta} wp_usermeta2 ON ({$wpdb->users}.ID = wp_usermeta2.user_id)
WHERE 1=1
AND wp_usermeta.meta_key = 'wp_capabilities'
AND CAST(wp_usermeta.meta_value AS CHAR) LIKE '%\"subscriber\"%'
AND wp_usermeta2.meta_key = '$key'
ORDER BY wp_usermeta2.meta_value ASC
LIMIT $offset, $limit";
$site_users = $wpdb->get_results($sql);
$found_rows = $wpdb->get_var("SELECT FOUND_ROWS();");
foreach ($site_users as $site_user) {
// user info here
}
Try something like
...
WHERE 1=1
AND wp_whatever.name_of_city IS NOT NULL
AND LENGTH(wp_whatever.name_of_city) > 0
AND wp_usermeta.meta_key = 'wp_capabilities'
...
Currently I am updating my different tables like so
$q = $dbc -> prepare("UPDATE boardposts SET lastPosterID = ?, postOrder = NOW() WHERE postID = ?");
$q -> execute(array($user['id'], $_GET['view']));
$q = $dbc -> prepare("UPDATE accounts SET msgBoardPosts = msgBoardPosts+1 WHERE id = ?");
$q -> execute(array($user['id']));
Is it possible to put these kinda update in one query?
Yes It is possible, Something like:
UPDATE boardposts AS bs
left join accounts AS a on bs.AccountId = bs.lastPosterId
set bs.lastPosterId = ?, bs.postOrder = NOW(),
a.msgBoardPosts = msBoardPosts + 1
WHERE bs.postId = ?,
a.id = ?
I didn't now the structure of your two tables but this is the basic idea see the syntax of UPdate:
UPDATE [LOW_PRIORITY] [IGNORE] table_reference SET....
The table_references clause lists the tables involved in the join. Its syntax is described JOIN Syntax.