SSRS add event data and manually updating SSRS Parameter - reporting-services

I am not sure if this is the best way, but it work for me. THe only downside is I need to add a random seconds of delay in order to use the current parameter.
Please look at my code below.
In SQL JOB of SSRS I modify step 1 of the SQL JOB. Prior to do exec addevent I update parameter value.
declare #xml as xml
set quoted_identifier ON
DECLARE db_cursor CURSOR FOR
SELECT C.[City]
FROM #FService C
GROUP BY C.[City]
ORDER BY C.[City]
DECLARE #CityLoop VARCHAR(50);
OPEN db_cursor;
FETCH NEXT FROM db_cursor INTO #CityLoop;
WHILE ##FETCH_STATUS = 0
BEGIN
-- THIS IS TO UPDATE XML
set #xml = (select Parameters
from ReportServer.dbo.Subscriptions sb
where sb.SubscriptionID = 'GUID')
set #xml.modify(' replace value of
(/ParameterValues/ParameterValue[2]/Value/text())[1] with
sql:variable("#CityLoop") ')
-- HERE WE UPDATE PARAMETER TABLE
update ReportServer.dbo.Subscriptions
set parameters = convert(nvarchar(max), #xml)
where SubscriptionID = 'GUID'
-- HERE IS THE ORIGINAL EXEC
exec [ReportServer].dbo.AddEvent #EventType='TimedSubscription', #EventData='GUID'
WAITFOR DELAY '00:00:30';
--RAISERROR (#DealLoop, 0, 1) WITH NOWAIT
FETCH NEXT FROM db_cursor INTO #CityLoop;
END;
CLOSE db_cursor;
DEALLOCATE db_cursor;
Notice we add a 30 seconds delay for the SSRS to pickup and execute the report.
is there a way to avoid random seconds and execute the report directly? or AddEvent is the only way to do this?
if I don't have a delay, the report will only run using the last #cityLoop parameter. that is why I add the delay then the job works properly by sending email every 30 seconds for each City that is available in the parameter query
I am using SSRS and SQL Server 2012
thank you for your help

I adapted this in order to solve the risky random wait times.
SQL Server central article[https://www.sqlservercentral.com/articles/dynamically-name-reportfile-export-in-ssrs]
It basically uses a wait for execution
-- WAIT for EXECUTION
set #bool1 = ISNULL((select eventdata from event where eventdata = #SBidEventData), '')
while #bool1 <> ''
begin
set #bool1 = ISNULL((select eventdata from event where eventdata = #SBidEventData), '')
if #bool1 = ''
begin
-- SET PATH AND PARAMETER TO THE WAY IT WAS
WAITFOR DELAY '000:00:07'
update Catalog
set Path = '/Report/Report1'
where ItemID = #ItemID
update Subscriptions
set Parameters = '<ParameterValues><ParameterValue><Name>Year</Name><Value>2019</Value></ParameterValue><ParameterValue><Name>Category</Name><Value>HONDA</Value></ParameterValue><ParameterValue><Name>Month</Name><Value>12</Value></ParameterValue></ParameterValues>'
where SubscriptionID = #SBidEventData
end
end

Related

MySQL Stored Procedure variable format not working in SELECT statement

I am having trouble getting a variable to work right in a MySQL Stored Procedure. Here is a sample of the stored procedure:
BEGIN
DECLARE x_parent varchar(100);
DECLARE term_ids_current varchar(100);
DECLARE term_ids_list INT(100);
DECLARE x_counter INT(11);
DECLARE y_counter INT(11);
DECLARE z_counter INT(11);
SET #term_ids_current = 189;
SET #term_ids_list = #term_ids_current;
SET #y_counter = 0;
SET #z_counter = 0;
parent_child: LOOP
# used for debugging #
SET #z_counter = #z_counter + 1;
# first i check to see if my criteria is met to continue the loop. #
IF EXISTS (SELECT tt.term_id FROM `bitnami_wordpress`.`wp_2_term_taxonomy` tt WHERE tt.parent IN (#term_ids_current)) THEN
# used for debugging #
SET #y_counter = #y_counter + 1;
BEGIN
DECLARE x_finished INT(11) DEFAULT 0;
DECLARE parent_child_cursor CURSOR FOR
SELECT tt.term_id FROM `bitnami_wordpress`.`wp_2_term_taxonomy` tt WHERE tt.parent IN (#term_ids_current);
# declare NOT FOUND handler
DECLARE CONTINUE HANDLER
FOR NOT FOUND SET x_finished = 1;
OPEN parent_child_cursor;
SET #x_counter = 0;
single_parent: LOOP
FETCH parent_child_cursor INTO x_parent;
IF x_finished = 1 THEN
LEAVE single_parent;
ELSEIF #x_counter = 0 THEN
SET #term_ids_current = x_parent;
ELSE
SET #term_ids_current = CONCAT (x_parent,",",#term_ids_current);
END IF;
SET #x_counter = #x_counter + 1;
END LOOP single_parent;
SET #term_ids_list = CONCAT (#term_ids_current,",",#term_ids_list);
CLOSE parent_child_cursor;
END;
ELSE
LEAVE parent_child;
END IF;
END LOOP parent_child;
# used for debugging #
SELECT #z_counter, #y_counter, #term_ids_current, #term_ids_list;
END;
In the single_parent loop I am populating the #term_ids_current variable with the results from my query. That variable is then used in the query for the main loop to determine if the criteria is still met. If it is then my loop happens again. I have tested each step of the procedure as an individual query and the results are accurate. The problem exists in getting the main query in the parent_child loop to run properly the second time.
When I run the SP above against my existing data set, the #term_ids_current variable contains this data set:
218,200,199,198,197,196,195,194,193,192,191,190
What I expect to happen is this data set is now passed in the query for the parent_child loop so that query would now be:
(SELECT tt.term_id FROM `bitnami_wordpress`.`wp_2_term_taxonomy` tt WHERE tt.parent IN (218,200,199,198,197,196,195,194,193,192,191,190))
When I run that query manually against my DB, the "IF EXISTS" statement is true meaning my parent_child loop should run again. But the loop is stopping after the IF EXISTS statement and not running again. I have verified this with the z_counter and y_counter variable. Every time I run the SP #z_counter = 2 and #y_counter = 1. So I know the IF statement is moving to the ELSE clause and not running.
To test this further I updated the SP and manually set my #term_ids_current variable at the end of the parent_child loop to see what would happen. So at the end of my altered SP I have set #term_ids_current this way:
SET #term_ids_current = (190,191,192,193,194,195,196,197,198,199,200,218);
IF #z_counter = 2 THEN
LEAVE parent_child;
END IF;
I added the second IF statement to keep the SP from running an endless loop.
Now when I execute the SP I get this error message: "MySQL said: #1241 - Operand should contain 1 column(s)." So I updated the SP again to SET the #term_ids_current variable, but this time I used double quotes instead of parentheses.
SET #term_ids_current = "190,191,192,193,194,195,196,197,198,199,200,218";
With the variable manually set the SP returns the expected result. I have tried to use both CAST and CONVERT in my SELECT statement to have the variable treated as a string. Here is an example:
SELECT tt.term_id FROM `bitnami_wordpress`.`wp_2_term_taxonomy` tt WHERE tt.parent IN (CONVERT(#term_ids_current,CHAR));
This does not work. If I were doing this in Python I would transform the variable for the SELECT statement with:
str(#term_ids_current)
But I cannot find a way to do this same thing in MySQL.
So my question is, how can I get my variable #term_ids_current to be treated as a string to work properly in my SELECT statement?
The query
SELECT tt.term_id
FROM `bitnami_wordpress`.`wp_2_term_taxonomy` tt
WHERE tt.parent IN (#term_ids_current)
will not work if the #term_ids_currentis a comma separated string. The reason for this is that #term_ids_current is treated as string and not as a set.
Couple of suggestions:
Instead of using a user defined variable (#var) use a temporary table for the current id's
It is better to use local variables (declare v_var int) instead of user defined variables in procedures as the scope of the user defined variable is the connection whereas the scope of the local variables is the procedure. This matters if your procedure calls other procedures that use same variables.
Try the suggestion given by #slaakso or use find_in_set
so you need to change the in clause into something similar to your codes
WHERE find_in_set(tt.parent,#term_ids_current)

Trigger Loop keeps putting currentStock on 0

So I'm trying to work out a trigger that automatically adds the ingredients from a order (dhh_purchaseorder) to the table dhh_ingredients. It should get the new ordered amount out of dhh_purchaseorderingredient and dump it into the orderAmount variable. Then it should combine the current available stock with the ordered amount in newStock. Then it should update the ingredient table and set the new amounts for the correct ingredients but for some reason it keeps putting the currentStock column in ingredients to 0.
Here is my trigger:
BEGIN
DECLARE orderStatus, orderIngredientName VARCHAR(50);
DECLARE finished INTEGER DEFAULT 0;
DECLARE currentStock, newStock DECIMAL(10,2);
DECLARE orderNo, orderAmount int(10);
DECLARE lookupCursor CURSOR FOR SELECT INGREDIENTingredientName from dhh_purchaseorderingredient WHERE dhh_purchaseorderingredient.PURCHASEpurchaseOrderNo = NEW.purchaseOrderNo;
DECLARE CONTINUE HANDLER FOR NOT FOUND SET finished = 1;
SET orderNo = NEW.purchaseOrderNo;
SET orderStatus = NEW.status;
IF(orderStatus = "DELIVERED") THEN
OPEN lookupCursor;
update_loop: LOOP
FETCH lookupCursor INTO orderIngredientName;
IF finished = 1 THEN
LEAVE update_loop;
END IF;
SET orderAmount = (SELECT amount from dhh_purchaseorderingredient WHERE dhh_purchaseorderingredient.PURCHASEpurchaseOrderNo = orderNo AND dhh_purchaseorderingredient.INGREDIENTingredientName = orderIngredientName);
SET currentStock = (SELECT currentStock FROM dhh_ingredient WHERE ingredientName = orderIngredientName);
SET newStock = currentStock + orderAmount;
INSERT INTO temp VALUES(currentStock);
UPDATE dhh_ingredient
SET currentStock = newStock
WHERE ingredientName = ingredientName;
END LOOP update_loop;
CLOSE lookupCursor;
END IF;
END
This is partly an answer and partly a suggestion for a better approach.
One of the many reasons I believe stored procedures are a very bad idea is that they are hard to debug. In this case, it's not easy to figure out what value is being assigned to the various stock amount variables.
A better approach is to use query that does update - without the need for SP variable. It's really easy to test it; just execute and inspect the results. By using such a query in your SP, you eliminate incorrect query logic from your code, allowing you to condense the SP to just the bits that matter - the update.
Fortunately, MySQL provides a way to update through a join, via its multi-table update syntax.
This is what the query could look like:
UPDATE dhh_ingredient, dhh_purchaseorderingredient
SET = currentStock = currentStock - amount
WHERE dhh_purchaseorderingredient.INGREDIENTingredientName = orderIngredientName
AND ingredientName = orderIngredientName
AND dhh_purchaseorderingredient.PURCHASEpurchaseOrderNo = NEW.purchaseOrderNo
AND NEW.status = 'DELIVERED';
No need for local variables or any loops, because the update query does a join and the NEW values are used directly in the query.
So your entire stored procedure becomes simply:
BEGIN
UPDATE dhh_ingredient, dhh_purchaseorderingredient
SET = currentStock = currentStock - amount
WHERE dhh_purchaseorderingredient.INGREDIENTingredientName = orderIngredientName
AND ingredientName = orderIngredientName
AND dhh_purchaseorderingredient.PURCHASEpurchaseOrderNo = NEW.purchaseOrderNo
AND NEW.status = 'DELIVERED';
END
which is easy to understand, maintain and debug - a better solution.

Why does MySQL find an error in the CONCAT statement?

I'm trying to write a stored procedure in MySQL, where I need to loop on a cursor, and execute an SQL statement that uses a piece of data from the cursor that gets fetched into a variable, then executed as SQL. The cursor orders_cur is sorted on this_addr; in a given block of this_addr records, the first record is skipped, and the rest need to be flagged by setting the duplicateorder="1".
For some reason, I cannot get the line with the CONCAT function to work without giving me an error:
OPEN orders_cur;
order_loop: LOOP
-- Now loop on orders_cur until this_addr = match_addr
find_addr_loop: REPEAT
FETCH orders_cur INTO this_addr,this_orderid;
UNTIL this_addr = match_addr
END REPEAT;
-- Skip the first order that matched by performing another fetch
FETCH orders_cur INTO this_addr,this_orderid;
-- Now start next loop which does the real work; set duplicateorder on the remaining records in cursor,
-- using the orders_cur.order_id to locate the actual record in the Reservations table.
set_dupe_loop: WHILE this_addr = match_addr
SET #sql = CONCAT('UPDATE Reservations SET duplicateorder = \'1\' WHERE order_id=',this_orderid);
PREPARE runme FROM #sql;
EXECUTE runme;
FETCH orders_cur INTO this_addr,this_orderid;
END WHILE set_dupe_loop:;
DEALLOCATE PREPARE runme;
END LOOP order_loop;
I have tried every variation possible on escaping the literals that I need around the '1' to no avail, and am going cross-eyed...if anyone sees where my error lies, I would very much appreciate it...
--rixter
You don't need a cursor for this operation. You can do:
UPDATE Reservations r JOIN
(SELECT this_addr, MIN(order_id) as minoi
FROM Reservations r2
WHERE this_addr = match_addr
GROUP BY this_addr
) dups
ON r.this_addr = dups.this_addr and r.order_id > dups.minoi
SET r.duplicateorder = 1;
In general, you should avoid cursors, especially those that require dynamic SQL. When you can express the logic as a set operation, it is usually best to do so.
Nothing wrong with the CONCAT, the loop is not initiated/enclosed properly.
This set_dupe_loop: WHILE this_addr = match_addr
should be this set_dupe_loop: WHILE this_addr = match_addr DO

Nested Cursor call is calling inner cursor only once in MYSQL

There are 2 procedures. When tested separately they execute fine.
When first SP calls 2nd SP, this is not called after the first call.
Please help resolve the issue.
First Cursor:
BEGIN
DECLARE vAttendEmpid,vNoOfDays, vempid INT DEFAULT 0;
DECLARE processed BOOLEAN DEFAULT FALSE;
DECLARE curEmp CURSOR FOR Select distinct empid as d1 from rawattendance where DATE_FORMAT( indatetime,'%m') = process_month and DATE_FORMAT( indatetime,'%Y') = process_year order by empid;
DECLARE CONTINUE HANDLER FOR NOT FOUND SET processed = TRUE ;
OPEN curEmp;
loopemp: LOOP
FETCH FROM curEmp INTO vEmpid;
IF processed THEN
CLOSE curEmp;
LEAVE loopemp;
END IF;
--select vEmpid;
CALL sp_attendance(vEmpid,process_month,process_year);
END LOOP loopemp;
END
2nd Cursor.. Nested cursor
BEGIN
DECLARE vInDateTime, vOutDateTime,vTempInDateTime, vTempOutDateTime DATETIME ;
DECLARE vAttendEmpid ,vDiffHr INT DEFAULT 0;
DECLARE eprocessed BOOLEAN DEFAULT FALSE;
DECLARE curAttendance CURSOR FOR Select empid, indatetime ,outdatetime from rawattendance where empid=vEmpid and DATE_FORMAT( indatetime, '%m' ) = process_month and DATE_FORMAT( indatetime, '%Y' ) = process_year
order by indatetime;
OPEN curAttendance;
att_loop:LOOP
FETCH curAttendance INTO vAttendEmpid, vInDateTime,vOutDateTime;
select concat ('In Time 0 ==',vInDateTime, ' out ==', vOutDateTime, ' Empid=',vAttendEmpid);
select 'looping';
IF eprocessed THEN
select 'loop end';
select concat ('In Time 4 ==',vTempInDateTime, ' out ==', vTempOutDateTime, ' Empid=',vAttendEmpid);
SET vDiffHr =TIMESTAMPDIFF(HOUR,vTempInDateTime,vTempOutDateTime);
insert into emp_attendance_processed(empid,in_date_time, out_date_time, workedhr)
values(vAttendEmpid,vTempInDateTime,vTempOutDateTime, vDiffHr);
SET vTempOutDateTime=vOutDateTime;
CLOSE curAttendance;
END IF;
END LOOP att_loop;
END
Try to reset your continue handler variable processed after the inner procedure is called.
SET processed = FALSE;
The Problem is that the HANDLER FOR NOT FOUND is not only executed if the cursor in your first procedure returns no rows, but also if a nested SELECT returns no rows. And as your second procedure contains SELECT statements, I believe one of them is returning an empty set at least one time. If that's not intended, it may have to do with faulty parameters assigned.
OPEN curEmp;
loopemp: LOOP
FETCH FROM curEmp INTO vEmpid;
IF processed THEN
CLOSE curEmp;
LEAVE loopemp;
END IF;
--select vEmpid;
CALL sp_attendance(vEmpid,process_month,process_year);
SET processed = FALSE;
END LOOP loopemp;

Is the error that I am using a cursor or is there an issue with the rest of this script?

This code works fine in SQL 2005 but appears to miss a random number of records from the end of the select in SQL 2008 or SQL 2008R2. I use this code to backup databases on my production servers. The 2008 Server has 37 db's on it (not counting tempdb) and it backs up between 17 to 35 of those db's each day (even though if I run the select I always get 37 rows returned). The job it is in completes with no errors, but doesn't back up all of the databases.
DECLARE #today VARCHAR(10)
SELECT #today = Convert(varchar(10),dateadd(day,0,Dateadd(day,datediff(day,0,getdate()),0)),120)
DECLARE #DBName varchar(500)
DECLARE DB_Cursor CURSOR FOR
SELECT name FROM sys.databases
OPEN DB_Cursor;
FETCH NEXT FROM DB_Cursor INTO #DBNAME
WHILE ##FETCH_STATUS = 0
BEGIN
IF #DBNAME <> 'tempdb'
BEGIN
declare #Path varchar(500)
select #Path = 'g:\DBBackups\'
declare #FileName varchar(4000)
select #FileName = #Path + #DBNAME + '_Full_' + #today + '.bak'
BACKUP DATABASE #DBName
TO DISK = #FileName
WITH NoInit, NoFormat, SKIP
END
FETCH NEXT FROM DB_Cursor INTO #DBNAME;
END;
CLOSE DB_Cursor;
DEALLOCATE DB_Cursor;
I talked about this briefly starting here:
Execute a Command in the Context of Each Database in SQL Server - Part 2
Basically, change the cursor to use READ_ONLY LOCAL FORWARD_ONLY STATIC and you shouldn't be affected by locks or changes to databases (which is the only wild stab guess I have for an actual explanation). This was the only true difference I could find in all the cases where the mysterious database skipping did not occur.
I did not attempt to investigate the values of ##FETCH_STATUS - it is possible that as the cursor progresses, this changes to values other than 0 and -1 (I usually check against the latter, not the former). So maybe change WHILE ##FETCH_STATUS = 0 to WHILE ##FETCH_STATUS <> -1.
Or better yet, don't use a cursor at all. :)
The script is heavily flawed. It doesn't check the state of the database so if you have a database that is not online like a database mirror or log shipping secondary, the process will fail and all databases that come after it won't be backed up. Also, your using the wrong data types