0

I'm using data pulled from an SQL query to build two charts later in my code. And example query would be something like:

SELECT purchase_location, purchase_item, SUM(purchase_amount) as totalPurchase
FROM purchases
GROUP BY purchase_item_id, purchase_location

Not an exact example, but the idea is there. I then iterate through my results to build the two data sets.

$locationData = [];
$itemData     = [];
foreach($queryResults as $result) {
   $locationData[$result['purchase_location']] += $result['totalPurchase'];
   $itemData[$result['purchase_item']]         += $result['totalPurchase'];
}

Since I want the data from two different points of view, I have to use += to get the correct totals. My question is this: doing the += operator on an unset index of an array is incredibly slow. I've found that if I do the following:

if (isset($locationData['purchase_location'])) {
  $locationData['purchase_location'] += $result['totalPurchase'];
} else {
  $locationData['purchase_location'] = $result['totalPurchase'];
}

Using an = for the first time the index is seen. This speeds up the code significantly (As an example, my code went from 8-10 second run time down to less than half a second). My question is, is that the correct/cleanest way to handle this?

And before anyone mentions, I know I could write the query to handle all of this in this simple case, this was just a really easy example to show the issue, that of using += on an, as of yet, undefined array index.

5
  • Yes, you should check that the index is set before adding to it, as you are doing in your last snippet, unless you are 100% certain they are already defined (perhaps defined to 0?). Commented Mar 28, 2019 at 13:41
  • 2
    This speeds up the code significantly (As an example, my code went from 8-10 second run time down to less than half a second) - I don't believe this. The performance test you made is probably invalid. Commented Mar 28, 2019 at 13:46
  • See Why should I provide an MCVE for what seems to me to be a very simple SQL query? for providing example data and expected results .. Also questions about SQL/query performance should include tables structure SHOW CREATE TABLE table for every table involved in the question and a EXPLAIN query output Commented Mar 28, 2019 at 13:51
  • "The performance test you made is probably invalid. " to add to @Xatenev 's comment i think you measure caching timing in the second run.. Commented Mar 28, 2019 at 14:04
  • Using a microtime(true) comparison before and after my code, with isset runtime is 0.001549 seconds. Without it clocks in at 13-14 seconds when I'm getting all of the data. Commented Mar 28, 2019 at 14:12

4 Answers 4

1

I would suggest initializing the array with that index already, avoiding the need for isset checks and making the code cleaner:

$locationData = ['purchase_location' => 0];
$itemData     = ['purchase_item' => 0];

foreach($queryResults as $result) {
   $locationData['purchase_location'] += $result['totalPurchase'];
   $itemData['purchase_item']          = $result['totalPurchase'];
}

For my projects I usually try to limit the usage of isset to the validation of received data from outside sources (ex: GET, POST) that I can't fully control.

Since you've updated you're answer, now it makes sense to use isset in this case to avoid another loop to construct the array.

$locationData = [];
$itemData     = [];

foreach($queryResults as $result) {
    if (!isset($locationData[$result['purchase_location']])) {
        $locationData[$result['purchase_location']] = 0;
    }

    if (!isset($itemData[$result['purchase_item']])) {
        $itemData[$result['purchase_item']] = 0;
    }

    $locationData[$result['purchase_location']] += $result['totalPurchase'];
    $itemData[$result['purchase_item']]         += $result['totalPurchase'];
}

If you're using PHP 7+ you can use the null coalesce ?? to simplify your code like this:

$locationData = [];
$itemData     = [];

foreach($queryResults as $result) {
    $locationData[$result['purchase_location']] = $result['totalPurchase'] + ($locationData[$result['purchase_location']] ?? 0);
    $itemData[$result['purchase_item']] = $result['totalPurchase'] + ($itemData[$result['purchase_item']] ?? 0);
}
Sign up to request clarification or add additional context in comments.

2 Comments

I had some incorrect coding in my question. It has been updated. Would it be worthwhile to iterate through the results, get all of the location data into an array, initialize my locationData array with those indices before totaling everything up?
I'd never seen the null coalesce before. Unfortunately I'm pre-PHP 7 (Current project is on 5.6.4 and the higher ups are hesitant to upgrade), it would have been an easy thing to use. I write a TON of KPI reports for my company that follow very similar logic, so I run into this issue often. I like the idea of initializing the index whenever I come across it, as your fix example has, so I'm going with that for now, and pinning the Null Coalesce in the back of my mind in the possible future where we upgrade PHP versions. Thanks!
1

You could try the following:

$locationData = [];
$itemData     = [];
foreach($queryResults as $result) {
   $locationData[$result['purchase_location']] = ($locationData[$result['purchase_location']]??0) + $result['totalPurchase'];
   $itemData[$result['purchase_item']]         = ($itemData[$result['purchase_item']]??0) + $result['totalPurchase'];
}

But I think it's cleaner and more obvious just to initialise everything to zero separately first:

foreach($queryResults as $result) {
   $locationData[$result['purchase_location']] = 0;
   $itemData[$result['purchase_item']]         = 0;
}

and then do the work of addition in another loop.

Comments

0

for your question, yes you need to create this index before adding += to this index.

instead of check it every iteration, so just configure it before the foreach loop, and then this index will exits and you will be able to sum += and thats all

$locationData = [
    'purchase_location' => 0
];
$itemData     = [];
foreach($queryResults as $result) {
   $locationData['purchase_location'] += $result['totalPurchase'];
   $itemData['purchase_item']          = $result['totalPurchase'];
}

2 Comments

I had some incorrect code in my question, I have it updated. It should have been $locationData[$result['purchase_location']] += $result['totalPurchase']; So I would end up with an array with several locations, rather than just the one.
in this case so so what you have dome looks ok, since it's an a dynamic array so you must check if the index exists and to set it if not.
0

My question is, is that the correct/cleanest way to handle this?

Without example data and expected results it's hard to know that results you are after.
A educated guess would be using MySQL WITH ROLLUP GROUP BY Modifier to get a added total record.

SELECT purchase_location, purchase_item, SUM(purchase_amount) as totalPurchase
FROM purchases
GROUP BY purchase_item_id, purchase_location WITH ROLLUP 

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.