I've got the following stored procedure that runs perfectly in SSMS.
Create Store Proc MyTT
SELECT
MAX(TableName) as TableName,
aco_code,acc_code,
ctr_id,cte_id,usr_code,
-- convert(datetime,convert(varchar,current_timestamp,112),112)
Cast(DATEPART(hour,current_timestamp) as varchar(3))+':'+ CAST(DATEPART(minute,current_timestamp) as varchar(3))as [Time]
INTO #AB
FROM
(
SELECT 'TM_ACO_Account_Comment'as TableName,
a.aco_code,
a.acc_code,
a.ctr_id,
a.cte_id,
a.usr_code
FROM
TM_ACO_Account_Comment a with(NOLOCK)
UNION ALL
SELECT 'TM_ACO_Account_Comment'as TableName,
b.aco_code,
b.acc_code,
b.ctr_id,
b.cte_id,
b.usr_code
FROM
[172.17.14.77].[IS_ND_BLAKE].[dbo].[TM_ACO_Account_Comment] b with(NOLOCK)
)zzz
GROUP BY aco_code,
acc_code,
ctr_id,
cte_id,
usr_code
HAVING COUNT(*) = 1
ORDER BY
aco_code
SELECT *
FROM
#AB
DROP TABLE #AB
But when I try to include the procedure in a query to send out mail, I get the following error:
Must pass parameter number 4
My Mail Query looks like this.
EXEC msdb.dbo.sp_send_dbmail
#profile_name = 'DBMail',
#recipients = 'mitch#domain.com',
#query = 'exec SP,`MYTT`
#execute_query_database = 'MYDB',
#subject = 'Work Order Count',
#attach_query_result_as_file = 1 ;
In your EXEC msdb.dbo.sp_send_dbmail command, you seem to have not closed the string literal in this line properly:
#query = 'exec SP,`MYTT`
It is missing a closing ', as well as a comma to separate this argument from the next one:
#query = 'exec SP,`MYTT`',
However, the passed query itself looks a bit strange. Your stored procedure is called MyTT and if you meant it to be called in the query you are passing to msdb.dbo.sp_send_dbmail, you specified the exec statement incorrectly. It should probably be simply like this:
#query = 'exec MyTT',
Although it might be a good idea to specify the database and the schema too:
#query = 'exec YourDBName.YourSchema.MyTT',
I have slightly corrected the name proper to match the one in your definition exactly. If your server's default collation is case-insensitive (which is likely), that may be unnecessary, but it would not harm to be consistent in such matters.
Related
This is my code
DECLARE #stringvariable nvarchar(200) = 'Hello';
DECLARE #sql nvarchar(2000) = SELECT * INTO ##global FROM OPENQUERY(DB1, ''EXEC GETCASE ''' + #stringvariable + ''''')'
Printing #sql returns a correctly formatted query, however SQL Server doesn't like #stringvariable and returns an error
Msg 102, Level 15, State 1, Line 11
Incorrect syntax near 'Hello'.
Here is what the outputted query looks like
SELECT * INTO ##global FROM OPENQUERY(DB1, 'EXEC GETCASE 'Hello'')
How can I avoid this error? It seems like because my stored procedure takes a string parameter, it's throwing off the query. I've read that OPENQUERY does not support variables, but I've parameter the variable so it should work?
Appreciate your help!
The stored procedure exists in a database and a schema. You need to supply those. Supposing database db_name and schema schema_name:
DECLARE #stringvariable nvarchar(200) = 'Hello';
SET #stringvariable=REPLACE(#stringvariable,'''',''''''''''); -- doubly doubled single quotes for the dynamic statement
DECLARE #sql nvarchar(2000) = 'SELECT * INTO ##global FROM OPENQUERY(DB1, ''SET FMTONLY OFF;EXEC db_name.schema_name.GETCASE ''''' + #stringvariable + ''''''')';
I've also made sure single quotes are properly escaped in the #stringvariable.
It's also likely you need to start the query with SET FMTONLY OFF; so I've added that.
Update: To test this I created following simple procedure on a linked server local_server in database TEST_TT
CREATE PROCEDURE [dbo].[tst]
#i VARCHAR(128)
AS
SELECT #i AS field;
I then ran the following:
DECLARE #var VARCHAR(128)='TT.';
SET #var=REPLACE(#var,'''',''''''''''); -- doubly doubled single quotes for the dynamic statement
DECLARE #stmt VARCHAR(4000)='SELECT * INTO ##tt FROM OPENQUERY(local_server,''SET FMTONLY OFF;EXEC TEST_TT.dbo.tst '''''+#var+''''''');';
EXEC (#stmt);
SELECT * FROM ##tt;
DROP TABLE ##tt;
And I received the results. I count 7 (!!) single quotes at the end of the query... yuck! Updated original part with the same number of quotes.
I have a big query which works and I want to write a stored procedure for it.
I'm getting this error:
the OLE DB provider SQLNCLI11" for linked server "theServer" does not contain the table ""#dbName"."dbo"."tableName"
What I am trying to do:
create PROCEDURE [sys.sp_myProcedure]
(
#dbName varchar(30) output,
#rid varchar (10) output,
#mdate output
)
AS
BEGIN
declare #prt varchar(12)
declare #pid int
declare #cid int
--declare #rid int
declare #aid int
SET NOCOUNT ON;
set #cid= (select CID from theServer.[#dbName].dbo.tableName where RID= #rid)
set #pid= (select PID from theServer.[#dbName].dbo.tableName where RID= #rid)
set #aid= (select aid from theServer.[#dbName].dbo.tableName where RID= #rid)
--then my query begins
theServer.[#dbName].dbo.tablename is a linked server.
What I want to do is:
execute [sys.sp_myProcedure] 'someDbname', '123', '2012-03-03'
and the parameters passed here would set/update the variables #dbName, #rid, #mdate at runtime. ( #mdate I have it further away in the query, it's too big to adapt it with myTable and to change all the sensitive data).
How can I do this ?? (using SQL Server 2012)
edit (based on the comments and answers):
so, it's #thatString = '--insert the query here ' . Then, in my case how can i set those variables according to the parameters inside the query? Should i do it with replace? like this: set #thatString= replace(#thatString, dbName, #dbname) ?
**
edit 2
**
set #sql = '
use [someDbName];
use [123];
use [2012-03-03];
select ... '
set #sql = replace (#sql, 'someDbName', #dbName)
set #sql = replace (#sql, '123', #rid)
set #sql = replace (#sql, '2012-03-03', #mdate)
execute #sql
end
Did i get it right? is the execute #sql in the right place?
I'm asking cause it doesnt work. i'm getting the name ' --part of my query here' is not a valid identifier
Names of databases or other objects cannot be specified dynamically from variables. The workaround is to compose a dynamic SQL query in a string, into which you concatenate the required names, and then execute (#thatString).
(You might think you can employ use, but it is scoped such that you would have to include the rest of your query within the same executed string.)
--
Edit with more info as requested. You can compose the string however you like. If you need any more guidance, there are plenty of pages that discuss dynamic T-SQL. But hey, two ideas:
set #myDynamicQuery =
'
use [' + #myDynamicDatabase + '];
select BLAH from WHOM where DATA = ''what'';
';
or if you will be using the name a lot, you could reduce the hassle caused by breaking in and out of single quotes as follows - though I personally never use this as I don't like how it looks:
set #myDynamicQuery =
'
use [A_RARE_PLACEHOLDER];
select BLAH from WHOM where DATA = ''what'';
-- lots more uses of A_RARE_PLACEHOLDER
';
set #myDynamicQuery = replace(
#myDynamicQuery,
'A_RARE_PLACEHOLDER',
#myDynamicDatabase
);
Then execute (#myDynamicQuery);
I'm working on a query that builds a list of table names from the sys.database master table. I then use those name in a concat statement to pull a specific piece if information out of each database. I'm running the whole thing through a while loop to hit all tables, with a counter.
i.e.
set #sql = 'select top 10 * from ' + (select dbname from #table where tabid = #i + '.dbo.**tablename** where NAME = '**String Value**'
When I just print the results of #sql, it gives me the correct syntax, and I am able to run the command with no problem.
When I set #sql to exec, I get an error
"database select top 10 * from dbname does not exist."
It's like the execute is ignoring everything after the select.
I'm suspecting you're calling EXEC incorrectly.
When calling 'exec' with a T-SQL query, be sure to enclose the target string in parenthesis, eg
DECLARE #FOO VARCHAR(100)
Set #FOO = 'SELECT TOP 5 * from SOMETABLE'
EXEC (#FOO) -- not EXEC #FOO
I am trying to send a resultset via email using xp_sendmail.
I need to send the email when an earlier executed query has any results.
Got the query results into a table variable/temp table and then
in xp_sendmail, using
Declare #table_var table(...)
..query execution..
EXEC master.dbo.xp_sendmail #recipients = 'xx#xx.com',
#query = 'select * from #table_var'
it gives error saying that
#table_var must be declared.
Even if I use temporary table, the message I get is
cannot reference object in tempdb database.
Any ideas on this?
Thanks in advance
You'll need to use a real table for this. Try..
If exists (select * from sys.tables where name = 'mytable')
drop table mytable
Create Table mytable table(...)
..query execution..
EXEC master.dbo.xp_sendmail #recipients = 'xx#xx.com',
#query = 'select * from mydatabase.dbo.mytable'
I am trying to convert the SQL statement to support the sp_executesql to make it safe but I ran into an unsafe area. Hopefully you guys can help me with this. I've created the temp table to make it easier to demonstrate the problem.
The problem is at STEP # 6. I can use the STEP # 5 BUT this is not safe and it can be hacked easily. I don't really want break the keywords and search multiple times because of the system performance.
Error for MS SQL 2008 Msg 4145, Level 15, State 1, Line 4 An expression of non-boolean type specified in a context where a condition is expected, near 'ORDER'.
GO
/****** Object: StoredProcedure [dbo].[ups_MultiWareHouse] Script Date: 06/14/2012 09:12:38 ******/
SET ANSI_NULLS ON
GO
SET QUOTED_IDENTIFIER OFF
GO
create PROCEDURE ups_TestSearch(
#Keywords nvarchar(4000),
#SortColumns nvarchar(4000)
)
AS
--STEP #1 - Create Temp Table - Begin
CREATE TABLE #TempTable
(
ProductID uniqueidentifier,
ProductName varchar(600),
Price decimal(18,2),
Active bit
)
--STEP #2 - Insert couple records to search
INSERT INTO #TempTable (ProductID,ProductName,Price,Active) VALUES(NEWID(),'Mouse','10.12','1')
INSERT INTO #TempTable (ProductID,ProductName,Price,Active) VALUES(NEWID(),'Keyboard','20.45','1')
INSERT INTO #TempTable (ProductID,ProductName,Price,Active) VALUES(NEWID(),'Monitor','150.87','0')--Disable this product
--STEP #3 - Display the current table data
select 'STEP #3' as STEP, * FROM #TempTable
--STEP #4 - SETTING UP sp_executesql to support parameter substitution
--Set definition
DECLARE #ParmDefinition nvarchar(4000);
SET #ParmDefinition='
#Param1ProductName nvarchar(4000),
#Param2SortColumns nvarchar(4000)
'
DECLARE #SQLString nvarchar(4000);
--STEP #5- CONVERT THE #SQLString TO use #Keywords and #SortColumns
--Run query for the below like this ups_TestSearch'ProductName=''Mouse'' OR ProductName=''Keyboard''', 'Price DESC, ProductName ASC'
SET #SQLString = N'SELECT ''STEP #5'' as STEP, #TempTable.* FROM #TempTable WHERE ('+#Keywords+') ORDER BY '+#SortColumns;--unsafe, open to hackers
EXECUTE sp_executesql #SQLString, #ParmDefinition, #Param1ProductName = #Keywords, #Param2SortColumns=#SortColumns;
--STEP #6- CONVERT THE #SQLString TO use #Keywords and #SortColumns
--Run query for the below like this ups_TestSearch'ProductName=''Mouse'' OR ProductName=''Keyboard''', 'Price DESC, ProductName ASC'
SET #SQLString = N'SELECT ''STEP #6'' as STEP, #TempTable.* FROM #TempTable WHERE (#Param1ProductName) ORDER BY #SortColumns';--Safe but not working
SELECT #SQLString AS SeeStatement
EXECUTE sp_executesql #SQLString, #ParmDefinition, #Param1ProductName = #Keywords, #Param2SortColumns=#SortColumns;
--Drop temp table
DROP TABLE #TempTable
I think the issue is that in Step 5 you aren't using parameter substitution - that is, you are basically building the SQL statement by string concatenation. When you execute it via sp_executesql you could really just do this:
EXECUTE sp_executesql #SqlString
The code in Step 6 is performing parameter substitution. In this case, however, you are limited to using parameters only in locations where they are allowed in "normal" SQL expressions. For example, you can't do this in T-SQL:
DECLARE #Criteria NVARCHAR(500);
SET #Criteria = N' WHERE ProductName = ''Mouse'''
SELECT * FROM #MyTempTable + #Criteria
Depending upon how complex you expect your filter to be, you might be able to write the criteria to a temporary table and perform a join to the temporary table to limit the resulting data that is returned. Off the top of my head I am not sure how best to sort the resulting data unless you did that in the calling code perhaps?
Your error message indicates that the WHERE clause in step 6 is invalid, and so is the ORDER BY clause. This is because you're passing in strings as parameters to sp_executesql and trying to use them as entire clauses. Additionally, the statement references a parameter #SortColumns, but you appear to have named the parameter #Param2SortColumns.
Have a read of what some SQL server MVPs have written:
http://www.sommarskog.se/dynamic_sql.html
More to the point: http://www.sommarskog.se/dyn-search.html
http://www.sqlmag.com/article/tsql3/parameterizing-result-order
I don't see a simple way to alter your procedure to make this work, since you're passing in entire WHERE and ORDER BY clauses as parameters. What you should really do is redesign the proc. Supply each WHERE criterion as an individual parameter to ups_TestSearch. You resupply each WHERE parameter to sp_executesql and structure your initial SQL statement in this fashion:
SET #SQLString = SELECT and JOIN portions of command
SET #SQLString = #SQLString + 'WHERE 1 = 1 '
IF (#WhereParam1 IS NOT NULL)
SET #SQLString = #SQLString + 'AND (SomeTable.SomeColumn = #WhereParam1) '
IF (#WhereParam2 IS NOT NULL)
SET #SQLString = #SQLString + 'AND (SomeTable.SomeColumn = #WhereParam2) '
...
If necessary, you can use the same structure to add joins to the statement.
The ORDER BY structure depends on how complex this might get, and whether you know all possible involved columns. If it's relatively simple, you can write it out as a CASE statement as follows, or break it up as individual parameters, as I recommend for the WHERE clause.
ORDER BY
CASE WHEN CHARINDEX(#SortColumns, 'SortCol1') > 0 THEN SortCol1 ELSE NULL END,
CASE WHEN CHARINDEX(#SortColumns, 'SortCol2') > 0 THEN SortCol2 ELSE NULL END,
...
The simplest thing to do here might be to sort at the application level rather than the DB, but that could be just as infeasible as a complex ORDER BY clause would be to parameterize.