Table:
id | date | number
----------------------
1 | 01.01 | 12
2 | 02.01 | 15
3 | 03.01 | 174
4 | 04.01 | 14
5 | 05.01 | 65
6 | 06.01 | 78
7 | 07.01 | 69
8 | 08.01 | 147
9 | 09.01 | 12
10 | 10.01 | 87
I'm trying to output last 7 values from date ordered by id with some implode.
$day=$mysqli->query("SELECT `date` FROM `online` ORDER BY `id` DESC LIMIT 7");
$day=$day->fetch_all();
$day="'". implode("', '", $day) . "'";
echo $day;
echo returns 'Array', x7. How to output '01.01','02.01', etc.?
First of all, your code uses $day to store at least three different things during its lifetime. While the compiler doesn't complain, it is confusing and difficult to understand for humans. Let rewrite your code to use different variables for different items:
$result = $mysqli->query("SELECT `date` FROM `online` ORDER BY `id` DESC LIMIT 7");
$days = $result->fetch_all();
$text = "'" . implode("', '", $days) . "'";
echo $text;
A quick print_r($days) will reveal you that the value returned by $result->fetch_all(); is a two-dimensional array. Each of its items is an array, one row from the result set.
There are many ways to get the date column of each row returned by $result->fetch_all(). The way that is the easiest to read and understand is to ask mysqli_result::fetch_all() to return the rows as associative arrays indexed by column names (by default it returns arrays numerically indexed) then use the function array_column() to get only the values of the date column and pass only them to implode().
The code is like this:
$result = $mysqli->query("SELECT `date` FROM `online` ORDER BY `id` DESC LIMIT 7");
$days = $result->fetch_all(MYSQLI_ASSOC);
$text = "'" . implode("', '", array_column($days, 'date')) . "'";
echo $text;
The next step towards readability (with a small performance penalty) and correctness is to quote each day individually before joining them into the final string. The function array_map() can be used for that; it applies a callback function to each item of the passed array and returns a new array that contains the values returned by the callback function.
$result = $mysqli->query("SELECT `date` FROM `online` ORDER BY `id` DESC LIMIT 7");
$days = $result->fetch_all(MYSQLI_ASSOC);
$text = implode(', ', // Join the quoted days with commas
array_map(
function($day) {
return "'{$day}'"; // Put each day in single quotes
},
array_column($days, 'date') // Get only the 'date' column
)
);
echo $text;
A quick test with a query that doesn't return any result (use an empty table or add an impossible WHERE condition) reveals that this code doesn't produce any output (which is correct since there is no data retrieved from the database) while the original code produces an empty pair of apostrophes (''), which is not correct (let's remember the requirement was to put each day in single quotes and separate the days with comma and a space.)
Issue is $day->fetch_all() generate associative array and implode doesn't support associative arrays
Try:
$sql = "SELECT `date` FROM `online` ORDER BY `id` DESC LIMIT 3";
$result = $conn->query($sql);
$day = [];
while($row = $result->fetch_row()) {
$day[]=$row[0]; }
$day="'". implode("', '", $day) . "'"; echo $day;
Each element in the fetched array is an array of its columns.
One way to unravel it with with an array_map call:
$first = function($arr) {
return $arr[0];
};
$day = $day->fetch_all();
$day = array_map($first, $day);
$day = "'". implode("', '", $day) . "'";
echo $day;
Without any additional php functions, just pure MySql approach:
$result = $mysqli->query("SELECT group_concat(`date` separator ', ') as dates
FROM `online` ORDER BY `id` DESC LIMIT 7");
$days = $result->fetch_assoc(); // $days['dates'] contains the needed string
https://dev.mysql.com/doc/refman/5.7/en/group-by-functions.html#function_group-concat
To see if what you have is what you want, begin to replace :
echo $day;
With :
print_r($day);
If you see what you want, it's that your SQL request works well. Next, navigate in arrays like #Mureinik says.
If you want a cleaner display, you can use :
echo "<pre>";print_r($day);echo "</pre>";
https://developer.mozilla.org/en-US/docs/Web/HTML/Element/pre "pre" element represents preformatted text. Whitespace inside this element is displayed as typed.
Related
Two or 3 ids are coming in $pks as follows and I am sending these id values in sql. For example, when this for runs 3 times, total_ticket and from_user_id values will come in 3 different arrays. How can I group these 3 arrays according to from_user_id value? So I want to make it into a single array
so I want to group the 2 or 3 $props returned by the value from_user_id
$pks = $request->input('pkids');
for ($i = 0; $i < count($pks); $i++) {
$pk = PK::findOrFail($pks[$i]);
$table_ym = date('Ym', $pk->create_time);
$table = 'video_' . $table_ym;
$props = DB::select('select sum(total_ticket) as total_ticket, from_user_id from '
. $table . ' where pk_id=' . $pk->prop_pk_id . ' group by from_user_id');
}
Essentially this is all you have to do:
$pks = $request->input('pkids');
$pks = array_filter(array_map('intval', $pks)); //int's above 0 only
$results = DB::select('
SELECT
SUM(total_ticket) AS sum_total_ticket,
MIN(create_time) AS min_create_time,
from_user_id
FROM
'.$table.'
WHERE pk_id IN ('.implode(',', $pks).')
GROUP BY from_user_id');
This will return 1 row per from_user_id with the total_ticket as that "groups" sum. I also included the min create time which will be the lowest value of that group for that column. Or the first (earliest) created date.
All you have to do is retrive the results which should look like this
sum_total_ticket min_create_time from_user_id
53 2022-08-20 00:00:00 1
224 2021-08-20 00:00:00 18
Make sense.
I have been trying to manage duplicate data which is shown to users.
I thought I can add the varibales to arrays and use the function array_unique
I want to be able to manage the rows which contain a duplicate date and split them into different sections for example
if(duplicate.exists == true)
{
//do something to the duplicate row
}
else
{
//do something to the row which isnt a duplicate
}
I cant figure out why array_unique is not working.
Help would be appreciated, Thanks.
$result = mysqli_query($con, "SELECT *
FROM quotes order by DATE asc ");
$index1 = array();
$fact1 = array();
$newDate1 = array();
while ($row = mysqli_fetch_array($result)) {
$index = $row['id'];
$dbdate = $row['date'];
$fact = $row['quote'];
$newDate = date("d-m-Y", strtotime($dbdate));
$index1[] = $fact;
$fact1[] = $fact;
$newDate1[] = $newDate;
}
Then have a function which loops through each array and finds out if a certain date has already exists.
for($i=0; $i<count($index1); $i++) {
echo(array_unique($newDate1));
}
else
{
}
Thats an example of the data that will be in the DB.
It's the id, fact, date example 1, fact, 2015-01-22
1 Steve Jobs unveiled the first Apple #Mac computer and changed technology forever (1984) - 2015-01-24
2 In 2011, the Urban Technology Innovation Center was launched in New York City - 2015-01-25
3 #Bebo was launched a whole decade ago today (2005), who feels old? - 2015-01-26
4 Sun Microsystems was acquired by Oracle Corporation for $7.4 bn (2010) - 2015-01-27
Considering you are sorting your query on date and that makes something a duplicate, all you need to do is track the last date.
$lastdate = '';
while ($row = mysqli_fetch_array($result)) {
$dbdate = $row['date'];
if ($lastdate==$dbdate) {
//duplicate
} else {
//first or unique
}
$lastdate = $dbdate;
}
It can be quicker to do this in SQL
Find the duplicates
SELECT * FROM quotes GROUP BY `date` HAVING COUNT(`date`) > 1 order by DATE asc
Find the non-duplicates
SELECT * FROM quotes GROUP BY `date` HAVING COUNT(`date`) = 1 order by DATE asc
So as noted by the OP, he wants a way to detect duplicates and not remove them.
To detect duplicates you can use something like this, answered in another question.
I would prefer this:
function array_has_dupes($array) {
return count($array) !== count(array_unique($array));
}
Use SQL "count" and "group".
create table z (x varchar(100),y varchar(100));
insert into z values ('a','b');
insert into z values ('a','b');
insert into z values ('a','c');
select x,y,count(*) as count from z group by x,y;
You get values:
+------+------+-------+
| x | y | count |
+------+------+-------+
| a | b | 2 |
| a | c | 1 |
+------+------+-------+
And use it in php code.
Please could someone help me? I want to select all values in mysql table where the column that i want to check get a value is mix with aingle or array of values....... So to be more clear I have a table to store all messages from many sender to one or many reciever....
my functions is
public static function find_messagesTo_by_user_id($mess_to=0) {
global $database;
$mess_to = $database->escape_value($mess_to);
$sql = "SELECT * FROM ".self::$table_name;
$sql .= " WHERE mess_to = '{$mess_to}'";
$sql .= " AND mess_deleted = 0";
$sql .= " ORDER BY mess_created_date DESC";
$result_array = parent::find_by_sql($sql);
return $resultrray;
}
So 'mess_to ' has array and single value .... they are only numbers Like (1, 15, 25 ,26 ,27 , array(1,25, 27) , 31, 42, .......)
Please, i break my head on it :)
I waiting for any help?
Building on #Maluchi's answer. Make sure your data looks like:
| mess_to |
+-----------------+
| ,123, |
| ,123,456,152,1, |
| ,456,567, |
| ,3, |
So surround each value in ,. then you can safely do:
WHERE `mess_to` LIKE "%,{$mess_to},%"
This ensures that $mess_to = 1 will match only the 2nd row, and not the 1st as well.
You could also denormalize your data and make a table to JOIN on.
If I'm reading it correctly, $mess_to is passed into the function and could contain either a single value or it could be passed in an array.
When matching multiple values, the SQL should be looking for a comma-separated list. The where clause needs to be IN the list rather than EQUAL to the list.
Try:
public static function find_messagesTo_by_user_id($mess_to=0) {
global $database;
$mess_to = $database->escape_value($mess_to);
$sql = "SELECT * FROM ".self::$table_name;
$sql .= " WHERE mess_to IN (" . implode(',', $mess_to) . ")";
$sql .= " AND mess_deleted = 0";
$sql .= " ORDER BY mess_created_date DESC";
$result_array = parent::find_by_sql($sql);
return $resultrray;
}
See this line in particular:
$sql .= " WHERE mess_to IN (" . implode(',', $mess_to) . ")";
Code edited with geomagas's comments! (Thank you geomagas!)
asuming your column is like this
| mess_to |
+---------+
| 1 |
| 1,2,3,4 |
| 2 |
| 3 |
you can use the LIKE operator:
$sql .= " WHERE mess_to LIKE '%{$mess_to}%'";
this will match every row where mess_to has the string value of $mess_to (your column data type should be string for this to work).
Question1:
MySQL table
id | array
1 | 1,2,3
2 | 2
3 | 2,3
4 | 4,5,6
$_GET['id'] = 2;
$a = mysql_query("SELECT * FROM `table` WHERE `array` ??? '$_GET[id]'");
In this step, I want to run through the entire array and see if it matches with the $_GET['id'], so it should output:
ids: 1,2,3
Question2:
MySQL table
id | array
1 | 4,5,6
2 | 3,4,7
$_GET['id'] = 4;
$a = mysql_query("SELECT * FROM `table` WHERE `array` ??? '$_GET[id]'");
In this step, I only want to match against the first element in the array, so it should output:
id: 4
I can only think of using PHP to do this, but I'd rather do all that just within the MySQL query, if that is even possible.
$a = mysql_query("SELECT * FROM `table`");
while($b = mysql_fetch_assoc($a))
{
$elements = explode(',', $b['array']);
foreach($elements as $element)
{
if($element == $_GET['id'])
{
echo $b['id'].'<br />';
}
}
}
or
$a = mysql_query("SELECT * FROM `table`");
while($b = mysql_fetch_assoc($a))
{
$array = $b['array'];
if(in_array($_GET['id'], $array))
{
echo $b['id'].'<br />';
}
}
that would look just awful.
That you can/should structure your database differently has already been mentioned (see http://en.wikipedia.org/wiki/Database_normalization). But....
See FIND_IN_SET()
mysql> SELECT FIND_IN_SET('b','a,b,c,d');
-> 2
e.g.
<?php
$mysql = init();
bar($mysql, 1);
bar($mysql, 2);
bar($mysql, 3);
bar($mysql, 4);
function bar($mysql, $x) {
$sql_x = mysql_real_escape_string($x, $mysql);
$result = mysql_query("SELECT id, foo FROM soTest WHERE FIND_IN_SET('$sql_x', foo)", $mysql) or die(mysql_error());
echo "$x:\n";
while( false!==($row=mysql_fetch_array($result, MYSQL_ASSOC)) ) {
echo $row['id'], ' ', $row['foo'], "\n";
}
echo "----\n";
}
function init() {
$mysql = mysql_connect('localhost', 'localonly', 'localonly') or die(mysql_error());
mysql_select_db('test', $mysql) or die(mysql_error());
mysql_query('CREATE TEMPORARY TABLE soTest (id int auto_increment, foo varchar(64), primary key(id))', $mysql) or die(__LINE__.' '.mysql_error());
mysql_query("INSERT INTO soTest (foo) VALUES ('1,2,3'), ('2,4'), ('3'), ('2,3'), ('1,2')", $mysql) or die(__LINE__.' '.mysql_error());
return $mysql;
}
prints
1:
1 1,2,3
5 1,2
----
2:
1 1,2,3
2 2,4
4 2,3
5 1,2
----
3:
1 1,2,3
3 3
4 2,3
----
4:
2 2,4
----
MySQL can't use indices to perform this search, i.e. the query results in a full table scan, see Optimizing Queries with EXPLAIN
edit:
For your second question you only have to change the WHERE-clause to
WHERE FIND_IN_SET('$sql_x', foo)=1
Your data structure in the DB is not optimal for querying the way you want it.
For the first question:
mysql_query("SELECT * FROM table WHERE array LIKE '%,$_GET[id],%' OR array LIKE '$_GET[id],%' OR array LIKE '%,$_GET[id]' OR array = '$_GET[id]'");
For the second:
mysql_query("SELECT id, SUBSTR(array, 1, POSITION(',' IN array) - 1) AS array FROM table WHERE array LIKE '$_GET[id],%' OR array = '$_GET[id]'");
As you can see, these queries aren't pretty, but they'll do what you want.
Untested, but you should be able to use:
Question 1:
SELECT * FROM table WHERE array REGEXP '(^|,)?(,|$)';
// Match either the start of the string, or a , then the query value, then either a , or the end of the string
Question 2:
SELECT * FROM table WHERE array REGEXP '^?(,|$)';
// Match the start of the string, then the query value, then either a , or the end of the string
Where ? is replaced with your $_GET value.
No idea on the performance of this.
I'd recommend you to bring your database to the first normal form, e. g.
CREATE TABLE t_master (
id INT PRIMARY KEY AUTO_INCREMENT
);
CREATE TABLE t_array (
id INT PRIMARY KEY AUTO_INCREMENT,
master_id INT NOT NULL,
value INT,
CONSTRAINT fk_array_master_id FOREIGN KEY (master_id) REFERENCES t_master (id)
);
Then you can find records in t_master that have a specific value with
$q = 'SELECT m.* ' .
'FROM t_master AS m INNER JOIN t_array AS a ON a.master_id = m.id ' .
"WHERE a.value = '" . mysql_real_escape_string($_GET['id'], $db) . "' " .
'GROUP BY m.id';
The most important advantage is that if you have a lot of values, you can add an index to find them much faster:
ALTER TABLE t_array ADD INDEX idx_value (value);
A less evident, but not the last advantage is that your queries become more logical and structured.
If you can't normalise your schema (which is the best option:
SELECT *
FROM table
WHERE ','+array+',' LIKE '%,$_GET[id],%'
But if you need to access the records by id, then you really should normalise
First One:
SELECT * FROM table WHERE array LIKE '$_GET[id],%' OR array LIKE '%,$_GET[id],%' OR array LIKE '%,$_GET[id]' OR array = '$_GET[id]
Second One:
SELECT * FROM table WHERE array LIKE '$_GET[id],%' OR array = '$_GET[id]
Explanation:
'$_GET[id],%' will match, if array is start with $_GET[id]
'%,$_GET[id],%' will match, if $_GET[id] is between any two of array items
'%,$_GET[id]' will match, if array is end with $_GET[id]
array = '$_GET[id]' match, if the array contains only one item equal to $_GET[id]
if i have a query like :
SELECT * FROM table WHERE id IN (3,6,1,8,9);
this array of the ids is build in php dynamically ,
and the order is important to me.
$my_array = array (3,6,1,8,9) ;
how can i sort the results by the order by which the elements appear in my array ?
its possible to do it in MYSQL query,
or i must to order it after via php ?
You can order by a value derived from a column. You can use a CASE operator to specify the order:
SELECT * FROM table
WHERE id IN (3,6,1,8,9)
ORDER BY CASE id WHEN 3 THEN 1
WHEN 6 THEN 2
WHEN 1 THEN 3
WHEN 8 THEN 4
WHEN 9 THEN 5
END
I haven't tested but this PHP solution should work:
<?php
$my_array = array (3,6,1,8,9) ;
$sql = 'SELECT * FROM table WHERE id IN (3,6,1,8,9)';
$sql .= "\nORDER BY CASE id\n";
foreach($my_array as $k => $v){
$sql .= 'WHEN ' . $v . ' THEN ' . $k . "\n";
}
$sql .= 'END ';
echo $sql;
?>
This generates the following SQL code:
SELECT * FROM table WHERE id IN (3,6,1,8,9)
ORDER BY CASE id
WHEN 3 THEN 0
WHEN 6 THEN 1
WHEN 1 THEN 2
WHEN 8 THEN 3
WHEN 9 THEN 4
END
If you must do it this way you'll have to manipulate the data in PHP. MySQL can only order by natural orderings ascending or descending.
Got to question though - why do you need the data returned in this very specific order? There may be an easier solution to your problem by re-jigging something further up in the code.
SELECT * FROM table WHERE id IN (3,6,1,8,9) ORDER BY FIELD(id,3,6,1,8,9);
You can load the results into an array with IDs as indexes:
while ($row = mysql_fetch_array($l)) $items[$row['id']] = $row;
and then simply iterate it in your order
foreach ($my_array as $id) { $current_row = $items[$id]; ... }