This is a mysql query result. Now I have to calculate the available quantity in php based on per challan Id. The expected result will be:
ChallanId | Avaiable Qty | unitCostPrice | totalPrice
11 | 7 | 2 | 14
12 | 10 | 1 | 10
How it can be done in php? useing foreach or any other tricks.
Though you want to achieve this for PHP solution, but here I think SQL query also can do that:
select
ChallanId,
sum(case when Narration = 'in' then ItemIn when Narration = 'out' then 0 - ItemOut end) as Avaiable_Qty,
unitCostPrice,
sum(case when Narration = 'in' then ItemIn when Narration = 'out' then 0 - ItemOut end) * unitCostPrice as totalPrice
from (your query here...)
having Avaiable_Qty > 0
group by ChallanId
I would do the calculation in SQL by having a smarter query.
select
ChallanId,
UnitCostPrice,
sum(`ItemIn`)-sum(`ItemOut`) as AvailableQty,
(sum(`ItemIn`)-sum(`ItemOut`))*UnitCostPrice as totalPrice
from tbl
group by ChallanId
Live demo
This is psuedo-code kind of, since you didn't share your implementation with PHP.
Firstly, you can assume the Narration from whether ItemIn or ItemOut is not zero, in fact it is probably better to do that since you could then introduce items in as well as items out with the same line. But that's not the question.
$output = array();
foreach ($dbResults as $row) {
// Create an output entry
if (!array_key_exists($row['ChallanID'], $output)) {
$output[$row['ChallanID']] = array('Qty' => 0, 'CostPrice' => 0, 'Total' => 0);
}
// Add to totals
$output[$row['ChallanID']]['Qty'] += ($row['ItemIn'] - $row['ItemOut']);
$output[$row['ChallanID']]['CostPrice'] = $row['UnitCostPrice']; // you may want to average this instead?
$output[$row['ChallanID']]['Total'] += $output[$row['ChallanID']]['Qty'] * $output[$row['ChallanID']]['CostPrice'];
}
$output should now contain your desired output. Untested.
Related
QUERY:
SELECT month(date_created), count(a.ticket_num)
FROM ticket as a
LEFT JOIN user_management as b on b.engineer_id = a.ticket_engineer
WHERE b.tl_id = 'sample_id'
AND year(date_created) = '2019'
GROUP BY extract(year from date_created), extract(month from date_created)
SAMPLE OUTPUT:
month | ticket_num
----------------------
2 | 12
4 | 24
6 | 78
EXPECTED SAMPLE OUTPUT:
month | ticket_num
----------------------
1 | 0
2 | 12
3 | 0
4 | 24
5 | 0
6 | 78
As you can see the above expected output, i'm trying to place all existing month in the first column and set all the count to zero if not existed in the second column. As of now, i only have the query for sorting the ticket count by month that is existed when the ticket is created.
There are different approaches to this problem. One is pure SQL for example.
But I would say a PHP based solution is simpler. Basically you need to get your data into array, then create a loop that outputs the desired months order, and have a condition that sees whether we have a corresponding row in our array and outputs ether the actual data or a zero accordingly.
The only tricky part is to have such an array that would let us check the data availability. For this we have to index it with month numbers. Not a big deal actually
$sql = "SELECT month(date_created), count(a.ticket_num) ...";
$res = $mysqli($sql);
$data = [];
while($row = mysqli_fetch_row($res)) {
$data[$row[0]] = $row[1];
}
Now $data is an array indexed by the month number. The rest is a primitive loop
foreach (range(1,12) as $month) {
echo $data[$month] ?: 0;
}
On a side note I would like to advertise using PDO as opposed to mysqli for your database interactions as this case clearly displays the superiority of the former. Using PDO we can get the indexed array right away, without an explicit loop, thanks to a special fetch mode:
$sql = "SELECT month(date_created), count(a.ticket_num) ...";
$data = $data = $pdo->query($sql)->fetchAll(PDO::FETCH_KEY_PAIR);
That's all!
$query = "SELECT COUNT(id) FROM complaint WHERE ID_complntCategory = ?";
$complntCategory = $database->prepare($query);
try {
$complntCategory->execute(array());
$complntCategory->setFetchMode(PDO::FETCH_ASSOC);
foreach ($complntCategory as $key) {
$totaalM = $key['1'];
$totaalV = $key['2'];
$totaalG = $key['3'];
}
}
catch(PDOException $e) {
echo "Error";
}
Above you see my PHP code, and here is what I'm trying to do:
I'm trying to get the amount of rows from the table 'complaint' into 3 different variables (totaalM, totaalV and totaalG). The totaalM variable should contain the amount of rows 'WHERE ID_complntCategory = 1'.
For the other variables the 'ID_complntCategory' should be 2 and 3
('ID_complntCategory' is either 1, 2 or 3)
There should be a way where I don't have to write 3 queries, right?
I'm clearly approaching this the wrong way, and I'm not sure how I should tackle this problem...
What you are trying to do is called pivot rows into columns, but MySQL doesn't have pivot table operator like other RDBMS, but you cane use the case expression like this in one query:
SELECT
SUM(CASE WHEN ID_complntCategory = 1 THEN 1 ELSE 0 END) AS totaalM,
SUM(CASE WHEN ID_complntCategory = 2 THEN 1 ELSE 0 END) AS totaalV,
SUM(CASE WHEN ID_complntCategory = 3 THEN 1 ELSE 0 END) AS totaalG,
COUNT(Id) AS Total
FROM complaint;
Or you can make it shorter like this:
SELECT
SUM(ID_complntCategory = 1) AS totaalM,
SUM(ID_complntCategory = 2) AS totaalV,
SUM(ID_complntCategory = 3) AS totaalG,
COUNT(Id) AS Total
FROM complaint;
Demo
This will give you something like this:
| totaalM | totaalV | totaalG | Total |
|---------|---------|---------|-------|
| 2 | 3 | 1 | 7 |
Here you need some magic, involving special SQL and PDO features.
First, you need an SQL query that is giving you desired results in one query. To get that you need a GROUP BY operator:
SELECT ID_complntCategory, count(*) FROM complaint GROUP BY ID_complntCategory
it will give you counts split by ID_complntCategory.
Next, you can use one of PDO's magnificent features, PDO::FETCH_KEY_PAIR fetch mode, that will give you an array where key would be category id and value is count
$sql = "SELECT ID_complntCategory, count(*) FROM complaint GROUP BY ID_complntCategory";
$stmt = $database->prepare($sql);
$key = $stmt->fetchAll(PDO::FETCH_KEY_PAIR);
$totaalM = $key['1'];
$totaalV = $key['2'];
$totaalG = $key['3'];
note that you should never catch a PDO errors only to say "error". Let PHP error reporting to do it instead.
I am attempting to create an end-user page where I present different servers that are available to check-out. A server at any given time can have either an "Available" status or a "Reserved" status. I'm using a MySQL backend. This is how I am doing my query:
SELECT *, COUNT(CASE WHEN Status = 'Available' THEN 1 ELSE NULL END) AS Amount
FROM products GROUP BY id
This is the result I get:
id,Server_Type,Status,Amount
1,BL460,Available,1
2,BL460,Available,1
3,BL460,Reserved,0
4,BL460,Reserved,0
5,BL460,Reserved,0
6,DL360,Available,1
7,DL360,Reserved,0
8,DL360,Reserved,0
Where Reserved is equal to 0, and Available is equal to 1. I only want the end-user to be able to checkout a server in Available status.
To the question: What I want to do in the page is present the list of servers on the page in this way, where Available is equal to the amount:
BL460 - Amount: 2
DL360 - Amount: 1
How can I achieve this format in PHP?
Another option is a crosstab query -
SELECT `Status`,
SUM(IF(`Server_Type` = 'BL460' AND `Status` = 'Available', `Amount`, 0)) AS `BL460`,
SUM(IF(`Server_Type` = 'DL360' AND `Status` = 'Available', `Amount`, 0)) AS `DL360`
FROM `products`
GROUP BY `Status`
Your table would look like this -
Status | BL460 | DL360 |
Available | 2 | 1 |
Reserved | 0 | 0 |
Here is an EXAMPLE
Even better would be to flip things around -
SELECT `server_type`,
SUM(IF(`status` = 'Available', 1, 0)) AS `Available`,
SUM(IF(`status` = 'Reserved', 1, 0)) AS `Reserved`
FROM `servers`
GROUP BY `server_type`;
Which would result in a table that looks like this (based on data in the fiddle) -
server_type | Available | Reserved
BL460 | 3 | 1
DL360 | 1 | 2
Here is that EXAMPLE
Here I could continue to add servers to the table without having to worry about adding them to the query as you would have to do in the first query. If you add an additional status you would have to change the query.
Note in both cases there is no need for an Amount column as the status is the item counted. By placing the load on the database server it makes it much easier to output the HTML as you are just going row bu row as you normally would.
Well, that would probably be easier if you do it directly in your SQL query:
SELECT Server_Type, COUNT(*) AS Count FROM products WHERE Status = 'Available' GROUP BY Server_Type
This should give you exactly the table you want.
If you want to do it in PHP, the easiest solution would probably be to loop through your SQL result and count the number of available servers per Server_Type in an associative array where the Server_Type is your array key:
$amounts = array();
foreach($sql_result as $entry) {
if($entry['Amount'] == 1) {
if(isset($amounts[$entry['Server_Type']])) {
$amounts[$entry['Server_Type']]++;
} else {
$amounts[$entry['Server_Type']] = 1;
}
}
}
echo $amounts;
Edit: in order to print the values as described in the question, you could use the following code snippet:
foreach($amounts as $name=>$amount) {
echo $name + " - Amount: " + $amount + "<br>";
}
Hi I have 2 tables structured as follows
cdr
src | bill sec | clean_dst
------------------------------
100 | 10 | 18006927753
100 | 22 | 18006927753
100 | 9 | 441138973356
dialing_codes
id | dial_code | tele2id
-----------------------------
1 | 1 | 1422
2 | 1800 | 1433
3 | 441 | 1024
4 | 4413 | 1086
I need to get the tele2id for the closest match in dial_code against clean_dst my best effort so far is
$query = "SELECT tele2id, dial_code FROM dialing_codes ORDER by dial_code DESC";
$result = $mysqli->query($query) or die($mysqli->error.__LINE__);
while($row = $result->fetch_assoc()) {
$tele2id = $row['tele2id'];
$dialcode = $row['dial_code'];
$query2 = "SELECT clean_dst FROM cdr WHERE clean_dst LIKE '".$dialcode."%'";
$result2 = $mysqli->query($query2) or die($mysqli->error.__LINE__);
while($row2 = $result2->fetch_assoc()) {
Which I thought was working but on closer inspection it only returns the correct result the first time if a clean_dst is repeated
eg
clean_dst dial_code tele2id
18006927753 1800 1433
18006927753 1 1422
What am i doing wrong? Thanks
If it helps I need the result with the most matching digits?
Although not in php, this one sql can handle your first and secondary query all in one... AND properly handle returning the longest matching entry per dial.
select
PQ.clean_dst,
PQ.dial_code,
PQ.tele2id,
#Rank := if( #lastDst = PQ.clean_dst, #Rank +1, 1 ) as dialRank,
#lastDst := PQ.clean_dst as ForNextRowCompare
from
( SELECT distinct
cdr.clean_dst,
dc.dial_code,
dc.tele2id,
length( trim( dc.dial_code )) as Longest
from
cdr
JOIN dialing_codes dc
on cdr.clean_dst like concat( dc.dial_code, '%' )
order by
cdr.clean_dst,
Longest DESC ) PQ,
( select #lastDst := '',
#Rank := 0 ) sqlvars
having
dialRank = 1
The first part is the inner query resulting in alias "PQ" (preQuery). It is getting a list of distinct combinations for any call data record to its matching POSSIBLE dial codes. Critical component is to put the order by each phone number dialed, THEN based on the longest dial code in descending order. This will put your "1800" at the top of the list per phone number using it.
Next comes the outer query where the MySQL #variables are applied. These work like in-line programming loop for you and goes for every record in the "PQ" result set. It starts the variables with blank and zero respectively.
Every record compares its dialed number to the last dialed number record (in cases like your 1800 and 1 multiple return sets). If they ARE the same phone, add 1 to the existing #Rank, otherwise, it is a change in phone numbers... always start a phone number change back to rank 1. THEN, it assigns the #lastDst to the phone number it just processed so it can be the basis of the next phone record being tested.
At the end is a HAVING clause to only include those of DialRank = 1
So, per your record set samples, the query would result in records looking something like...
Dial Number Dial_Code Tele2ID Longest DialRank ForNextRowCompare
18006927753 1800 1433 4 1 18006927753 <-- Keep this
18006927753 1 1422 1 2 18006927753
441138973356 441 1024 3 1 441138973356 <-- Keep this
Feedback per comment. TO handle your update, you can just wrap it up
update cdr,
( full query ) as FromThisQuery
where cdr.clean_dst = FromThisQuery.clean_dst
set tele2id = FromThisQuery.tele2id
Please try this query:
select dial_code, clean_dst from cdr c, dialing_codes d where c.clean_dst
like concat(d.dial_code, '%');
You don't need to code all that logic in php. MySQL gives you the functions and comparisons to do it natively in SQL, which is simpler and much more concise.
Hope this helps.
I got stuck with a simple query which I can't figure out why isn't doing what I expect it to do. I have 3 values set on database like this:
$measure = 'kg';
$country_code = 'DE';
$weight = '5';
WEIGHT_UNIT | COUNTRIES | MAX_WEIGHT | PRICE
kg | DE,AT | 10 | 25.55
lbs | DE,AT,CH | 5 | 15.99
My PHP query looks like this:
SELECT *
FROM `article_shipping_options`
WHERE `weight_unit` = '$measure'
AND `countries` LIKE '%$country_code%'
AND `max_weight` <= '$weight'
LIMIT 1;
The result I was expecting was the row with the 25.55 price.
I know I am doing something wrong here despise my 2 days search on google...any help would be mostly appreciated :)
Did you mean MAX_WEIGHT >= $weight ?
I think you have the wrong inequality operator. Shouldn't it be max_weight >= '$weight'?
Try using FIND_IN_SET() and use max_weight >= '$weight'
SELECT *
FROM article_shipping_options
WHERE weight_unit='$measure' AND
FIND_IN_SET($country_code, countries) > 0 AND
max_weight >= '$weight'
LIMIT 1;
FIND_IN_SET()
You have $weight set to 5, but in the row's MAX_HEIGHT is 10.
Then the last condition for that row evaluates as 10 <= 5. Since the condition was not met, the row was not returned.