Doctrine: Multiple orWhere with LIKE condition - php

Use table with structure:
id | count
/string/id1 | 3
/string/id1/r1 | 2
/string/id1/r2 | 1
/string/id2/r1 | 2
/string/id2 | 3
/string/id2/r1 | 2
/string/id3/r1 | 5
and I want to select all rows which have needed substring in id.
i.e.
I need all rows which have substring in id: /string/id1 and /string/id2
The query should be easy - plain sql:
select * from table_name where id LIKE '/string/id1%' OR id LIKE '/string/id2%';
Result should be:
id | count
/string/id1 | 3
/string/id1/r1 | 2
/string/id1/r2 | 1
/string/id2/r1 | 2
/string/id2 | 3
/string/id2/r1 | 2
Unfortunately, if you try to use the same query in symfony and doctrine2:
$ids = array('/string/id1', '/string/id2');
$query = $this->createQueryBuilder('r')
->select('r');
foreach ($ids as $id) {
$query->orWhere("r.linkedId LIKE :id ")
->setParameter('id', $id."%");
}
$plainQuery = $query->getQuery()->getSQL();
$results = $query->getQuery()->getResult();
Plain query looks the same like select * from table_name where id LIKE '/string/id1%' OR id LIKE '/string/id2%';, but results are not.
results contains only rows of last item in ids - /string/id2
id | count
/string/id2/r1 | 2
/string/id2 | 3
/string/id2/r1 | 2
How to solve it? Where is my mistake? Do you have any suggestion?

I cannot be sure but this seems to me like a conflict with parameter identifiers.
This is what you're trying to do:
Construct the basic SELECT * FROM table_name statement
Append WHERE r.linkedId LIKE :id
set the value of id parameter to /string/id1%
Append OR r.linkedId LIKE :id
set the value of id parameter to /string/id2% (override the previous value) <-- AN ERROR
Basically, you are telling Doctrine to override previously defined value of id parameter with new one.
You could easily overcome this issue. Just add $i to parameter name
foreach ($ids as $i => $id) {
// $i here has the value of 0,1,2, etc...
$query->orWhere("r.linkedId LIKE :id$i" ) // append $i
->setParameter("id$i", $id."%"); // but also append it here
}
Be sure to use double quotes, or concatenate ("id" . $i) instead ;)

Related

Database select one common field if conditions match

I'm building Laravel 5.4 web application and I have below database table:
==================================
product_id|attribute_id|option_id
==================================
1 | 1 | 1
1 | 2 | 3
1 | 4 | 10
2 | 1 | 1
2 | 2 | 4
... etc
So i submit form with attributes id and options id so i can build array from it or whatever.
What i want to achieve that I select from the database the product_id which match exact combination for example:
[
attribute_id => 1,
option_id => 1
attribute_id => 2,
option_id => 3
attribute_id => 4,
option_id => 10
]
This condition only apply to product with product_id = 1
Don't know if i can do it using database query or by php.
First, make a model that reflects your data. Then use the Eloquent query builder to get the data you're looking for. If you need just one number returned that matches, make sure to add on to the end the query "->distinct()".
You may also pass an array of conditions to the 'where' clause.
Your code may look something like this:
$match = DB::table('products')
->where('attribute_id', 1)
->where('option_id', 1)
->distinct()
->get();
https://laravel.com/docs/5.4/queries#introduction
If you just want the product with product_id = 1
Assumed you have stored this in "product_attribute_option" table
and its fields are product_id |attribute_id | option_id as you shown.
$query = DB::table('product_attribute_option as pao');
$query->where('pao.product_id', 1);
$results = $query->get();

Yii2: ActiveRecord with distinct query

I need to write an ActiveRecord query where I get all fields of rows without duplicates in one field.
Example: Here is a table of books. I want to get all data of the rows with
distinct isbn. From duplicates the first row should be taken. The result should be the rows with id 1,2,4
id | title | isbn
--- | ------- | ------
1 | hello | 1001
2 | world | 1002
3 | this | 1002
4 | is | 1003
5 | funny | 1003
My first attempt was
$books = Books::find()->select('isbn')->distinct()->all();
but that fills only the isbn field in $books[0], $books[1], ....
Is this possible using Yii2 ActiveRecord?
You may use groupBy() for this:
$books = Books::find()->groupBy(['isbn'])->all();
But this returns random row with matching isbn value and may not work in some DBMS. Your requirements are too ambiguous to handle it in predictable way. But if you want always first row, you may use subquery to fetch matching isbn and id:
$query = (new Query())
->select(['MIN(id) as id, isbn'])
->from(Books::tableName())
->groupBy(['isbn']);
return Books::find()
->alias('book')
->innerJoin(['subquery' => $query], 'book.id = subquery.id AND book.id = subquery.id')
->all();
The description given in Yii2 for the distinct property is as follows:
Whether to select distinct rows of data only. If this is set true, the SELECT clause would be changed to SELECT DISTINCT.
-- https://www.yiiframework.com/doc/api/2.0/yii-db-query#$distinct-detail
therefore you should pass true with distinct property if you need to select the distinct values of 'isbn' as follows:
$books = Books::find()->select('isbn')->distinct(true)->all();

Exploding in php

In my table 'users' there are 'friends' ,
Like this :
+----+------+---------+
| id | name | friends |
+----+------+---------+
| 1 | a | 0,1,2 |
| 2 | b | 0,1,3 |
| 3 | c | 0,1 |
+----+------+---------+
How do I use the explode function to get the friends id one by one (not 0,1,2) that are separated by a comma (,) ;
How do I select the id? (Example) :
$sql = Select id from users where id = (exploded)
if (mysql_num_rows($sql) > 0 ) {
$TPL->addbutton('Unfriend');
}else{
$TPL->addbutton('Add as Friend')
}
The solution here is actually a slight change in your database structure. I recommend you create a "many-to-many" relational table containing all of the users friends referenced by user.
+---------+-----------+
| user_id | firend_id |
+---------+-----------+
| 1 | 2 |
| 1 | 3 |
| 1 | 4 |
| 2 | 1 |
| 2 | 5 |
+---------+-----------+
If you are storing lists of values within one field then that is the first sign that your database design is not quite optimal. If you need to search for a numerical value, it'll always be better to place an index on that field to increase efficiency and make the database work for you and not the other way around :)
Then to find out if a user is a friend of someone, you'll query this table -
SELECT * FROM users_friends WHERE
`user_id` = CURRENT_USER AND `friend_id` = OTHER_USER
To get all the friends of a certain user you would do this -
SELECT * FROM users_friends WHERE `user_id` = CURRENT_USER
Just a simple example that will make you clear how to proceed:
// Obtain an array of single values from data like "1,2,3"...
$friends = explode(',', $row['friends']);
Then, back in your query:
// Obtain back data like "1,2,3" from an array of single values...
$frieldslist = implode(',', $friends);
$sql = "SELECT * FROM users WHERE id IN ('" . $frieldslist . "')";
to get an array of if ids from your string explode would be used like this
$my_array = explode("," , $friends);
but you'd probably be better using the mysql IN clause
$sql = "Select id from users where id in (".$row['friends'].")";
Just a quick idea. Change your database's table. It is certain that after a while many problems will arise.
You could have something like this.
id hasfriend
1 2
1 3
2 1 no need to be here (You have this already)
2 4
.....
You can do this by using indexes for uniqueness or programming. You may think of something better. Change your approach to the problem to something like this.

Remove particular id from database field

Am storing a string separated using |, which lists the groups allowed, now my issue is if I delete a group than I am not able to remove the ID from that particular field, for example
allowed_group_id
+---------------+
1332|4545|5646|7986
So for example am deleting the group say no 5646, so how do I alter the scripts and remove that particular group from the allowed_group_id in script table?
I recommend taking the entry, exploding it by "|", removing the appropriate entry, imploding it back and updating.
$allowedGroupId = '1332|4545|5646|7986';
$parts = explode('|', $allowedGroupId);
if(($key = array_search($deleteGroup, $allowedGroupId)) !== false) {
unset($allowedGroupId[$key]);
}
$update = " ... "; //update with the new imploded values
Hope it helps
You can try this-
update table tableName set allowed_group_id = REPLACE(allowed_group_id, '5646', '');
Use explode:
$allowed_ids = explode('|', $current_allowed_group_ids);
if($remove_key = array_search($remove_id, $allowed_ids) !== false) {
unset($allowed_ids[$remove_key]);
}
$update_query = 'UPDATE table_name SET allowed_group_id = "'. implode('|', $allowed_ids) .'" WHERE id= ...';
But you might want to alter your database design slightly, creating a pivot table to check for allowed ids. Example:
+------------+
| GROUPS |
+----+-------+
| id | name |
+----+-------+
| 1 | grp_1 |
| 2 | grp_2 |
...
+--------------------+
| ALLOWED_GROUPS |
+--------------------+
| user_id | group_id |
+---------+----------+
| 2 | 1 |
| 2 | 2 |
| 5 | 2 |
...
Using Suresh response and improving it:
UPDATE TABLE tableName SET allowed_group_id = REPLACE(
REPLACE(allowed_group_id, '5646', ''),
'||',
'|');
First you search for '5646' string in allowed_group_id and replace it with empty string ''. Secondly you search and replace two bars from your result '||' with only one bar '|'. This will avoid having '1332|4545||7986' in your allowed_group_id.
Or you should have a table with 2 columns 1 with the id of the action you whant to allow and one with the id of a group.
Whilst the other answers will solve your problem as-is: you might want to consider normalising your database.
For example, if you currently have a table table_name containing id and allowed_group_id, then you would create a new table allowed_group containing multiple rows for each allowed group:
foreign_id | group_id
-----------+---------
1 | 1332
1 | 4545
1 | 5646
1 | 7986
...and so on. Here, foreign_id is the ID of the row in your existing table_name table. Now, instead of your above problem, you can simply DELETE FROM allowed_group WHERE foreign_id = 1 AND groupId = 5646.
This is called putting your data in first normal form.

How to parse results from MySQL Table with PHP using row data?

Typically when I make calls into a mysql db, I reference a column for values I need. In this particular instance however, I need to retrieve a set of rows and parse with PHP accordingly. The example is this:
Table format
ID | Level
-----------------
1 | 1
2 | 2
3 | 2
4 | 3
5 | 4
6 | 4
I am ultimately trying to retrieve all possible levels and count the number of results by those levels. A simple GROUP BY, COUNT() will do the trick:
'Select Level, Count(*) as counter FROM table GROUP BY Levels ORDER BY Levels ASC'
Which will return:
table_new
Level | Count
--------------
1 | 1
2 | 2
3 | 1
4 | 2
The problem I face though is when retrieving these results with PHP, I am not quite sure how to set a variable, say 'level1' and set it to the value returned in the count column.
I assume the logic would follow:
while ($row = $stmt->fetch()) {
$count_level = $row['counter']
}
(but then I would need to create counts for each level type. Any suggestions?
$level = array();
while ($row = $stmt->fetch()) {
$level[$row['level']] = $row['counter']
}
then you have $level array and $level[1], $level[2] etc variables

Categories