I have a stored procedure for some selection in JSON.
CREATE PROC [pr_MySP]
-- params
WITH ENCRYPTION
AS
BEGIN
SELECT
...
FOR JSON PATH;
END
And now I want to use this stored procedure in another.
CREATE PROC [pr_MySP_1]
-- params
WITH ENCRYPTION
AS
BEGIN
DECLARE #result_sp NVARCHAR(MAX);
EXEC #result_sp = [pr_MySP];
SELECT #result_sp;
END
But when I try SELECT #result_sp; it return 0.
What am I doing wrong?
I do not know what else you are doing within your first SP, but this might be better solved within an inlineable UDF:
CREATE FUNCTION dbo.CreateJSON()
RETURNS NVARCHAR(MAX)
AS
BEGIN
RETURN (SELECT TOP 10 * FROM sys.objects FOR JSON AUTO);
END
GO
--You can use the UDF in any context. You can define parameters to control the behaviour
DECLARE #TheJson NVARCHAR(MAX);
SET #TheJson=dbo.CreateJSON();
SELECT #TheJson;
GO
--Clean up
DROP FUNCTION dbo.CreateJSON;
In most cases an inline TVF is better in performance! But you'd have to join its resultset, which is not so intuitive...
You can store it in table variable instead.
DECLARE #result_sp (results NVARCHAR(MAX))
INSERT INTO #result_sp
EXEC [pr_MySP]
SELECT TOP 1 results FROM #result_sp
and if you still wanted it to store in a normal variable,
DECLARE #final NVARCHAR(MAX)
DECLARE #result_sp (results NVARCHAR(MAX))
INSERT INTO #result_sp
EXEC [pr_MySP]
SELECT TOP 1 #final = results FROM #result_sp
SELECT #final
Related
I am a beginner to MYSQL and currently practicing stored procedures. I am trying to create a procedure that should fetch a row of an entire field when given an input parameter. Is there any workaround for this? If yes, then it would be an immense help.
Many Thanks
Welcome to the Forum.
A stored procedure can simple consist of a SELECT statement that returns all the columns in the row you want. e.g.
CREATE PROCEDURE `myproc`(IN `p_id` INT(11))
READS SQL DATA
SELECT id, COL2, COL3, ETC
FROM `mytable`
WHERE id = 'p_id';
If you need to traverse a table, looking at every row that meets some criteria, then you need to DECLARE a "cursor", and use that to fetch the rows you want. Here is a template I use to create new procedures that need to traverse a table - it can be adapted to suit your needs. In this template I nly retrieve two columns from the cursor, but you can retrieve any number. You have to declare a local variable for each column in your SELECT so the FETCH statement has somewhere to put the data it retrieves from the cursor:
CREATE PROCEDURE `traverse_table`()
READS SQL DATA
BEGIN
DECLARE var_id INT(11) UNSIGNED;
DECLARE var_data varchar(16383);
DECLARE done INT DEFAULT FALSE;
DECLARE ecode varchar(1000) ;
DECLARE emsg varchar(1000) ;
DECLARE nextrecord CURSOR FOR # here is the CURSOR DECLARATION
SELECT `myid`,`mydata` # It will return these rows, one
FROM `mytable` # at a time
WHERE `mydata` LIKE "ABC%";
DECLARE CONTINUE HANDLER FOR NOT FOUND SET done = TRUE;
DECLARE CONTINUE HANDLER FOR SQLEXCEPTION
BEGIN
GET DIAGNOSTICS CONDITION 1 ecode = RETURNED_SQLSTATE, emsg = MESSAGE_TEXT;
SELECT var_id,var_data,ecode,emsg;
END;
OPEN nextrecord;
FETCH nextrecord into var_id,var_data; # here is the first Fetch
WHILE (done = FALSE) DO
# >>>>>> do something with var_id,var_data here <<<<<<
SET done = FALSE;
FETCH NEXT FROM records into var_id,var_data; # here is the second and subsequent Fetch
END WHILE;
CLOSE nextrecord;
END
It is necessary to write a procedure that makes a request, which, in turn, should output all the objects of a particular column.
Here is what I tried:
CREATE PROCEDURE AttributeRequest(n CHAR(200))
begin
SELECT n FROM table;
end
But this variable is perceived as the name of the column itself and nothing comes out.
Tell me how to make such a request by the attribute of the object, please
I searched a bit, read the answers below, and I managed to implement this task in the following way:
CREATE PROCEDURE AttributeRequest(n CHAR(200))
begin
SET #t =CONCAT("SELECT ",n ," FROM table");
PREPARE e FROM #t;
EXECUTE e;
end
you will have to use dynamic sql for this
CREATE PROCEDURE AttributeRequest(#n CHAR(200)) AS
begin
declare #query varchar(1000);
set #query = 'select ['+#n+'] FROM table;
exec AttributeRequest #query
end
then you can excute it ,
exec AttributeRequest 'columnname'
I'm having a lot of troubles with one situation on my DB.
I'm trying to code a Stored Procedure that inserts an entry to a log table for audit purposes after a SELECT is done in a specific table (the idea is that it should work similar to how a AFTER SELECT Trigger would work). The Stored Procedure has one input parameter, the WHERE condition/clause.
The user executes the SP and writes the condition (for example IDCultura=1). The SP uses that parameter to make a SELECT statement like this: SELECT * FROM dba.medicoes WHERE *IDCultura=1*
My problem arrives when I try to make a cursor that loops the results from that query, so it inserts one line in the log table for each result of the SELECT.
I can't use the parameter as WHERE clause, but if I manually write the same text in the clause, it works.
I've seen some solutions that use a CONCAT to join all the parts of the query before execution. But because I'm using the SELECT query when declaring the cursor, I can't SET a variable before.
Here's the code I'm working with right now:
CREATE DEFINER=`root`#`localhost` PROCEDURE `select_medicoes`(
whereCondicao varchar(200))
BEGIN
DECLARE ID_novo int;
DECLARE ID_Variavel int;
DECLARE ID_Cultura int;
DECLARE NumMed int;
DECLARE DataHoraMed date;
DECLARE ValorMed int;
DECLARE done INT DEFAULT FALSE;
DECLARE curs_medicoeslog cursor for
(SELECT *
FROM `dba`.medicoes
WHERE `whereCondicao`);
DECLARE CONTINUE HANDLER FOR NOT FOUND SET done = TRUE;
SELECT `whereCondicao`;
OPEN curs_medicoeslog;
read_loop: LOOP
FETCH curs_medicoeslog INTO ID_Variavel, ID_Cultura, NumMed, DataHoraMed, ValorMed;
INSERT INTO log_medicoes (IDVariavel, IDCultura, NumMedicao, DataHoraMedicao, ValorMedicao, Utilizador, `Data`, Operacao)
VALUES (ID_Variavel, ID_Cultura, NumMed, DataHoraMed, ValorMed, current_user(), now(), 'S');
IF done THEN
LEAVE read_loop;
END IF;
END LOOP;
CLOSE curs_medicoeslog;
END
I am curious how to reference an existing stored procedure SELECT statement from a secondary query or SET call within the same stored procedure.
For example:
CREATE PROCEDURE 'mysp' (OUT sumvalue INT)
BEGIN
-- This is the core recordset the SP returns
SELECT * FROM Table
-- I also want to return a value based on the above recordset
SET sumvale = SUM(previousselect.values)
END
Essentially, I have a SP that returns a detailed recordset, and I need to return SUM and custom values based on the data within that recordset. The issue is I cannot figure out how to reference the data after the SELECT statement (e.g. does it create an internal reference I can use such as #recordset1.X).
Any help would be appreciated.
Try using cursor from this link:
As MySql does not allow you to return a recordset from either store procedures or functions, you could try this:
CREATE DEFINER=`root`#`localhost` PROCEDURE `some_procedure`(out some_id int)
BEGIN
declare done boolean default false;
declare id int;
declare tot decimal(10,2);
declare some_cursor cursor for
select id, total from some_table where id = some_id;
declare continue handler for not found set done = true;
open some_cursor;
loop1: loop
fetch some_cursor into id, tot;
if done=true then
leave loop1;
end if;
//do your calculation here or whatever necessary you want to do with the code
end loop loop1;
END;
Consider the following stored procedure :
create procedure [dbo].[MyTest] ( #p_SqlStatement nvarchar(max) )
as
begin
exec sp_executesql #p_SqlStatement
if ##ROWCOUNT = 1
begin
select 1;
end
else if ##ROWCOUNT <> 1
begin
select 0;
end
end
This stored procedure currently returns 2 datasets, one with the exec sp_executesql #p_SqlStatement data, and the other one would be either 1 or 0. Is there a way to suppress the first dataset? I mean, would it be possible that this stored procedure returns only 1 or 0 ?
I tried adding a RAISERROR( 'MyError', 18, 1 ) right after the exec sp_executesql #p_SqlStatement and then in the catch block select something else, but the first result set is always returned to my stored procedure caller...
You can embed the query in an if exists(.
alter procedure [dbo].[MyTest] ( #p_SqlStatement nvarchar(max) )
as
begin
set #p_SqlStatement = 'if exists('+#p_SqlStatement+') select 1 else select 0'
exec sp_executesql #p_SqlStatement
end
There are however some queries where this won't fly.
Multiple statements
Queries terminated with ;
Queries that uses CTE's
There might be more but these are the ones I can think of right now.
Update:
You could try to use openrowset.
alter procedure [dbo].[MyTest] ( #p_SqlStatement nvarchar(max) )
as
begin
declare #S nvarchar(max)
set #S =
'if exists(
select *
from openrowset(
''SQLNCLI'',
''Server=localhost;Trusted_Connection=yes;'',
'+quotename(#p_SqlStatement, '''')+'
) as T
)
select 1
else
select 0'
exec (#S)
end
I have never used this in productions but from the tests I made i looks like it should work with SP's, CTE's and multiple lines.
You have to allow ad hoc distributed queries.
You may try the NOCOUNT statement ( http://msdn.microsoft.com/en-us/library/ms189837.aspx )
create procedure [dbo].[MyTest] ( #p_SqlStatement nvarchar(max) )
as
begin
SET NOCOUNT ON;
exec sp_executesql #p_SqlStatement
if ##ROWCOUNT = 1
begin
select 1;
end
else if ##ROWCOUNT <> 1
begin
select 0;
end
SET NOCOUNT OFF;
end
try this approach:
declare #mycount bigint
exec sp_executesql N'select #mycount = count(name) from Page where name like ''P%''', N'#mycount bigint OUTPUT', #mycount OUTPUT
select #mycount
It is important that your statement #p_SqlStatement incorporates a count.
If that is not the case, meaning your want to run this sp for any SQL you come upon, then this is no help. I don't think your can't suppress output of the sp_executesql.
Edit: You could also try this:
declare #mycount bigint
exec sp_executesql N'SELECT * INTO ##MyTempTable from Page where name like ''P%'''
select count(*) from ##MyTempTable
drop table ##MyTempTable
This means that you will have to add the following to every query (don't know if this works with sp's?) "SELECT * INTO ##MyTempTable FROM " - That shouldn't be to hard.
"##temptables" are global scoped temptables. Which means that they are also available outside the sp_executesql sp. You must drop the table explicitly.
I was not able to find any other workaround than using the suggested OPENROWSET. However, I found a way to be independant from the servername / instance. I still have to reconfigure the server to accept ad hoc distributed queries. Here is the final result :
create procedure [dbo].[MyTest] ( #p_SqlStatement nvarchar(max) )
as
begin
declare #sql nvarchar(max) = N'SELECT * INTO ##TMP FROM OPENROWSET(''SQLOLEDB'',''Server=' + ##SERVERNAME + ';Trusted_Connection=Yes;'',''' + #p_SqlStatement + ''')';
exec sp_executesql #sql
if ( select COUNT(1) from ##TMP ) = 1
begin
select 1;
end
else
begin
select 0;
end
drop table ##TMP;
end
This solution has it's limitations :
Every column in the #p_SqlStatement must have a name
I have to enable Ad Hoc Distributed Queries on my server.
I have to use #sql variable along with sp_executesql on the OPENROWSET because I was not able to use variables inside OPENROWSET in another way, so this makes dynamic SQL on an OPENROWSET, which is pretty bad performance-wise.
I do reconfigure the server using the following script :
sp_configure 'Ad Hoc Distributed Queries', 1;
RECONFIGURE;
GO