Incorrect Stored Procedure Syntax - tsql

I am using the below stored procedure to upload files into a db where each file has a correspondence table but there are times when the table not exist in that case i want to add the file name into a table called NewTables.I can not get the stored proc syntax working can someone help me.I believe the mistake is in the first part where i check if the table exist
ALTER proc [dbo].[UploadCSVFiles]
#FilePath varchar(100) ,
#FileName varchar(100),
#TableName varchar(250)
AS
BEGIN
DECLARE #SqlStmt nvarchar(max)
DECLARE #ErrorCode int
SET #SqlStmt='Truncate table dbo.[' + #TableName +']'
EXEC(#SqlStmt);
set #SqlStmt =N'
IF not EXISTS (SELECT * FROM sys.objects WHERE object_id = OBJECT_ID(N''[dbo].['+#TableName +N']'') AND type in (N''U''))
BEGIN
INSERT INTO dbo.NewTables ('+#TableName+N','+#FileName+N') Values('+#TableName+N','+#FileName+N')
END
ELSE
BEGIN
INSERT INTO '+#TableName+N'
select *
from openrowset(''MSDASQL''
,''Driver={Microsoft Access Text Driver (*.txt, *.csv)};
DefaultDir='+#FilePath+N'''
,''select * from "'+#FileName+N'"'')
END
'
EXEC(#SqlStmt);
Thanks
Rao
Thanks fpop and Christine, I have made the suggestions you made but still I get an error
Msg 4701, Level 16, State 1, Line 1
Cannot find the object "Customer" because it does not exist or you do not have permissions.
it seems the If statement does not insert the new table
here is the final version
USE [MyDB]
GO
SET ANSI_NULLS ON
GO
SET QUOTED_IDENTIFIER ON
GO
ALTER proc [dbo].[UploadFiles_2]
#FilePath varchar(100) ,
#FileName varchar(100),
#TableName varchar(250)
AS
BEGIN
DECLARE #SqlStmt nvarchar(max)
DECLARE #ErrorCode int
SET #SqlStmt='Truncate table dbo.[' + #TableName +']'
EXEC sp_executesql #SqlStmt;
set #SqlStmt =N'
IF ( NOT EXISTS (SELECT * FROM sys.objects WHERE object_id = OBJECT_ID(N''[dbo].['+#TableName +N']'') AND type in (N''U''))
BEGIN
INSERT INTO dbo.NewTables (TableName,FileName) Values('''+#TableName+N''','''+#FileName+N''')
END
ELSE
BEGIN
INSERT INTO '+#TableName+N'
select *
from openrowset(''MSDASQL''
,''Driver={Microsoft Access Text Driver (*.txt, *.csv)};
DefaultDir='+#FilePath+N'''
,''select * from "'+#FileName+N'"'')
END'
EXEC sp_executesql #SqlStmt;
END

Hint: You can always add PRINT #SQLSTMT to see the code your procedure has generated.
There are two error in the script, both in line:
INSERT INTO dbo.NewTables ('+#TableName+N','+#FileName+N') Values('+#TableName+N','+#FileName+N')
First error: NewTables table must have 2 columns to keep table name and file name. Let's call them COL_TBL, COL_FILE
Second error: You have to add quotes in Values part of statement
Here is how it should look like:
INSERT INTO dbo.NewTables (COL_TBL, COL_FILE) Values('''+#TableName+N''','''+#FileName+N''')
btw, You didn't post code entirely, there is an END at the end missing. Please, next time copy the whole code so others can reproduce the error.
EDIT: Please consider following: using sp_executesql instead of EXEC, to avoid sql injection bugs do not concatenate parameters into dynamic sql and finally check if table exists before truncating it

Try this
IF (NOT EXISTS (SELECT * FROM sys.objects WHERE [type] LIKE 'U' AND name LIKE 'mytable'))
SELECT 'not found';
ELSE
SELECT 'found';

Related

Value not Store in Dynamic SQL

I've different different tables to categorically store data and a log table where all the transactions log are recorded
e.g. 1) VoucherNO, Add, ...
2) VoucherNO, Delete, ..
After I backup the database and restore in another server for my Reporting Purpose. That time I want to ensure all the log data and transaction are available in TestDB if not then I remove log from 'AUD_USER_ACTIVITY'.
To find the transaction exist or not, I create a dynamic sql select statement and check whether record is exist or not.
Basis on #RecExist Value I do the action like if records is not available in TestDB the log will be remove, if record exist immediately break this loop and going for next procedure
But #RecExist variable is not updating in Dynamic SQL Execution. Please guide me
declare #MvDocNo varchar(50)
DECLARE #SCtr as DECIMAL(10,0)
declare #LocationCode varchar(4)
declare #UName Nvarchar(40)
declare #toe varchar(30)
declare #QryTxt as nvarchar(MAX);
Declare #RecExist as INT =0;
SET #RecExist=0
WHILE #RecExist=0
BEGIN
select top 1 #MvDocNo=DOCNO, #SCtr=SrlNo,#LocationCode =DMLTYPE,#UName=TABLENAME
FROM R_AUDDB..AUD_USER_ACTIVITY
WHERE DBNAME='TestDB' and DMLTYPE not in ('AD','D','PD') ORDER BY SRLNO DESC;
select top 1 #toe=docno from TestDB..M_TYPEOFENTRY where TBLNAME=#UName;
set #QryTxt='Select #RecExist=1 From R_TestDB..'+#UName+ ' Where '+#toe+'='''+#MvDocNo+''''
exec (#QryTxt)
IF #RecExist=0
BEGIN
DELETE R_AUDDB..AUD_USER_ACTIVITY WHERE SRLNO=#SCtr
END
END
The following code sample demonstrates how to check for a row in a table with a specific column and value using dynamic SQL. You ought to be able to change the values of the first three variables to reference a table and column in your database for testing.
Note that SQL injection is still possible: there is no validation of the table or column names.
-- Define the table to check and the target column name and value.
declare #TableName as SysName = 'Things';
declare #ColumnName as SysName = 'ThingName';
declare #TestValue as NVarChar(32) = 'Beth';
-- Create a SQL statement to check for a row in the target table with the specified column name and value.
declare #SQL as NVarChar(1024);
declare #Result as Bit;
-- Note that only object names are substituted into the statement at this point and QuoteName() is used to reduce problems.
set #SQL = N'select #iResult = case when exists ( select 42 from dbo.' + QuoteName( #TableName ) +
N' where ' + QuoteName( #ColumnName ) + N' = #iTestValue ) then 1 else 0 end;'
select #SQL as SQL;
-- Execute the SQL statement.
-- Note that parameters are used for all values, i.e. the target value and return value.
execute sp_executesql #stmt = #SQL,
#params = N'#iTestValue NVarChar(32), #iResult Bit output',
#iTestValue = #TestValue, #iResult = #Result output
-- Display the result.
select #Result as Result;

two table input parameters in stored procedure

I am working on C# project which needs a stored procedure which will take two table names as inputs.
First table will copy data to a temp table which has two columns URL & channelID. This URL column is then matched with other input table's URL column & if match is found then it will update channel id from temp table to other tables channel ID.
I have written stored procedure as
CREATE PROCEDURE [dbo].[UpdateTables]
#excelTable NVARCHAR(128) ,
#TableName NVARCHAR(128)
AS
Declare #channel_Id nvarchar(50)
Declare #url varchar(400)
BEGIN
Select *
Into #Temp
From QUOTENAME(#excelTable)
END
While EXISTS(SELECT * From #Temp ) > 0
Begin
Select Top 1
#channel_Id = channel_Id, #url = url
From #Temp
update QUOTENAME(#TableName)
set channelid = #channelid
where pagefullurl like '%'+ #url + '%'
Delete #Temp
Where channelid = #channelid
End
I don't have much knowledge in TSQL and my above code has errors.
Incorrect syntax near '>'.
Msg 137, Level 15, State 2, Procedure UpdateTables, Line 20
Must declare the scalar variable "#channelid".
Msg 137, Level 15, State 2, Procedure UpdateTables, Line 22
Must declare the scalar variable "#channelid".
Please suggest what changes needs to done
I don't have MS SQL server handy to test it, but you declare your variable as #channel_Id, and later try to use it as #channelid (without the underscore) so you get errors about the undeclared variable.
I've corrected your SP and this is how it should look
CREATE PROCEDURE [dbo].[UpdateTables]
#excelTable NVARCHAR(128) ,
#TableName NVARCHAR(128)
AS
Declare #channel_Id nvarchar(50)
Declare #url varchar(400)
BEGIN
Select *
Into #Temp
From QUOTENAME(#excelTable)
While EXISTS(SELECT * From #Temp )
Begin
Select Top 1
#channel_Id = channel_Id, #url = url
From #Temp
update QUOTENAME(#TableName)
set channelid = #channel_Id
where pagefullurl like '%'+ #url + '%'
Delete #Temp
Where channelid = #channel_Id
End
END

Need help in creating a stored procedure to iterate tables in a database, then run a SQL statement on each table

Our application does not delete data as we retain it for a period of time, instead we have a column "deleted" (bit) in most tables of the database that store data which we mark 1 when deleted, otherwise the default is 0.
I'd like to create a stored procedure that iterates all tables in the database, checks for the existence of a column named "deleted" and if it exists, I run a check against the LastUpdatedUtc column (datetime2) and if the date is over 6 months old and deleted = 1 then we delete the row.
This application is under continuous development so tables could be added which is why I want to create a script that iterates tables instead of having to add a line for each table and remember to add them as new tables are added.
Any help in a SQL Server 2008 R2 stored procedure to this would be a great help.
Thank you.
EDIT (thank you Omaer) here is what I've come up with so far. Anyone that knows a better way let me know.
IF OBJECT_ID('tempdb..#tmpTables') IS NOT NULL DROP TABLE #tmpTables
GO
CREATE TABLE #tmpTables
(
ID INT,
TableName NVARCHAR(100) NOT NULL
)
GO
SET NOCOUNT ON
GO
INSERT #tmpTables
SELECT [object_id], [name] FROM sys.all_objects WHERE type_desc = 'USER_TABLE' ORDER BY [name]
DECLARE #TN NVARCHAR(100)
DECLARE #SQL NVARCHAR(max)
DECLARE #PurgeDate VARCHAR(50)
SET #PurgeDate = DATEADD(MONTH, -6, GETUTCDATE())
WHILE (SELECT COUNT(*) FROM #tmpTables) > 0
BEGIN
SELECT TOP 1 #TN = TableName FROM #tmpTables
IF EXISTS(SELECT * FROM sys.columns WHERE name = 'deleted' AND OBJECT_ID = OBJECT_ID(#TN))
BEGIN
IF EXISTS(SELECT * FROM sys.columns WHERE name = 'LastUpdatedUtc' AND OBJECT_ID = OBJECT_ID(#TN))
BEGIN
SET #SQL = 'SELECT Count(*) As Counter FROM ' + #TN + ' WHERE [deleted] = 1 AND [LastUpdatedUtc] < ''' + #PurgeDate + '''' -- this will be the delete line when the code is final, just outputting results for now
EXEC(#SQL)
END
END
DELETE #tmpTables WHERE TableName=#TN
END
DROP TABLE #tmpTables
This is my first attempt, not tested it so there might be some typos/syntax errors but this should get you started:
declare #date6MonthsBack varchar(50)
select #date6MonthsBack = dateadd(month, -6, getdate());
declare c cursor for
select 'delete from ' + quotename(name) + ' where [deleted] = 1 and [LastUpdatedUtc] <= ''' + #date6MonthsBack + '''' from sys.tables
where object_id in (select object_id from sys.columns where name = 'deleted')
and object_id in (select object_id from sys.columns where name = 'LastUpdatedUtc')
declare #sql varchar(max)
open c; fetch next from c into #sql
while (##fetch_status = 0) begin
print(#sql)
--exec(#sql) --uncomment this line to do the actual deleting once you have verified the commands.
fetch next from c into #sql; end
close c; deallocate c
You could use undocummented sp_MSforeactable procedure instead of loop or cursor. Something like code below. I created procedure that runs your code and is executed with sp_MSforeachtable. The disadvantage is - the procedure is undocumented and may not be supported in next SQL Server releases
IF OBJECT_ID('dbo.usp_cleanup') IS NULL
EXEC ('CREATE PROCEDURE dbo.usp_cleanup AS SELECT 1')
GO
ALTER PROCEDURE dbo.usp_cleanup
#sTblName VARCHAR(200)
AS
BEGIN
-- your variables
DECLARE #PurgeDate VARCHAR(50)
DECLARE #SQL VARCHAR(MAX)
SET #PurgeDate = DATEADD(MONTH, -6, GETUTCDATE())
-- we can check columns existence in one condition
IF
EXISTS(SELECT * FROM sys.columns WHERE name = 'deleted' AND OBJECT_ID = OBJECT_ID(#sTblName))
AND EXISTS(SELECT * FROM sys.columns WHERE name = 'LastUpdatedUtc' AND OBJECT_ID = OBJECT_ID(#sTblName))
BEGIN
SET #SQL = 'SQL CODE GOES HERE' -- this will be the delete line when the code is final, just outputting results for now
PRINT #SQL
--EXEC(#SQL) -- uncomment for execution
END
ELSE
-- for debugging
BEGIN
PRINT #sTblName + ' has no [delete] and [LastUpdatedUtc] columns'
END
END
EXEC sp_MSforeachtable 'exec usp_cleanup ''?'''
GO

Entity framework stored procedure issue

I have following stored procedure defined:
USE [BcmMetrice]
GO
SET ANSI_NULLS ON
GO
SET QUOTED_IDENTIFIER ON
GO
ALTER PROCEDURE [dbo].[ActivityAdd_proc] #Name nvarchar(max),#Description nvarchar(max) =null ,#Users nvarchar(max),#Object_id nvarchar (15) =null, #Source nvarchar(10) =null, #TemplateId bigint =null, #UserID bigint =null
AS
DECLARE activityUsers_cursor CURSOR FOR
select s from dbo.SplitString(#Users, ';')
DECLARE
#new_ActivityId bigint,
#new_CommentId bigint,
#activityUser_l bigint
BEGIN TRY
INSERT INTO [BcmMetrice].[dbo].[Activity]
([Name]
,[Description]
,[Type]
,[Created])
VALUES
(#Name
,#Description
,ISNULL(#TemplateId,0)
,GETDATE())
SET #new_ActivityId = (SELECT SCOPE_IDENTITY())
INSERT INTO [BcmMetrice].[dbo].[Comment] ([UserID],[CommentText],[Timestamp])
VALUES (ISNULL(#UserID,151),'Activity has been created',GETDATE())
SET #new_CommentId = (SELECT SCOPE_IDENTITY())
INSERT INTO [BcmMetrice].[dbo].[ActivityComment] ([ActivityID],[CommentID])
VALUES (#new_ActivityId, #new_CommentId)
INSERT INTO [BcmMetrice].[dbo].[Log]([Timestamp],[Type],[Data],[StackTrace]) VALUES (GETDATE(),'SQL.ActivityAdd_proc','users='+ISNULL(CAST(#Users as varchar(max)),'empty'),null)
OPEN activityUsers_cursor
FETCH NEXT FROM activityUsers_cursor INTO #activityUser_l
WHILE ##FETCH_STATUS = 0
BEGIN
INSERT INTO [BcmMetrice].[dbo].[Log]([Timestamp],[Type],[Data],[StackTrace]) VALUES (GETDATE(),'SQL.ActivityAdd_proc','Inserting users='+ISNULL(CAST(#activityUser_l as varchar(max)),'empty'),null)
INSERT INTO [BcmMetrice].[dbo].[ActivityUser]
([ActivityId]
,[UserId]
,[Role])
VALUES
(#new_ActivityId
,#activityUser_l
,1)
FETCH NEXT FROM activityUsers_cursor INTO #activityUser_l
END
CLOSE activityUsers_cursor
DEALLOCATE activityUsers_cursor
END TRY
BEGIN CATCH
PRINT 'ERROR'
INSERT INTO [BcmMetrice].[dbo].[Log]([Timestamp],[Type],[Data],[StackTrace]) VALUES (GETDATE(),'SQL.ActivityAdd_proc','ERROR CATCHED!'+ERROR_MESSAGE(),null)
END CATCH
select #new_ActivityId
The thing I would like to do is to return from the procedure the id of a newly added activity. That is why at the very and I use line:
select #new_ActivityId
When testing this procedure in SQL Management Studio everything seems to be working fine. Problem starts when I try to use this procedure in my .NET project. I updated my edmx model form database, but when I execute this procedure the return value is invalid.
Procedure execution looks like this:
int ret = dc.Db.ActivityAdd_proc(name, description, users, object_id, source, templateId, userId);
Does anyone have an idea what I might be doing wrong?
I found solution to my problem. When you try to return data from stored procedure via select statement you must create Function Import in model browser of edmx. In a wizard you choose a type of returned collection (in my case it was scalar of type int64).

How to handle an empty result set from an OpenQuery call to linked analysis server in dynamic SQL?

I have a number of stored procedures structured similarly to this:
DECLARE #sql NVARCHAR(MAX)
DECLARE #mdx NVARCHAR(MAX)
CREATE table #result
(
[col1] NVARCHAR(50),
[col2] INT,
[col3] INT
)
SET #mdx = '{some dynamic MDX}'
SET #sql = 'SELECT a.* FROM OpenQuery(LinkedAnalysisServer, ''' + #mdx + ''') AS a'
INSERT INTO #result
EXEC sp_executesql #sql
SELECT * FROM #result
This works quite well when results exist in the cube. However, when the OpenQuery results are empty, the INSERT fails with this error:
Column name or number of supplied
values does not match table
definition.
My question is, what is the best way to handle this scenario? I'm using the results in a static report file (.rdlc), so the explicit typing of the temp table is (I'm pretty sure) required.
Use TRY/CATCH in your stored procedure, you'll notice there is a specific error number for your problem, so check the error number and if it is that, return an empty result set. As you already have the table defined that'll be easier.
PseudoCode looks something like this:
SET #mdx = '{some dynamic MDX}'
SET #sql = 'SELECT a.* FROM OpenQuery(LinkedAnalysisServer, ''' + #mdx + ''') AS a'
BEGIN TRY
INSERT INTO #result
EXEC sp_executesql #sql
END TRY
BEGIN CATCH
IF ERROR_NUMBER <> 'The error number you are seeing'
BEGIN
RAISERROR('Something happened that was not an empty result set')
END
END CATCH
SELECT * FROM #result
You'll want to check for that particular error, so that you don't just return empty result sets if your SSAS server crashes for example.
There is another solution to this issue, similar to the accepted answer, which involves using an IF statement instead of TRY...CATCH.
http://www.triballabs.net/2011/11/overcoming-openquery-mdx-challenges/
IF (SELECT COUNT(*)
FROM OPENQUERY("SSAS1",
'SELECT [Measures].[Target Places] ON COLUMNS
FROM [ebs4BI_FactEnrolment]
WHERE [DimFundingYear].[Funding Year].&[17]')) > 0
EXEC sp_executesql N'SELECT CONVERT(varchar(20),
"[DimPAPSCourse].[Prog Area].[Prog Area].[MEMBER_CAPTION]")
as ProgArea,
convert(float, "[Measures].[Target Places]") as Target
FROM OPENQUERY("SSAS1",
''SELECT [Measures].[Target Places] ON COLUMNS,
[DimPAPSCourse].[Prog Area].[Prog Area] ON ROWS
FROM [ebs4BI_FactEnrolment]
WHERE [DimFundingYear].[Funding Year].&[17]'')'
ELSE
SELECT '' as ProgArea, 0 as Target
WHERE 1=0