Active Record query for update between two ids in YII2 without using row query - sql

I am noob in the YII2.
I am searching for an update query using Active Record in Yii2.
I need to update some record which are between two ids.
The Query:
UPDATE
table_name
SET status_name = 1
WHERE
id BETWEEN 1 AND 10;
What I have tried in Active Record:
$command = Yii::$app->db->createCommand('UPDATE table_name SET status_name = 1 WHERE id BETWEEN 1 AND 10 ');
$command->execute();
But I need Activerecord query without using row SQL queries.

You can use static updateAll method from Active Record:
TableName::updateAll(['status_name' => 1], 'id BETWEEN 1 AND 10');
Or query builder:
Yii::$app->db
->createCommand()
->update(
'table_name',
['status_name' => 1],
'id BETWEEN 1 AND 10'
)
->execute();

Like an another solution to question mentioned you can use Update counters:
Updating Counters
It is a common task to increment or decrement a column in a database table. We call these columns "counter columns". You can use updateCounters() to update one or multiple counter columns. For example,
$posts = TableName::find()->where(['between', 'id', "1", "10" ])->all();
$posts->updateCounters(['status_name' => 1]);
Note: If you use yii\db\ActiveRecord::save() to update a counter
column, you may end up with inaccurate result, because it is likely
the same counter is being saved by multiple requests which read and
write the same counter value.
Update:
Like mentioned in comments for Events like EVENT_AFTER_UPDATE to be occur You should do something like this:
$model = TableName::findOne()->where(['between', 'id', "1", "10" ])->all();
$model->status_name = new \yii\db\Expression('status_name + 1');
$model->save();
This issue was discussed here: Event EVENT_AFTER_UPDATE does not occur when updateCounters

Related

GORM query - how to use calculated field in the query and filter according to its value

Currently, I have a GORM query that calculates a counter for each table entry using a second table and returns the first table with a new field "locks_total" which doesn't exist in the original.
Now, what I want to achieve is the same table returned (with the new "locks_total" field) but filtered with "locks_total" = 0.
I can't seem to make it happen because it doesn't recognize this field in the table, what can I do to make it happen? is it possible to run the query and then execute the filter on the new result table?
This is how we currently do it-
txn := dao.postgresManager.DB().Model(&models.SecretMetadataResponse{})
txn.Table(dao.firstTableName + " as s").
Select("s.*, (SELECT COUNT(*) FROM " + dao.secondTableName + " as l where s.id = l.secret_id) locks_total ")
var secretsTotal int64
var metadataResponseEntries []models.SecretMetadataResponse
txn = txn.Count(&secretsTotal). // Saving the count before trimming according to the pagination parameters
Limit(params.Limit).
Offset(params.Offset).
Order(constants.FieldName).
Order(constants.FieldId).
Find(&metadataResponseEntries)
When the SecretMetadataResponse contains the SecretMetadata which is the same fields as in the table and the LocksTotal is the new calculated field that we want to have.
type SecretMetadataResponse struct {
SecretMetadata
LocksTotal int `json:"locks_total"`
}
Thanks in advance :)

How to write database/SQL query in Yii2, with and without a model

I have a table with multiple column and I want to return a column name using another column name as search criteria. How do I achieve this in yii2?
Below is sample code, normal sql should be:
$name = SELECT type_name FROM ProductTable WHERE type_id = 1;
echo $name;
This should return the value of the column type_name where the value of the column type_id equals 1. I tried this, but it doesn't work
$type_name = ProductTable::find()->where(['type_id' =>$model->type_id]);
$type_name = Product::find(['type_name'])->where(['type_id' =>$model->type_id]);
I also tried this, but I guess it was wrong
I hope my question is clear enough and any help will he appreciated
and u could also use createCommand!
$name = \Yii::$app->getDb()->createCommand("SELECT type_name FROM ProductTable WHERE type_id=:typeId", ['typeId'=>$model->type_id])->queryAll();
For a general introduction to Yii2's ActiveRecord, see the guide: http://www.yiiframework.com/doc-2.0/guide-db-active-record.html
If you want the complete row and have a model, you're just missing a one():
Product::find()->where(['type_id' =>$model->type_id])->one();
If you do have a Model defined and just want a single value, try:
Product::find()->select('type_name')->where(['type_id' =>$model->type_id])->scalar();
Which basically generates an ActiveQuery via the model, and changes it to return only the first column in the first row of matched results.
If you do NOT have a model, you could also generate a normal query without ActiveRecord usage (http://www.yiiframework.com/doc-2.0/yii-db-query.html)
$name = (new Query())->select('type_name')
->from('ProductTable')
->where(['type_id' =>$model->type_id])
->scalar();
I assume you generated ProductTable by using Gii module.
Also, if type_id column is a primary key:
$product = ProductTable::findOne($model->type_id);
if($product !== null) { $product->typeName /*... read value ...*/}
or to get all records
$products = ProductTable::findAll($model->type_id); //match all records.
for any other column use the following syntax instead:
$product = ProductTable::findOne(['type_id' => $model->type_id]);
Use following code to get type_name
$PTable=ProductTable::find()->select('type_name')->where(['type_id' =>$model->type_id])->one();
echo $PTable->type_name;

Send e-mail when a row changes

I will try to best explain what I would like to do (for simplicity I only included two columns that I am working with):
I have two different tables, dbo.device and dbo.devicestatus.
They are both tied by DeviceDN which is primary key.
On dbo.devicestatus, there are two columns called TonerLowB and TonerLowY that can have a value of either 1 or 0.
What I would like to do is have the application (SQLExpress 2008) send an e-mail when one of the rows of TonerLowB or TonerLowY has it's value updated to "1", but not when it has it's value updated to "0" and have that row "merged" with the data from dbo.device.
What I managed to do so far is combine the data from dbo.device with the data from dbo.devicestatus and show just values that are greater than 0 as a select statement:
`
select d.DeviceDN, s.TonerLowB, s.TonerLowY
from dbo.Device d,
dbo.DeviceStatus s
where
d.DeviceDN = s.DeviceDN
and s.TonerLowB + s.TonerLowY > 0 ;
`
I then tinkered with sp_send_dbmail to see if I can send the results via mail and I did manage to do that after defining a profile and creating myself a test mail server. I used the following command:
`
EXEC msdb.dbo.sp_send_dbmail
#profile_name = 'MyMailProfile',
#recipients = 'alex#testing.ro',
#query = 'select d.DeviceDN, s.TonerLowB, s.TonerLowY
from dbo.Device d,
dbo.DeviceStatus s
where
d.DeviceDN = s.DeviceDN
and s.TonerLowB + s.TonerLowY > 0 ;' ,
#subject = 'Your Query Results',
#attach_query_result_as_file = 1 ,
#query_result_separator = '|' ;
`
The problem is that this sends me all the lines that have a value greater than 1 and I don't know how to send for each update and just for the updated row instead of this "bulk" method.
I am thinking of adding two new columns to the database db.devicestatus, TonerLowB_LastUpdate and TonerLowY_LastUpdate, both set up as "datetime".
Then I wanted to create two triggers that would update TonerLowB_LastUpdate or TonerLowY_LastUpdate with "set gettime()" when TonerLowB or TonerLowY updates it's value and also send an e-mail with the line that changed it's value (using the mail procedure I mentioned above), if that date is older than 3 days.
Unfortunately I don't know how to declare this trigger and this is what I am trying to find help on.
Would this be possible and if so, would it be a good (reliable) mechanism?
A wise way would be use 'Triggers' in this context.
Take a look at this post to get started on this
Using Triggers to do actions after specific values are updated
To summarize, define an update trigger on the field:
CREATE TRIGGER TonerValueChanged
ON DeviceStatus
AFTER UPDATE
AS
BEGIN
// here you need to check for the condition if the value is updated to 1 and send the mail
END
Now on every value updated, based on your condition, the mail will be triggered
for every row update.
Hope this helps.

Yii CDbCriteria get all records with same character in column

i'm not good with sql, i'm making a CListView for all records with a selected id in column to be displayed on the page.. just for practice, i wanted to add a dropdown that will select another kind of sort (id DESC is already there).
i have a table with columns: id, Name, Project_id, User_id, Assigned, etc...
for now this is my code:
public function actionView($id)
{
$model = $this->loadModel($id);
$criteria = new CDbCriteria(array(
'order'=>'id desc',
'condition'=>'Project_id='.$id
));
$dataProvider = new CActiveDataProvider('Task', array(
'criteria'=>$criteria
));
$this->layout = 'column2';
$this->render('view', array('model'=>$model, 'dataProvider'=>$dataProvider));
}
i'm selecting all records that has the requested project_id, so what i'm trying to do, which i have no clue how to do, is to add another criteria selection on the column "Assigned" the problem is, some of this records has more than 1 single assigned and its being saved as "1,2,5,6". so if i add another selection with assigned 1, it will just show me thoses records that have in assigned "1" and not thoses that have "1,3,5,6".. i was thinking on make a search on string but i dont know how it works on sql (Because i dont know SQL that much)
Try this -
$criteria = new CDbCriteria(array(
'order'=>'id desc',
'condition'=>'Project_id='.$id.' AND find_in_set($assigned, Project_id)'
));
But here $assigned can have only single value i.e. $assigned = 1 or $assigned = 2.
If $assigned = "1,2" where 1,2 itself will be considered as single value and so result will be returned only if the pattern is 1,2.. in the table and not 2,1,...
I hope it gives some idea.
found my solution.. seems like im using SQLite and SQLite does not support find_in_set(), instead. i would have to use (',' || Assigned || ',') LIKE '%,1,%'. like this..
$criteria = new CDbCriteria(array(
'order'=>'id desc',
'condition'=>'Project_id='.$id.' AND ("," || Assigned || ",") LIKE "%,5,%"'
));

Yii Framework: How to get the num_rows?

As the official documentation does not say how to do a simply "num_rows" with their system, i need some help here: How to get the amount of rows in the result set ?
Assuming:
$connection=Yii::app()->db;
$command=$connection->createCommand($sql);
This will work for insert, update and delete:
$rowCount=$command->execute();
execute(): performs a non-query SQL statement, such as INSERT, UPDATE and DELETE. If successful, it returns the number of rows that are affected by the execution.
For select, you could do the following:
$dataReader=$command->query();
This generates the CDbDataReader instance and CDbDataReader provides a rowCount property
http://www.yiiframework.com/doc/api/1.1/CDbDataReader#rowCount-detail
$rowCount = $dataReader->rowCount;
About rowCount => Returns the number of rows in the result set. Note, most DBMS may not give a meaningful count. In this case, use "SELECT COUNT(*) FROM tableName" to obtain the number of rows.
ActiveRecord has count method which can be used.
$cntCriteria = new CDbCriteria();
$cntCriteria->condition = "categoryId = :categoryId";
$cntCriteria->params[':categoryId'] = $categoryRow->categoryId;
$articleCount = Article::model()->count($cntCriteria);
There is one more way to do this. When we execute a sql query it will return the result as array only. So we can able get the count of the rows using count() function like below.
$output=User::model()->findAllBySql("select * from user");//User is a model belongs to the user table
$count_val=count($output);//$count_val has the value of number of rows in the output.