Query where_in comma separated list - php

I have a 'list_table' table looks like:
id : list
1 : 1,2,44,5
2 : 4,3,5,2,56,66
Is it possible to check if '44' is in List column in mysql database?
I'm using codeigniter and my code looks like:
$this->db->select('*');
$this->db->from("list_table");
$this->db->where("find_in_set('44', 'list')");
$query = $this->db->get();
return $query->result();
I also tried with WHERE_IN but didn't get correct result.
This is what query I get when I enable_profile:
SELECT *
FROM `poslovi`
LEFT JOIN `firme` ON `firme`.`f_id` = `poslovi`.`po_firma_id`
LEFT JOIN `kategorije` ON `kategorije`.`k_id` = `poslovi`.`po_category`
WHERE `po_date_istek` > '2022-03-21 10:37:25'
AND (`po_naziv_oglasa` LIKE '%Radnik u ćevabdžinici%' ESCAPE '!' OR `f_name`
LIKE '%Radnik u ćevabdžinici%' ESCAPE '!')
AND find_in_set("61", po_category) <> 0
AND `po_status` = '1'
ORDER BY `po_date_istek` DESC
LIMIT 10
This is what I have in my database:
Just to mention, if I remove 'find_in_set' I get correct result so the rest of the query is good as I noticed

You need a true or false condition in the WHERE clause, so a comparison
$this->db->select('*');
$this->db->from("list_table");
$this->db->where("find_in_set('44', 'list') <> 0");
$query = $this->db->get();
return $query->result();
But it ot recomended to store data this way . Read mor in Is storing a delimited list in a database column really that bad?

In a screenshot you posted of your data, its possible to see that you include a space after each comma.
The value of po_category is 2, 7, 61, not 2,7,61—and find_in_set does not ignore those spaces!
You've noticed that find_in_set works when you search for the first entry, this is because that does not have a leading space; 7 and 61 do.
find_in_set(" 61", po_category) would match, in this case, but then it wouldn't match if it is the first entry. While you could do (find_in_set("61", po_category) <> 0 || find_in_set(" 61", po_category)) <> 0 to support both cases, that is unnecessarily slow and taxing. Just save your data without spaces. Or, better yet, not as a comma separated list.
In nbk's answer there's a link that explains why doing this is not optimal. One way to save a list of IDs is making a separate table for them and using JOINs. This will be better for performance. Another option that is slightly more complex to implement, if you are using MySQL 8.0.17 or higher, is to save it as a JSON array & index that array.

As nbk said, you need the true/false condition, however, the answer is not working for the OP. You need to remove the single quotes around list in that answer:
$this->db->where("find_in_set('44', 'list') <> 0");
Rewrite the code as below, minus the quotes around list:
$this->db->select('*');
$this->db->from("list_table");
$this->db->where("find_in_set('44', list) <> 0");
$query = $this->db->get();
return $query->result();
That should solve the issue for you.

Related

count of subquery in doctrine - querybuilder

I have simple query:
$this->qb->select('l.value')
->addSelect('count(l) AS cnt')
->addSelect('hour(l.time) AS date_hour')
->from(Logs::class, 'l')
->where('l.header = :header')
->groupBy('l.value')
->addGroupBy('date_hour')
->setParameter('header', 'someheader')
This code select 3 columns have one condition and two groupBy.
I want get records count of this query. Of course I dont want to download all records and check size of downloaded data.
Question:
How to rebuild this query and get result from db as singleScalarValue()?
I think you should use (id column for count) here:
->addSelect('count(l) AS cnt')
Something like this, but if you show your Entity i can suggest right solution:
$this->qb->select('l')
->addSelect('count(l.id) AS cnt')
->addSelect('hour(l.time) AS date_hour')
->from(Logs::class, 'l')
->where('l.header = :header')
->groupBy('l.value')
->addGroupBy('date_hour')
->setParameter('header', 'someheader')
$count = $qb->getQuery()->getSingleScalarResult();
Since GROUP BY + COUNT + getSingleScalarResult does not seem to work together in DQL queries, I guess your best option is to count afterwards
$sub
->select('l.id')
->addSelect('l.value as hidden val')
->addSelect('hour(l.time) AS hidden date_hour')
->from(Logs::class, 'l')
->where('l.header = :header')
->groupBy('val')
->addGroupBy('date_hour')
->setParameter('header', 'someheader')
;
$subIds = array_column($sub->getQuery()->getResult(), 'id');
$count = count($subIds);

active record in codeigniter automatically adds quotes around where clause values

I've tried reading other posts on stackoverflow and also checked the active record documentation for ci, but i can't seem to find the answer to my question
I have the following logic in my model:
$query = $this->db->get_where('categories', array('parent_id' => $category_id));
the sql this generates as per the last_query() method is:
SELECT * FROM (categories) WHERE parent_id = '8'
I need to remove the quotes around the number 8. How would I do that?
I've tried using the select statement and passing false as the second parm. So for example:
$this->db->select('*', false);
$this->db->from('categories');
$this->db->where('parent_id=',$category_id);
But that didn't really change much. Any suggestions?
Thank you
By default, CodeIgniter tries to predict the data type in your comparison, and use the appropriate SQL syntax accordingly. If your query is using single quotes, it might indicate that $category_id is being treated as a string rather than an integer. What happens if you try:
$this->db->select('*');
$this->db->from('categories');
$this->db->where('parent_id', (int) $category_id);
Alternatively, you can construct your own WHERE statement manually:
$this->db->where('parent_id = ' . (int) $category_id);
For MIN and MAX query I used null and false keyword to remove the quotes.
$this->db->where("$value > min_column",null,false);
$this->db->where("$value < max_column",null,false);
The idea of the methods is to auto escape to protect against SQL injections, if for some reason you don't want to you can send a raw query like this :
$q = "select * from categories where parent_id = $category_id";
$this->db->query($q)->result();
Which i find much easier. However i think you can send an extra false paremeter to disable it, something like :
$query = $this->db->get_where('categories', array('parent_id' => $category_id),false);
FYI, if you want to send raw queries and escape them(for more complex queries) you can use :
$category_id = $this->db->escape($category_id);

Query using three terms in SQL/PHP

I'm using WordPress, but this question is more pertaining to the SQL involved. I'll gladly move it if I need to.
I'm working on http://www.libertyguide.com/jobs and I'm trying to alter the filtering mechanics. Currently it's a global OR query.
Anyways, I have three filtering lists, and I'm storing what's selected into three strings (interests, type, experience) in the following way:
"( $wpdb->terms.slug = 'webdevelopment' OR $wpdb->terms.slug = 'journalism' OR ... ) AND"
It's populated by whatever is selected in my filtering lists.
When it comes down to it, I have this as a basic query (I'm leaving out the LEFT JOINS):
Before:
SELECT * FROM $wpdb->posts WHERE ($wpdb->terms.slug = 'fromlist1'
OR $wpdb->terms.slug = 'fromlist2' OR $wpdb->terms.slug = 'fromlist3')
AND $wpdb->term_taxonomy.taxonomy = 'jobtype'...
After:
SELECT * FROM $wpdb->posts WHERE
($wpdb->terms.slug = 'fromlist1' OR $wpdb->terms.slug = 'fromlist1again')
AND ($wpdb->terms.slug = 'fromlist2' OR $wpdb->terms.slug = 'fromlist2again')
AND ($wpdb->terms.slug = 'fromlist3' OR $wpdb->terms.slug = 'fromlist3again')
AND $wpdb->term_taxonomy.taxonomy = 'jobtype'...
So essentially I want to go from an
OR filter
to
an AND filter with OR filtering inbetween.
My new filtering only works when one item overall is selected, but returns nothing when I select more than one thing (that I know would match up with a few posts).
I've thought through the logic and I don't see anything wrong with it. I know nothing is wrong with anything else, so it has to be the query itself.
Any step in the right direction would be greatly appreciated. Thanks!
UPDATE
From the confusion, basically I have this:
"SELECT ...... WHERE $terms ..."
but I WANT
"SELECT ....... WHERE $interests AND $type AND $experience"
I don't want to have it filter $interest[1] OR $interest[2] OR $type[1] OR $experience[1], but instead want it to filter ($interest[1] OR $interest[2]) AND ($type[1]) AND ($experience[1])
I hope this makes more sense
*UPDATE 2*
Here's and example:
In my interests list, I select for example three things: WebDevelopment, Academia, Journalism.
In my type list, I choose two things: Fulltime, Parttime
In my experience list, I choose three things: Earlycareer, Midcareer, Latecareer.
When I run my query, I want to make sure that each record has AT LEAST one of each of the three lists. Possible Results: (WebDevelopment, Parttime, Midcareer), (Academia, Fulltime, Earlycareer, Midcareer).
NOT A RESULT: (Journalism, Earlycareer) - missing fulltime or parttime
I really hope this clears it up more. I'm willing to give compensation if I can get this working correctly.
Okay, I'll take a shot at this:
SELECT * FROM $wpdb->posts WHERE
(
$wpdb->terms.slug IN ('$interest1', '$interest2') AND
$wpdb->terms.slug IN ('$type1', '$type2') AND
$wpdb->terms.slug IN ('$exp1', '$exp2')
)
AND $wpdb->term_taxonomy.taxonomy = 'jobtype'
The IN keyword will return true if any member of the set matches.
I think you're looking for a WHERE category IN (comma, seperated, list, of, values) that you can generate dynamically from the form. If you combine it with the other categories, you can require them to select something from each with...
WHERE category1 IN (a, comma, seperated, list, of, values)
AND category2 IN (another, list, of, values)
AND ...
Which will only return a value if there is something selected from each category and will return nothing if any of the selection lists are empty; actually it may well kick out an error, so I would also generate the query dynamically if there is any content whatsoever for a given category.
if (!empty($arrayOfCategory1)) {
//sanitize input logic here
$Category[1] = 'category1 IN ('. implode(', ', $arrayOfCategory1) .')';
} else {
$Category[1] = '';
}
You concatenate the resultant string together and build the query with that. The WHERE 1=1 trick is problematic because if nothing is chosen, everything in the database will match, so I strongly recommend going through the process of adding the AND operators properly.
EDIT: it occurs to me that if you build the conditional statements as an array, you can implode those with ' AND ' and get the query in a fairly small number of lines of code.
Sort of confused a bit by what you are saying but if I wanted to build a filter I would be dynamically generating the SQL query based on the submitted filter values. Something like:
$sql = "SELECT * FROM $wpdb->posts WHERE 1=1";
if ( !empty($interest) ) { // they ticked the interested in ??? checkbox
$sql .= " AND $wpdb->terms.slug = $interest"
}
Obviously you will need to filter and escape any values that have been submitted.

php count returning 1 not 0 from mysql_query when empty

I am trying to get a count and I am getting 1 instead of 0 from it. I have looked thoroughly though the web and this site. I have even been trying to figure it out on my own for a long time. I keep coming empty handed here.
So Basically what I am trying to do is make a like system for my users. I can get everything to work correctly the count works except for one thing. When they have liked it it returns 1 not 0 which it should be.
Here is my code for the count. I am not posting all the coding for security reasons and it really doesn't need to since its about the counting part not the rest.
$sql_like = mysql_query("SELECT * FROM posts WHERE mem2_id='$id' ORDER BY post_date DESC LIMIT 40");
while($row = mysql_fetch_array($sql_like)){
$like1 = $row['like_array'];
$like3 = explode(",", $like1);
$likeCount = count($like3);
}
So here is the code that determines the number. Any ideas what is wrong with this? Why its returning 1 not 0 when the item is empty?
// you do escape your id right??? (sql injection prevention)
$sql_like = mysql_query("SELECT * FROM posts WHERE mem2_id='$id' ORDER BY post_date DESC LIMIT 40");
while($row = mysql_fetch_array($sql_like)){
$likeCount = 0;
$like1 = trim($row['like_array']);
if ($like1) {
$like3 = explode(",", $like1); // exploding emtpy string would result in array('')
$likeCount = count($like3);
}
}
Calling explode on an empty string gives an array containing the empty string. This is one element, not zero.
I would suggest that you change your database design if possible so that you don't store the values separated by commas. Use a separate table instead.
If you can't change the database design you can handle the empty string separately.
count() returns the number of indexes in an array or something in an object (PHP: count - Manual).
if a string var is used rather than an array or object it returns 1. it has to get a null value in order to return 0.
you can give it a go by trying:
print count("");
and
print count(null);
You'll have better luck if you use explode() to break the sql output into an array and then run a check with an if statement. Something like the following:
$like3 = explode(',',$like1);
if (count($like1)=1 && $like1[0] == '')
// etc ..
I hope this helps

PHP mysql - ...AND column='anything'...?

Is there any way to check if a column is "anything"? The reason is that i have a searchfunction that get's an ID from the URL, and then it passes it through the sql algorithm and shows the result. But if that URL "function" (?) isn't filled in, it just searches for:
...AND column=''...
and that doesn't return any results at all. I've tried using a "%", but that doesn't do anything.
Any ideas?
Here's the query:
mysql_query("SELECT * FROM filer
WHERE real_name LIKE '%$searchString%'
AND public='1' AND ikon='$tab'
OR filinfo LIKE '%$searchString%'
AND public='1'
AND ikon='$tab'
ORDER BY rank DESC, kommentarer DESC");
The problem is "ikon=''"...
and ikon like '%' would check for the column containing "anything". Note that like can also be used for comparing to literal strings with no wildcards, so, if you change that portion of SQL to use like then you could pre-set the variable to '%' and be all set.
However, as someone else mentioned below, beware of SQL injection attacks. I always strongly suggest that people use mysqli and prepared queries instead of relying on mysql_real_escape_string().
You can dynamically create your query, e.g.:
$query = "SELECT * FROM table WHERE foo='bar'";
if(isset($_GET['id'])) {
$query .= " AND column='" . mysql_real_escape_string($_GET['id']) . "'";
}
Update: Updated code to be closer to the OP's question.
Try using this:
AND ('$tab' = '' OR ikon = '$tab')
If the empty string is given then the condition will always succeed.
Alternatively, from PHP you could build two different queries depending on whether $id is empty or not.
Run your query if search string is provided by wrapping it in if-else condition:
$id = (int) $_GET['id'];
if ($id)
{
// run query
}
else
{
// echo oops
}
There is noway to check if a column is "anything"
The way to include all values into query result is exclude this field from the query.
But you can always build a query dynamically.
Just a small example:
$w=array();
if (!empty($_GET['rooms'])) $w[]="rooms='".mysql_real_escape_string($_GET['rooms'])."'";
if (!empty($_GET['space'])) $w[]="space='".mysql_real_escape_string($_GET['space'])."'";
if (!empty($_GET['max_price'])) $w[]="price < '".mysql_real_escape_string($_GET['max_price'])."'";
if (count($w)) $where="WHERE ".implode(' AND ',$w); else $where='';
$query="select * from table $where";
For your query it's very easy:
$ikon="";
if ($id) $ikon = "AND ikon='$tab'";
mysql_query("SELECT * FROM filer
WHERE (real_name LIKE '%$searchString%'
OR filinfo LIKE '%$searchString%')
AND public='1'
$ikon
ORDER BY rank DESC, kommentarer DESC");
I hope you have all your strings already escaped
I take it that you are adding the values in from variables. The variable is coming and you need to do something with it - too late to hardcode a 'OR 1 = 1' section in there. You need to understand that LIKE isn't what it sounds like (partial matching only) - it does exact matches too. There is no need for 'field = anything' as:
{field LIKE '%'} will give you everything
{field LIKE 'specific_value'} will ONLY give you that value - it is not partial matching like it sounds like it would be.
Using 'specific_value%' or '%specific_value' will start doing partial matching. Therefore LIKE should do all you need for when you have a variable incoming that may be a '%' to get everything or a specific value that you want to match exactly. This is how search filtering behaviour would usually happen I expect.

Categories