Merge SQL database values when items have same value in specified column - php

I know how to solve this in a non-elegant, non-practical way, meaning sending requests, one by one, for each value in specified column that is shared and then handle all data from that specific output, then, do the same for another value, etc., etc. But I was wondering if someone out there thought of an ELEGANT and PRACTICAL way, which would mean all this is handled by one single PHP function.
Basically, I have this table:
location | title | description | quantity
============|===========|=============|==========
shelf | apple | red | 2
drawer | banana | yellow | 4
shelf | kiwi | green | 2
cupboard | lemon | yellow | 1
fridge | melon | orange | 3
drawer | peach | orange | 1
And what I eventually want is to create a jQuery pie chart that tells me what percentage of title is in each location. But before that I need a function that outputs shelf => 4 (2+2), drawer => 5 (4+1), etc., etc.
So, the question is, is there an elegant, practical way to make this happen? Meaning, to retrieve all rows but group together all data by location and, then, sum each location's quantity to be later on turned into a percentage for the pie chart?
Thanks!

select location, sum (quantity) as sum_quantity from table group by location;
and later using $rows as result from query above,
function prepareForChart ($rows) {
$ret = array();
$total = 0;
foreach ($rows as $el) $total += $el["sum_quantity"];
foreach ($rows as $el) {
$ret[] = array(
$el["location"] => $el["sum_quantity"],
"percentage" => 100 * $el["sum_quantity"]/$total,
);
}
return $ret;
}

This should work
SELECT location, SUM(quantity) as total_quantity
FROM table
GROUP BY location

Related

How to update a single column of a dynamic number of rows using php and sql?

EDIT - Disclaimer: Noob at PHP and SQL
I need to subtract a different value from each amount row based on the vegetable that is selected.
For example: If the user selects tomatoes and capsicum, I would need to subtract 200 and 400 from each of those amounts respectively.
Is there some way to attach these values to the vegetable?
I have an array that looks like this:
**Array ( [0] => tomatoes [1] => onions [2] => spinach **
ingredientsTable
+----+-------------+---------+--------+-------+
| id | name | type | amount | unit |
+----+-------------+---------+--------+-------+
| 1 | tomatoes | veggies | 1000 | grams |
| 2 | onions | veggies | 1000 | grams |
| 3 | spinach | veggies | 1000 | grams |
| 4 | capsicum | veggies | 1000 | grams |
| 5 | basil | veggies | 1000 | grams |
+----+-------------+---------+--------+-------+
I have an array of vegetables in PHP. I am thinking of iterating over that array and updating the column based on that vegetable. So possibly:
foreach($vegetable as $subject)
($query = $conn->prepare("UPDATE ingredients set amount = amount - ? where `name` = '$item';"))
$query->bind_param("i", $amount);
$query->execute();
$insertresult = $query->get_result();
`
The issue is, I don't know how to change the $amount and I know this code does not subtract values at all.
At the moment, I've hardcoded $amount as 200, but I don't want to just update the amount for each row to 200. I need it to somehow subtract 200 from whatever the current value is - in this case it is 1000, so 1000 - 200, and update the value to 800. And for capsicum, I would need to subtract 400.
I've looked at Update multiple rows in 1 column in MySQL but that does appears not to be dynamic. It assumes we know in advance which rows to change and what the final value will be.
EDIT: I just checked Mysql query: decrease value by 1 which I think solves the problem of subtracting values, but it doesn't explain how to link those values to the vegetable in PHP. Example: How do I get and link the 200 amount to tomatoes, and 400 to capsicum?
EDIT 2: Updated code to subtract value and corrected syntax, clarified question.
The answer was to modify the way I was getting the data. So from this:
<input type="checkbox" name="topping[]" id="toppingCheckbox" value="tomatoes">
I needed to instead change the name and value attributes:
<input type="checkbox" name="topping[tomatoes]" id="toppingCheckbox" value="200">
The subtraction was a simple sql query, answered here. Mysql query: decrease value by 1

Join arrays into one associative array

I am saving many waypointIDs, bearings, and distances in three columns in a single row of my database. Each column stores it's respective item separated by a comma.
WaypointIds: 510,511,512
Bearings: 65,50,32
Distances: 74,19,14
I think I might have coded myself into a corner! I now need to pull them from the database and run through adding them to a table to output on screen.
I'm using the following to put the corresponding columns back into an array but am a little stuck with where to go after that, or if that is even the way to go about it.
$waypointArrays = explode (",", $waypointsString);
$bearingArrays = explode (",", $bearingsString);
$waypointsString & $bearingsStrings are the variables set by my database call.
I think I need to add them all together so I can iterate through each one in turn.
For example, waypointId 510, will have bearing 065, and distance 0.74.
I think I need an associative array but not sure how to add them all together so I can run through each waypoint ID and pull out the rest of the entry.
e.g. for each waypointId give the corresponding bearing and waypoint.
I have checks in place to ensure that as we add waypoints/bearings/distances that they don't fall out of step with each other so there will always be the same number of entries in each column.
Don't continue with this design: your database is not normalised and therefore you are not getting any benefit from the power that a database can offer.
I don't think working around this problem by extracting the information in PHP using explode, ..etc is the right way, so I will clarify how your database should be normalised:
Currently you have a table like this (possibly with many more columns):
Main table: route
+----+---------+-------------+----------+-----------+
| id | Name | WaypointIds | Bearings | Distances |
+----+---------+-------------+----------+-----------+
| 1 | myroute | 510,511,512 | 65,50,32 | 74,19,14 |
| .. | .... | .... | .... | .... |
+----+---------+-------------+----------+-----------+
The comma-separated lists violate the first normal norm:
A relation is in first normal form if and only if the domain of each attribute contains only atomic (indivisible) values, and the value of each attribute contains only a single value from that domain.
You should resolve this by creating a separate table for each of these three columns, which will have one record for each atomic value
Main table: route
+----+---------+
| id | Name |
+----+---------+
| 1 | myroute |
| .. | .... |
+----+---------+
new table route_waypoint
+----------+-------------+------------+----------+
| route_id | waypoint_id | bearing_id | distance |
+----------+-------------+------------+----------+
| 1 | 510 | 65 | 74 |
| 1 | 511 | 50 | 19 |
| 1 | 512 | 32 | 14 |
| 2 | ... | .. | .. |
| .. | ... | .. | .. |
+----------+-------------+------------+----------+
The first column is a foreign key referencing the id of the main table.
To select the data you need, you could have an SQL like this:
select route.*, rw.waypoint_id, rw.bearing_id, rw.distance
from route
inner join route_waypoints rw on rw.route_id = route.id
order by route.id, rw.waypoint_id
Now PHP will receive the triplets (waypoint, bearing, distance) that belong together in the same record. You might need a nested loop while the route.id remains the same, but this is how it is done.
To answer your question, code below will work as long as waypointsIds are unique. That beign said, as other mentioned, fix your database layout. What you have here could really benefit from a separate table.
<?php
$waypointIds = [510, 511, 512];
$bearings = [65, 50, 32];
$distances = [74, 19, 14];
$output = [];
for ($i = 0; $i < count($waypointIds); $i++) {
$output[$waypointIds[$i]] = [
'bearing' => $bearings[$i],
'distance' => $distances[$i]
];
}
print_r($output);
$waypoints = array(510, 511, 512);
$bearings = array(65, 50, 32);
$distances = array(74, 19, 14);
for ($i = 0; $i < count($waypoints); $i++) {
$res[] = array(
'waypoint' => $waypoints[$i],
'bearing' => sprintf("%03d", $bearings[$i]),
'distance' => $distances[$i]/100
);
}
print_r($res);

Creating multivalue search, struggling with SQL

Based on user input, which could be a single or multiple values, and using the following table
+------+--------+
| seed | item |
+------+--------+
| 1 | apple |
| 1 | grapes |
| 2 | apple |
| 3 | grapes |
| 3 | banana |
+------+--------+
I want to return
1 when the user entered (apple, grape),
[1, 2] for (apple), and
nothing for (apple, banana).
My current PHP code
$keyword = Input::get('keyword');
$searchTerms = explode(",", $keyword);
$query = DB::table('items');
foreach($searchTerms as $term)
{
$query->where('item', 'LIKE', '%'.$term.'%');
}
$results = $query->distinct()->get(array('seed'));
works for single values. Iterating in the loop, I'm just appending more search terms to the current $query. At the end, I need to find the intersection of all the queries. This is currently my main concern.
With the input available try
SELECT seed
FROM Items
WHERE item IN ('apple', 'grapes')
GROUP BY seed
HAVING COUNT(item) >= 2
;
SELECT seed
FROM Items
WHERE item IN ('apple')
GROUP BY seed
HAVING COUNT(item) >= 1
;
SELECT seed
FROM Items
WHERE item IN ('apple', 'banana')
GROUP BY seed
HAVING COUNT(item) >= 2
;
The total to compare against in the HAVING clause is the count of items, you are checking in the respective batch.
SQL Fiddle
Please comment, if further detail or adjustment is required.

Searching multiple values in sql table, with a range

I have a table that contains some ingredients. Example:
id | title | ingredients
1 | ex1 | ketchup, chicken, salt, honey, mountain dew
2 | ex2 | beef, pepper, chili, honey, salt
And when the user searchs for the ingredients like:
ketchup, salt, honey
I generate a sql-query:
select * from recipes where(
(ingredients LIKE '%ketchup%')
AND (ingredients LIKE '%salt%')
AND (ingredients LIKE '%honey%')
And it returns all recipes containing these specific ingredients, and it works grey.
Now. I've added a range-slider, to pick how many of the entered ingredients that should match for the search to return anything. Lets say i chose that 2 ingredients should match at least, i want to make a PHP function that outputs a sql string that pairs everyone of the entered ingredients, but i simply don't have the mindset for it.
Example:
(ingredients LIKE '%ketchup%') AND (ingredients LIKE '%salt%')
OR
(ingredients LIKE '%ketchup%') AND (ingredients LIKE '%honey%')
OR
So on. So ketchup & salt pair, ketchup & honey pair, salt & honey pair.
And of course variable so theres no limit to the ingredients inputted. I've tried for hours but no success. Hope i've explained my self clearly & someone will be able to help or teach me something :-)
My php function that does the current string looks like this:
$c_soeg = "ketchup, whatever, whatever. etc";
$c_ing_arr = explode(",", $c_soeg);
$c_count_arr = count($c_ing_arr);
$i = 0;
$c_sql = "SELECT * FROM recipes WHERE (";
while($i < $c_count_arr){
$c_sql .= "(ingredients LIKE '%".trim($c_ing_arr[$i])."%')";
if($i != $c_count_arr-1){
$c_sql .= " AND ";
}
$i++;
}
$c_sql .= ")";
And the variable that contains the value of the range is named
$c_range;
Instead of AND and OR conditions count the met criteria. This example gives you all records where at least two ingredients match:
select * from recipes
where
case when ingredients like '%ketchup%' then 1 else 0 end
+
case when ingredients like '%salt%' then 1 else 0 end
+
case when ingredients like '%honey%' then 1 else 0 end
> = 2;
I think you should make 3 tables meaning
one for the title and another for the ingredients and one to connect them
recipy
id | title |
1 | ex1 |
3 | ex2 |
recipyingredients
recipyid | ingredientsid
1 | 1
1 | 2
1 | 3
1 | 4
1 | 5
2 | 1
2 | 6
2 | 7
ingredients
id | ingredients
1 | ketchup
2 | chicken
3 | salt
4 | honey
5 | mountain dew
6 | beef
7 | pepper
In that case one recipy can have many ingredients and viceversa
The database would be clearer and you would not have to use like % as much.
Also you do not have to write the same ingredients every time for a recipy

Match multiple columns from a form

I'm doing a price table, and I was wondering if there is a way to match multiple columns with different values base on the values from a form... please allow me.
Table: Price
id | name | color | class | year | price
________________________________________
1 | BMW | Red | XML | 2001 | 1200
2 | BMW | Red | XML | 2003 | 1201
3 | BMW | Blue | LXX | 2004 | 1230
4 | VW | Red | LXS | 2001 | 1100
5 | VW | Blue | LXV | 2003 | 1103
Basically my table looks like that, the idea is to get the price value by matching four columns...
if name is bmw and color is red and class is xml and year is 2001 then the price is 1200
How do I make that query?... I don't have any code for this type of query just yet... but I was thinking to do a few queries base on the first value....
SELECT * FROM price
WHERE name = '$user_query' AND (color = 'Red' OR class = 'XML' )
if [...] ... that just wont work...
I don't know how to do that query... I can go one by one matching the inputs from the form but I will have to make many queries... four queries actually... I'd love to simplified my query to get that value from column price base on the request from the user...
The other thing is to use switch case : value from the first query ... or use if, elseif .... r just IF ... that is because I know the values that the user will chose from the form and the form has only 4 selects ... one for each "column" name, color, class and year
You may think that I want you to do this for me... well yes and no... yes because I want to learn how to do this type of queries and NO because I have this combinations using IF's without a data base... I'd like to use a data base...
if ($name == 'bmw' && $color == 'red' && [...]) {
$price = 1200;
} [...] and so on...
about 23 elseif {} ...
Thank you for taking the time and share your thoughts on this idea...
** E D I T **
So far I have two good answers and I'm using both, and for that I have a bit of trouble choosing the right answer...
Mahmoud Gamal and Rajesh Paul
So I'm going to leave it like that for a few more days whichever gets more votes if any, thats the one that I'm going to choose...
Thank you.
if it's about reducing the query length then I suggest this-
SELECT * FROM price
WHERE (name, color, class) = ('$user_query', 'Red', 'XML' );
Just grouping the set of attributes instead of cascading the conditions using AND, OR definitely reduces the length of the query.
You must change the syntaxis
WHERE name = '$user_query' AND (color = 'Red' OR class = 'XML' )
WHERE name = '$user_query' AND color = 'Red' AND class = 'XML'
What you need here is a optional parameters in the WHERE clause:
SELECT *
FROM price
WHERE ($ColorPatam IS NULL OR Color = $ColorParam)
AND ($ClassParam IS NULL OR Class = $ClassParam)
...
If one of the parameters has a NULL values, then the expression is ignored.

Categories