I have a procedure that should always set a date, but it does not:
CREATE PROCEDURE 'player_extend_membership` (pid INTEGER, daysToAdd INTEGER, OUT result INTEGER)
BEGIN
SELECT PlayerMembershipEndDate INTO #memDate FROM players WHERE players.PlayerID = pid LIMIT 1;
SELECT ROW_COUNT() INTO #num;
IF #num = 0 THEN
SET result = -1;
ELSE
IF #memDate = NULL OR DATE(#memDate) < DATE(NOW()) THEN
SET #finalDate = DATE_ADD(DATE(NOW()), INTERVAL daysToAdd DAY);
ELSE
SET #finalDate = DATE_ADD(DATE(#memDate), INTERVAL daysToAdd DAY);
END IF;
SELECT #finalDate, #memDate;
UPDATE players SET PlayerMembershipEndDate = #finalDate
WHERE players.PlayerID = pid;
SET result = 1;
END IF;
END
When I check the return value, it is 1, therefore the account does exist. It tells me the result of the select query is always that #finalDate is NULL.
However, if ake it out of the IF and just do:
SET #finalDate = DATE_ADD(DATE(NOW()), INTERVAL daysToAdd DAY);
The date is set correctly.
I'm not sire what I am doing wrong.
Thanks
Your procedure seems way too complicated. Perhaps this does what you want:
set #result = -1;
UPDATE players
SET PlayerMembershipEndDate = (case when (#result := 1) is null then NULL
when #memDate IS NULL OR DATE(#memDate) < DATE(NOW())
then DATE_ADD(DATE(NOW()), INTERVAL daysToAdd DAY)
else DATE_ADD(DATE(#memDate), INTERVAL daysToAdd DAY)
end)
WHERE players.PlayerID = pid;
The first condition in the case just sets #result if a row is found. I've left your formulation of DATE(NOW()) even though CURDATE() is more succinct.
I suspect the actual problem with your logic was the = NULL. This always returns "UNKNOWN", which is treated as false. The correct expression is is NULL.
Related
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
I have a query that returns value if the date range is match. and if it is not match it returns nothing.
SELECT start, end
FROM (`taxemployee`)
LEFT JOIN `project_staff_assignment` ON `project_staff_assignment`.`taxemployee_id` = `taxemployee`.`id`
WHERE (start >= '2014-09-01' AND end <= '2014-09-15')
OR (end >= '2014-09-15' AND start <= '2014-09-01')
GROUP BY `taxemployee`.`id`
ORDER BY `assigned_hours`ASC
if I put this on WHERE
OR coalesce(`start`, `end`) IS NULL
it returns null values, but not includes with value
How do I return result even if it is null and with value
here is sqlfiddle
http://sqlfiddle.com/#!2/8bbe4/3
UPDATE:
Refer fiddle here : http://sqlfiddle.com/#!2/2babf/2
I am using case statement to display the date if it falls in the range and am displaying null if not.
SELECT `taxemployee`.`id`, Nickname,
case when project_staff_assignment.startdt >= '2014-09-01' THEN project_staff_assignment.STARTdt ELSE NULL END AS STARTDATE,
case when project_staff_assignment.ENDdt >= '2014-09-15' THEN project_staff_assignment.ENDdt ELSE NULL END AS ENDDATE
FROM (`taxemployee`)
LEFT JOIN `project_staff_assignment` ON `project_staff_assignment`.`taxemployee_id` = `taxemployee`.`id`
GROUP BY `taxemployee`.`id`
ORDER BY `taxemployee`.`id` ASC
ORIGINAL ANSWER:
Refer Fiddle here: http://sqlfiddle.com/#!2/8bbe4/52
SELECT `taxemployee`.`id`, Nickname, start, end
FROM (`taxemployee`)
LEFT JOIN `project_staff_assignment` ON `project_staff_assignment`.`taxemployee_id` = `taxemployee`.`id`
WHERE (start >= '2014-08-01' AND end <= '2014-08-15')
OR (end >= '2014-08-15' AND start <= '2014-09-01')
OR coalesce(`start`, `end`) IS NULL
GROUP BY `taxemployee`.`id`
ORDER BY `taxemployee`.`id` ASC;
Records were not returned because,
Value was available only for 01.08.2014 whereas, your condition was for 01.09.2014
I have the following PostgreSQL which I want to re-write to make it compatible for MySQL stored function.
CREATE OR REPLACE FUNCTION test.yearly_demand_function(IN paramstartdate date,
OUT network character varying,
OUT season_number integer,
OUT week_trans numeric,
OUT month_trans numeric,
OUT year_trans numeric )
RETURNS SETOF record AS
$BODY$
BEGIN
RETURN QUERY
(select qq.network::varchar,
qq.season_number,
qq.week_trans::numeric,
qq.month_trans::numeric,
qq.year_trans::numeric
from
(
SELECT coalesce(nullif(mpf.studio,''),fi.name) AS network,
coalesce(mpf.season_number, mpf.reported_season_number) AS season_number ,
sum(case when activity_date >= date_trunc('week', paramStartDate::timestamp) - interval '7 day' and activity_date <= paramStartDate then mpf.units_sold else 0 END) as week_trans,
sum(case when activity_date >= date_trunc('month', paramStartDate::timestamp) and activity_date <= paramStartDate then mpf.units_sold else 0 END) as month_trans,
sum(case when activity_date >= date_trunc('year', paramStartDate::timestamp) and activity_date <= paramStartDate then mpf.units_sold else 0 END) as year_trans
FROM customer.dim_product_view mpf
left join customer.feed_indicator fi on mpf.series_name = fi.indicator_value
and mpf.series_name = fi.indicator_value
left join
(
select p.series_name,p.season_number,count(*) as episode_count
from product p
where source in ('Amway','FifthThird')
and p.episode_number is not null
group by 1,2) as pec on mpf.series_name = pec.series_name
and mpf.season_number = pec.season_number
WHERE mpf.activity_date BETWEEN date_trunc('year', paramStartDate::timestamp) AND paramStartDate
AND 1=1
AND ( mpf.demographic is null or '' not in ( '' ) or ('' in ( '' ) and mpf.demographic = 'Persons') )
AND mpf.customer_product_id not ilike '%unallocated%'
GROUP BY 1,2,3,7,34,35,36
)qq
);
end;
$BODY$
LANGUAGE plpgsql VOLATILE
COST 100
ROWS 1000;
ALTER FUNCTION yearly_demand_function(date);
Now Im not sure how to write the RETURNS SETOF record AS from above PostgreSQL (RETURN QUERY part) for MYSQL
Although I have started writing the stored proc for MY SQL as below
-- DROP FUNCTION IF EXISTS looptest;
DELIMITER $$
CREATE FUNCTION test.yearly_demand_function( IN paramstartdate date,
OUT network varchar(256),
OUT season_number integer,
OUT week_value numeric,
OUT month_value numeric,
OUT year_value numeric
) RETURNS <What to Write >
LANGUAGE SQL
BEGIN
RETURN QUERY
(select qq.network::varchar,
qq.season_number,
qq.week_value::numeric,
qq.month_value::numeric,
qq.year_value::numeric
from
(
SELECT coalesce(nullif(mm.studio,''),fi.name) AS network,
coalesce(mm.season_number, mm.reported_season_number) AS season_number ,
sum(case when final_date >= date_trunc('week', paramStartDate::timestamp) - interval '7 day' and final_date <= paramStartDate then mm.units_sold else 0 END) as week_value,
sum(case when final_date >= date_trunc('month', paramStartDate::timestamp) and final_date <= paramStartDate then mm.units_sold else 0 END) as month_value,
sum(case when final_date >= date_trunc('year', paramStartDate::timestamp) and final_date <= paramStartDate then mm.units_sold else 0 END) as year_value
FROM customer.product_view mm
left join customer.f_indicator fi on mm.series_name = fi.indicator_value
and mm.series_name = fi.indicator_value
left join
(
select p.name,p.s_number,count(*) as episode_count
from product p
where source in ('Amway','FifthThird')
and p.e_number is not null
group by 1,2) as tt on mm.series_name = tt.series_name
and mm.season_number = tt.season_number
WHERE mm.final_date BETWEEN date_trunc('year', paramStartDate::timestamp) AND paramStartDate
AND 1=1
AND ( mm.demographic is null or '' not in ( '' ) or ('' in ( '' ) and mm.demographic = 'Persons') )
AND mm.customer_product_id not ilike '%unallocated%'
GROUP BY 1,2,3,7,34,35,36
)qq
);
END;
$$
DELIMITER
how to write the RETURNS SETOF record AS from above PostgreSQL (RETURN QUERY part) for MYSQL
Instead of CREATE FUNCTION use the CREATE PROCEDURE syntax. You can then write a normal SELECT statement inside that block. To execute the stored procedure you created use the CALL syntax (i.e. CALL test.yearly_demand_function('2013-01-01')). You also don't need to specify your 5 OUT parameters. The value of the 5 OUT parameters will just correspond to the 5 columns that you will specify in your SELECT statement.
New to MySQL Stored Procedures.
If I uncomment any of the 4 SELECT lines (Below) then the routine EXITS out of the FETCH loop. Do not understand why
-- --------------------------------------------------------------------------------
-- Routine DDL
-- Note: comments before and after the routine body will not be stored by the server
-- --------------------------------------------------------------------------------
DELIMITER $$
CREATE DEFINER=`root`#`localhost` PROCEDURE `UpdateStatusAudit`()
BEGIN
-- Create loop for all $Service records
DECLARE svc_id INT;
DECLARE test INT;
DECLARE svc_name VARCHAR(100);
DECLARE no_more_rows BOOLEAN;
DECLARE up_duration DECIMAL(11,2);
DECLARE down_duration DECIMAL(11,2);
DECLARE maint_duration DECIMAL(11,2);
DECLARE degr_duration DECIMAL(11,2);
DECLARE services_cur CURSOR FOR SELECT service_id,service_name FROM services ORDER BY service_id;
-- Declare 'handlers' for exceptions
DECLARE CONTINUE HANDLER FOR NOT FOUND
SET no_more_rows = TRUE;
OPEN services_cur;
the_loop: LOOP
FETCH services_cur INTO svc_id,svc_name;
IF no_more_rows THEN
CLOSE services_cur;
LEAVE the_loop;
END IF;
SET up_duration = 0;
SET down_duration = 0;
SET maint_duration = 0;
SET degr_duration = 0;
SELECT svc_id;
BEGIN
-- SELECT IFNULL(sum(duration),0) INTO up_duration FROM daily_audit_summary where service_id = svc_id AND status = 'UP' AND Date = current_date - 1 group by date,service_id,status;
-- SELECT IFNULL(sum(duration),0) INTO down_duration FROM daily_audit_summary where service_id = svc_id AND status = 'DOWN' AND Date = current_date - 1 group by date,service_id,status;
-- SELECT IFNULL(sum(duration),0) INTO maint_duration FROM daily_audit_summary where service_id = svc_id AND status = 'MAINT' AND Date = current_date - 1 group by date,service_id,status;
-- SELECT IFNULL(sum(duration),0) INTO degr_duration FROM daily_audit_summary where service_id = svc_id AND status = 'DEGR' AND Date = current_date - 1 group by date,service_id,status;
END;
-- insert into daily_status
INSERT INTO daily_status (date,service_id,time_up,time_down,time_maint,time_degraded) values (current_date-1,svc_id,up_duration,down_duration,maint_duration,degr_duration);
END LOOP the_loop;
END
Did you try assigning the variables like this:
SELECT
up_duration := IFNULL(SUM(duration), 0)
FROM daily_audit_summary
WHERE service_id = svc_id
AND status = 'UP'
AND Date = current_date - 1
GROUP BY
date,
service_id,
status;
?
You could also combine all the assignments into a single SELECT:
SELECT
up_duration := SUM(CASE status WHEN 'UP' THEN duration ELSE 0 END)
down_duration := SUM(CASE status WHEN 'DOWN' THEN duration ELSE 0 END)
maint_duration := SUM(CASE status WHEN 'MAINT' THEN duration ELSE 0 END)
degr_duration := SUM(CASE status WHEN 'DEGR' THEN duration ELSE 0 END)
FROM daily_audit_summary
WHERE service_id = svc_id
AND status = 'UP'
AND Date = current_date - 1
GROUP BY
date,
service_id,
status;
But maybe you could avoid the cursor (and thus the loop) by using a single statement to do all the job:
INSERT INTO daily_status (
date,
service_id,
time_up,
time_down,
time_maint,
time_degraded
)
SELECT
d.Date,
s.service_id,
SUM(CASE das.status WHEN 'UP' THEN das.duration ELSE 0 END),
SUM(CASE das.status WHEN 'DOWN' THEN das.duration ELSE 0 END),
SUM(CASE das.status WHEN 'MAINT' THEN das.duration ELSE 0 END),
SUM(CASE das.status WHEN 'DEGR' THEN das.duration ELSE 0 END)
FROM services s
CROSS JOIN (SELECT CURRENT_DATE - 1 AS Date) AS d
LEFT JOIN daily_audit_summary AS das
ON s.service_id = das.service_id
AND das.Date = d.Date;
I guess that I needed to give a better explanation...
The Code is a Work In Progress and due to requirements, I cannot get rid of the "Services_cur" Cursor.
What I am finding is that when the query for the "Services_Cur" returns say 10 records and within the "the Loop" If I use a SELECT INTO statement from a TABLE such as
"SELECT F1 INTO MyVar from Atable where Afld = somevalue" the LOOP exits as if the "Services Cur" cursor was out of data???
If I issue a "SELECT 1234 INTO MyVar" the Loop works and I get 10 results (As Expected).
I am new to Stored Procedures for MySql and could not find an example of someone doing a series of "SELECT value for table" while within a Loop of FETCHES.
I hope this helps explain the issue better
Thanks for any help.
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.