how to format sql server rows using php that look like this:
id company value monthyear
1 companyone 30 january2012
2 companytwo 20 february2012
3 companyone 10 february2012
into this:
monthyear: ['january2012', 'february2012']
and this:
company: 'companyone', value: [30, 10]
company: 'companytwo', value: [0, 20]
each instance of a month from the db is combined into one instance.
company one, which has two rows, is combined into one instance where each value is lined up in order of the month. company two, which only has one instance, has it's value defined as 0 where it has no instance in a month.
the farthest i've gotten is are two two dimensional array with array_merge_recursive and some conditional statements but then my head goes into knots.
SELECT
company,
GROUP_CONCAT(value SEPARATOR ',') AS value,
GROUP_CONCAT(monthyear SEPARATOR ',') AS monthyear
FROM
yourTable
GROUP BY
company
Some Reference for GROUP_CONCAT.
PHP solution:
Select the to be grouped attribute sorted (company). Loop over them and open a new group every time you encounter a different value for company. As long as the current row has the same row as the previous, add value and monthyear to the current company.
You could do this even without sorting:
while($row = mysql_fetch_assoc($resource))
{
$values[$row["country"]][] = $row["value"];
$monthyear[$row["country"]][] = $row["monthyear"];
}
Some output example
foreach ($values as $country => $valuesOneCountry)
{
// each country
var_dump($country);
foreach ($valuesOneCountry as $i => $value)
{
// value, monthyear for each original row
var_dump($value, $monthyear[$country][$i]);
}
}
Elegant way with OOP:
class Tuple
{
public $country, $values, $monthyears;
public function __construct($country, $values = array(), $monthyears = array())
{
$this->country = $country;
$this->values = $value;
$this->monthyears = $monthyears;
}
}
$tuples = array();
while($row = mysql_fetch_assoc($resource))
{
if (!isset($tuples[$row["country"]]))
$tuples[$row["country"]] = new Tuple($row["country"]);
// save reference for easy access
$tuple = $tuples[$row["country"]];
// or some method like $tuple->addValue($row["value"]);
$tuple->values[] = $row["value"];
$tuple->monthyears[] = $row["monthyear"];
}
var_dump($tuples);
Related
I have a table that looks like this:
ID
app_id
field_id
value
xxx
yyy
9
First Name
xxx
yyy
2
Last Name
The "value" column contains data like First Name, Last Name, Adress, E-Mail and so on. The only way to identify what kind of value is situated in the "value" column, is by the "field_id". Meanwhile the "app_id" acts as an unique identifier for the user / a User ID.
(The table is provided by a WordPress Plugin, therefore however terrible it might be I cannot change this.)
This means to get First name and last name of an user I would have to search for 2 rows with the same app_id and then get the value column where the field_id is 9 for first name and 2 for last name.
So what I wanna print out is something like: (pseudo code)
for ($app_id) {
if (app_id == $app_id && field_id = 9) {
$first_name = value
}
if (app_id == $app_id && field_id = 2) {
$last_name = value
}
echo $first_name . $last_name;
}
So far I only have the following code, which puts all the data in a multidimensional array:
$mysqli = new mysqli("localhost","dbane","dbpass","dbuser");
$mysqli->set_charset("utf8mb4");
$fields = $mysqli -> query("SELECT * FROM name_of_table");
$results = $fields->fetch_all();
foreach ($results as $result) {
foreach ($result as $key => $value) {
/* Lost what to do here */
}
}
How would I go about getting the first name and last name of each user and put them together?
The database contains about 20.000 rows so using multiple mysqli_queries is not an option as the load time is 10min.+ and puts a terrible load on the server.
I solved it doing the following, with the help of RiggsFolly:
for ($i = $count; $i >= ($count - 1000); $i--) {
$data = $mysqli -> query("SELECT * FROM table_name WHERE app_id = $i AND field_id IN (2,9,15,5,10,11,6,3)");
$names = $data->fetch_all();
foreach ($names as list($a, $b, $c, $d)) {
switch ($c)
case 9:
$first_name = $d;
break:
case 15:
$last_name = $d;
break;
}
}
I was given an academic assignment. Basically, I want to take an array named 'rank' through a procedure/function/service to crank out the following outputs in the following fashion:
example 1 : rank = [1,3,2,1] -> [1,4,3,2]
example 2 : rank = [1,1,2,3] -> [1,2,3,4]
example 3 : rank = [2,1,2,3] -> [2,1,3,4]
example 4 : rank = [2,1,2,3] -> [2,1,3,4]
example 5 : rank = [1,1,1,1] -> [1,2,3,4]
Each of the elements in the initial array represent someone getting a place (like 1st place, 2nd place, etc.) in a contest or say crossing a finish line. Each of the elements in the output array basically represent breaking ties. So, say 4 people got first place (in example 5), the first guy/gal gets 1st, the second person gets 2nd, and so on.
Now, I've got the following semi-working code:
<?php
class Ranker
{
private $rank, $result, $doWork;
public function rank() {
/*
rank = [1,3,2,1] -> [1,4,3,2]
rank = [1,1,2,3] -> [1,2,3,4]
rank = [2,1,2,3] -> [2,1,3,4]
rank = [2,1,2,3] -> [2,1,3,4]
rank = [1,1,1,1] -> [1,2,3,4]
*/
$rank = array(
"person1" => 1,
"person2" => 1,
"person3" => 1,
"person4" => 1
);
$result = array();
asort($rank);
/* Get funky */
foreach ($rank as $place) {
$initialplace = $place;
if (!empty($result)) {
{
while(in_array($place, $result)) {
$place++;
if (!in_array($place, $result)) {
break;
}
}
}
foreach (array_keys($rank, $initialplace) as $key) {
if (!array_key_exists($key, $result)) {
$result[$key] = $place;
break;
}
}
} else {
/* array_search returns first match */
$result[array_search($initialplace, $rank)] = $initialplace;
}
}
ksort($result);
/* Printing it out */
foreach ($result as $finalplace) {
echo $finalplace . ' ' . array_search($finalplace, $result) . '</br>';
}
}
}
/* Execute class function */
$ranker = new Ranker;
$doWork = $ranker->rank();
?>
It's semi-working because it doesn't strictly conform to the mapping scheme listed above. For example, it'll take [1,1,1,1] and spit out [4,3,2,1]. This may not really be an issue given that there's some flexibility in the use-case that I need it for. But I do have two questions:
(1) How can I clean up my code and make it way better/more efficient - (I'm not a PHP guy).
(2) How can I change my code to make it conform more precisely to the initial mappings in examples 1-5 above?
I have a MySQL query using Laravel that I convert to a PHP Array.
The rows have values similar to this:
name | override | percentage
Eclipse | 1 | 50%
Eclipse | 0 | 75%
MySQL query
select * from table
Both rows (it's many more than just 2 in reality) have the same name, but one has override set to 0 and one has it set to 1.
How can I get rid of all records in my query result (PHP array) that are duplicates (determined by the name) AND have override set to 0? I want only the records that have been overridden with a new record which I have done, but I need a way to remove the records with override = 0, given that the records are the same but have a different percentage value.
How can this be done?
Thanks.
Try following query,
SELECT * from testtable GROUP BY `name` HAVING count(`name`) = 1 OR `override` = 1;
check this sqlfiddle
If I understand your needs correctly, you need to filter out records that have duplicate name and override = 0.
If you sort your result set by name (SELECT * FROM TABLE ORDER BY name), you can use this function.
function removeDuplicatesFromArray($rows) {
$result = array();
$old_name = '';
foreach($rows as $row) {
if($row['name'] != $old_name) {
$result[] = $row;
$old_name = $row['name'];
}
elseif($row['override'] == 1) {
array_pop($result);
$result[] = $row;
}
}
return $result;
}
NOTE: Doing this in SQL will be WAYYYYYYYYY faster and use far less memory. I would only try this PHP approach if you cannot modify the SQL for some reason.
Maybe try out... hit the db twice, first time only get non-overrides, then get the overrides in second pass -- coerce your arrays to be indexed by name and array_merge them. (Uses a fair chunk of memory given the number of arrays and copies - but it's easy to understand and keeps it simple.
$initial = get_non_overridden();
$override = get_overridden();
$init_indexed = index_by_name($initial);
$over_indexed = index_by_name($override);
$desired_result = array_merge($init_indexed, $over_indexed);
Assuming your database gives you a standard rowset (array of rows, where each row is a hash of fields->values). We want something that looks like this instead:
[
'Eclipse' => [
'name' => 'Eclipse',
'override' => '0',
'percentage' => '75%'
],
'Something' => [
'name' => 'Something',
'override' => '0',
'percentage' => '20%'
],
]
So index_by_name would be:
function index_by_name($rowset) {
$result = array();
foreach ($rowset as $row) {
$result[ $row['name'] ] = $row;
}
return $result;
}
There are ways to tweak your efficiency either in memory or run time, but that's the gist of what I was thinking.
array_merge then overwrites the initial ones with the overridden ones.
NOTE: this all assumes that there is only one row where Eclipse override is 1. If you have twenty Eclipse|0 and one Eclipse|1, this will work, if you have two Eclipse|1 you'd only see one of them... and there's no way to say which one.
I have database that contains scores which are stored daily. I want to average each months scores for each user. So far I have this:
DB structure:
id | name | tscore | added
int| string | float(100 or less)| date(2014-01-01 16:34:22)
Code:
while($row = mysql_fetch_assoc($getChartData)){ // Data from MySQL
$added_date = explode(' ',$row['added']); // Date formate 2014-01-01 16:34:22
$chartData[] = array(
'id' => $row['name'],
'tscore' => $row['tscore'],
'added' => $added_date[0] // Here I take the month only
);
}
if($_POST['range'] == 'month'){
foreach($chartData as $key => $value){
$added = explode('-',$chartData[$key]['added']);
$count = 1;
foreach($chartData as $key2 => $value2){
$added2 = explode('-',$chartData[$key2]['added']);
if($chartData[$key]['id'] === $chartData[$key2]['id'] && $added[1] === $added2[1]){ // if user is the same and the month is the same, add the scores together, increment counter, and unset 2nd instance
$chartData[$key]['tscore'] = ((float)$chartData[$key]['tscore'] + (float)$chartData[$key2]['tscore']);
$count++;
unset($chartData[$key2]);
}
}
$chartData[$key]['tscore'] = ($chartData[$key]['tscore']/$count); // Average all the scores for the month.
}
}
The problem is this method is deleting all the elements of the $chartData array. What can I try to resolve this?
You should try to solve it with MySQL. Try something like this (replace 'your_scores_table' with your table name):
SELECT
Score.name,
AVG(Score.tscore) AS `avg`,
CONCAT(YEAR(Score.added), '-', MONTH(Score.added)) AS `year_month`
FROM
your_scores_table AS Score
GROUP BY
Score.name ASC,
YEAR(Score.added) DESC,
MONTH(Score.added) DESC
;
Your logic is wrong. You are looping through the same array twice. Which means that the following if will always evaluate to true which means that array item will always get unset
//This will always be true
if($chartData[$key]['id'] === $chartData[$key2]['id'] && $added[1] === $added2[1]){
It may be simpler for you to create another array where you keep your scores. Something like
$aScores = array();
$count = 1;
foreach($chartData as $key => $value){
//Add score to a different array
$aScores[$value['name']]['tscore'] = (($aScores[$value['name']]['tscore'] + $value['tscore']) / $count);
$count++;
}
Also I would look into the MySQL AVG function. You could use that to save you having to do it in PHP
I have two tables.
The chapters table has the columns id and name.
The chapters_chapter table has columns id, master_id, and slave_id.
Lets say that the chapters table has 7 records:
id name
1 test01
2 test02
3 test03
4 test04
5 test05
6 test06
7 test07
And in the chapters_chapters table I have these records:
id master_id slave_id
1 1 5
2 1 6
3 6 7
4 7 2
Given that data, how can I extract the hierarchy of that data so that it looks like this?
test01
test05
test06
test07
test02
test03
test04
So this was kind of a pain because of the fact that we had to have the hierarchy stored in the DB. Because of this, each item can have multiple children, and each child can have multiple parents.
This second part means we cannot simply loop through the list once and be done with it. We might have to insert an item in multiple places in the hierarchy. While you probably won't actually structure your data that way, the database schema you've described supports this scenario, so the code must support it too.
Here's a high-level version of the algorithm:
Query both tables
Create a map (array) of a parent (number) to its children (another array)
Create a set of items that are children (array of numbers)
Create a function that displays a single item, indenting it to the desired depth.
If that item has children, this function increases the depth by one, and calls itself recursively
Loop through all items that aren't children (root items).
Call the function for each of those items, with a desired depth of 0 (no indent).
Here's two hours work. Enjoy :)
Note that I stuck it within a <pre> block, so you might have to mess with how the indentation is done (output something other than two spaces, mess with the style of the divs, etc).
<?php
$con = mysql_connect("localhost", "test_user", "your_password");
if(!$con)
{
die("could not connect to DB: " . mysql_error());
}
mysql_select_db("your_db", $con);
// get chapters
$chapters = array();
$result = mysql_query("SELECT * FROM chapters");
while($row = mysql_fetch_array($result))
{
$id = $row["id"];
$name = $row["name"];
$chapters[$id] = $name;
}
// get chapters_chapters - We'll call it "parent/child" instead of "master/slave"
$parent_child_map = array();
$is_child = array();
$result = mysql_query("SELECT master_id, slave_id FROM chapters_chapters");
while($row = mysql_fetch_array($result))
{
$parent_id = $row["master_id"];
$child_id = $row["slave_id"];
$children = $parent_child_map[$parent_id];
if($children == null)
{
$children = array();
}
$children[] = $child_id;
$parent_child_map[$parent_id] = $children;
$is_child[$child_id] = true;
}
// display item hierarchically
$display_item_and_children = function($id, $name, $depth)
use ($chapters, $parent_child_map, &$display_item_and_children)
{
echo "<div><pre>";
// indent up to depth
for($i = 0; $i < $depth; $i++)
{
echo " ";
}
echo "id: " . $id
. " name: " . $name
. "</pre></div>";
// if there are children, display them recursively
$children = $parent_child_map[$id];
if($children != null)
{
foreach($children as $child_id)
{
$child_name = $chapters[$child_id];
$display_item_and_children($child_id, $child_name, $depth + 1);
}
}
};
// display all top-level items hierarchically
foreach($chapters as $id => $name)
{
// if it is a top-level item, display it
if($is_child[$id] != true)
{
$display_item_and_children($id, $name, 0);
}
}
mysql_close($con);
?>
And here's a screenshot:
The question becomes how complex you want your solution to be. I'd do it with the following pseudo code.
SELECT all the chapters
SELECT all the *chapters_chapters*
loop over the chapters to create an array chapter objects
loop over the `chapters_chapters* and create the relationships using the chapter objects
Essentially you're creating a link-list.