How can I make the following queries done in one single query and get the result just the way it is in below?
// Begining of January
$ob = mysql_query(" SELECT SUM(salary_amount) AS total FROM teacherexpense WHERE month(disburse_date)='01' AND year(disburse_date)='$year' ");
$nt = mysql_fetch_assoc($ob);
$salaryamount= $nt['total'];
$ob = mysql_query(" SELECT SUM(other_expense_amount) AS expenseamount FROM otherexpense WHERE month(other_expense_date)='01' AND year(other_expense_date)='$year' ");
$nt = mysql_fetch_assoc($ob);
$expenseamount= $nt['expenseamount'];
$jk = mysql_query(" SELECT SUM(amountpaid) AS revenue FROM studentpayment1 WHERE month(received_date)='01' AND year(received_date)='$year' ");
$t = mysql_fetch_assoc($jk);
$revenue= $t['revenue'];
$ob = mysql_query(" SELECT SUM(other_earning_amount) AS otherearningamount FROM otherearning WHERE month(other_earning_date)='01' AND year(other_earning_date)='$year' ");
$nt = mysql_fetch_assoc($ob);
$otherearningamount= $nt['otherearningamount'];
$January= ($revenue+$otherearningamount)-($salaryamount+$expenseamount);
// End of January
Stuff it in a Stored Procedure?
The database drivers for PHP won't let you run several queries separated with ; for security reasons.
Have you tried mysqli drivers instead of mysql?
Take a look at: mysqli_multi_query
Executes one or multiple queries which are concatenated by a semicolon.
SELECT 'withdrawals' t, SUM( amount ) sum
FROM withdrawals
UNION
SELECT 'statement' t, SUM( amount ) sum
FROM statement
while($row = mysql_fetch_assoc($result))
{
$total[$row['t']] = $row['sum'];
}
echo $total['withdrawals']; # 100
echo $total['statement']; # 624.x
You could use MySQL UNION - but you'll have to iterate over the resultset, because in that case you will get 4 records instead of one/statement group
Related
I'm try to display two data of one query using codeigniter.
$query = "SELECT count(distinct p.id_paciente), count(c.pacientes_id_paciente) FROM paciente p, cita c WHERE p.id_paciente=c.pacientes_id_paciente AND p.usuarios_id_usuario=43 AND p.aseguradoras_id_aseguradora=8 AND c.dia_cita>='2015-04-16' AND c.dia_cita<='2015-04-16'";
$sql = $this->db->query($query);
How can I to show the two results of count(distinct p.id_paciente) and the count(c.pacientes_id_paciente)
I try using
foreach ($sql->result_array() as $row)
{
echo $row['id_paciente'];
echo $row['pacientes_id_paciente'];
}
But only display the content of the array...
Thanks
There are several issues with your code:
Use aliases to name columns in the resultset to be able to address them later by name
SELECT COUNT(distinct p.id_paciente) AS count1, ...
^^^^^^
Don't interpolate query strings yourself. Use Codeigniter's query bindings
This may not be relevant to you but if dia_cita has time component to it (i.e. is of type datetime) you may want to change your WHERE condition to
c.dia_cita >= ? AND c.dia_cita < ? + INTERVAL 1 DAY
There is no need for foreach loop. You always get only one row with this query. Therefore use Codeigniter's row() or row_array().
That being said your code may look like
$sql = "
SELECT COUNT(DISTINCT p.id_paciente) AS count1,
COUNT(c.pacientes_id_paciente) AS count2
FROM paciente p JOIN cita c
ON p.id_paciente = c.pacientes_id_paciente
WHERE p.usuarios_id_usuario = ?
AND p.aseguradoras_id_aseguradora = ?
AND c.dia_cita >= ?
AND c.dia_cita < ? + INTERVAL 1 DAY
";
$bindings = array(43, 8, '2015-04-16', '2015-04-16')
$row = $this->db
->query($sql, $bindings)
->row_array();
echo $row['count1'], $row['count2'];
Hi i have such table information:
what i want to do with php with while or just in mysql, is to SUM (time_used) of the rows with status 44 until its reached row with status 55. after that it should begin from start with new summing.
first query should return 37, second 76 (keep in mind it should be universal, for unlimited occurrences of 55 status row)
i thought of a way with time/date filtering and have this:
select sum(time_used) as sumed
from timelog
where start_time > (select end_time from timelog where (status='55')
ORDER BY id DESC LIMIT 1) ORDER BY id DESC
but this works only for last combination of 44 and 55
i know i will need two way filtering( < end_time and > end_time) so it will work for all cases, but cant think of a way to do it in php
can anyone help me?
EDIT:
sqlfiddle whoever want it:
http://sqlfiddle.com/#!2/33820/2/0
There are two ways to do it: Plain SQL or PHP. If you treat thousands of rows, it may be interresting to choose between the two by testing performance.
Plain SQL
select project_id, task_id, user_id, sum(time_used) as time_used,
min(start_time) as start_time, max(end_time) as end_time, max(comment) as comment from
(select t.id, t.project_id, t.task_id, t.user_id, t.time_used,
count(t2.id) as count55, t.start_time, t.end_time, t.comment
from timelog t
left join timelog t2 on t.id>t2.id and t2.status=55 and t.task_id=t2.task_id
group by t.id) as t
group by count55;
I assume here that a task can belong to one user only
SQL and PHP
$link = mysqli_connect( ... );
$query = "select id, project_id, task_id, user_id, time_used, start_time, end_time, status
from timelog order by id";
$result = mysqli_query($link, $query);
$table = array();
$time_used = 0;
$start_sum = true;
$i = 0;
while($row = mysqli_fetch_assoc ($result)){
if($start_sum){
$table[$i] = $row;
$start_sum = false;
} else {
$table[$i]['time_used'] += $row['time_used'];
$table[$i]['end_time'] += $row['end_time'];
}
if($row['state'] == 55){
$i++;
$start_sum = true;
}
}
If two tasks can run in simultaneously, solution 1 will work, but solution 2 will need to be adapted in order to take this in account.
here is my intepretation:
http://sqlfiddle.com/#!2/33820/45
set #n=0;
select project_id, task_id, user_id,sum(time_used) from (
SELECT time_used,project_id, task_id, user_id,
#n:=if(status=55,#n+1,#n),
if(status=55,-1,#n) as grouper FROM timelog
) as t
where grouper>-1
group by grouper;
I'm neither a php nor MySQL programmer, but I can explain the logic you want to follow. You can then code it.
First, query your db and return the results to php.
Next, set two sum variables to 0.
Start looping through your query results. Increment the first sum variable until you reach the first row that has status 55. Once you do, start incrementing the second variable.
The tricky part will be to sort your query by the row number of the table. Here is a link that will help you with that part.
I am trying to calculate how much a user has earned so it reflects on the users home page so they know how much their referrals have earned.
This is the code I have.
$get_ref_stats = $db->query("SELECT * FROM `members` WHERE `referral` = '".$user_info['username']."'");
$total_cash = 0;
while($ref_stats = $get_ref_stats->fetch_assoc()){
$get_ref_cash = $db->query("SELECT * FROM `completed` WHERE `user` = '".$ref_stats['username']."' UNION SELECT * FROM `completed_repeat` WHERE `user` = '".$ref_stats['username']."'");
$countr_cash = $get_ref_cash->fetch_assoc();
$total_cash += $countr_cash['cash'];
$countr_c_rate = $setting_info['ref_rate'] * 0.01;
$total_cash = $total_cash * $countr_c_rate;
}
It worked fine when I just had
$get_ref_cash = $db->query("SELECT * FROM `completed` WHERE `user` = '".$ref_stats['username']."'");
but as soon as I added in the UNION it no longer calculated correctly.
For example, there is 1 entry in completed and 1 entry in completed_repeat both of these entries have a cash entry of 0.75. The variable for $countr_c_rate is 0.10 so $total_cash should equal 0.15 but instead it displays as 0.075 with and without the UNION it acts as if it is not counting from the other table as well.
I hope this makes sense as I wasn't sure how to explain the issue, but I am very unsure what I have done wrong here.
In your second query instead of UNION you should use UNION ALL since UNION eliminates duplicates in the resultset. That is why you get 0.075 instead of 0.15.
Now, instead of hitting your database multiple times from client code you better calculate your cash total in one query.
It might be inaccurate without seeing your table structures and sample data but this query might look like this
SELECT SUM(cash) cash_total
FROM
(
SELECT c.cash
FROM completed c JOIN members m
ON c.user = m.username
WHERE m.referral = ?
UNION ALL
SELECT r.cash
FROM completed_repeat r JOIN members m
ON r.user = m.username
WHERE m.referral = ?
) q
Without prepared statements your php code then might look like
$sql = "SELECT SUM(cash) cash_total
FROM
(
SELECT c.cash
FROM completed c JOIN members m
ON c.user = m.username
WHERE m.referral = '$user_info['username']'
UNION ALL
SELECT r.cash
FROM completed_repeat r JOIN members m
ON r.user = m.username
WHERE m.referral = '$user_info['username']'
) q";
$result = $db->query($sql);
if(!$result) {
die($db->error()); // TODO: better error handling
}
if ($row = $result->fetch_assoc()) {
$total_cash = $row['cash_total'] * $setting_info['ref_rate'];
}
On a side note: make use of prepared statements in mysqli instead of building queries with concatenation. It's vulnerable for sql-injections.
With $countr_cash = $get_ref_cash->fetch_assoc(); you only fetch the first row of your result. However, if you use UNION, you get in your case two rows.
Therefore, you need to iterate over all rows in order to get all values.
Ok, So there is only one row in members table. You are iterating only once on the members table. Then you are trying to get rows using UNION clause which will result in two rows and not one. Then you are just getting the cash column of the first row and adding it to the $total_cash variable.
What you need to do is iterate over the results obtained by executing the UNION query and add the $total_cash variable. That would give you the required result.
$get_ref_stats = $db->query("SELECT * FROM `members` WHERE `referral` = '".$user_info['username']."'");
$total_cash = 0;
while($ref_stats = $get_ref_stats->fetch_assoc()){
$get_ref_cash = $db->query("SELECT * FROM `completed` WHERE `user` = '".$ref_stats['username']."' UNION SELECT * FROM `completed_repeat` WHERE `user` = '".$ref_stats['username']."'");
while($countr_cash = $get_ref_cash->fetch_assoc()){
$total_cash += $countr_cash['cash'];
}
$countr_c_rate = $setting_info['ref_rate'] * 0.01;
$total_cash = $total_cash * $countr_c_rate;
}
This question already has answers here:
Subquery returning more than 1 row
(3 answers)
Closed 1 year ago.
I want to run a foreach loop from database, but I dont know how to start. I have an array which I have generated from a while loop:
/* mysql query for geting leave_type ID */
$leaveType = mysql_query("
SELECT `leave`.leave_type_id_leave_type,
`leave`.staff_leave_application_staff_id_staff,
`leave`.date,
`leave`.date_updated,
`leave`.active
FROM `leave`
WHERE `leave`.staff_leave_application_staff_id_staff = $iid
GROUP BY `leave`.leave_type_id_leave_type
");
/* Now put all leave Type ID in an array */
echo "<table>";
$types = array();
while($leaveFW = mysql_fetch_array( $leaveType )){
$types[] = $leaveFW['leave_type_id_leave_type'];
}
print_r($types);
Now I want to run a foreach loop which will query below code for each ID in array. :
$leaveQ = mysql_query("SELECT Count(*) as total, monthname(date) as
month FROM `leave`
WHERE `leave`.staff_leave_application_staff_id_staff = $iid
and `leave`.leave_type_id_leave_type = $type");
I want to show $leaveQ['month'] and $leaveQ['total'] in foreach loop.
May be my foreach like this, but how to get $type['month'] and $type['total'] :
foreach ($types as $type)
{
$leaveQ = mysql_query("SELECT Count(*) as total, monthname(date) as month
FROM `leave` WHERE `leave`.staff_leave_application_staff_id_staff = $iid
and `leave`.leave_type_id_leave_type = $type");
}
May youwant something like this
foreach($types as $result)
{
$iid = $result['staff_leave_application_staff_id_staff'];
$type = $result['leave_type_id_leave_type'];
$leaveQ = mysql_query("SELECT Count(*) as total, monthname(date) as
month FROM `leave`
WHERE `leave`.staff_leave_application_staff_id_staff = $iid
and `leave`.leave_type_id_leave_type = '".$leaveFW['leave_type_id_leave_type']."'");
while($row = mysql_fetch_assoc($leaveQ))
{
echo $row['month']."<br>";
echo $row['total']."<br>";
}
}
Don't do this!
mysql_ functions are deprecated as of PHP 5.5!
doing this (get one query, then run another query for each rows in a foreach loop) is a bad approach, commonly seen in a lot of code flying around the 'net...
Also, I think you want to get the count for each month; in that case you have to use the GROUP BY clause for that
The proper way to do this is using JOIN operations, and instead of a query for each line, only one query to get all the data.
Blindly following that advice, not changing too much, just merging the two queries, your query should look like this:
SELECT Count(*) as total, monthname(date) as month, types.leave_type_id_leave_type
FROM `leave`
JOIN (SELECT DISTINCT `leave`.leave_type_id_leave_type
FROM `leave`
WHERE `leave`.staff_leave_application_staff_id_staff = $iid) as types
ON leave.leave_type_id_leave_type = types.leave_type_id_leave_type
WHERE `leave`.staff_leave_application_staff_id_staff = $iid
GROUP BY monthname(date), types.leave_type_id_leave_type
Differences to your approach:
the inner query is almost the same as your 1st query, but
instead of GROUP BY, I used the DISTINCT - in this case, it is the same, but I think this is easier to read - and that is an important aspect!
I only selected the relevant column for it (leave_type_id_leave_type)
I altered the outer query a bit more
the JOIN does what replaces the "foreach" approach
only those rows are "taken into count" (in this case, literally :) ), that are appropriate for the inner query
this will return the count for each month and each type.
To make this even better:
Use properly parametrized prepared statements: better performance, and getting used to it makes you avoid SQL injection in situations where that applies...
you can use PDO for that, it is not deprecated...
Looking at the resulting query, it is easy to see that this can be further simplified, and does not need the inner query, also getting rid of one filter on $iid
SELECT Count(*) as total, monthname(date) as month, leave_type_id_leave_type
FROM `leave`
WHERE `leave`.staff_leave_application_staff_id_staff = $iid
GROUP BY monthname(date), leave_type_id_leave_type
Differences now:
huge performance jump...
a lot less, and a lot readable code
EDIT
Here is the SQL fiddle to see how this works
foreach($types as $type){
$leaveQ = mysql_query("SELECT Count(*) as total, monthname(date) as
month FROM `leave`
WHERE `leave`.staff_leave_application_staff_id_staff = $iid
and `leave`.leave_type_id_leave_type = $type");
while($leave = mysql_fetch_assoc($leaveQ)){
var_dump($leave['total'] , $leave['month']);
}
}
If I understand correctly you want to loop thru the types found from your block of code and then issue more database queries and extract more data.
You can basically reuse your first block of code again, wrapped with a foreach loop:
foreach($types as $type){
$query = mysql_query("YOUR_SQL_USING_$TYPE");
while($row = mysql_fetch_array( $query )){
print_r($row);
// or print($row['COLUMN_NAME']);...
}
}
i have this code:
while ($sum<16 || $sum>18){
$totala = 0;
$totalb = 0;
$totalc = 0;
$ranka = mysql_query("SELECT duration FROM table WHERE rank=1 ORDER BY rand() LIMIT 1");
$rankb = mysql_query("SELECT duration FROM table WHERE rank=2 ORDER BY rand() LIMIT 1");
$rankc = mysql_query("SELECT duration FROM table WHERE rank=3 ORDER BY rand() LIMIT 1");
while ($rowa = mysql_fetch_array($ranka)) {
echo $rowa['duration'] . "<br/>";
$totala = $totala + $rowa['duration'];
}
while ($rowb = mysql_fetch_array($rankb)) {
$totalb = $totalb + $rowb['duration'];
}
while ($rowc = mysql_fetch_array($rankc)) {
$totalc = $totalc + $rowc['duration'];
}
$sum=$totala+$totalb+$totalc;
}
echo $sum;
It works fine, But the problem is until "$sum=16" the "echo $rowa['duration']" executes, the question is, is there a away to "echo" only the latest executed code in the "while ($rowa = mysql_fetch_array($ranka))" i this while loop?
Because most of the times returns all the numbers until the "$sum=16"
You are explicitly echoing the $rowa['duration'] in the first inner while loop. If you only want to print the last duration from the $ranka set, simple change the echo to $rowa_duration = $rowa['duration'] then echo it outside the loop.
while ($rowa = mysql_fetch_array($ranka)) {
$rowa_duration = $rowa['duration'];
$totala = $totala + $rowa['duration'];
}
echo $rowa_duration . '<br/>';
What you are doing there is bad on multiple levels. And your english horrid. Well .. practice makes perfect. You could try joining ##php chat room on FreeNode server. That would improve both your english and php skills .. it sure helped me a lot. Anyway ..
The SQL
First of all, to use ORDER BY RAND() is extremely ignorant (at best). As your tables begin the get larger, this operation will make your queries slower. It has n * log2(n) complexity, which means that selecting querying table with 1000 entries will take ~3000 times longer then querying table with 10 entries.
To learn more about it , you should read this blog post, but as for your current queries , the solution would look like:
SELECT duration
FROM table
JOIN (SELECT CEIL(RAND()*(SELECT MAX(id) FROM table)) AS id) as choice
WHERE
table.id >= choice.id
rank = 1
LIMIT 1
This would select random duration from the table.
But since you you are actually selecting data with 3 different ranks ( 1, 2 and 3 ), it would make sense to create a UNION of three queries :
SELECT duration
FROM table
JOIN (SELECT CEIL(RAND()*(SELECT MAX(id) FROM table)) AS id) as choice
WHERE
table.id >= choice.id
rank = 1
LIMIT 1
UNION ALL
SELECT duration
FROM table
JOIN (SELECT CEIL(RAND()*(SELECT MAX(id) FROM table)) AS id) as choice
WHERE
table.id >= choice.id
rank = 2
LIMIT 1
UNION ALL
SELECT duration
FROM table
JOIN (SELECT CEIL(RAND()*(SELECT MAX(id) FROM table)) AS id) as choice
WHERE
table.id >= choice.id
rank = 3
LIMIT 1
Look scary, but it actually will be faster then what you are currently using, and the result will be three entries from duration column.
PHP with SQL
You are still using the old mysql_* functions to access database. This form of API is more then 10 years old and should not be used, when writing new code. The old functions are not maintained (fixed and/or improved ) anymore and even community has begun the process of deprecating said functions.
Instead you should be using either PDO or MySQLi. Which one to use depends on your personal preferences and what is actually available to you. I prefer PDO (because of named parameters and support for other RDBMS), but that's somewhat subjective choice.
Other issue with you php/mysql code is that you seem to pointlessly loop thought items. Your queries have LIMIT 1, which means that there will be only one row. No point in making a loop.
There is potential for endless loop if maximum value for duration is 1. At the start of loop you will have $sum === 15 which fits the first while condition. And at the end that loop you can have $sum === 18 , which satisfies the second loop condition ... and then it is off to the infinity and your SQL server chokes.
And if you are using fractions for duration, then the total value of 3 new results needs to be even smaller. Just over 2. Start with 15.99 , ends with 18.01 (that's additional 2.02 in duration or less the 0.7 per each). Again .. endless loop.
Suggestion
Here is how i would do it:
$pdo = new PDO('mysql:dbname=my_db;host=localhost', 'username', 'password');
$pdo->setAttribute(PDO::ATTR_EMULATE_PREPARES, false);
$sum = 0;
while ( $sum < 16 )
{
$query = 'that LARGE query above';
$statement = $pdo->prepare( $query );
if ( $statement->execute() )
{
$data = $statement->fetchAll( PDO::FETCH_ASSOC );
$sum += $data[0]['duration']+$data[1]['duration']+$data[2]['duration'];
}
}
echo $data[0]['duration'];
This should do what your code did .. or at least, what i assume, was your intentions.