MySql - Loop through dates in a month for each row - mysql

I have 2 tables. One is Jobs (master) table & other is Allocations (transactions) table. For each Job, I need to print the number of allocations done on each day in a month.
I need to print the number of allocations in the following format.
I tried this using while loop in a stored procedure. But it did not help.
BEGIN
SET #start_day = DATE('2018-11-01');
SET #end_day = DATE_ADD(#start_day, INTERVAL 30 DAY);
SELECT
job_id into #job_id
FROM
jobs
WHERE
job_post_date BETWEEN #start_day AND #end_day;
WHILE(#start_day < #end_day) DO
SELECT COUNT(allocation_id) FROM allocations WHERE allocations_job_id =
#job_id AND allocations_assigned_date = #start_day;
SET #start_day = DATE_ADD(#start_day, INTERVAL 1 DAY);
END WHILE;
END

If you can create dynamic sql by looping for all the number of days for the month in question, that should solve your problem.
Dynamic sql should look like
select j.job_name,
sum(case when a.allocations_assigned_date = '2019-01-01'
then 1 else 0 end) as "1-JAN-2019",
sum(case when a.allocations_assigned_date = '2019-01-02'
then 1 else 0 end) as "2-JAN-2019",
sum(case when a.allocations_assigned_date = '2019-01-31'
then 1 else 0 end) as "31-JAN-2019"
from jobs j inner join allocations a
on j.job_id = a.allocations_job_id
group by j.job_name

Related

Where do I begin Looping statements in MySQL Workbench

I need to have this query run 12 times (previous 12 months) and append the results to a table. I am not very good with looping, looking for input. I am just not sure where to put my counter variables or any other looping statements. I think I may need two variables to loop because of the Previous Month First Day and Last day variables.
SET #PM_FD = last_day(curdate() - interval 2 month) + interval 1 day;
SET #PM_LD = last_day(curdate() - interval 1 month);
insert into sandbox.metrics_history
SELECT
'CHI' as Company
,count(*) as Result
,'SSRM10' as Metric_ID
,'PONoReqLine' as Metric_Name
, MONTHNAME(#PM_FD) as Month, year(#PM_FD) as Year
FROM
poline pol
INNER JOIN
purchorder po ON pol.company = po.company
AND pol.po_number = po.po_number
AND pol.po_release = po.po_release
AND pol.po_code = po.po_code
LEFT JOIN
polinesrc src ON pol.company = src.company
AND pol.po_number = src.po_number
AND pol.po_release = src.po_release
AND pol.line_nbr = src.line_nbr
AND pol.po_code = src.po_code
LEFT JOIN
buyer byr ON pol.buyer_code = byr.buyer_code
WHERE
pol.buyer_code != 'POC'
AND src.company IS NULL
AND po.po_date >= #PM_FD
AND po.po_date <= #PM_LD
ORDER BY pol.company , pol.po_number , pol.line_nbr

Diffrence between sum of two products > 0

I want to select the sum of T_No where Transactions are equal to R and subtract it by T_No where Transactions are equal to D and the answer of this should greater than zero for a CustomerID which would be a input (an int input declared in a stored procedure)
((Sum(T_No) where Transactions = R - Sum(T_No) where Transactions = D ) > 0) where CoustomerID = #input
Example : for ID = 1 it would be ((20+15) - 10) > 0
I Have tried so many things but either syntax is wrong, wrong value or it does not accept, and I am literally Stuck, this was my final attempt
SELECT
(select ( select Sum(T_No) where Transactions = R) - (select Sum(T_No) where Transactions = D) as C_T )
FROM CustomerTrans WHERE C_T > 0 ;
Conditional aggregation should help:
SELECT
SUM(CASE WHEN Transaction = 'R' THEN t_no ELSE 0 END) - SUM(CASE WHEN Transaction = 'D' THEN t_no ELSE 0 END)
FROM CustomerTrans
WHERE CoustomerID = #yourCustomerIdVariable
As you're writing a sproc you can assign the result of this to a variable and then decide what to do if the result is negative. (I would personally log an error for example, rather than just hide those results). If the result is null, then there were no transactions for that customer
ps; I used Transaction because that's what your screenshot showed, and I figured a screenshot is less likely to contain a typo than code with syntax errors. Adjust if required
you where kinda close, I would sum like you, only the syntax is a bit off, you can't have aggregate fields in Where, thats why you should use having, also case when syntax is not correct.
Select
CoustomerID,
Sum(case when Transactions = 'R' then T_No else 0 end) -
Sum(case when Transactions = 'D' then T_No else 0 end) as C_T
FROM CustomerTrans
group by CoustomerID
having (Sum(case when Transactions = 'R' then T_No else 0 end) -
Sum(case when Transactions = 'D' then T_No else 0 end))>0

How to minimize sql query

I have a booking table in MySQL database where I need to get 3 data.
1) Total Booking
2) Pending Booking ( where is_confirm = 1 )
3) Complete Booking ( where is_confirm = 0 )
Now, I am writing 3 separate query to get this but how can I get it using 1 query?
current query:
$booking = new Admin;
$booking->rowQuery("SELECT count(bid) AS totalBooking FROM booking");
$bookingData = $booking->result->fetch_assoc();
$totalBooking = $bookingData['totalBooking'];
$booking->rowQuery("SELECT bid FROM booking WHERE is_confirm = 1 ");
$completeBooking = $booking->rows;
$booking->rowQuery("SELECT bid FROM booking WHERE is_confirm = 0 ");
$pendingBooking = $booking->rows;
you can use case sql . Try this:
SELECT
COUNT(CASE WHEN is_confirm = 1 THEN 1 END) AS confirmCount,
COUNT(CASE WHEN is_confirm = 0 THEN 1 END) AS noconfirmCount,
COUNT(*) AS total
FROM booking;
SELECT count(bid) AS totalBooking FROM booking
UNION
SELECT bid FROM booking
WHERE is_confirm=1
UNION
SELECT bid FROM booking
WHERE is_confirm=0
Aggregate functions usually skip nulls, including group_concat. If you don't mind explodeing the result string later, you could use case expressions to get the total bookings and coma-delimited strings for the IDs:
SELECT COUNT(bid) AS totalBooking,
GROUP_CONCAT(CASE is_confirm WHEN 1 THEN bid END) AS pendingBookings,
GROUP_CONCAT(CASE is_confirm WHEN 0 THEN bid END) AS completeBookings
FROM booking

MySQL Attendance System

I have made an attendance system for a project, However I'm stuck right now by trying to create a query.
SELECT
COUNT(Students.idStudents) total,
SUM(case when Attendance.status LIKE 'present' then 1 else 0 end) present,
SUM(case when Attendance.status LIKE 'late' then 1 else 0 end) late,
SUM(case when Attendance.status is null then 1 else 0 end) absents
FROM Students, Schools, Tags LEFT JOIN Attendance
ON Attendance.tagCode = Tags.tagCode
WHERE Schools.idSchools = Students.idSchools
AND Tags.idStudents = Students.idStudents
This code works and generates an attendance. However this will show all the dates.
When I add in another line to specify date
AND Attendance.date = DATE(NOW());
It will not show anything..
There's 'Present', 'Late' status for the attendance however if the student's record in that table doesn't exist, it is considered as absent.
How do I do that?
Assuming you're using a case insensitive collation, your purported solution can be rewritten as follows:
SELECT COUNT(p.idStudents) total
, SUM(CASE WHEN a.status = 'present' THEN 1 ELSE 0 END) present -- [or just SUM(a.status = 'present')]
, SUM(CASE WHEN a.status = 'late' THEN 1 ELSE 0 END) late
, SUM(CASE WHEN a.status IS NULL THEN 1 ELSE 0 END) absents
FROM Students p
JOIN Schools s
ON s.idSchools = p.idSchools
JOIN Tags t
ON t.idStudents = p.idStudents
LEFT
JOIN Attendance a
ON a.tagCode = t.tagCode
AND a.date = CURDATE() ;
For next time: Your ERD shows 10 tables, but only 4 feature in this problem. If a table isn't likely to be part of the proposed solution, don't show it. Don't provide pictures. Instead, where possible, provide proper DDLs (and/or an sqlfiddle), TOGETHER WITH THE DESIRED RESULT SET based upon a minimal, but properly representative data set.
Welcome to SO.
Figured it out..
SELECT
COUNT(Students.idStudents) total,
SUM(case when Attendance.status LIKE 'present' then 1 else 0 end) present,
SUM(case when Attendance.status LIKE 'late' then 1 else 0 end) late,
SUM(case when Attendance.status is null then 1 else 0 end) absents
FROM Students, Schools, Tags LEFT JOIN (SELECT * FROM Attendance WHERE Attendance.date = NOW()) AS Attendance
ON Attendance.tagCode = Tags.tagCode
WHERE Schools.idSchools = Students.idSchools
AND Tags.idStudents = Students.idStudents
The problem with the original query was you were asking for students where their attendance was "NOW" where no student was ever now but they are current attending classes. They may have signed in at 8am and you run the query at 10am. You'd need to manipulate the datetime to choose your start time and end time based on the current day.
timestampadd(HOUR, 08, CURDATE()) - this will give you 8am, you'd then query for when attendance.date is greater than or equal to 8am and then potentially less than or equal to 4pm?

MySQL transaction with 1 update statement takes >4s in a 4000 row table

Most of the time this transaction takes <1s, average of 200ms or so. But occasionally, it takes >4s! This sproc gets run 5-6 times per second or so.
My sproc is pretty simple (innoDB - REPEATABLE READ):
START TRANSACTION;
SELECT end_time INTO currentEndTime FROM auctions WHERE id=var_auction_id;
IF (ADDTIME(currentEndTime , var_time_increment) < NOW()) THEN
UPDATE auctions SET end_time = ADDTIME(NOW(), var_time_increment), price = price+var_price_increment, leader_id = var_leader_id, modified = NOW() WHERE id = var_auction_id AND closed = 0;
ELSE
UPDATE auctions SET end_time = ADDTIME(end_time, var_time_increment), price = price+var_price_increment, leader_id = var_leader_id, modified = NOW() WHERE id = var_auction_id AND closed = 0;
END IF;
SELECT ROW_COUNT() INTO myRowCount;
IF (_error) THEN
ROLLBACK;
ELSE
SET var_return = myRowCount;
COMMIT;
END IF;
I want to figure out what's causing the 4s spikes, things I have tried:
I thought it might be concurrent, but I've seen this sproc called 5 times in 1 second, and those transactions take <100ms. And for the 4s ones, it's not that many concurrent transactions
Index is set properly on id
Table is small... 4000 rows or so.
Can't really run slow query log since it's MySQL 5.0, want to avoid rebooting the server to turn on the slow query flag unless it's the last resort.
I need some suggestions on the cause or what else to investigate.
Not sure if this will help but these lines:
SELECT end_time INTO currentEndTime FROM auctions WHERE id = var_auction_id;
IF (ADDTIME(currentEndTime, var_time_increment) < NOW()) THEN
UPDATE auctions
SET end_time = ADDTIME(NOW(), var_time_increment)
, price = price + var_price_increment
, leader_id = var_leader_id
, modified = NOW()
WHERE id = var_auction_id
AND closed = 0;
ELSE
UPDATE auctions
SET end_time = ADDTIME(end_time, var_time_increment)
, price = price+var_price_increment
, leader_id = var_leader_id
, modified = NOW()
WHERE id = var_auction_id
AND closed = 0;
END IF;
can be rewritten as:
UPDATE auctions
SET end_time
= ADDTIME( CASE WHEN ADDTIME(end_time, var_time_increment) < NOW()
THEN NOW()
ELSE end_time
END
, var_time_increment
)
, price = price + var_price_increment
, leader_id = var_leader_id
, modified = NOW()
WHERE id = var_auction_id
AND closed = 0;
or:
UPDATE auctions
SET end_time
= ADDTIME( CASE WHEN end_time < ADDTIME(NOW(), - var_time_increment)
THEN NOW()
ELSE end_time
END
, var_time_increment
)
, price = price + var_price_increment
, leader_id = var_leader_id
, modified = NOW()
WHERE id = var_auction_id
AND closed = 0;
A compound index on (closed, id) should also help the UPDATE statement to avoid reading from the table when closed <> 0.