I've searched through older threads but haven't yet found a solution for the following issue: is it possible to group and arrange mysql results without resorting to nested queries?
Please see the sample below.
I have:
Month Jan, Location USA, Program DDD
Month Jan, Location UK, Program EEE
Month Jan, Location USA, Program LLL
Month FEB, Location UAE, Program EEE
Month FEB, Location USA, Program DDD
Month FEB, Location UK, Program MMM
Month MAR, Location USA, Program FFF
Month MAR, Location UAE, Program FFF
Month MAR, Location UK, Program FFF
I want them to display like this:
+---------++---------++---------+
|Month Jan||Month Feb||Month Mar|
+---------------+---------++---------++---------+
|Location USA | || || |
+---------------+---------++---------++---------+
| |DDD ||DDD ||FFF |
+---------------+---------++---------++---------+
| |EEE || || |
+---------------+---------++---------++---------+
| |LLL || || |
+---------------+---------++---------++---------+
|Location UK | || || |
+---------------+---------++---------++---------+
| |EEE ||MMM ||FFF |
+---------------+---------++---------++---------+
|Location UAE | || || |
+---------------+---------++---------++---------+
| | ||EEE ||FFF |
+---------------+---------++---------++---------+
I have tried grouping the query but I'm unable to render it to the HTML table.
SELECT
event.`event_id`,
event.`event_program_id`,
event.`event_month`,
event.`event_location_id`,
location.`location_name`,
program.`program_name`,
program.`program_shortname`
FROM
`event`
LEFT JOIN
`location`
ON
event.`event_location_id` = location.`location_id`
LEFT JOIN
`program`
ON
event.`event_program_id` = program.`program_id`;
Right now its listing like this:
column1 column2 column3
line1 34
line2 34
line3 34
line5 34
Where I wanted like this:
column1 column2 column3
line1 34 34
34 34
34
line2 34
34 34 34
line3 34
line5 34
You can do some inner queries to achieve this for each column, if you have a predefined number of columns in the expected result, something like just the months of the year.
I had some experience with reports based on SQL that used subqueries like this, at the end of the day, this solution preved to be a pain to manage :) today I just read the data, store in a matrix in memory and at the end generate the result based on the matrix, much easier to change in the future.
cheers !
here is an example in PHP
<pre>
<?php
$columns = array();
$data = array();
// add the data from the database
$data["line1"]["column1"] += 34;
$data["line2"]["column1"] += 34;
$data["line3"]["column4"] += 34;
$data["line5"]["column2"] += 34;
$data["line1"]["column1"] += 34;
$data["line3"]["column4"] += 34;
// find the columns
foreach ($data as $line => $column) {
foreach ($column as $cname => $value) {
if( ! in_array($cname, $columns) ){
$columns[] = $cname;
}
}
}
sort($columns);
// display column names
echo " \t";
foreach ($columns as $index => $cname) {
echo $cname . "\t";
}
echo "\n";
// display the data
foreach ($data as $line => $column) {
echo $line . "\t";
foreach ($columns as $index => $cname) {
echo $column[$cname] . "\t";
}
echo "\n";
}
?>
</pre>
Here's another way to solve the problem
<pre>
<?php
$columns = array();
$data = array();
// add the data from the database
// sort the data in the SQL
$data[] = array("l" => "line1", "c"=> "column1", "v" => "AAA");
$data[] = array("l" => "line1", "c"=> "column1", "v" => "BBB");
$data[] = array("l" => "line1", "c"=> "column3", "v" => "CCC");
$data[] = array("l" => "line2", "c"=> "column2", "v" => "AAA");
$data[] = array("l" => "line3", "c"=> "column2", "v" => "AAA");
// find the columns
foreach ($data as $line => $column) {
if( ! in_array($column["c"], $columns) ){
$columns[] = $column["c"];
}
}
sort($columns);
// display column names
echo " \t";
foreach ($columns as $index => $cname) {
echo $cname . "\t";
}
echo "\n";
$name = '';
$count = 0;
// display the data
foreach ($data as $line => $column) {
if( $column['l'] == $name ){
$count ++;
} else {
$name = $column['l'];
$count = 0;
}
if( $count == 0 ){
echo $name . "\t";
} else {
echo " \t";
}
foreach ($columns as $index => $cname) {
if( $column['c'] == $cname ){
echo $column['v'] . "\t";
} else {
echo " \t";
}
}
echo "\n";
}
?>
</pre>
other reference using html as output
<table border="1">
<?php
$columns = array();
$data = array();
// add the data from the database
// sort the data in the SQL
$data[] = array("l" => "line1", "c"=> "column1", "v" => "AAA");
$data[] = array("l" => "line1", "c"=> "column1", "v" => "BBB");
$data[] = array("l" => "line1", "c"=> "column3", "v" => "CCC");
$data[] = array("l" => "line2", "c"=> "column2", "v" => "AAA");
$data[] = array("l" => "line3", "c"=> "column2", "v" => "AAA");
// find the columns
foreach ($data as $line => $column) {
if( ! in_array($column["c"], $columns) ){
$columns[] = $column["c"];
}
}
sort($columns);
// display column names
echo "<tr>";
echo "<td> </td>";
foreach ($columns as $index => $cname) {
echo "<td>".$cname."</td>";
}
echo "</tr>";
$name = '';
$count = 0;
// display the data
foreach ($data as $line => $column) {
echo "<tr>";
if( $column['l'] == $name ){
$count ++;
} else {
$name = $column['l'];
$count = 0;
}
if( $count == 0 ){
echo "<td>".$name."</td>";
} else {
echo "<td> </td>";
}
foreach ($columns as $index => $cname) {
if( $column['c'] == $cname ){
echo "<td>".$column['v']."</td>";
} else {
echo "<td> </td>";
}
}
echo "</tr>";
}
?>
</table>
Related
I have fetched json data from server and decode using json_decode(value, true);
using print_r function I got this array as output
Array
(
[id] => 120
[key] => 7ca04960a36dbb7f4b7c8607bb3
[num] => 0
)
Array
(
[id] => 121
[key] => 7ca04960a36dccgki49g6dfg57
[num] => 0
)
I want to display decoded data using while loop.
while($row = mysqli_fetch_assoc($result)) {
$json = json_decode($row['post_req'], true);
echo "<pre>";
print_r($json);
echo "</pre>";
}
ex:
id = 120,
key = 7ca04960a36dbb7f4b7c8607bb3,
num = 0
how can I print data ?
Edited: It looks like you changed some details.
It looks like you are getting multiple arrays
$output = array();
while($row = mysqli_fetch_assoc($result)) {
$json = json_decode($row['post_req'], true);
$ouput[] = $json;
}
This will give you a multi-layered array and if you just want the id you can do this
for ($i = 0; $i < sizeof($output); $i++) {
echo "id =" . $output[$i]['id'] . " <br/>";
}
or you can use this to get id only
for ($i = 0; $i < sizeof($output); $i++) {
foreach ( $output[$i] as $key => $value ) {
if ('id' === $key) {
echo "$key = $value <br />";
}
}
}
The expected output is
id = 120
id = 121
If you want all three values use this
for ($i = 0; $i < sizeof($output); $i++) {
foreach ( $output[$i] as $key => $value ) {
echo "$key = $value <br />";
}
}
Expected output
id = 120
key = 7ca04960a36dbb7f4b7c8607bb3
num = 0
id = 121
key = 7ca04960a36dccgki49g6dfg57
num = 0
foreach($json as $key => $value) {
echo "$key: $value \n";
}
How can I generate header for printed 2d array in php?
So my array look like this:
$tab=array(
array(0,1,2,3),
array(1,2,3,4),
array(2,3,4,5)
)
This code:
foreach ($tab as $key => $row){
echo '<b>o<sub>'.($key+1).'</sub></b> ';
foreach ($row as $item) {
echo $item.' ';
}
echo '<br>';
}
Print this:
o1 0 1 2 3
o2 1 2 3 4
o3 2 3 4 5
But I need this:
a1 a2 a3 d
o1 0 1 2 3
o2 1 2 3 4
o3 2 3 4 5
Where lenght of rows may be diferent and last column always must be d
Thanks for help
You can check if this is the first iteration in the first foreach and if so, add the first line.
foreach ($tab as $key => $row) {
// If first iteration, add the header
if ($key === 0)
{
foreach ($row as $i => $item)
{
// Last header must be 'd'
if ($i === count($row) - 1)
echo '<b>d</b>';
else
echo '<b>a<sub>' . ($i + 1) . '</sub></b> ';
}
echo '<br />';
}
// Add the current row
echo '<b>o<sub>' . ($key + 1) . '</sub></b> ';
foreach ($row as $item) {
echo $item . ' ';
}
echo '<br />';
}
If the first row is a static row, then you can simply print it using echo function at first.
But if its a dynamic tab as well, then you can use the following code:
$count = count($tab[0]);
for($i = 1; $i <= $count; $i++)
{
if($i != $count) echo '<b>a<sub>' . ($i + 1) . '</sub></b> ';
else echo '<b>d</b> ';
}
foreach ($tab as $key => $row){
echo '<b>o<sub>'.($key+1).'</sub></b> ';
foreach ($row as $item) {
echo $item.' ';
}
echo '<br>';
}
You can try this, except use <table> or <div> to align your elements.
foreach ($tab[0] as $key => $item)
echo $key === 0 ? title(' ', ' ') : title('a', $key);
echo title('d', '') . '<br>';
function title(string $letter, string $index)
{
return "<b>{$letter}<sub>{$index}</sub></b>";
}
Error
Defined array in the controller, then transformed to the model which uses the implode function.
Problem = Data are inserted in the sql table in the same column. Eg: Coke,Pepsi,Slice (1 column)
t_id| product_name | unit | cost
1 | Coke,Pepsi,Slice | 5 | 1000
Solution Wanted = Data should be inserted in multiple column such as
t_id| product_name | unit | cost
1 | Coke | 5 | 1000
2 | Pepsi | 2 | 500
3 | Slice | 3 | 600
View
<div class="col-lg-6">
<?php
$getNameValue = $this->session->userdata('nameValue');
echo '<h3>' .$getNameValue['name'] .' paid Rs ' .$getNameValue['cash_amount']. '</h3>';
$getValue = $this->session->userdata('sessiondata');
if ($getValue != NULL){
echo '<table class="table table-bordered table-striped">';
echo '<tr>';
echo '<td><strong>Product Name</strong></td>';
echo '<td><strong>Unit</strong></td>';
echo '<td><strong>Cost</strong></td>';
echo "<form method='post' action=''>";
foreach ($getValue as $row)
{
echo '<tr>';
echo '<td><input type="hidden" name="allProduct[product_name][]" value="'.$row['product_name'].'">' .$row['product_name']. '</td>';
echo '<td><input type="hidden" name="allProduct[unit][]" value="'.$row['unit'].'">' .$row['unit'].'</td>';
echo '<td><input type="hidden" name="allProduct[cost][]" value="'.$row['cost'].'">' .$row['cost'].'</td>';
echo '</tr>';
}
echo '<tr><td></td><td><td><button type="submit" class="btn btn-success">POST</button></td></td></tr>';
echo "</form>";
echo '</table>';
}
?>
</div>
Controller
public function newSystemBatch($getBatch)
{
if ($data = $this->input->post('systemProduct')) {
$data['batch_id'] = $getBatch;
if (isset($data['sum'])) {
$data['cost'] = $data['cost'] / $data['unit'];
unset($data['sum']);
}
$productName = $data['product_name'];
$unit = $data['unit'];
$cost = $data['cost'];
$getValue = $this->session->userdata('sessiondata');
$getValue[] = array(
'product_name' => $productName,
'unit' => $unit,
'cost' => $cost
);
$this->session->set_userdata('sessiondata', $getValue);
$this->session->set_flashdata('message', $data);
redirect('inventory/newSystemMessage/' . $getBatch['batch_id']);
}
if ($data = $this->input->post('allProduct')) {
$this->header();
$this->footer();
$this->transaction->addNew($data);
$this->load->view("admin/newSystemTable", $data);
}
else {
$this->header();
$data['action'] = 'System';
$data['users_id'] = $this->user->users_id;
$this->load->view("admin/newSystemTable", $data);
$this->footer();
}
}
Model
function addNew($data)
{
if(is_array($data['product_name'])) $data['product_name'] = implode(",", $data['product_name']);
if(is_array($data['unit'])) $data['unit'] = implode(",", $data['unit']);
if(is_array($data['cost'])) $data['cost'] = implode(",", $data['cost']);
$this->db->insert('try', $data);
}
Model:
function addNew($data)
{
// Test that all all sub arrays are equal size
$length = array_unique(array_map('count', $data));
if (count($length) == 1) {
$length = current($length);
$keys = array_keys($data);
$res = array();
for($i = 0; $i < $length; $i++)
foreach($keys as $key)
$res[$i][$key] = $data[$key][$i];
$this->db->insert_batch('try', $res);
}
else {
// incorrect data
return false;
}
return true;
}
Codepad Example
I 'm trying to create a comparison table from an array like this:
Array ( [0] => Array ( [name] => Sony X852
[4G] => 1
[Backlighting] => 0
)
[1] => Array ( [name] => Nokia 12hb
[4G] => 0 // have trouble with this element
[Backlighting] => 0
)
[2] => Array ( [name] => Asus 23yh
[4G] => 1
[Backlighting] => 0
)
)
$table = "";
$table .= "<table><tbody>";
foreach($array as $key=>$val)
{
$table .= "<tr>";
foreach($val as $k=>$v)
{
if($k == "name")
{
$table .= "<td>$v</td>";
}
else if($v > 0)
{
$table .= "<td>$k</td><td>$v</td>";
}
}
$table .= "</tr>";
}
$table .= "</tbody></table>";
print $table;
Here's my expected result:
<table>
<tbody>
<tr><td>Sony X852</td><td>4G</td><td>1</td></tr>
<tr><td>Nokia 12hb</td><td>4G</td><td>0</td></tr> // don't skip the 4G td for this phone
<tr><td>Asus 23yh</td><td>4G</td><td>1</td></tr>
</tbody>
</table>
But the output I'm getting from the code above is this:
<table>
<tbody>
<tr><td>Sony X852</td><td>4G</td><td>1</td></tr>
<tr><td>Nokia 12hb</td></tr>
<tr><td>Asus 23yh</td><td>4G</td><td>1</td></tr>
</tbody>
</table>
1 and 0 here indicate whether a product has that feature. How can I skip over an element only if the same key/value pairs in the other rows are 0? For example, all of the three products don't have Backlighting features, so I use if($v > 0) to exclude the whole row of Backlighting, but I want to keep any 0 element like Nokia 12hb's 4G because the other phones contain that feature.
Expanding from the comment from #Barmar
$features = array();
foreach ($array as $row) {
foreach ($row as $key => $value) {
if ($key != 'name' && $value > 0) {
$features[] = $key;
}
}
}
foreach ($array as $row) {
$table .= "<tr><td>{$row['name']}</td>";
foreach ($row as $key => $value) {
if ($key != 'name' && in_array($key, $features)) {
$table .= "<td>{$key}</td><td>{$value}</td>";
}
}
$table .= "</tr>
}
First make a list of all the features that have non-zero in some element:
$features = array('name' => true);
foreach ($array as $item) {
foreach ($item as $feature => $val) {
if (!isset($features[$feature]) && $val > 0) {
$features[$feature] = true;
}
}
}
Then use this in your output loop:
foreach($array as $key=>$val)
{
$table .= "<tr>";
foreach($val as $k=>$v)
{
if($k == "name")
{
$table .= "<td>$v</td>";
}
elseif(isset($features[$k]))
{
$table .= "<td>$k</td><td>$v</td>";
}
}
$table .= "</tr>";
}
The answer appears simple. The value for $v is never greater than zero in the second Nokia array! So..your else if($v > 0){.. never fires!
The solution here would be to check for $v==0 || $v < 0, then print the desired output as well. Right now you aren't doing that so your output ignores when $v is zero or negative.
I'm upgrading all my mysql_* to PDO. I can handle simple SELECTs and INSERTs. But I have one complex SELECT using nested loops that I am making a dog's dinner of. I'll post the mysql_* code, which outputs a two-column table with total registrations at A1 level in the first column and total registrations at A1 level who have paid in brackets in the second column.
-------------------------
Smithsville : A1 | |
10 | (7) |
-------------------------
Grange : A1 | |
4 | (4) |
-------------------------
Beau Ridge : A1 | |
23 | (16)|
-------------------------
Jonesboro : A1 | |
9 | (9) |
-------------------------
Lexing : A1 | |
3 | (1) |
-------------------------
In the full application further tables are genertated across the page for the other levels.
$levels = array('A1', 'A2', 'B1', 'B2');
$centres = array('Smithsville', 'Grange', 'Plateau Ridge', 'Jonesboro', 'Lexing');
for ($j = 0; $j < 1; $j++) /* Does not loop; selects only A1 in queries below*/
{
for ($k=0; $k<5; $k++) /* Cycles through the centres */
{
$show_count = mysql_query("SELECT COUNT(Centre)
FROM exam
WHERE exam.Centre='".$centres[$k]."'
AND exam.Level='".$levels[$j]."'");
$show_paid = mysql_query("SELECT COUNT(Centre)
FROM exam
JOIN personal
ON exam.P_ID=personal.P_ID
WHERE personal.Paid='1'
AND exam.Centre='".$centres[$k]."'
AND exam.Level='".$levels[$j]."'");
$result_rows = mysql_num_rows($show_count);
$result_rows2 = mysql_num_rows($show_paid);
if ($result_rows == 0)
{ echo 'No registrations';}
if ($result_rows2 == 0)
{ echo 'No payments';}
echo '<table class="Font3White">
<tr class="Title">
<td class="width8">'.$centres[$k].' : '.$levels[$j].'</td>
<tr>';
while (($row = mysql_fetch_row($show_count)) && ($row2 = mysql_fetch_row($show_paid)))
{
foreach ($row as $key => $value)
{
echo '<td>';
echo $value;
echo '</td>';
}
foreach ($row2 as $key2 => $value2)
{
echo '<td> (';
echo $value2;
echo ')</td>';
}
echo '</tr>';
}
echo '</table>';
}
}
My PDO attempt is
for ($j = 0; $j < 1; $j++) /* Does not loop; selects only A1 */
{
for ($k=0; $k<5; $k++) /* Cycles through the centres */
{
/*//////////////////////////*/
/* Prepares statements */
/*//////////////////////////*/
$stmt1 = $db->query("SELECT COUNT(Centre)
FROM exam
WHERE exam.Centre=:centre
AND exam.Level=:level");
$stmt2 = $db->query("SELECT COUNT(Centre)
FROM exam
JOIN personal
ON exam.P_ID=personal.P_ID
WHERE personal.Paid='1'
AND exam.Centre=:centre
AND exam.Level=:level");
/*/////////////////////*/
/* Binds parameters */
/*/////////////////////*/
$stmt1->bindParam(':centre', $centre, PDO::PARAM_STR);
$stmt1->bindParam(':level', $level, PDO::PARAM_STR);
$stmt2->bindParam(':centre', $centre, PDO::PARAM_STR);
$stmt2->bindParam(':level', $level, PDO::PARAM_STR);
/*//////////////////////////*/
/* Executes statements */
/*//////////////////////////*/
$stmt1->execute(array($centre, $level));
$stmt2->execute(array($centre, $level));
echo '<table class="Font3White">
<tr class="Title">
<td class="width8">'.$centres[$k].' > '.$levels[$j].'</td>
<tr>';
while (($row = $stmt1->fetch(PDO::FETCH_ASSOC)) && ($row2 = $stmt2->fetch(PDO::FETCH_ASSOC)))
{
foreach ($row as $key => $value)
{
echo '<td>';
echo $value;
echo '</td>';
}
foreach ($row2 as $key2 => $value2)
{
echo '<td> (';
echo $value2;
echo ')</td>';
}
echo '</tr>';
}
echo '</table>';
}
}
I know this is terrible but I hope it can be worked with.
I know it's wrong to make you the dirty work, but that's it.
I made some changes to your code, so compare mine with yours.
Hope it helps you.
$levels = array('A1', 'A2', 'B1', 'B2');
$centres = array('Smithsville', 'Grange', 'Plateau Ridge', 'Jonesboro', 'Lexing');
foreach ($levels as $level) {
foreach ($centres as $centre) {
// As you said, PREPARE
$stmt1 = $db->prepare("SELECT COUNT(Centre)
FROM exam
WHERE exam.Centre=:centre
AND exam.Level=:level");
$stmt2 = $db->prepare("SELECT COUNT(Centre)
FROM exam
JOIN personal
ON exam.P_ID=personal.P_ID
WHERE personal.Paid='1'
AND exam.Centre=:centre
AND exam.Level=:level");
$stmt1->bindParam(':centre', $centre, PDO::PARAM_STR);
$stmt1->bindParam(':level', $level, PDO::PARAM_STR);
$stmt2->bindParam(':centre', $centre, PDO::PARAM_STR);
$stmt2->bindParam(':level', $level, PDO::PARAM_STR);
//You don't need to pass parameters here just on PDOStatement::bindParam
$stmt1->execute();
$stmt2->execute();
echo '<table class="Font3White">
<tr class="Title">
<td class="width8">' . $centre . ' > ' . $level . '</td>
<tr>';
// Fetch stuffs
$row = $stmt1->fetch(PDO::FETCH_ASSOC);
$row2 = $stmt2->fetch(PDO::FETCH_ASSOC);
// Don't use while with PDOStatement::fetch, use fetchAll then foreach it.
if ($row && $row2) {
foreach ($row as $key /* <= Do you need it? */ => $value) {
echo '<td>';
echo $value;
echo '</td>';
}
foreach ($row2 as $key2 /* <= Do you need it? */ => $value2) {
echo '<td> (';
echo $value2;
echo ')</td>';
}
echo '</tr>';
}
echo '</table>';
}
}
Can you show us how you set $centre and $level variables? Maybe they are arrays, not strings? If they are - you may have to create new variable and assign value from array to it. Like this:
$value_to_bind = $centres[$k];
Execute does not need parameters if you used bindParam() before.
I never tried to run 2 queries with same parameter names and same variables binded to them. Maybe this is a problem?