I have the following stored procedure:
CREATE PROCEDURE MergeTable #TableName VARCHAR(256)
AS
BEGIN
DECLARE #Test VARCHAR(max)
SET #Test = (SELECT Query
FROM dbo.QueryMergeDWH
WHERE SourceTableName = #TableName)
EXEC #Test
END
GO
EXEC MergeTable #TableName = 'SGPREINVOICE'
The table dbo.QueryMergeDWH holds queries for different tables I want to run using the stored procedure. The variable #Test holds the query (a MERGE statement) for the table 'SGPREINVOICE' in this case.
However, when I run this code I get the following error:
Msg 203, Level 16, State 2, Procedure MergeTable, Line 11 [Batch Start Line 19]
The name 'MERGE dwh.SGPREINVOICE t
USING stg.SGPREINVOICE s
ON t.VOUCHER = s.VOUCHER
WHEN NOT MATCHED
THEN INSERT VALUES (BATCHNAME, COMPANYNR, BUDGETHOUDER, VENDORNR, BANKACCOUNTID, [DESCRIPTION], EXTERNALINVOICENR, TAXVERLEGD, INVOICEDATE, BOOKINGDATE, INVOICEAMOUNTDEB, INVOICEAMOUNTCRED, SGAMOUNTHIGH, SGAMOUNTLOW, SGAMOUNTNOTAX, SGTAXAMOUNTLOW, SGTAXAMOUNTHIGH, SGTAXTTV, G_ACCOUNT, G_AMOUNT, INVOICETYPE, SGAUTOINCASSO, SGIMPORTPROCESSID, SGINVOICEBLOCKED, SGCOMPANYNRORIG, SGVENDORNRORIG, SGBANKACCOUNTIDORIG, SGG_ACCOUNTORIG, SGPOSTBUSNR, SGADDRESS, SGHUISNR, SGHUISNRTOEV, SGPOSTCODE, SGWOONPL, SGBTWNR, SGBEDRIJFSNM, SGINVOICEAMOUNTORIG, V' is not a valid identifier.
It seems like the variable #Test does not contain the entire statement but only part of it. How can I solve this problem?
The actual reason for this error is the difference between EXEC #Test (to execute a stored procedure or function) and EXEC (#Test) (to execute a dynamic statement). Note, that you may use sp_executesql to execute a dynamically generated statement:
CREATE PROCEDURE MergeTable #TableName VARCHAR(256)
AS
BEGIN
DECLARE #Test VARCHAR(max)
SELECT #Test = Query
FROM dbo.QueryMergeDWH
WHERE SourceTableName = #TableName
EXEC (#Test)
-- Or using sp_executesql
-- DECLARE #err int
-- EXEC #err = sp_executesql #Test
-- IF #err <> 0 PRINT 'Error'
END
GO
EXEC MergeTable #TableName = 'SGPREINVOICE'
Related
I was unable to find the solution for the issue and unable to execute a stored procedure. Am I missing anything in the stored procedure?
Main aim is to run multiple tables inserts into different tables (different metadata).
This is my stored procedure:
CREATE OR ALTER PROCEDURE newsample
AS
BEGIN
DECLARE #sqlquery varchar(max);
SET #sqlquery = 'insert into dbo.table1 select 2 as newcol;
insert into dbo.table2 select 2 as newcol;
insert into dbo.table3 select 2 as newcol;
insert into dbo.table4 select 2 as newcol;';
EXEC #sqlquery
END
EXEC dbo.newsample
Error:
Msg 2812, Level 16, State 62, Procedure dbo.newsample, Line 6 [Batch Start Line 9]
Could not find stored procedure 'insert into dbo.table1 select 2 as newcol'.
Appreciate your help.
Thank you
To execute arbitrary dynamic sql, you have to use exec(), not exec.
exec('select 0'); exec(#myDynamicSql);
If you use exec arg, then arg is a stored procedure or function, or the name of a stored procedure or function. Yep, it can be a function. And these can either be the literal names, or strings with the value of the name. All of the following works:
create procedure dbo.p as begin set nocount on; end;
go
create function dbo.funcwithoutargs() returns int as begin return 2; end
go
create function dbo.funcwithargs(#i int) returns int as begin return #i; end
go
declare #i int = 0, #module sysname;
exec dbo.p;
exec #i = dbo.funcwithoutargs;
exec #i = dbo.funcwithargs #i;
set #Module = 'dbo.p';
exec #module;
set #module = 'dbo.funcwithoutargs';
exec #i = #module;
The line
EXEC #sqlquery
Should be
EXEC sp_executesql #stmt = #sqlquery
CREATE PROC AllRowsAndagain
#table1 NVARCHAR(128)
AS
BEGIN
select count(*) FROM #table1
END;
I am getting this error -
Msg 1087, Level 16, State 1, Procedure AllRowsAndagain11, Line 9 [Batch Start Line 0]
Must declare the table variable "#table1".
I want to pass the tablename as a parameter here
You can't use a variable to replace the name of a table. You would need to use dynamic code for that. Fortunately, there's a more efficient way to retrieve the row count from any table.
CREATE PROC AllRowsAndagain
(
#table1 NVARCHAR(128)
)
AS
BEGIN
SELECT SUM(row_count) AS row_count
FROM sys.dm_db_partition_stats
WHERE OBJECT_NAME( object_id) = #table1
AND index_id IN (0,1);
END;
Like many have hinted in the comments you will need to use dynamic SQL. Dynamic SQL however can be unsafe and you should try and avoid using it.
To answer your question you would need to use something like this:
CREATE PROCEDURE AllRowsAndagain
#table1 NVARCHAR(128)
AS
BEGIN
DECLARE #SafeTableName AS NVARCHAR(128)
SELECT #SafeTableName = QUOTENAME( TABLE_NAME )
FROM INFORMATION_SCHEMA.TABLES
WHERE TABLE_NAME = #table1
DECLARE #sql AS NVARCHAR(MAX) = 'select count(*) from ' + #SafeTableName + ';'
EXEC(#SQL)
END
Checking the table against INFORMATION_SCHEMA.TABLES makes your dynamic sql a lot safer. As it will only execute the dynamic statement if a table has been passed as the variable and not some malicious statement.
Your problem is that you are trying to count the number of rows in a string variable.
Seems a bit OTT but here you are.......
CREATE PROC AllRowsAndagain
#table1 NVARCHAR(128)
AS
BEGIN
--DECLARE A VARIABLE TO HOLD THE STATEMENT
DECLARE #Statement NVARCHAR(1024);
--BUILD THGE STATMENT USING THE TABLE NAME YOU PASS IN
SET #Statement = CONCAT('SELECT COUNT(*) FROM ', #table1, ';');
--ONLY RUN FIRST COMMAND
SET #Statement = SUBSTRING(#Statement, 1, CHARINDEX(';', #Statement));
--RUN THE SQL
exec sp_executesql #Statement;
END;
BACKGROUND
I have a procedure that has INSERT INTO ... EXEC (#sql) from dynamic SQL.
This procedure's results is INSERTed INTO table outside of procedure. When I attempt this, I get get an error:
[S0001][8164] An INSERT EXEC statement cannot be nested.
This error is discussed in other questions, but for inner procedures' calls instead of dynamic SQL:
Errors: "INSERT EXEC statement cannot be nested." and "Cannot use the ROLLBACK statement within an INSERT-EXEC statement." How to solve this?
An insert exec statement cannot be nested
Example with error:
-- =================================
-- table with test data
-- =================================
CREATE TABLE dbo.TestInsertIntoDynamicData1
(
data nvarchar(max)
)
INSERT INTO dbo.TestInsertIntoDynamicData1
VALUES ('one1'), ('two1'), ('three1')
GO
-- =================================
-- another table with test data
-- =================================
CREATE TABLE dbo.TestInsertIntoDynamicData2
(
data nvarchar(max)
)
INSERT INTO dbo.TestInsertIntoDynamicData2
VALUES ('one2'), ('two2'), ('three2')
GO
-- =================================
-- procedure with dynamic query
-- =================================
CREATE PROCEDURE dbo.TestInsertIntoDynamicProc
#TableName nvarchar(100)
AS
BEGIN
DECLARE #Results table(
data nvarchar(max)
)
DECLARE #sql nvarchar(max)
SET #sql = '
SELECT data
FROM dbo.' + #TableName + ';
'
-- FIRST INSERT INTO ... EXEC ...
INSERT INTO #Results -- this INSERT is required for example
EXEC (#sql)
SELECT *
FROM #Results;
END
GO
-- =================================
-- CALL
-- =================================
DECLARE #ResultsOfProc table(
data nvarchar(max)
)
-- SECOND INSERT INTO ... EXEC ...
INSERT INTO #ResultsOfProc (data)
EXEC dbo.TestInsertIntoDynamicProc #TableName = 'TestInsertIntoDynamicData2'
SELECT *
FROM #ResultsOfProc;
GO
DROP TABLE dbo.TestInsertIntoDynamicData1
DROP TABLE dbo.TestInsertIntoDynamicData2
DROP PROCEDURE dbo.TestInsertIntoDynamicProc
https://stackoverflow.com/a/2917775/7573844
QUESTION
How can we get around this error?
Move INSERT INTO to dynamic query.
Refactor table variable to temp table in order to use it in dynamic query.
Fixed procedure:
CREATE PROCEDURE dbo.TestInsertIntoDynamicProcFixed
#TableName nvarchar(100)
AS
BEGIN
CREATE TABLE #Results ( -- refactor to temp table
data nvarchar(max)
)
DECLARE #sql nvarchar(max)
SET #sql = '
INSERT INTO #Results -- move INSERT to here
SELECT data
FROM dbo.' + #TableName + ';
'
-- FIRST INSERT INTO ... EXEC ...
EXEC (#sql)
SELECT *
FROM #Results;
END
GO
I am doing a procedure that accepts an input parameter of a filepath, reads the json data from it and inserts the data into a table. However, when I try to execute the procedure, I get this error:
Msg 201, Level 16, State 4, Procedure main.loadData, Line 0 [Batch Start Line 244]
Procedure or function 'loadData' expects parameter '#filePath', which was not supplied.
The surprising thing is that I am adding a parameter..
The code for the procedure is this:
ALTER PROCEDURE main.loadData
(#filePath VARCHAR(200))
AS
BEGIN
DECLARE #pathScript AS VARCHAR(MAX)
SET #pathScript='DECLARE #jsonVariable NVARCHAR(max);
SELECT #jsonVariable = BulkColumn
FROM OPENROWSET (BULK ''' + #filepath
+ ''', SINGLE_CLOB) as j;
INSERT INTO main.jsonData(restaurant, priceRange, country, score, reviewDate)
SELECT *
FROM OPENJSON(#jsonVariable, ''$.reviews.row'')
WITH
(restaurant VARCHAR(100) ''$.restaurant'',
priceRange VARCHAR(50) ''$.priceRange'',
country VARCHAR(50) ''$.country'',
score INTEGER ''$.score'',
reviewDate DATETIME ''$.reviewDate''
);';
EXEC(#pathScript);
END;
GO
And the execution code is this:
EXEC main.loadData 'C:\data.json';
Try setting a parameter in the declaration.
ALTER PROCEDURE main.loadData
#filePath VARCHAR(200) NOT NULL
AS
BEGIN
DECLARE #pathScript AS VARCHAR(MAX), #params VARCHAR(200);
SET #params = #filepath VARCHAR(200);
SET #pathScript='DECLARE #jsonVariable NVARCHAR(max);
SELECT #jsonVariable = BulkColumn
FROM OPENROWSET (BULK ''' + #params
+ ''', SINGLE_CLOB) as j;
INSERT INTO main.jsonData(restaurant, priceRange, country, score, reviewDate)
SELECT *
FROM OPENJSON(#jsonVariable, ''$.reviews.row'')
WITH
(restaurant VARCHAR(100) ''$.restaurant'',
priceRange VARCHAR(50) ''$.priceRange'',
country VARCHAR(50) ''$.country'',
score INTEGER ''$.score'',
reviewDate DATETIME ''$.reviewDate''
);';
EXEC(#pathScript);
END;
GO
And try executing like this
EXEC main.loadData #filepath = 'C:\data.json'
I found the problem. And it's the silliest problem.. Part of my program has a trigger which fires after this procedure inserts the data into the table. Now, to avoid inserting multiple times on the same table (meaning if u execute 2 times, you get double the data), i added a DELETE FROM main.jsonData, and then EXEC main.loadData (which at time didn't need any parameters). Due to this execution line, which didnt give the parameter, it gave me the error. Thanks to everyone that helped!
I need some help with simple SQL code:
DECLARE #procExists int
SET #procExists = (SELECT COUNT(*) FROM INFORMATION_SCHEMA.ROUTINES WHERE ROUTINE_SCHEMA = 'dbo' AND ROUTINE_NAME = 'Table_Exists' AND ROUTINE_TYPE = 'PROCEDURE')
IF NOT #procExists > 0
BEGIN
-- test query
-- SELECT 'Something' = #procExists;
-- error throwing code
-- CREATE PROCEDURE Table_Exists
-- #schemaName varchar(50),
-- #tableName varchar(50)
-- AS
-- RETURN (SELECT COUNT(*) FROM INFORMATION_SCHEMA.TABLES WHERE TABLE_SCHEMA = #schemaName AND TABLE_NAME = #tableName)
END
The simple code above:
- declares an int variable
- checks if procedure dbo.Table_Exists exists
- IF NOT exists it creates it
My problem is this error information:
Msg 156, Level 15, State 1, Line 9
Incorrect syntax near the keyword 'PROCEDURE'.
Msg 137, Level 15, State 2, Line 13
Must declare the scalar variable "#schemaName".
I don't know why, but..
- when i execute 'CREATE PROCEDURE' body alone it works
- when i execute whole IF section excluding 'CREATE PROCEDURE' body, simple query works
- when i execute whole IF section including 'CREATE PROCEDURE' body, error is thrown
What am i missing?
CREATE PROCEDURE has to be in it's own batch
So, dynamic SQL is one way:
IF OBJECT_ID('Table_Exists') IS NULL
BEGIN
EXEC ('CREATE PROCEDURE Table_Exists
#schemaName varchar(50),
#tableName varchar(50)
AS
RETURN (SELECT COUNT(*) FROM INFORMATION_SCHEMA.TABLES WHERE TABLE_SCHEMA = #schemaName AND TABLE_NAME = #tableName)
')
END
or DROP first
IF OBJECT_ID('Table_Exists') IS NOT NULL
DROP PROC Table_Exists
GO
CREATE PROCEDURE Table_Exists
#schemaName varchar(50),
#tableName varchar(50)
AS
RETURN (SELECT COUNT(*) FROM INFORMATION_SCHEMA.TABLES WHERE TABLE_SCHEMA = #schemaName AND TABLE_NAME = #tableName)
GO
Note the use of OBJECT_ID to see if the proc exists.
You can do this using SET NOEXEC ON. This instructs SQL Server to ignore all SQL code until SET NOEXEC OFF is reached.
IF EXISTS (SELECT *
FROM INFORMATION_SCHEMA.ROUTINES
WHERE ROUTINE_TYPE = 'PROCEDURE'
AND ROUTINE_SCHEMA = 'dbo'
AND ROUTINE_NAME = 'HelloWorld')
BEGIN
SET NOEXEC ON
END
GO
CREATE PROCEDURE dbo.HelloWorld
AS
PRINT 'Hello world'
GO
SET NOEXEC OFF
GO
From MSDN:
The CREATE PROCEDURE statement cannot be combined with other Transact-SQL statements in a single batch.
Therefore, what you are trying to do is not possible, unless you are fine with implementing it via a dynamic query.
if OBJECT_ID('PROC1') IS NULL
EXEC('CREATE PROCEDURE DBO.PROC1 AS SELECT 1')
GO
ALTER PROCEDURE DBO.PROC1(#PARAM1 INT, #PARAM2 INT)
AS
.................