Trying to do something very simple.
Want to Run a SP that checks if one of the arguments is null. If so, store an empty string in a log table, otherwise store the value.
CREATE DEFINER=`root`#`localhost` PROCEDURE `SP_TestVariable`(
Code1 varchar(255) ,
CodeToTest varchar(255)
)
BEGIN
CASE
WHEN CodeToTest IS NULL THEN
set #FinalCode = ' '
ELSE
set #FinalCode = CodeToTest
end
-- Now do the insert into the log table
INSERT INTO `TempLogTable` ( strField1, strField2)
VALUES (Code1 , #FinalCode );
You can reduce all that to a simle query
INSERT INTO `TempLogTable` (strField1, strField2)
SELECT Code1, coalesce(CodeToTest, '')
and for that I would even drop the procedure and just use the query instead.
Related
I have made a stored procedure. I want it to filter the data by different parameters. If I pass one parameter, it should be filtered by one; if I pass two, it should be filtered by two, and so on, but it is not working.
Can anyone help me please?
DROP PROCEDURE IF EXISTS medatabase.SP_rptProvince2;
CREATE PROCEDURE medatabase.`SP_rptProvince2`(
IN e_Region VARCHAR(45)
)
BEGIN
DECLARE strQuery VARCHAR(1024);
DECLARE stmtp VARCHAR(1024);
SET #strQuery = CONCAT('SELECT * FROM alldata where 1=1');
IF e_region IS NOT NULL THEN
SET #strQuery = CONCAT(#strQuery, ' AND (regionName)'=e_Region);
END IF;
PREPARE stmtp FROM #strQuery;
EXECUTE stmtp;
END;
AFAIK, you can't have a variable argument list like that. You can do one of a couple of things:
Take a fixed maximum number of parameters, and check them for null-ness before concatenating:
CREATE PROCEDURE SP_rptProvince2(a1 VARCHAR(45), a2 VARCHAR(45), ...)
...
IF a1 IS NOT NULL THEN
SET #strQuery = CONCAT(#strQuery, ' AND ', a2);
END IF;
If you need predetermined fields to which the criteria in the argument apply (like the e_Region parameter in your existing code), then you modify the CONCAT operation appropriately.
Possible invocation:
CALL SP_rptProvince2('''North''', 'column3 = ''South''')
Take a single parameter that is much bigger than just 45 characters, and simply append it to the query (assuming it is not null).
Clearly, this places the onus on the user to provide the correct SQL code.
Possible invocation:
CALL SP_rptProvince2('RegionName = ''North'' AND column3 = ''South''')
There's not a lot to choose between the two. Either can be made to work; neither is entirely satisfactory.
You might note that there was a need to protect the strings in the arguments with extra quotes; that is the sort of thing that makes this problematic.
I found a JSON-based approach which works with the latest MySQL/MariaDB systems. Check the link below (Original Author is Federico Razzoli): https://federico-razzoli.com/variable-number-of-parameters-and-optional-parameters-in-mysql-mariadb-procedures
Basically, you take a BLOB parameter which is actually a JSON object and then do JSON_UNQUOTE(JSON_EXTRACT(json object, key)) as appropriate.
Lifted an extract here:
CREATE FUNCTION table_exists(params BLOB)
RETURNS BOOL
NOT DETERMINISTIC
READS SQL DATA
COMMENT '
Return whether a table exists.
Parameters must be passed in a JSON document:
* schema (optional). : Schema that could contain the table.
By default, the schema containing this procedure.
* table : Name of the table to check.
'
BEGIN
DECLARE v_table VARCHAR(64)
DEFAULT JSON_UNQUOTE(JSON_EXTRACT(params, '$.table'));
DECLARE v_schema VARCHAR(64)
DEFAULT JSON_UNQUOTE(JSON_EXTRACT(params, '$.schema'));
IF v_schema IS NULL THEN
RETURN EXISTS (
SELECT TABLE_NAME
FROM information_schema.TABLES
WHERE
TABLE_SCHEMA = SCHEMA()
AND TABLE_NAME = v_table
);
ELSE
RETURN EXISTS (
SELECT TABLE_NAME
FROM information_schema.TABLES
WHERE
TABLE_SCHEMA = v_schema
AND TABLE_NAME = v_table
);
END IF;
END;
I have a simple mysql procedure which must return an query string. But, it returns almost every time QueryResult (column name) as <null> value.
create procedure return_table_rename_query(
IN targetTable VARCHAR(100),
IN tblPrefix VARCHAR(100)
)
BEGIN
SET #returnQuery = CONCAT('SELECT "MYSQLIMPORT can not rename table for target ', #targetTable, '";');
SET #totalRows = (SELECT COUNT(*) FROM table);
if IFNULL(#totalRows, 0) > 0
then
SET #returnQuery = CONCAT('drop table if exists table_name.', ...);
end if;
SELECT #returnQuery AS 'QueryResult';
end;
#targettable is not the same variable as targettable - you are mixing user defined variables and parameter variables and it seems likely that #targettable is null and if any element in a concat is null then the result is null.
Please read How to declare a variable in MySQL?
Simple version of the DDL:
create function rm00dv1.no_concurrent_schedules()
returns integer
LANGUAGE SQL
READS SQL DATA
NO EXTERNAL ACTION
NOT DETERMINISTIC
BEGIN
declare num_overlaps integer;
select count(*)
into num_overlaps
from
rm00dv1.schedules a
where
a.id != 0
and
exists (
select 1
from rm00dv1.schedules b
where
b.id = 0 -- matches the key of a given record
and rm00dv1.isConcurrent(b.schdl_eff_dt, b.schdl_trm_dt, a.schdl_eff_dt, a.schdl_trm_dt) != 0
);
return num_overlaps;
end;
Table:
create table rm00dv1.schedules (
id int not null,
schdl_eff_dt date not null,
schdl_trm_dt date not null,
info_chg_ts timestamp(6) not null with default
)
in RM00DV1.TSRMDV01 ;
alter table rm00dv1.schedules add constraint no_schedule_overlap
check ((schdl_trm_dt < '01/01/2015')
or
rm00dv1.no_concurrent_schedules() <= 0);
I am getting an SQL00551N - no execution privilege and that is odd because I can execute the function in a select statement.
Any idea to solve this problem?
Thanks.
Looks like you can't. I'm looking at the DB2 10 for z/OS reference for ALTER TABLE reference and it says the following under CHECK (check-condition): "A check-condition is a search condition, with the following restrictions: ... must not contain... Built-in or user-defined functions...".
Since your function looks like it won't convert to a check condition, defining triggers on the table might be the next best option.
I learned that AFTER triggers do not get a -746 like BEFORE triggers do. I had really wanted to use a CONSTRAINT because that best captures the intent for people who come after me, with a BEFORE trigger to terminate the active schedules. But, it looks like a sequence of triggers is going to be the way to go. It is a bit clunky because the triggers all have to be created separately and you have to look at them together to get the intent, and because correct behavior is dependent on their creation order. Yes, it is documented that they will be executed in the order of their creation.
Happy path termination of rows without a specified termination date:
CREATE TRIGGER terminate_no_trm
after
INSERT ON schedules
referencing new as new
FOR EACH ROW
MODE DB2SQL
BEGIN ATOMIC
update schedules
set
schdl_trm_dt = max(schdl_eff_dt, new.schdl_eff_dt - 1 days) -- prob not necessary, but don't set the trm before the eff
, info_chg_ts = new.info_chg_ts
where
new.keyCombo = keyCombo
and
schdl_trm_dt = '9999-12-31'
and schdl_eff_dt < new.schdl_eff_dt;
end
Prevent insert of rows if that insert causes an overlap:
CREATE TRIGGER no_overlapping_schedules_i
after
insert ON schedules
referencing new as n
FOR EACH ROW
MODE DB2SQL
when (num_concurrent_schedules(n.keyCombo) > 0)
begin atomic
SIGNAL SQLSTATE '75001' (
'Concurrent schedules detected: '
concat ' ' concat cast(n.keyCombo as varchar(32))
concat ': ' concat cast(n.schdl_eff_dt as varchar(32))
concat ' to ' concat cast(n.schdl_trm_dt as varchar(32))
);
end
and prevent UPDATE if that would result in an overlap
CREATE TRIGGER no_overlapping_schedules_u
after
update ON schedules
referencing new as n
FOR EACH ROW
MODE DB2SQL
when (num_concurrent_schedules(n.keyCombo) > 0)
begin atomic
SIGNAL SQLSTATE '75001' (
'Concurrent schedules detected: '
concat ' ' concat cast(n.keyCombo as varchar(32))
concat ': ' concat cast(n.schdl_eff_dt as varchar(32))
concat ' to ' concat cast(n.schdl_trm_dt as varchar(32))
);
end
Thanks for the ideas.
I have this stored procedure query. I'm using this code in my vb.net in dataset so i need is to pass parameter in my every where clause. or can i pass my whole where clause in this stored procedure from my vb.net.If not how can i do the "where IN clause" because im getting error if I'm call my stored procedure.
Maybe someone can give me some idea how can i handle this problem.
DELIMITER $$
DROP PROCEDURE IF EXISTS `lcs_rdb`.`sp_MissedCallsReport`$$
CREATE DEFINER=`root`#`localhost` PROCEDURE `sp_MissedCallsReport`()
BEGIN
select
cdr_extension_no, cdr_charge_to, COUNT(cdr_call_type_code) as answered,
SUM(cdr_call_type_code = 'BSY') as Busy,
sum(cdr_call_type_code = 'ABN') as abandon,
sum(cdr_call_type_code in ('BSY','ABN')) as total,
coalesce((sum(case cdr_call_type_code when 'ABN' then cdr_duration_number/60000 else 0 end) / sum(cdr_call_type_code = 'ABN')),0) as avg_abandon,
coalesce((sum(cdr_call_type_code in ('BSY','ABN')) /
(sum(cdr_call_type_code in ('BSY','ABN')) + COUNT(cdr_call_type_code))) *100,0) as missed_calls_rate
from cdr_departments
where cdr_site_id = '{0}' AND
cdr_datetime BETWEEN '{1}' AND '{2}'
AND cdr_call_class_id IN({3}) AND cdr_call_type_id IN({4})
AND cdr_extension_id IN({5}) or cdr_route_member_id IN ({6})
GROUP BY cdr_extension_no;
END$$
DELIMITER ;
I suggest you use IN parameters for your stored procedure to use with WHERE clause.
Example:
PROCEDURE `sp_MissedCallsReport`(
IN param_cdr_site_id INT
, IN param_cdr_datetime_min DATETIME
, IN param_cdr_datetime_max DATETIME
, IN param_cdr_call_class_id_csv VARCHAR(1024) -- csv integers
, IN param_cdr_call_type_id_csv VARCHAR(1024) -- csv integers
, IN param_cdr_extension_id_csv VARCHAR(1024) -- csv integers
, IN param_cdr_route_member_id_csv VARCHAR(1024) -- csv integers
)
I suggest CSV form of int valuess as varchar params for parameter placeholder numbers 3,4,5, and 6. It is because, you want to use them with IN as a set to search in them. As we can't pass an array of values as procedure parameters, we can make use of CSV form as an alternative.
And as the input is in CSV format, the IN is not suitable to use for search.
We can use FIND_IN_SET with CSV values.
Example: (for your where clause):
where cdr_site_id = param_cdr_site_id
AND cdr_datetime BETWEEN param_cdr_datetime_min AND param_cdr_datetime_max
AND FIND_IN_SET( cdr_call_class_id, param_cdr_call_class_id_csv )
AND FIND_IN_SET( cdr_call_type_id, param_cdr_call_type_id_csv )
AND ( FIND_IN_SET( cdr_extension_id, param_cdr_extension_id_csv )
or FIND_IN_SET ( cdr_route_member_id, param_cdr_route_member_id_csv ) )
Refer to:
CREATE PROCEDURE Syntax
FIND_IN_SET( str, strlist )
I have a variable table:
DECLARE #A_Table TABLE(ID INT, att1 VARCHAR(100), att2 nvarchar(200))
I want to make dynamic sql, so I insert into this table some data (all inside a loop):
WHILE (#i <= 100) BEGIN
SELECT #other_att = NAME FROM #other_Table where ID = #i;
SET #sql = 'INSERT ' + #A_Table+ '(ID,att1,att2) SELECT '+CAST(#i AS VARCHAR)+' , '''+ #other_att+''', SUM('+ #other_att') FROM '+ #EVEN_OTHER_Table;
EXEC (#sql);
END
sql every time would look like:
INSERT INTO #A_Table SELECT 1 , 'subject', SUM(subject)
INSERT INTO #A_Table SELECT 2 , 'age', SUM(age)
INSERT INTO #A_Table SELECT 3 , 'sex', SUM(sex)....
AND after executing this :
SO I will get:
#A_Table:
id att1 att2
1 subject 4.3
2 age 4.5
3 sex 4.1
but I get an error:
Msg 137, Level 16, State 1, Line 48
Must declare the scalar variable "#A_Table".
SO what is it the syntax to insert dynamically into a variable table?
Ok I have understood it.
You could use the INSERT ... EXEC syntax to insert the data returned by the dynamic SELECT. Of course, you would then need to remove the INSERT part from the dynamic statement.
WHILE (#i <= 100) BEGIN
SELECT #other_att = NAME FROM #other_Table where ID = #i;
SET #sql = 'SELECT '+CAST(#i AS VARCHAR)+' , ''' + #other_att+''', SUM('+ #other_att + ') FROM '+ #EVEN_OTHER_Table;
INSERT INTO #A_Table (ID,att1,att2)
EXEC (#sql);
END
You have a table variable, not a variable that contains the table name.
So you would need the following.
WHILE (#i <= 100) BEGIN
SELECT #other_att = NAME FROM #other_Table where ID = #i;
SET #sql = 'INSERT INTO #A_Table (ID,att1,att2) SELECT '+CAST(#i AS VARCHAR)+' , '''+ #other_att+''', SUM('+ #other_att') FROM #EVEN_OTHER_Table';
EXEC (#sql);
END
You would also need to declare the table variable as a statement inside the #sql variable, and execute your declare table and inserts together, or use a local/global temporary table.
With a local temporary table (stored in the tempdb) you could do something like this.
CREATE TABLE #testtbl (ID INT);
EXEC ('INSERT INTO #testtbl VALUES (1)');
SELECT * FROM #testtbl
DROP TABLE #testtbl
Some good info about temporary tables in BOL
http://msdn.microsoft.com/en-us/library/ms174979.aspx - quite far down the page
And the table type.
http://msdn.microsoft.com/en-us/library/ms175010.aspx
Your EXEC statement occurs in a different context and is therefore unaware of any variables created in your original context.
To create dynamic insert query it is really a task, I also struggle to find it ,finally I have tried in the following way and it's successfully working. Please find the code below.
CREATE PROCEDURE [dbo].[InsertTodaysData] (#tbl varchar(50),#Days int,
#MDate varchar(50), #EValue varchar(50), #Speed varchar(50),
#Totalreturn varchar(50),#Closingv varchar(50), #TotalReturnV varchar(50))
AS
BEGIN
-- SET NOCOUNT ON added to prevent extra result sets from
-- interfering with SELECT statements.
SET NOCOUNT ON;
DECLARE #SQLQuery varchar(2000)
-- Insert statements for procedure here
set #SQLQuery = 'INSERT INTO '+#tbl+' (ID,MDate,EValue,Speed,TotalReturnRatio,ClosingValue,
TotalReturnValue) VALUES ('+#Days+','''+#MDate+''', '+#EValue+', '+#Speed+',
'+#Totalreturn+', '+#Closingv+', '+#TotalReturnV+')'
EXECUTE(#SQLQuery)
END
Hope this will help you..