Yii2 findBySql: How to specify $params? - yii2

Yii2 provides ActiveRecord::findBySql für raw SQL queries:
public static yii\db\ActiveQuery findBySql ( $sql, $params = [] )
Since there is no hint in the documentation: How to specify $params?

UPDATE
The reason there isn't any docs about the params passed to the findBySql() method is because the method returns the instance of ActiveQuery and if you see the last line of this method in yii\db\ActiveRecord.php it sets the $params via $query->params($params), means the yii\db\ActiveQuery function params($params) which defines the $params as
$params list of query parameter values indexed by parameter
placeholders. For example, [':name' => 'Dan', ':age' => 31].
I guess you should try the following way if lets say you have a table with name product
+----+-----------------+------------+--------+
| id | name | is_deleted | price |
+----+-----------------+------------+--------+
| 1 | shugal | 1 | 65.00 |
| 2 | spoon | 1 | 55.00 |
| 4 | kettle | 1 | 15.00 |
| 5 | spoon | 0 | 15.00 |
| 6 | plates | 0 | 105.00 |
| 7 | dishes | 0 | 15.00 |
| 8 | forks | 0 | 15.00 |
| 10 | pressure cooker | 0 | 203.00 |
| 16 | shugal | 1 | 65.00 |
| 17 | something | 0 | 25.00 |
| 25 | multi product | 0 | 0.00 |
| 66 | pans | 0 | 15.00 |
+----+-----------------+------------+--------+
using the following code you can select all the products that are deleted using params
$q = Product::findBySql(
"SELECT * FROM product where is_deleted=:deleted",
[':deleted' => 1]
)->all();
Hope this helps

Related

Update one Table Using another Table Record

I have two tables as follows that joined by budget_id.
finance_budget table
+-----------+--------+------------+-----------------+---------------+
| budget_id | amount | date | transfer_status | budget_status |
+-----------+--------+------------+-----------------+---------------+
| 1 | 135000 | 2019-10-01 | Pending | issue |
| 2 | 25000 | 2019-10-02 | Pending | issue |
| 3 | 234000 | 2019-10-03 | Pending | issue |
| 4 | 175000 | 2019-10-03 | Pending | issue |
+-----------+--------+------------+-----------------+---------------+
finance_budget_issue table
+----+-----------+-----------+--------+-----------------+---------------+
| id | budget_id | office_id | amount | transfer_status | budget_status |
+----+-----------+-----------+--------+-----------------+---------------+
| 1 | 1 | 100 | 135000 | Pending | issue |
| 2 | 2 | 101 | 12500 | Pending | issue |
| 3 | 2 | 102 | 12500 | Pending | issue |
| 4 | 3 | 100 | 100000 | Pending | issue |
| 5 | 3 | 105 | 75000 | Pending | issue |
| 6 | 3 | 104 | 59000 | Pending | issue |
| 7 | 4 | 102 | 125000 | Pending | issue |
| 8 | 4 | 110 | 50000 | Pending | issue |
+----+-----------+-----------+--------+-----------------+---------------+
I tried to change the "transfer_status" of above two tables from "Pending" to "Approved" using my model
Controller
public function approveIssues($id){
$this->checkPermissions('index', 'pendingIssues');
if(empty($id)){
redirect('budget/pendingIssue');
}
if($this->Budget_model->approveIssues($id)){
$this->session->set_flashdata('message', 'Allocation Approved successfully ..!!');
redirect('budget/pendingIssue');
}else{
$this->session->set_flashdata('error', 'Error ...!!!');
redirect('budget/pendingIssue');
}
}
Model
function approveIssues($id)
{
$this->db->update('finance_budget_issue', array('transfer_status'=>'Approved'), array('id' => $id));
$this->db->update('finance_budget', array('transfer_status'=>'Approved'), array('budget_id' => $id));
if ($this->db->affected_rows()) {
$activity=FZ_Controller::activity('approve','finance_budget_issues',$id,NULL);
$this->db->insert('finance_user_activity',$activity);
return true;
}
return false;
}
When I press the Approved button in my view the system shows "Error". If I remove the line, $this->db->update('finance_budget', array('transfer_status'=>'Approved'), array('budget_id' => $id));
in my model the operation is executing successfully and shows the "Allocation Approved successfully ..!!" Message.
Desired Output
I also need the transfer_status of finance_budget table should be into "Approved".
You can try this Model Code
function approveIssues($id) {
$this->db->set('finance_budget.transfer_status','Approved');
$this->db->set('finance_budget_issue.transfer_status','Approved');
$this->db->where('finance_budget.budget_id', $id);
$this->db->where('finance_budget_issue.id', $id);
$this->db->update('finance_budget JOIN finance_budget_issue ON finance_budget.budget_id = finance_budget_issue.id');
if ($this->db->affected_rows()) {
$activity=FZ_Controller::activity('approve','finance_budget_issues',$id,NULL);
$this->db->insert('finance_user_activity',$activity);
return true;
}
return false;
}
i think it's very helpful for you.

I understand that the PIVOT command can transform a dataset, is this the correct way how to do it?

I have a dataset that looks like this:
+----+-------------+
| ID | StoreVisit |
+----+-------------+
| 1 | Home Depot |
| 2 | Lowes |
| 3 | Home Depot |
| 2 | ACE |
| 2 | Lowes |
| 1 | Home Depot |
| 4 | ACE |
| 5 | ACE |
| 4 | Lowes |
+----+-------------+
I'm new(ish) to SQL and I know I can select all and then either use Excel (pivot table / functions / paste special) or R (tidyr) to transpose.. however, if I have a lot of data, this is not efficient. Is the query below correct? If so, how can I define all values of StoreVisit if there are thousands of types of stores without typing each one in the query?
select * from Stores
pivot (COUNT(StoreVisit) for StoreVisit in ([ACE],[Lowes],[Home Depot])) as StoreCounts
+----+-------+-----------+-----+
| ID | Lowes | HomeDepot | ACE |
+----+-------+-----------+-----+
| 1 | 0 | 2 | 0 |
| 2 | 2 | 0 | 0 |
| 3 | 0 | 1 | 0 |
| 4 | 1 | 0 | 1 |
| 5 | 0 | 0 | 1 |
+----+-------+-----------+-----+
Please excuse the formatting of this post! Many apologies.
Use conditional aggregation:
select id,
sum(storevisit = 'Lowes') as lowes,
sum(storevisit = 'HomeDepot') as HomeDepot,
sum(storevisit = 'Ace') as ace
from t
group by id;

Get the DISTINCT id of users and find the MAX of their column then UPDATE, LARAVEL

What I want to do is, the distinct id based on the max value of the associated column and then update a column accordingly.
For example
id | name | total | description | updateThis
1 | john | 100 | example | 0
2 | dave | 300 | example | 0
2 | johno | 500 | example | 0
4 | derik | 900 | example | 0
5 | Sam | 1000 | example | 0
4 | bool | 12200 | example | 0
1 | john | 1200 | example | 0
5 | john | 300 | example | 0
I want it to look like this:
id | name | total | description | updateThis
1 | john | 1200 | example | 1
2 | johno | 500 | example | 1
4 | bool | 12200 | example | 1
5 | Sam | 1000 | example | 1
I am using Query Builder not the eloquent way of doing it, either would be a useful answer and if someone can give both that would be awesome too, I just need help thanks.
You can use this:
$rows = DB::table('table')
->select('id', DB::raw('max(total) as total'))
->groupBy('id')
->get();
foreach($rows as $row) {
DB::table('table')
->where('id', $row->id)
->where('total', $row->total)
->update(['updateThis' => 1]);
}

Creating a view in MySQL with columns created from row data in a table?

I've got a MySQL database containing three tables. The database contains information about various electrical and mechanical components. It has three tables.
Tables:
componentSource - contains information about where the information in the database was sourced from.
component - contains part number information, description, etc. Multiple entries will refer to a single entry in the componentSource table as its source (Each source file describes multiple components).
componentParams - contains parametric information about the components. Multiple parameter entries will refer to a single entry the component table (each component has multiple parameters).
See simplified example tables...
Database Tables and Relationships:
+-------------------------------+
| Table: componentSource |
+-------------------------------+
| compSrcID* | sourceFile |
+-------------------------------+
| 1 | comp1.txt |
| 2 | comp2.txt |
| 3 | comp3.txt |
+-------------------------------+
^
|
+---------------------------------------------------+
( many to one reference) |
^
^
+---------------------------------------------------------------+
| Table: component |
+---------------------------------------------------------------+
| compID* | partNum | mfrPartNum | mfr | compSrcID |
+---------------------------------------------------------------+
| 1 | 1234 | ABCD | BrandA | 1 |
| 2 | 2345 | BCDE | BrandB | 1 |
| 3 | 3456 | CDEF | BrandC | 3 |
| 4 | 4567 | DEFG | BrandD | 2 |
+---------------------------------------------------------------+
^
|
+---------------+ (many to one reference)
|
^
^
+-------------------------------------------------------+
| Table: componentParams |
+-------------------------------------------------------+
| compParamID* | compID | paramName | paramValue |
+-------------------------------------------------------+
| 1 | 1 | ParamA | 50 |
| 2 | 1 | ParamB | 123 |
| 3 | 1 | ParamC | 10% |
| 4 | 1 | ParamD | 0.5 |
| 5 | 1 | ParamE | Active |
| 6 | 2 | ParamA | 25 |
| 7 | 2 | ParamB | 10K |
| 8 | 2 | ParamC | 5% |
| 9 | 2 | ParamD | 0.25 |
| 10 | 2 | ParamE | Proto |
| 11 | 3 | ParamA | 53.6 |
| 12 | 3 | ParamE | Active |
| 13 | 4 | ParamY | 123-56 |
| 14 | 4 | ParamZ | True |
+-------------------------------------------------------+
I would like to create a view of the database that merges information from the three tables. I would like to have a row for each line in the component table that merges the relevant lines from the componentSource table, and all of the relevant parameters out of the componentParams table.
See example view...
Database View:
+-------------------------------------------------------------------------------------------------------------------------------------------------------+
| View: componentView |
+-------------------------------------------------------------------------------------------------------------------------------------------------------+
| compID* | partNum | mfrPartNum | mfr | SourceFile | ParamA | ParamB | ParamC | ParamD | ParamE | ParamY | ParamZ |
| 1 | 1234 | ABCD | BrandA | comp1.txt | 50 | 123 | 10% | 0.5 | Active | | |
| 2 | 2345 | BCDE | BrandB | comp1.txt | 25 | 10K | 5% | 0.25 | Proto | | |
| 3 | 3456 | CDEF | BrandC | comp3.txt | 53.6 | | | | Active | | |
| 4 | 4567 | DEFG | BrandD | comp2.txt | | | | | | 123-56 | True |
+-------------------------------------------------------------------------------------------------------------------------------------------------------+
Since I want a line in the view for each component in the component table, I think merging the info from the componentSource table is fairly straight forward with a join, but the tricky part is creating columns in the view that correspond to the value in componentParam.paramName column. Seems like this requires some recursion to read all parameters associated with a component. Also note that not all components have all the same parameters in the parameter table, so the values for the parameters not used by a component would be null.
An alternative to creating a view, if that can't be done, would be to build another database table.
My SQL skills are super rusty, and were probably not up to this task when they were fresh.
Is it possible to create a view that creates columns that are based on row data (paramName) in a table? Could you show an example?
If not, can a table be built that does the same? Again, could you show an example?
Many thanks.
Conditional aggregation can do the pivoting for you
SELECT cp.compID,
ct.partNum,
ct.mfrPartNum,
ct.mfr,
cs.SourceFile,
MAX(CASE WHEN cp.paramName = 'ParamA' THEN cp.ParamValue END) as ParamA,
MAX(CASE WHEN cp.paramName = 'ParamB' THEN cp.ParamValue END) as ParamB,
MAX(CASE WHEN cp.paramName = 'ParamC' THEN cp.ParamValue END) as ParamC,
MAX(CASE WHEN cp.paramName = 'ParamD' THEN cp.ParamValue END) as ParamD,
MAX(CASE WHEN cp.paramName = 'ParamE' THEN cp.ParamValue END) as ParamE,
MAX(CASE WHEN cp.paramName = 'ParamY' THEN cp.ParamValue END) as ParamY,
MAX(CASE WHEN cp.paramName = 'ParamZ' THEN cp.ParamValue END) as ParamZ
FROM componentParameters cp
JOIN component ct ON cp.compId = ct.compId
JOIN componentSource cs ON cs.compSrcID = ct.compSrcID
GROUP BY cp.compID,
ct.partNum,
ct.mfrPartNum,
ct.mfr,
cs.SourceFile
It is also possible to use subqueries for this, however, I guess this should do the job better.

Parent-Child Relationship in JSON using PlayFramework

I need to give my Angular JS application a JSON which represents Parent-Child Relationship of to tables.
Parent(Group):
+----+---------------+-------------+----------------+
| id | external_code | supplier_id | notes |
+----+---------------+-------------+----------------+
| 19 | asdfas | 3 | sadfa |
| 23 | 454 | 1 | groupa1 |
| 24 | sadfas221 | 2 | asfd |
| 25 | dsafas | 2 | NULL |
| 21 | 4545 | 1 | asdfasf |
+----+---------------+-------------+----------------+
Child(GroupItems):
+----------+---------+--------+
| group_id | item_id | status |
+----------+---------+--------+
| 19 | 1 | 0 |
| 19 | 2 | 0 |
| 19 | 3 | 0 |
| 25 | 2 | 0 |
+----------+---------+--------+
My desired JSON should look like this:
[
{"groupId":"19",
"notes":"sadfa",
"extenalCode":"asdfas",
"supplierId":"2",
"itemCount":3
"items":[{"itemId": "1","status":"Created", "weight":23},
{"itemId": "2","status":"Created", "weight":23}
{"itemId": "3","status":"Created", "weight":23}
]
},
....
]
The question is how to insert and bind child items with parent representing JSON semantics using MySQL and PlayFramework2.0(Slick)?
roughly something like this:
val items =
GroupItems.join(Items).on(_.itemId === _.id).run // <- query fetching items with group_ids
.groupBy(_._1.groupId).toMap
.mapValues(_._2) // <- mapping Map values to only items
// render groups to json and add a field items with the items (I may be wrong about Play's json api names)
val json = Group.run.map(g => Json.toJson(g) ++ JsObject("items" -> Json.toJson(items(g.id))))