How can I add a trendline in pchart? - php

I am creating charts for a admin dashboard in our in-house CRM, however I am having a bit of trouble adding a trend line. I can't seem to find any documentation for a built in function so have attempted to do it using an sql query which is clunky and slow.
SELECT days.date,
(SELECT count(app.app_id) FROM li_appointments.li_appointments as app
where app_datetime >= date_add(days.date, interval -7 day)
and app_datetime <= days.date) / 5 as average
FROM li_appointments.li_days as days
where date >= date_add(date(now()), interval -30 day)
and date <= date(now())
It takes about 12 seconds just to grab the data for this one line on the chart. Is there any native way to do this with pChart, or is there a better PHP option available?
Here is the full code I am using right now with no trend line:
$getapps = "SELECT days.date, app.app_id as scheduled
,cnc.app_id as cnc
,dnc.app_id as dnc
,cancel.app_id as cancel
,covered.app_id as covered
FROM li_appointments.li_days as days
left Join (SELECT date(app_datetime) as app_datetime
, count(app_id) as app_id
FROM li_appointments.li_appointments
group by date(app_datetime))
as app on days.date = app.app_datetime
left Join (SELECT date(app_datetime) as app_datetime
, count(app_id) as app_id
FROM li_appointments.li_appointments
Where app_id in (select app_id from li_app_cnc)
group by date(app_datetime))
as cnc on days.date = cnc.app_datetime
left Join (SELECT date(app_datetime) as app_datetime
, count(app_id) as app_id
FROM li_appointments.li_appointments
Where app_id in (select app_id from li_app_dnc)
group by date(app_datetime))
as dnc on days.date = dnc.app_datetime
left Join (SELECT date(app_datetime) as app_datetime
, count(app_id) as app_id
FROM li_appointments.li_appointments
Where app_id in (select app_id from li_app_canceled)
group by date(app_datetime))
as cancel on days.date = cancel.app_datetime
left Join (SELECT date(app_datetime) as app_datetime
, count(app_id) as app_id
FROM li_appointments.li_appointments
Where terp_id is not null and terp_id <> ''
group by date(app_datetime))
as covered on days.date = covered.app_datetime
Where date > adddate(now(), Interval -30 day)
and date <= date(now())
group by date";
$result = mysql_query($getapps );
$date = array();
$scheduled = array();
$cnc = array();
$dnc = array();
$canceled = array();
$covered = array();
while($row = mysql_fetch_array($result)){
array_push($date, $row['date']);
array_push($scheduled, $row['scheduled']);
array_push($cnc, $row['cnc']);
array_push($dnc, $row['dnc']);
array_push($canceled, $row['cancel']);
array_push($covered, $row['covered']);
}
/* CAT:Line chart */
/* pChart library inclusions */
include("../class/pData.class.php");
include("../class/pDraw.class.php");
include("../class/pImage.class.php");
/* Create and populate the pData object */
$MyData = new pData();
$MyData->addPoints($scheduled,"Scheduled");
$MyData->addPoints($cnc,"CNC");
$MyData->addPoints($dnc,"DNC");
$MyData->addPoints($canceled,"canceled");
$MyData->addPoints($covered,"covered");
$MyData->setSerieTicks("Probe 2",4);
$MyData->setSerieTicks("Probe 3",4);
$MyData->setAxisName(0,"Appointments");
$MyData->addPoints($date,"Labels");
$MyData->setSerieDescription("Labels","Months");
$MyData->setAbscissa("Labels");

Related

How to join two query in MYSQL

I have 2 MYSQL base queries which dependent on each other, here are my quires
#$query = "SELECT * FROM coins_tokens";
$row = $db->Execute($query);
foreach ($row as $rowItem) {
$name = $rowItem['ct_id'];
#$sql1 = "SELECT * FROM historical_data WHERE `name` = '".$name."' GROUP BY name LIMIT 30";
$row2 = $db->Execute($sql1);
foreach ($row2 as $rowItem2){
$market_cap = $rowItem2['market_cap'];
if($market_cap >= 500000000){
}
}
}
It slow down my whole process and take lot of time to execute, as there are more then 1400 results in coins_tokens, then there are more then 600000 records again 1st table, in both table ct_id and name are conman.
And what I am trying to do is to get the currencies which have more then 500million market_cap in last 7 days. So am fetching the currencies from 1st table and there historical data from 2nd table and checking if market_cap there increased in last 7 days.
Here is the structure and data of historical_data table:
SELECT
c.*,
d.`date`,
d.market_cap
FROM coins_tokens AS c
LEFT JOIN historical_data AS d ON c.ct_id = d.name
WHERE d.market_cap >= '$mketcapgrter'
AND DATE(d.`date`) >= CURRENT_DATE() - INTERVAL 30 DAY
GROUP BY d.name
ORDER BY d.market_cap DESC LIMIT 100

Select all shift Subslips where this Employee isn't already working a shift

I currently have a select statement that will retrieve all the subslips from the subslip table that the employee can take if they have the certification for the subslip. I Just need to make the select statement return only subslips that do not conflict with any of the shifts the employee currently has.
A subslip is an contract of exchange of shifts between two employees. I am trying to make my web app display all subslips that the employee can take. If they click take shift on the subslip, the web app takes the shift from the old employee and gives it to the new one.
The select statement I currently have is...
SELECT S.CreatorID, S.ShiftID, S.subslipID, S.ShiftDate, S.startTime, S.endTime, S.Position, S.Reason, S.CreatedDateAndTime, E.Firstname, E.Lastname, E.Instructor, E.Lifeguard, E.Headguard, E.Supervisor
FROM `SubSlips` S
JOIN `Employees` E ON S.CreatorID = E.employeeID
WHERE `TakenTrueorFalse` = '0'
AND ShiftDate > '".$todaysDate."'
AND ( Position = 1 OR Position = 2 OR Position = 3 OR Position = 4 OR Position = 10);
This displays all subslips the employee can take based on wheather it taken or not, if the subslip's date is ahead of today's date, and if the subslips is one of the positions that the employee has certifications for.
I believe the select statement needs a "NOT IN (SELECT shifts that are at the same time" attached to the end of the statement.
NOT IN(
SELECT ShiftID FROM Shifts
WHERE `CurrentOwnerEmployeeID` = '".$EmployeeID."'
AND `date` = S.ShiftDate
AND `startTime` Between S.startTime AND S.endTime
OR `date` = S.ShiftDate
AND `endTime` Between S.startTime AND S.endTime
OR `date` = '".$date."'
AND S.startTime Between `startTime` AND `endTime`)
My Employee table looks like this...
My shift table looks like this...
This is the SubSlip Table...
So how can I make my Select Statement return the subslips that the currently logged in employee doesnt already have a shift at that time?
Edit
This is my select statement with the not in logic added.
SELECT S.CreatorID, S.ShiftID, S.subslipID, S.ShiftDate, S.startTime,
S.endTime, S.Position, S.Reason, S.CreatedDateAndTime, E.Firstname,
E.Lastname, E.Instructor, E.Lifeguard, E.Headguard, E.Supervisor FROM
`SubSlips` S JOIN `Employees` E ON S.CreatorID = E.employeeID WHERE
`TakenTrueorFalse` = '0' AND ShiftDate > '2017-12-31'
AND ( Position = 1 OR Position = 2 OR Position = 3 OR Position = 4 OR
Position = 10) AND
NOT IN( SELECT ShiftID FROM Shifts
WHERE `CurrentOwnerEmployeeID` = '8' AND
`date` = S.ShiftDate AND
`startTime` Between S.startTime AND S.endTime
OR `date` = S.ShiftDate AND `endTime` Between S.startTime AND S.endTime
OR `date` = S.ShiftDate AND S.startTime Between `startTime` AND `endTime`);

Minimize SQL queries

I have created a php code for listing the patients who have
1-email
2-zero future treatments
3-equal to or more than 1 finished treatments
4-latest finished treatment date is equal to 3 weeks ago from today
The problem is, I have more than 50K patients and for each patient, querying the above conditions taking a lot of time.
Is there a way of merging the sql queries into one, rather than for every patient having 3 or more queries which makes around 200k queries?
the code is below:
$today = date('Y-m-d');
$three_weeks_ago = date('Y-m-d', strtotime($today.'-3 weeks'));
$patients = $db->query("
SELECT
dg_patients_patients.id,
dg_patients_patients.first_name,
dg_patients_patients.last_name,
dg_patients_patients.email,
dg_clinics.clinic_name,
dg_clinics.clinic_address,
dg_clinics.clinic_phone
FROM dg_patients_patients
LEFT JOIN dg_clinics ON dg_patients_patients.clinic_id = dg_clinics.id
WHERE dg_patients_patients.email <> '' ORDER BY dg_patients_patients.first_name ASC ");
$now = date('Y-m-d H:i:s');
foreach ($patients as $row){
$patientID = $row['id'];
//Get Patient Future Treatments
$check_future_treatments = $db->column("SELECT id FROM dg_patient_treatment_finance WHERE treatment_type = :a1 AND patient_id = :a2 ",array("a1"=>"1","a2"=>"$patientID"));
$future_treatments = count($check_future_treatments);
//Get Patient Finished Treatments
$check_finished_treatments = $db->column("SELECT id FROM dg_patient_treatment_finance WHERE treatment_type = :a1 AND patient_id = :a2 ",array("a1"=>"2","a2"=>"$patientID"));
$finished_treatments = count($check_finished_treatments);
if($future_treatments == 0 && $finished_treatments > 0 ) {
$latest_finished_treatment_date = $db->single("SELECT plan_date FROM dg_patient_treatment_finance WHERE patient_id = :pid ORDER BY plan_date DESC LIMIT 1 ", array("pid"=>"$patientID"));
if($latest_finished_treatment_date == $three_weeks_ago){
echo $patientID.'- '.$row['first_name'].' '.$row['last_name'].' - '.$row['email'].'<br>';
}
}
You could try to LEFT JOIN on dg_patient_treatment_finance, use a GROUP BY and use SUM in combination with the CASE statement.
And at the same time calculate the MAX plan_date.
SELECT
p.id, p.first_name, p.last_name, p.email,
c.clinic_name, c.clinic_address, c.clinic_phone,
SUM(case when tf.treatment_type = 1 then 1 else 0 end) as total_treatment_type_1,
SUM(case when tf.treatment_type = 2 then 1 else 0 end) as total_treatment_type_2,
MAX(tf.plan_date) as max_plan_date
FROM dg_patients_patients p
LEFT JOIN dg_clinics c ON (p.clinic_id = c.id)
LEFT JOIN dg_patient_treatment_finance tf ON (p.id = tf.patient_id and tf.treatment_type IN (1,2))
WHERE p.email <> ''
GROUP BY
p.id, p.first_name, p.last_name, p.email,
c.clinic_name, c.clinic_address, c.clinic_phone
ORDER BY p.first_name, p.last_name
Then you can also simplify the calculation of $future_treatments and $finished_treatments by taking the content from the calculated sums.

Combining two SQL queries PDO

I'm quite stuck on the following issue. I have a series of tables:
What I want to do is get all the information on a room, assuming that the amount of bookings don't exceed the room number available for that Room.
So to get my Room details my SQL is this:
SELECT Rooms.RoomID as RoomID,
RoomName, NumOfRooms,
MaxPeopleExistingBeds,
MaxExtraBeds,
MaxExtraPeople,
CostPerExtraPerson,
MaximumFreeChildren,
IncludeBreakfast,
MinRate
FROM Rooms, RoomDetails
WHERE Rooms.AccommodationID = :aid AND
Rooms.RoomID = RoomDetails.RoomID
GROUP BY RoomName
Which upon return gets me a list of details for those rooms as follows:
I then use this query to get the number of bookings, and the ID of the room:
SELECT Booking.RoomID,
count(Booking.RoomID) as Bookings
FROM Booking
WHERE ArriveDate >= :aDate AND
DepartDate <= :dDate AND
AccommodationID = :aid
GROUP BY RoomID
I then combine both and feed the two arrays back in one array using this function:
public function get_availability($aid, $aDate, $dDate) {
$stmt = $this->db->prepare('SELECT Rooms.RoomID as RoomID, RoomName, NumOfRooms, MaxPeopleExistingBeds, MaxExtraBeds, MaxExtraPeople, CostPerExtraPerson, MaximumFreeChildren, IncludeBreakfast, MinRate FROM Rooms, RoomDetails WHERE Rooms.AccommodationID = :aid AND Rooms.RoomID = RoomDetails.RoomID GROUP BY RoomName');
$stmt->bindValue(':aid', $aid);
$stmt->execute();
$rooms = $stmt->fetchAll(PDO::FETCH_ASSOC);
$stmt2 = $this->db->prepare('SELECT Booking.RoomID, count(Booking.RoomID) as Bookings FROM Booking WHERE ArriveDate >= :aDate AND DepartDate <= :dDate AND AccommodationID = :aid GROUP BY RoomID');
$stmt2->bindValue(':aid', $aid);
$stmt2->bindValue(':aDate', $aDate);
$stmt2->bindValue(':dDate', $dDate);
$stmt2->execute();
$bookings = $stmt2->fetchAll(PDO::FETCH_ASSOC);
$room = array($rooms, $bookings);
return (!empty($room)) ? $room : false;
}
The thing is, what I actually want to do is only return the room details where NumOfRooms is less than the number of Bookings.
So for instance where I have $bookings, if it tells me that for room ID 4, I have 3 bookings for a set period, and my NumOfRooms is 1. Then I know that I have no capacity that week to take any more bookings on. If however I have 1 booking and one capacity then that is still full. But if I have NumOfRooms of 2, and bookings amount to 1, I know I have room.
So basically if NumOfRooms > BookingCount then the room is available.
How can I amalgamate both queries and simplify my code to make this possible?
I.E to put it simply, how do I select all of the info from RoomDetails given an ArriveDate in Booking and a DepartDate and a RoomID, where NumOfRooms > count(Booking.RoomID) (Where it is within those dates and the room id is equal to the room id of Rooms).
Your problem can be solved by simply updating the SQL statement itself:
SELECT r.RoomID AS RoomID,
RoomName,
NumOfRooms,
MaxPeopleExistingBeds,
MaxExtraBeds,
MaxExtraPeople,
CostPerExtraPerson,
MaximumFreeChildren,
IncludeBreakfast,
MinRate
FROM Rooms r
JOIN RoomDetails rd
ON r.RoomID = rd.RoomID
JOIN (
SELECT b.RoomID,
AccommodationID,
count(b.RoomID) AS Bookings
FROM Booking b
WHERE ArriveDate >= :aDate
AND DepartDate <= :dDate
GROUP BY RoomID
) t
ON t.AccommodationID = r.AccommodationID
WHERE r.AccommodationID = :aid
AND t.Bookings < NumOfRooms
GROUP BY RoomName
You can select out all of the booking counts per room for the desired date range as a subquery, and then LEFT JOIN that subquery against the list of your rooms filtered by your desired AccommodationID and the desired NumOfRooms > BookingCount criteria. The key here is in the join type used for this subquery, as an inner join would limit your results to only rooms that actually had bookings.
SELECT Rooms.RoomID as RoomID,
RoomName, NumOfRooms,
MaxPeopleExistingBeds,
MaxExtraBeds,
MaxExtraPeople,
CostPerExtraPerson,
MaximumFreeChildren,
IncludeBreakfast,
MinRate,
BookingCount
FROM Rooms
INNER JOIN RoomDetails on Rooms.RoomID = RoomDetails.RoomID
LEFT JOIN (
SELECT Booking.RoomID,
count(Booking.RoomID) as BookingCount
FROM Booking
WHERE ArriveDate >= :aDate AND
DepartDate <= :dDate
GROUP BY Booking.RoomID
) RoomBookings ON Rooms.RoomID = RoomBookings.RoomID
WHERE Rooms.AccommodationID = :aid
AND NumOfRooms > BookingCount
GROUP BY RoomName

Advice on SELECT statement and query

I need to either have a masive select statement or multiple queries. I need to break down data into specific timeframes during the day. This one works great for the first interval, but after that I'm stumped. If I have multiple queries, I'm having trouble with my "mysql_fetch_array", as to the syntax (using something like 'else' and going through them).
SELECT U.user_name,ROUND(SUM((TA.task_average*TC.completed)/60),2) AS equiv1, S.submit_date,
SUM(TC.completed) AS ttasks1,
FROM `summary` S
JOIN users U ON U.user_id = S.user_id
JOIN tasks TA ON TA.task_id = S.task_id
JOIN tcompleted TC ON TC.tcompleted_id = S.tcompleted_id
JOIN minutes M ON M.minutes_id = S.minutes_id
WHERE DATE(submit_date) = curdate( )
AND TIME(submit_date) BETWEEN '06:00:00' and '07:59:59'
GROUP BY U.user_name
LIMIT 0 , 30
My fetch array ( I would need to have a bunch more, but how to I combine them?)
<?php
while($rows=mysql_fetch_array($result1)){
?>
ok, given that you want the data across regular 2 hourly intervals, you could try something like this:
SELECT FLOOR(hour(S.submit_date)/2)*2, U.user_name,ROUND(SUM((TA.task_average*TC.completed)/60),2) AS equiv1, S.submit_date
SUM(TC.completed) AS ttasks1,
FROM `summary` S
JOIN users U ON U.user_id = S.user_id
JOIN tasks TA ON TA.task_id = S.task_id
JOIN tcompleted TC ON TC.tcompleted_id = S.tcompleted_id
JOIN minutes M ON M.minutes_id = S.minutes_id
WHERE DATE(submit_date) = curdate( )
GROUP BY U.user_name, FLOOR(hour(S.submit_date)/2)
LIMIT 0 , 30
where FLOOR(hour(S.submit_date)/2)*2 will map each hour to the first (even) hour of every 2 and you can group by this value. ie.
0, 1 -> 0
2, 3 -> 2
4, 5 -> 4
etc...
update with php included:
some notes:
i've used mysqli
i've left joined the original query with an hours derived table to ensure there are no
'gaps' in the time intervals (assumed 6:00 - 20:00)
i've ordered by user, so we as we loop through the results we can print table cells for a given user and then print a new table row when the user changes.
here's the code:
echo '<table border=1><tr><td></td>';
for ($i=6; $i<=18; $i=$i+2) {
echo '<td colspan=2>'.$i.' - '.($i+2).'</td>';
}
$mysqli = new mysqli('MY_HOST', 'MY_USER', 'MY_PASSWORD', 'MY_DATABASE');
$sql = "
SELECT user_name, IFNULL(equiv1,0) AS equiv1, IFNULL(ttasks1,0) AS ttasks1, hour
FROM
(
SELECT 6 AS hour
UNION SELECT 8
UNION SELECT 10
UNION SELECT 12
UNION SELECT 14
UNION SELECT 16
UNION SELECT 18
) hours LEFT JOIN
(
SELECT FLOOR(hour(S.submit_date)/2)*2 as task_hour, U.user_name, ROUND(SUM((TA.task_average*TC.completed)/60),2) AS equiv1, S.submit_date
SUM(TC.completed) AS ttasks1
FROM `summary` S
JOIN users U ON U.user_id = S.user_id
JOIN tasks TA ON TA.task_id = S.task_id
JOIN tcompleted TC ON TC.tcompleted_id = S.tcompleted_id
JOIN minutes M ON M.minutes_id = S.minutes_id
WHERE DATE(submit_date) = curdate( )
GROUP BY U.user_name, FLOOR(hour(S.submit_date)/2)
LIMIT 0 , 30
) task_summary
ON hours.hour = task_summary.task_hour
ORDER BY user_name, hour
";
$result = $mysqli->query($sql);
$user_name = '';
while ($row = $result->fetch_assoc()) {
if ($user_name <> $row['user_name']){
echo '</tr><tr><td>'.$row['user_name'].'</td>'; //start a new row if user changes
$user_name = $row['user_name']; //update user variable for checking on next iteration
}
echo '<td>'.$row['equiv1'].'</td>';
echo '<td>'.$row['ttasks1'].'</td>';
}
echo '</tr><table>';
This is in regard to your question "how to combine the fetch array".
Why not have an array to store all the values coming from your fetch array like the one below.
<?php
$customArray = array();
while($rows=mysql_fetch_array($result1)){
$customArray['field1'] = $row['field1'];
$customArray['field2'] = $row['field2'];
}
//another fetch array
while($rows=mysql_fetch_array($result2)){
$customArray['field1'] = $row['field1'];
$customArray['field2'] = $row['field2'];
}
//now $customArray will have all the required values you need.
//This is not a great option as it is making the logic expensive.
//Are you going to use this in a cron job ?
?>

Categories