I'm wondering if there's a way - using php - to loop through a mysqli query result and display the column name, and the data... so,
Name => Joe Bloggs,
Age => 23
effectively allowing me to change the query, without having to echo out each row individually.
You can get the column names by fetching the result rows 'associatively':
$mysqli = new mysqli('localhost', 'user', 'password', 'dbname');
$result = $mysqli->query("SELECT Name, Age FROM user");
while (($row = $result->fetch_assoc()) !== null) {
$output = array();
foreach ($row as $columnName => $columnValue) {
$output[] = $columnName . ' => ' . $columnValue;
}
echo implode(', ', $output) . PHP_EOL;
}
Would output something like:
Name => Joe Bloggs, Age => 23
Name => Another User, Age => 5
if you have two users in the user table.
Not sure what you mean. Then only way I understand your question (that makes sense to me) is to suggest you use "fetch_assoc" to iterate over your result set in order to gain easy access to the column names with which you can do as you please from then on. Maybe the "using php" is tripping me over.
Related
I have an associative array as follows:
$ a =
[
"2023-05-18" => 10.0
"2023-07-14" => 2.0
"2023-11-01" => 16.0
"2023-11-11" => 2.0
"2023-12-25" => 8.0
"2024-01-01" => 2.0
"2024-04-01" => 22.0
]
And in a SQL query i want to do something like:
function ($a) {
$sql = " ... some code before ...
IF (table.date IN ($a), value of date (ex 10.0), 1)
"
;
}
Not only with if with case when, or whatever the idea is to get the value in associative array according to the date (if it exist in the array);
I think the better way to do that is to concatenate the logic with your query and use a case when to handle that. something like
$s= "*\nCASE\n";
foreach ($yourArray as $dateKey => $dateValue) {
$s .= "WHEN DATE_FORMAT(table.date, '%Y-%m-%d') = '" . $dateKey . "' THEN
" . $dateValue . " \n"
;
}
$s .= "END\n";
And in you query you can directly do something like
"query do something
". $s ."
do something again "
I hope this would help
Your description of what you want is not very clear, but from the conversation and a bit of assumption I think it probably boils down to:
"list all the values from the PHP array where the date key matches at least one row from the SQL table"
If so, then here's one way you could do it:
Run a query which returns a distinct list of dates from the table where the date matches one of the keys in the array. This effectively gives you a list of dates which appear in both the array and the database.
Loop through that list to show the values matching those array keys.
Now here's some sample code. There may be a more efficient way to do it, but this is my initial thought:
$a =
[
"2023-05-18" => 10.0,
"2023-07-14" => 2.0,
"2023-11-01" => 16.0,
"2023-11-11" => 2.0,
"2023-12-25" => 8.0,
"2024-01-01" => 2.0,
"2024-04-01" => 22.0
];
$dates = array_keys($a);
$params = array_fill(0, count($dates), "?");
//sql to get all rows from database which match dates in the array
$sql = "SELECT `date` from `table` WHERE `date` IN (".implode(",", $params).")";
echo $sql; //just for debugging
//let's assume you're using PDO and have a connection object named $pdo, for the sake of argument
$stmt = $pdo->prepare($sql);
$result = $pdo->execute($dates);
$data = $result->fetchAll(PDO::FETCH_ASSOC);
//loop the returned set of matching dates
foreach ($data as $row)
{
//output values from the original array which match the dates returned from the database
echo $a[$row["date"]].PHP_EOL;
}
Related documentation:
https://www.php.net/manual/en/function.array-keys.php
https://www.php.net/manual/en/function.implode.php
https://www.php.net/manual/en/function.array-fill.php
I am having a bit of trouble getting the proper format of json string.
I have a database table that looks something like this:
Table Columns: emp month sales
Table rows: Bob 1 100
Bob 2 150
Jane 1 125
Jane 2 130
Mary 1 110
Mary 2 130
Within drawChart(), I can create something like this statically:
var data = google.visualization.arrayToDataTable([
['Month', 'Bob', 'Jane', 'Mary],
['Jan', 100, 125, 110],
['Feb', 150, 130, 130]
]);
In the end, the json string needs to look like this:
{"cols":[{"label":"Month","type":"string"},
{"label":"Bob","type":"number"},
{"label":"Jane","type":"number"},
{"label":"Mary","type":"number"}],
"rows":[{"c":[{"v":100},{"v":125},{"v":110}]},
{"c":[{"v":150},{"v":130},{"v":130}]}]}
But I am having trouble pulling from the table to come up with proper json formatting that is equivalent to the above. I am following the steps from here... PHP MySQL Google Chart JSON - Complete Example
But that example is only for a single data set. if you were to add multiple weeks instead of having just one data set, how do run the query?
To get your data in the format you want, you have to pivot your data. Some databases support pivotting, but others like MySQL don't. If you are stuck without pivot support, then you have to resort to trickery to make it happen. Here's one way you could do it:
SELECT
month,
SUM(if(employee = "Bob", sales, 0)) AS Bob,
SUM(if(employee = "Jane", sales, 0)) AS Jane,
SUM(if(employee = "Mary", sales, 0)) AS Mary
FROM myTable
GROUP BY month
This requires that you know ahead of time what the employee names are so that you can write the SQL statement (either when you write the code, or you could pull them from another SQL query and write a dynamic SQL query).
Asgallent, thank you. Your response gave me the direction I needed. I was able to do it all dynamically via SQL. I made two queries: 1 to the "saleperson" table to get the names, and then another to pivot the data as you suggested. For anyone else that might find this helpful, here is the code I have.
The queries (Note: I am using codeigniter):
$sp_qry = $this->db->query('select * from salespeople');
$qryString="";
foreach ($sp_qry->result_array() as $row)
{
$qryString.= ",SUM( IF( `salespeople_id` =" . $row['salespeople_id'] . ", `num_sold` , 0 ) ) AS " . $row['name'];
}
$qry= "SELECT `month` " . $qryString . " FROM `product_sales`
GROUP BY `month`";
$query = $this->db->query($qry);
return $query->result_array();
and in my viewing page
$rows = array();
$table = array();
$cols = array();
$cols[] = array('label' => 'Month', 'type' => 'string');
foreach ($salespeople as $sp)
{
$cols[] = array('label' => $sp['name'], 'type' => 'number');
}
$table['cols'] = $cols;
foreach ($sales as $chart_item)
{
$tmp=array();
$tmp[] = array('v' => (string) $chart_item['month']);
foreach ($salespeople as $sp)
{
$name=$sp['name'];
$tmp[] = array('v' => (int) $chart_item[$name]);
}
$rows[] = array('c' => $tmp);
}
$table['rows'] = $rows;
$jsonTable = json_encode($table);
I was told that it is a bad practice to use a query (select) within a loop because it slows down server performance.
I have an array such as
Array ( [1] => Los Angeles )
Array ( [2] =>New York)
Array ( [3] => Chicago )
These are just 3 indexes. The array I'm using does not have a constant size, so sometimes it can contain as many as 20 indexes.
Right now, what I'm doing is (this is not all of the code, but the basic idea)
For loop
query the server and select all people's names who live in "Los Angeles"
Print the names out
Output will look like this:
Los Angeles
Michael Stern
David Bloomer
William Rod
New York
Kary Mills
Chicago
Henry Davidson
Ellie Spears
I know that's a really inefficient method because it could be a lot of queries as the table gets larger later on.
So my question is, is there a better, more efficient way to SELECT information based on the stuff inside an array that can be whatever size?
Use an IN query, which will grab all of the results in a single query:
SELECT * FROM people WHERE town IN('LA', 'London', 'Paris')
To further add to MrCodes answer, if you start with an array:-
$Cities = array(1=>'Los Angeles', 2=>'New York', 3=>'Chicago');
$query = "SELECT town, personname FROM people WHERE town IN('".implode("','", $Cities)."') ORDER BY town";
if ($sql = $mysqliconnection->prepare($query))
{
$sql->execute();
$result = $sql->get_result();
$PrevCity = '';
while ($row = $result->fetch_assoc())
{
if ($row['town'] != $PrevCity)
{
echo $row['town']."<br />";
$PrevCity = $row['town'];
}
echo $row['personname']."<br />";
}
}
As a database design issue, you probably should have the town names in a separate table and the table for the person contains the id of the town rather than the actual town name (makes validation easier, faster and with the validation less likely to miss records because someone has mistyped their home town)
As #MrCode mentions, you can use MySQL's IN() operator to fetch records for all of the desired cities in one go, but if you then sort your results primarily by city you can loop over the resultset keeping track of the last city seen and outputting the new city when it is first encountered.
Using PDO, together with MySQL's FIELD() function to ensure that the resultset is in the same order as your original array (if you don't care about that, you could simply do ORDER BY city, which would be a lot more efficient, especially with a suitable index on the city column):
$arr = ['Los Angeles', 'New York', 'Chicago'];
$placeholders = rtrim(str_repeat('?, ', count($arr)), ', ');
$dbh = new PDO("mysql:dbname=$dbname", $username, $password);
$qry = $dbh->prepare("
SELECT city, name
FROM my_table
WHERE city IN ($placeholders)
ORDER BY FIELD(city, $placeholders)
");
if ($qry->execute(array_merge($arr, $arr))) {
// output headers
echo '<ul>';
$row = $qry->fetch();
while ($row) {
$current_city = $row['city'];
// output $current_city initialisation
echo '<li>'.htmlentities($current_city).'</li><ul>';
do {
// output name $row
echo '<li>'.htmlentities($row['name']).'</li>';
} while ($row = $qry->fetch() and $row['city'] == $current_city);
// output $current_city termination
echo '</ul>';
}
// output footers
echo '</ul>';
}
That's the purpose of prepared statements. You bind a placeholder to a value, and use it like a variable, with the same query. Since the query hasn't changed, you minimize the communication with the mysql server, resulting in an efficiency boost.
Example using PDO:
$cities = array(
"Los Angeles",
"New York",
"Chicago"
);
try {
//Change database parameters here (user, pass, database name)
$db = new PDO("mysql:host=localhost;dbname=users", "user", "pass");
$db->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);
$db->setAttribute(PDO::ATTR_EMULATE_PREPARES, false);
$stmt = $db->prepare("SELECT * FROM `users` WHERE `city` = :city");
foreach ($cities as $city) {
$stmt->bindValue(":city", $city);
$stmt->execute();
$result = $stmt->fetchAll(PDO::FETCH_ASSOC);
//Output data here. You can format it however you'd like
var_dump($city, $result);
}
}
catch (PDOException $e) {
//Error handling here
}
I want to run a query that gets all the data from a database then have the data split into arrays for each column. With this I intend to dynamically populate html. I am not very experienced with php and could use some assistance with how to put my query into multiple arrays depending on what column it was in.
Example: For the column name I want an array $itemName[] and it will contain every item name in asc order. Then for the image column I want an array $itemImage[] for every image/image url in the same order.
With this I plan to run a for loop where as x increases it will go through each diff array and pull from the specified location. There are no null values in my DB so I don't need to worry about that.
Any help you can give me with the writing the query into multiple arrays based on the column name is appreciated.
$mPos = array(mPos1, mPos2, mPos3, mPos4);
for (x=0; x<4; x++){
echo "<div class="$mPos[x]"> <div class="$mPos[x] . '_1'">"$title[x]"</div><div class="$mPos1 . '_2'">"$image[x]"</div>
Still doesn't make sense for me to separate it that way, but here you go.
Since you didn't provide a database/table structure, I will assume your db table got the following columns:
itemId | itemName | itemImage | itemDescription
In PHP you loop through the result row for row and populate your arrays like
foreach ( $result AS $row ) {
$itemNames[$row->itemId] = $row->itemName;
$itemImages[$row->itemId] = $row->itemImage;
$itemDescriptions[$row->itemId] = $row->itemDescription;
}
EDIT: After question was updated and now includes the HTML output, I'd suggest something like this.
foreach ( $result AS $row ) {
$items[$row->itemId] = array(
'name' => $row->itemName,
'image' => $row->itemImage,
'description' => $row->itemDescription,
'price' => $row->itemPrice,
'link' => $row->itemLink,
);
}
$x = 0;
while ($x<4) {
$x++;
$item = array_shift($items);
echo '<div class="mPos'.$x.'">
<div class="mPos'.$x.'_1">"'.$item['name'].'"</div>
<div class="mPos'.$x.'_2">"'.$item['price'].'"</div>
<div class="mPos'.$x.'_3"><a href="'.$item['link'].'">
<img src="'.$item['image'].'" /></a>
</div>
</div>';
}
$sth = $dbh->prepare("SELECT itemName, itemImage FROM myTable");
$sth->execute();
$result = $sth->fetchAll();
$myArr = array();
foreach($result as $row){
foreach($row as $colName => $colVal){
$myArr[$colName][] => $colVal;
}
}
echo '<pre>';
print_r($myArr);
echo '</pre>';
Although I do have misgivings about how you're actually approaching this from a design perspective.
I am bringing in brochures selected by visitors, and they can select multiple brochures. After three days they are meant to get an email reminding them of the brochures they have chosen.
Here is what I have so far:
$time_query = "SELECT * FROM users WHERE time < (now() - INTERVAL 1 minute)"; //" //GROUP BY time does group them into an array... well.. it doesnt display duplicate timestamps, so assume it saves it to an array'";
$time_query_result = mysql_query($time_query, $db) or
die("Could not execute sql: $time_query");
$users = array();
while($row = mysql_fetch_array($time_query_result)) {
if (!array_key_exists($users[$row["id"]], $users)) {
$users[$row["id"]] = array('email' => $row["email"], 'brochures' => array());
$users[$row["id"]]["brochures"] = array('b' => $row["brochures"], 't' => $row["time"]);
}
}
foreach ($users as $user) {
$text = '<html><body><p>Brochure reminder</p>';
$i = 0;
foreach ($user["brochures"] as $brochure) {
$text .= 'Brochures:<br />'.$i++ . $row["b"];
}
$text .= '</body></html>';
mail($user["email"], $subject, $text, $headers);
}
I am getting numbers through the emails instead of brochure names, and I think its something to do with the array_key_exists fuinction.
Each time a user selects a brochure, it creates its own row in the DB, and the idea was to pull in the multiple brochures a user selected at a time (by the time column), as many users can select brochures over a time period.
Any help would be appreciated :)
In your 'while' loop, you're creating a new 'brochures' element in your 'users' array, when I think you're wanting to append to it.
if (!array_key_exists($row["id"], $users)) {
$users[$row["id"]] = array('email' => $row["email"], 'brochures' => array());
}
$users[$row["id"]]["brochures"][] = array('b' => $row["brochures"], 't' => $row["time"]);
then in your 'foreach', you will want to use the brochure variable:
foreach ($user["brochures"] as $brochure) {
$text .= 'Brochures:<br />'.$i++ . $brochure["b"];
}
Your current code builds an array of users containing another array with the index 'brochures'. This array will always contain tow values.
{
'b' => $row["brochures"]
't' => $row["time"])
}
Regarding this fact the following statements doesn't make sense:
foreach ($user["brochures"] as $brochure) {
}
All you do is iterate over the two values with the index 'b' and 't'. If you want to iterate over a collection of brochures you need to adapt your code.
On the other hand you have a few important mistakes:
foreach ($user["brochures"] as $brochure) {
$text .= 'Brochures:<br />'.$i++ . $row["b"];
}
Why use a foreach if you don't even use the $brochure variable?
$text .= 'Brochures:<br />'.$i++ . $row["b"];
$row contains the last row, which is definitively not what you wanted. In fact, $row is out of scope, in a serious programming language you would see this.
$row["id"]
You use this about three times. So why not store it in a variable $id? Accessing arrays with indexes is a more expensive operation than simply accessing a variable.
In general I strongly encourage you to switch to an object oriented approach so you get rid of these ugly array in array in array solution...