MySQL PHP select where "X,Y" is in X,Y,Z - php

How do I complete this code below? I am trying to select news from the database and the locations which can be 23,22,88,90 location codes. I know you can just do IN('23', '22', '88', '90') but the problem is, my locations are a string so it comes out like IN('23,22,88,90') I believe.
How do I expand on the string of locations and select all or any including the locations in the string? So in the database, newsLocations could be 22 23 22,90 23,80,90 90. If that makes sense? so if $locationstoGet has 22,88,90 only, it will get the newsLocation even if the result is just 88,90 without the 22.
$locationsToGet = '22,88';
$db->query("SELECT * FROM news WHERE newsLocation IN($locationstoGet)");
I hope I explained this alright.

I saw a response on another site here
So I will adapt the solution there to your scenario. Change locationsToGet into an array, and use the implode function to generate the right syntax for the IN Clause.
$locationsToGetArr = array('22','88');
$locations = "'".implode("','",$locationsToGetArr)."'"; //this should output '22','88'
$db->query("SELECT * FROM news WHERE newsLocation IN($locations)");
This solution is assuming your database structure is as such
+--------+--------------+
| news | newsLocation |
+--------+--------------+
| 1 | 88 |
| 1 | 22 |
| 2 | 22 |
| 2 | 88 |
+--------+--------------+
But if you are storing your data as the following instead
+--------+--------------+
| news | newsLocation |
+--------+--------------+
| 1 | 88,22 |
| 2 | 22,88 |
+--------+--------------+
You will not have much choice besides to select all from news table and have PHP filter the location. Not a very efficient method.

If your data is comma separated stored in databse column then you can use MYSQL FIND IN SET as per below example.
SELECT FIND_IN_SET('b','a,b,c,d');
OR you can try with regular expression in MYSQL but it will be too slow.

You can make an array of your locations and then populate your query string with the items from the array
$locations = '22,88';
$locationsToGetArray = explode(",", $locationToGet)
$query = "SELECT * FROM news WHERE newsLocation IN(";
for ($i = 0; $i < count($locationsToGetArray); $i++) {
$query .= $locationsToGetArray[$i];
if($i == (count($locationToGetArray) - 1)) $query.= ")";
else $query .= ",";
}
$db->query($query);

Related

How to use LIKE statement to check if each value in an array is present in a SQL table? [duplicate]

I have the following example table and attributes:
---------------------------
| Name | Town |
---------------------------
| Name 1 | POOLE |
| Name 2 | POOLE/WALLASEY |
| Name 3 | POOLE/WALLASEY |
| Name 4 | POOLE |
---------------------------
I am using the following SQL statement in PHP to retrieve rows:
SELECT * FROM `table` WHERE `Town` LIKE '%".$global->getPlayerTown()."%'
Given the criteria POOLE the database returns:
---------------------------
| Name | Town |
---------------------------
| Name 1 | POOLE |
| Name 2 | POOLE/WALLASEY |
| Name 3 | POOLE/WALLASEY |
| Name 4 | POOLE |
---------------------------
However when using the criteria POOLE/WALLASEY the query returns:
---------------------------
| Name | Town |
---------------------------
| Name 2 | POOLE/WALLASEY |
| Name 3 | POOLE/WALLASEY |
---------------------------
How do I intelligently tell the PHP to split the string into separate criteria (i.e. POOLE and WALLASEY) in one query, so that the query retrieves all rows?
SELECT * FROM `table` WHERE `town` REGEXP 'POOLE|WALLASEY';
This will match any rows that has one or more instances of POOLE or WALLASEY.
As to the PHP side, depending on how many kinds of separators ('/' in this case) you have in your dataset, it can get rather messy rather quickly.
But replace '/' with '|' in getPlayerTown() would seem to be one way of doing it.
As to performance, I'm not sure how REGEXP is as opposed to LIKE.
https://dev.mysql.com/doc/refman/5.7/en/regexp.html
This is an iteration of an often-asked class of questions: How do I select on a single datum, if I have more than one in a field?
The answer, as always, is: You don't.
There are many reasons for that, but one of the most important is performance: Basically a LIKE '%...' can't use an index. That might be ok with a handful of test rows, but it quickly becomes a problem when scaling.
The only reliable ways are to
either normalize your data
or use a fulltext index
In your case I'd strongly vote for normalization: Create a towns table, then link it to the players via a join table. You can now search for any town with full index use, finding the players through the join.
As Marc B stated, using explode.
<?php
$array = explode("/",$global->getPlayerTown());
foreach($array as $Town){
$list = $list ."'%" .$Town ."%', ";
}
$SQL = "SELECT * FROM `table` WHERE `Town` LIKE ANY(" .$list .")";
?>
Please go the smart route and normalize your data. This idea may work, but that doesn't mean it is the best choice.
You could explode the towns, then loop through them and build the query like so:
$towns = explode('/', $global->getPlayerTown());
$first = true;
$like_sql = '';
foreach($towns as $town) {
$like_sql .= $first ? ' WHERE ' : ' OR ';
$like_sql .= "`Town` LIKE '%{$town}%'";
$first = false;
}
$query = "SELECT * FROM `table` {$like_sql}";
However I would recommend you normalise your data, and have a separate towns table, with a user_town pivot table.

Array in where clause in mysql

i am developing an small application which disease after asking about symptoms,,php + mysql
my table is
i have an array of symptoms, i want to get disease that match to array symptoms
$a= array('fever','pain');
$sql=mysql_query("select * from disease where `d_symptoms` like '$a'");
already tryed using join and in
echo $v=join(',',$a);
$sql=mysql_query("select * from disease where `d_id` in ($v)");
please help me
you need to have a new table called symptoms, which includes a foreign key of the disease id (d_id) in your current table and the symptom name (d_symptom). Then each row will have the name of the symptom and the id of the disease it is linked with. That way you will not have multiple values in the symptom field. You then call it be selecting all symptoms where id='d_id' to get the list of symptoms associated with that disease.
the query might be
$a= array('fever','pain');
$sql=mysql_query("SELECT d_name FROM disease, symptoms WHERE disease.d_id = symptoms.d_id AND d_symptom IN ($a)";);
or something..
The correct answer is to properly normalize your database. You shouldn't use comma separated values (as suggested in comments). I am sure you can find many articles teaching normalization, so I won't go into details here.
You need to separate the symptoms from the diseases.
Diseases table
id | d_name
---------------------
1 | Dengu
2 | Typhoid
3 | Cervical
Symtoms table
id | s_name
---------------------
1 | Fever
2 | Pain
3 | Vomit
4 | Abc
5 | Xyz
Diseases-Symptom table (this is an intersection)
id | d_id | s_id
---------------------------
1 | 1 | 1
2 | 1 | 2
3 | 1 | 3
2 | 2 | 3
3 | 2 | 2
1 | 2 | 4
2 | 3 | 2
3 | 1 | 5
This way you don't create duplicate symptoms and makes your data easier to use and present, for example
SELECT id, s_name FROM symptoms
will give you a list of all symptoms available.
SELECT diseases.id, diseases.d_name, symptoms.s_name
FROM diseases
JOIN diseases_symptoms ON d_id = diseases.id
JOIN symptoms ON symptoms.id = diseases_symptoms.s_id
WHERE diseases.id = 1;
will give you a result similar to:
id | d_name | s_name
---------------------------
1 | Dengu | Fever
2 | Dengu | Pain
3 | Dengu | Vomit
You may use a single FIND_IN_SET for each symtoms you are looking for:
$query = "SELECT * FROM disease WHERE 1=1 ";
foreach($a as $row)
$query += "AND FIND_IN_SET($row, d_symptoms)";
$sql=mysql_query($query);
Well, you shouldn't store multiple values in a single column, as a best practice rule.(I really would fix that).
But, maybe something like this would work if you want to continue the way you have it:
$query = "select * from disease where d_symptoms like " . $a[0];
for($i = 1; $i < count($a); i++){
$query = $query + " AND d_symptoms like " $a[$i];
}
$sql=mysql_query($query);

PHP Mysql MAX() returning wrong result

PHP:
$result = mysql_query("SELECT MAX(something) AS something FROM users");
$row = mysql_fetch_assoc($result);
$max = $row["something"];
echo $max;
Mysql:
+-----------+----------+
| Something | Name |
+-----------+----------+
| 9 | John |
| 984 | Somebody |
| 1 | Who |
Code results: 9, the question is why? Mysql "something" type is "text". My mysql table is bigger than this above, but it still results not the biggest "something".
Either do a math operation on the column to trigger an auto-cast
SELECT MAX(something * 1) AS something FROM users#
or cast explicitly with
SELECT MAX(cast(something as signed)) AS something FROM users
but even better - if that column only contains numbers then change the data type of that field ti int for instance.

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.

Efficient way to query SQL and format to CSV

I have a MySQL database that I am keeping temperature readings from several different sensors. I initially thought of using three different tables to store my data:
mysql> select * from sensor_info;
+----+------------------+------+--------+
| id | address | name | active |
+----+------------------+------+--------+
| 1 | 28D684CD02000057 | NULL | 1 |
| 2 | 28099B49030000D8 | NULL | 1 |
| 3 | 28339ACD0200004B | NULL | 1 |
+----+------------------+------+--------+
mysql> select * from data_period limit 4;
+----+---------------------+
| id | ts |
+----+---------------------+
| 1 | 2012-06-30 09:35:02 |
| 2 | 2012-06-30 09:36:22 |
| 3 | 2012-06-30 09:37:46 |
| 4 | 2012-06-30 09:40:36 |
+----+---------------------+
mysql> select * from data_points limit 4;
+----+-------------+-----------+-------+
| id | data_period | sensor_id | data |
+----+-------------+-----------+-------+
| 1 | 1 | 1 | 77.90 |
| 2 | 1 | 2 | 77.34 |
| 3 | 1 | 3 | 77.56 |
| 4 | 2 | 1 | 78.01 |
+----+-------------+-----------+-------+
What I'm trying to do is to take my stored data and put it into a CSV file so I can display it using dygraphs Javascript library. I need to get my data into a format like this:
date,temp1,temp2,temp3
2012-06-30 09:35:02,77.90,77.34,77.56
2012-06-30 09:36:22,78.01,77.36,77.59
....
Every way I start to do this (using PHP), I seem to make this overly complicated and have to put queries inside loops inside loops. Am I making this harder on myself than I need to?
Will most of the work be done using the queries or using PHP? Down the road, I will also want to add code that will place NULL in the CSV if a temperature reading is missing from a particular timestamp.
I'm not looking for a very specific answer, I just want to know what direction I should go. I don't even know how to start to format my data from the database or if I should try looking at a different format to store my info in the database.
I'd run a single select query to join the lot together. You can use an outer join where there might not be data.
SELECT data_period.ts AS date, dp1.data AS temp1, dp2.data AS temp2, dp3.data AS temp3
FROM data_period
LEFT OUTER JOIN data_points AS dp1 ON dp1.data_period=data_period.id AND dp1.sensor_id=1
LEFT OUTER JOIN data_points AS dp2 ON dp2.data_period=data_period.id AND dp2.sensor_id=2
LEFT OUTER JOIN data_points AS dp3 ON dp3.data_period=data_period.id AND dp3.sensor_id=3
(See: SQL Fiddle Example )
That should give you a single set of results that you can just loop through.
If you really want MySQL to do most of the work, you can change the first line to (I think)
SELECT data_period.ts +','+ IFNULL(dp1.data,'NULL') +','+ IFNULL(dp2.data,'NULL') +','+ IFNULL(dp3.data,'NULL')
To Synthesize the comment and the answer and to get this out to a file:
SELECT data_period.ts AS date, dp1.data AS temp1, dp2.data AS temp2, dp3.data AS temp3
INTO OUTFILE '/home/user/output.csv'
FIELDS TERMINATED BY ','
OPTIONALLY ENCLOSED BY '"'
ESCAPED BY '\\'
LINES TERMINATED BY '\n'
FROM data_period LEFT OUTER JOIN data_points AS dp1 ON dp1.data_period=data_period.id
AND dp1.sensor_id=1 LEFT OUTER JOIN data_points AS dp2 ON
dp2.data_period=data_period.id AND dp2.sensor_id=2 LEFT OUTER JOIN data_points AS dp3
ON dp3.data_period=data_period.id AND dp3.sensor_id=3;
To do this you can use two SQL statements giving an outer and an inner loop. The first statement
select ts from data_points as point
inner join data_period on data_period = data_period.id
group by ts
order by ts
will get a list of the dates that you have data for.
The second one on the inner loop will get a list of data values ordered by the sensor id - if you have missing values in the data_points then this list will be wrong and have gaps; so if you do have gaps you may need to do an extra step to get the list of sensors and then populate this list from the data coming back from the query.
The following example illustrates how you could proceed with this.
// get the outer list
$sql = "select ts from data_points as point
inner join data_period on data_period=data_period.id
group by ts
order by ts";
$result = mysql_query($sql);
$result || die(mysql_error());
while($row = mysql_fetch_row($result))
{
// get the date for the second query
$date = $row[0];
$sql = "select data from data_points as point
inner join data_period on data_period=data_period.id
inner join sensor_info on sensor_id = sensor_info .id
where ts = '$date'
order by sensor_id";
$result_1 = mysql_query($sql);
// now create an array from the values - we could use mysql_fetch_array to do this
// but this way allows us the possibility of extending the processing to
// take into account missing values.
$vals = array();
while($row_1 = mysql_fetch_row($result_1)) {
$vals[]=$row_1[0];
}
print "$date,".join(",",$vals)."\n";
}
I'd recommend just doing the csv formatting in php after the fact.
Here is a handy function I found online and really love using. And if you wrap your SQL calls in a class then you can just have this method available after you query. :)
Note I modified this to me MSSQL but its easy to replace it with just mysql.
public final function SQL2CSV() {
// set initial .csv file contents
$CSV = array();
$rowid = 0;
//This would be your query.
$res = $this->fetchQuery();
// get column captions and enclose them in doublequotes (") if $print_captions is not set to false
for ($i = 0; $i < mssql_num_fields($res); $i++) {
$fld = mssql_fetch_field($res, $i);
$colnames[] = $fld->name;
}
// insert column captions at the beginning of .csv file
$CSV[$rowid] = implode(",", $colnames);
// iterate through each row
// replace single double-quotes with double double-quotes
// and add values to .csv file contents
if (mssql_num_rows($res) > 0) {
while ($row = mssql_fetch_array($res, MSSQL_NUM)) {
$rowid++;
for ($i = 0; $i < sizeof($row); $i++)
//$row[$i] = '"'.str_replace('"', '""', $row[$i]).'"';
$row = str_replace (',', '', $row);
$CSV[$rowid] = "\n".implode(",", $row);
}
}
RETURN $CSV;
}

Categories