Incorrect syntax SQL - sql

Why do I get this error when I try to execute the following code?
I have a table NewTable1 with two columns: column1 and column2.
I get this error: Incorrect syntax near 'column2'.
--DROP COLUMN PROCEDURE
CREATE PROCEDURE DropColumn
#tableName varchar(50),
#columnName varchar(50)
AS
BEGIN
DECLARE #SQL nvarchar(500);
SET #SQL = N'ALTER TABLE ' + QUOTENAME(#tableName)
+ ' DROP COLUMN ' + QUOTENAME(#columnName);
EXEC sp_executesql #SQL;
END
RETURN 0
GO
USE SKI_SHOP;
EXEC DropColumn 'NewTable1', 'column2';
GO

Use appropriate data types. Also You will only be able to drop Columns for tables in callers default schema. Since procedure doesn't take schema into consideration, therefore you can only pass the table name and if a table exists in other than caller default schema they wont be able to delete it using this procedure .
CREATE PROCEDURE DropColumn
#tableName SYSNAME,
#columnName SYSNAME
AS
BEGIN
SET NOCOUNT ON;
DECLARE #SQL NVARCHAR(MAX);
SET #SQL = N' ALTER TABLE ' + QUOTENAME(#tableName)
+ N' DROP COLUMN ' + QUOTENAME(#columnName);
EXEC sp_executesql #SQL;
END
GO
I over looked some basic simple issues in my first approach, whenever creating of Dropping objects in SQL Server always check if they exist, to avoid any errors . A more complete and safe approach would be something like ...
This time I have also added schema as a parameter.
ALTER PROCEDURE DropColumn
#tableName SYSNAME,
#columnName SYSNAME,
#Schema SYSNAME,
#Success BIT OUTPUT
AS
BEGIN
SET NOCOUNT ON;
DECLARE #SQL NVARCHAR(MAX);
SET #SQL = N' IF EXISTS (SELECT * FROM sys.tables t
INNER JOIN sys.columns c
ON t.[object_id] = c.[object_id]
INNER JOIN sys.schemas sc
ON t.[schema_id] = sc.[schema_id]
WHERE t.name = #tableName
AND c.name = #columnName
AND sc.name = #Schema)
BEGIN
ALTER TABLE ' + QUOTENAME(#Schema)+ '.' + QUOTENAME(#tableName)
+ N' DROP COLUMN ' + QUOTENAME(#columnName)
+ N' SET #Success = 1; '
+ N' END
ELSE
BEGIN
SET #Success = 0;
END '
EXEC sp_executesql #SQL
,N'#tableName SYSNAME, #columnName SYSNAME, #Schema SYSNAME, #Success BIT OUTPUT'
,#tableName
,#columnName
,#Schema
,#Success OUTPUT
END
GO

Related

Stored procedure to drop the column in SQL Server

I created many tables and I have noticed that I have created one useless column in all the tables. I want to create a stored procedure which will drop one specific column and can be useful in all the column.
I created this stored procedure but I'm getting an error. Help me please
You cannot parametrize table and column names with parameters - those are only valid for values - not for object names.
If this is a one-time operation, the simplest option would be to generate the ALTER TABLE ... DROP COLUMN ... statements in SSMS using this code:
SELECT
'ALTER TABLE ' + SCHEMA_NAME(t.schema_id) + '.' + t.Name +
' DROP COLUMN Phone;'
FROM
sys.tables t
and then execute this code in SSMS; the output from it is a list of statement which you can then copy & paste to a new SSMS window and execute.
If you really want to do this as a stored procedure, you can apply the same basic idea - and then just use code (a cursor) to iterate over the commands being generated, and executing them - something like this:
CREATE PROCEDURE dbo.DropColumnFromAllTables (#ColumnName NVARCHAR(100))
AS
BEGIN
DECLARE #SchemaName sysname, #TableName sysname
-- define cursor over all tables which contain this column in question
DECLARE DropCursor CURSOR LOCAL FAST_FORWARD
FOR
SELECT
SchemaName = s.Name,
TableName = t.Name
FROM
sys.tables t
INNER JOIN
sys.schemas s ON t.schema_id = s.schema_id
WHERE
EXISTS (SELECT * FROM sys.columns c
WHERE c.object_id = t.object_id
AND c.Name = #ColumnName);
-- open cursor and start iterating over the tables found
OPEN DropCursor
FETCH NEXT FROM DropCursor INTO #SchemaName, #TableName
WHILE ##FETCH_STATUS = 0
BEGIN
DECLARE #Stmt NVARCHAR(1000)
-- generate the SQL statement
SET #Stmt = N'ALTER TABLE [' + #SchemaName + '].[' + #TableName + '] DROP COLUMN [' + #ColumnName + ']';
-- execute that SQL statement
EXEC sp_executeSql #Stmt
FETCH NEXT FROM DropCursor INTO #SchemaName, #TableName
END
CLOSE DropCursor
DEALLOCATE DropCursor
END
This procedure should work.
It loops through all cols and then deletes the column where sum(col) is zero.
Take a Backup of the Table
alter procedure deletecolumnsifzero #tablename varchar(1000)
as
set nocount on
declare #n int
declare #sql nvarchar(1000)
declare #sum_cols nvarchar(1000)
declare #c_id nvarchar(100)
set #n = 0
declare c1 cursor for
select column_name from information_schema.columns
where
table_name like #tablename
--Cursor Starts
open c1
fetch next from c1
into #c_id
while ##fetch_status = 0
begin
set #sql=''
set #sql='select #sum_cols = sum('+#c_id+') from ['+#tablename+']'
exec sp_Executesql #sql,N'#sum_cols int out,#tablename nvarchar(100)',#sum_cols out,#tablename
if(#sum_cols = 0)
begin
set #n=#n+1
set #sql=''
set #sql= #sql+'alter table ['+#tablename+'] drop column ['+#c_id+']'
exec sp_executesql #sql
end
fetch next from c1
into #c_id
end
close c1
deallocate c1

Invalid column name error when using QUOTENAME

I have several tables having the same structure. The tables are named by year that is 2001,2002 and so on. I am in need to search a column for a value in each table and get the count for each table.
I have created a stored procedure below but I keep getting an error
Invalid column 'lol'
This is the stored procedure used:
CREATE PROCEDURE [dbo].[CountSP]
#TableName NVARCHAR(128),
#SearchParam NVARCHAR(50),
#SearchInput NVARCHAR(200)
AS
BEGIN
SET NOCOUNT ON;
DECLARE #Sql NVARCHAR(MAX);
SET #Sql = N'SELECT COUNT('+QUOTENAME(#SearchParam)+') FROM ' + QUOTENAME(#TableName) +'WHERE'+QUOTENAME(#SearchParam)+'LIKE '+QUOTENAME(#SearchInput)+
+ N' SELECT * FROM '+QUOTENAME(#TableName)
EXECUTE sp_executesql #Sql
END
Executing it:
DECLARE #return_value INT
EXEC #return_value = [dbo].[CountSP]
#TableName = N'1999',
#SearchParam = N'USERDESC',
#SearchInput = N'lol'
SELECT 'Return Value' = #return_value
I don't know why you are using LIKE operator there while you don't use wildcards, also use SysName datatype directly for object names.
Create PROCEDURE [dbo].[CountSP]
(
#TableName SysName,
#SearchInput NVARCHAR(50),
#SearchParam SysName
)
AS
SET NOCOUNT ON;
DECLARE #SQL NVARCHAR(MAX) = N'SELECT COUNT(' +
QUOTENAME(#SearchParam) +
N') FROM ' +
QUOTENAME(#TableName) +
N' WHERE ' +
QUOTENAME(#SearchParam) +
N' = ' + --You can change it to LIKE if needed
QUOTENAME(#SearchInput, '''') +
N';';
-- There is no benifits of using LIKE operator there
EXEC sp_executesql #SQL;
Then you can call it as
EXEC [dbo].[CountSP] N'YourTableNameHere', N'SearchInput', N'ColumnName';
This is because it is currently translated to :
SELECT COUNT([USERDESC]) FROM [1999] WHERE [USERDESC] LIKE [lol]
this means that it is comparing the "USERDESC" column with the "lol" column but from what I am understanding lol isn't a column but a value? which means you should lose the QUOTENAME for that variable.
See the documentation here : https://learn.microsoft.com/en-us/sql/t-sql/functions/quotename-transact-sql?view=sql-server-2017
You need to pass your parameter #SearchInput as a parameter to sp_execute:
CREATE PROCEDURE [dbo].[CountSP] #TableName sysname, --This is effectively the same datatype (as sysname is a synonym for nvarchar(128))
#SearchParam sysname, --Have changed this one though
#SearchInput nvarchar(200)
AS
BEGIN
SET NOCOUNT ON;
DECLARE #Sql nvarchar(MAX);
SET #Sql = N'SELECT COUNT(' + QUOTENAME(#SearchParam) + N') FROM ' + QUOTENAME(#TableName) + N'WHERE' + QUOTENAME(#SearchParam) + N' LIKE #SearchInput;' + NCHAR(13) + NCHAR(10) +
N'SELECT * FROM ' + QUOTENAME(#TableName);
EXECUTE sp_executesql #SQL, N'#SearchInput nvarchar(200)', #SearchInput;
END;
QUOTENAME, by default, will quote a value in brackets ([]). It does accept a second parameter which can be used to define a different character (for example QUOTENAME(#Value,'()') will wrap the value in parentheses). For what you want though, you want to parametrise the value, not inject (a quoted) value.

Using values from Select Statement in a String for a Stored Procedure - MS SQL Server

I am writing a drop Procedure in SQL Server, but have hit a problem, in order to drop an index you must pass the table name in. I have wrote a piece of code to find the table (which works)
set #sqlndex = N'select name from sys.objects where object_id = (select object_id from sys.indexes where name = ''' + #objectname + ''')';
But I cant seem to take the returned value and use it, the code below is the full Procedure part I am working on:
What it should do:
It should print the table name (Testing reason / debugging)
It should then pass the name of the table to the drop string (+#result+)
if #objecttype='index' begin
IF EXISTS (SELECT * FROM sysindexes WHERE name = #objectname)
set #sqlndex = N'select name from sys.objects where object_id = (select object_id from sys.indexes where name = ''' + #objectname + ''')';
execute sp_executesql #sqlndex, #result = #result OUTPUT;
SELECT #result;
print #result;
set #SQL = 'DROP ' + #objecttype + ' [dbo].['+#result+'].['+#objectname+']';
select #SQL;
EXEC (#SQL);
end
Variables I am declaring
DECLARE #sql VARCHAR(MAX);
DECLARE #sqlndex NVARCHAR(MAX);
DECLARE #resultOUT NVARCHAR(MAX);
DECLARE #result NVARCHAR(MAX);
DECLARE #objecttype VARCHAR(MAX);
DECLARE #objectname VARCHAR(MAX);
Any help would be great
Try this:
Instead of
set #sqlndex = N'select name from sys.objects
where object_id = (select object_id from sys.indexes
where name = ''' + #objectname + ''')';
Write:
declare #result nvarchar(100)
declare #paramdef nvarchar(100)
set #sqlndex = N'select #resultout = name from sys.objects
where object_id = (select object_id from sys.indexes
where name = ''' + #objectname + ''')';
set #paramdef = N'#resultout nvarchar(100) output';
execute sp_executesql #sqlndex, #paramdef, #resultout = #result OUTPUT;
So you'll be able to manage #result value

Stored procedure to remove FK of a given table

I need to create a stored procedure that:
Accepts a table name as a parameter
Find its dependencies (FKs)
Removes them
Truncate the table
I created the following so far based on http://www.mssqltips.com/sqlservertip/1376/disable-enable-drop-and-recreate-sql-server-foreign-keys/ . My problem is that the following script successfully does 1 and 2 and generates queries to alter tables but does not actually execute them. In another word how can execute the resulting "Alter Table ..." queries to actually remove FKs?
CREATE PROCEDURE DropDependencies(#TableName VARCHAR(50))
AS
BEGIN
SELECT 'ALTER TABLE ' + OBJECT_SCHEMA_NAME(parent_object_id) + '.[' + OBJECT_NAME(parent_object_id) + '] DROP CONSTRAINT ' + name
FROM sys.foreign_keys WHERE referenced_object_id=object_id(#TableName)
END
EXEC DropDependencies 'TableName'
Any idea is appreciated!
Update:
I added the cursor to the SP but I still get and error:
"Msg 203, Level 16, State 2, Procedure DropRestoreDependencies, Line 75
The name 'ALTER TABLE [dbo].[ChildTable] DROP CONSTRAINT [FK__ChileTable__ParentTable__745C7C5D]' is not a valid identifier."
Here is the updated SP:
CREATE PROCEDURE DropRestoreDependencies(#schemaName sysname, #tableName sysname)
AS
BEGIN
SET NOCOUNT ON
DECLARE #operation VARCHAR(10)
SET #operation = 'DROP' --ENABLE, DISABLE, DROP
DECLARE #cmd NVARCHAR(1000)
DECLARE
#FK_NAME sysname,
#FK_OBJECTID INT,
#FK_DISABLED INT,
#FK_NOT_FOR_REPLICATION INT,
#DELETE_RULE smallint,
#UPDATE_RULE smallint,
#FKTABLE_NAME sysname,
#FKTABLE_OWNER sysname,
#PKTABLE_NAME sysname,
#PKTABLE_OWNER sysname,
#FKCOLUMN_NAME sysname,
#PKCOLUMN_NAME sysname,
#CONSTRAINT_COLID INT
DECLARE cursor_fkeys CURSOR FOR
SELECT Fk.name,
Fk.OBJECT_ID,
Fk.is_disabled,
Fk.is_not_for_replication,
Fk.delete_referential_action,
Fk.update_referential_action,
OBJECT_NAME(Fk.parent_object_id) AS Fk_table_name,
schema_name(Fk.schema_id) AS Fk_table_schema,
TbR.name AS Pk_table_name,
schema_name(TbR.schema_id) Pk_table_schema
FROM sys.foreign_keys Fk LEFT OUTER JOIN
sys.tables TbR ON TbR.OBJECT_ID = Fk.referenced_object_id --inner join
WHERE TbR.name = #tableName
AND schema_name(TbR.schema_id) = #schemaName
OPEN cursor_fkeys
FETCH NEXT FROM cursor_fkeys
INTO #FK_NAME,#FK_OBJECTID,
#FK_DISABLED,
#FK_NOT_FOR_REPLICATION,
#DELETE_RULE,
#UPDATE_RULE,
#FKTABLE_NAME,
#FKTABLE_OWNER,
#PKTABLE_NAME,
#PKTABLE_OWNER
WHILE ##FETCH_STATUS = 0
BEGIN
-- create statement for dropping FK and also for recreating FK
IF #operation = 'DROP'
BEGIN
-- drop statement
SET #cmd = 'ALTER TABLE [' + #FKTABLE_OWNER + '].[' + #FKTABLE_NAME
+ '] DROP CONSTRAINT [' + #FK_NAME + ']'
EXEC #cmd
-- create process
DECLARE #FKCOLUMNS VARCHAR(1000), #PKCOLUMNS VARCHAR(1000), #COUNTER INT
-- create cursor to get FK columns
DECLARE cursor_fkeyCols CURSOR FOR
SELECT COL_NAME(Fk.parent_object_id, Fk_Cl.parent_column_id) AS Fk_col_name,
COL_NAME(Fk.referenced_object_id, Fk_Cl.referenced_column_id) AS Pk_col_name
FROM sys.foreign_keys Fk LEFT OUTER JOIN
sys.tables TbR ON TbR.OBJECT_ID = Fk.referenced_object_id INNER JOIN
sys.foreign_key_columns Fk_Cl ON Fk_Cl.constraint_object_id = Fk.OBJECT_ID
WHERE TbR.name = #tableName
AND schema_name(TbR.schema_id) = #schemaName
AND Fk_Cl.constraint_object_id = #FK_OBJECTID -- added 6/12/2008
ORDER BY Fk_Cl.constraint_column_id
OPEN cursor_fkeyCols
FETCH NEXT FROM cursor_fkeyCols INTO #FKCOLUMN_NAME,#PKCOLUMN_NAME
SET #COUNTER = 1
SET #FKCOLUMNS = ''
SET #PKCOLUMNS = ''
WHILE ##FETCH_STATUS = 0
BEGIN
IF #COUNTER > 1
BEGIN
SET #FKCOLUMNS = #FKCOLUMNS + ','
SET #PKCOLUMNS = #PKCOLUMNS + ','
END
SET #FKCOLUMNS = #FKCOLUMNS + '[' + #FKCOLUMN_NAME + ']'
SET #PKCOLUMNS = #PKCOLUMNS + '[' + #PKCOLUMN_NAME + ']'
SET #COUNTER = #COUNTER + 1
FETCH NEXT FROM cursor_fkeyCols INTO #FKCOLUMN_NAME,#PKCOLUMN_NAME
END
CLOSE cursor_fkeyCols
DEALLOCATE cursor_fkeyCols
END
FETCH NEXT FROM cursor_fkeys
INTO #FK_NAME,#FK_OBJECTID,
#FK_DISABLED,
#FK_NOT_FOR_REPLICATION,
#DELETE_RULE,
#UPDATE_RULE,
#FKTABLE_NAME,
#FKTABLE_OWNER,
#PKTABLE_NAME,
#PKTABLE_OWNER
END
CLOSE cursor_fkeys
DEALLOCATE cursor_fkeys
END
For running use:
EXEC DropRestoreDependencies dbo, ParentTable
Use a cursor to go through your SELECT results, populating a variable with the single column, and executing that query with EXEC(#YourVariable). Be sure to use parens around the variable!
The issue is that you are only preparing the SQL statement and not executing it (I think)
CREATE PROCEDURE DropDependencies(#TableName VARCHAR(50))
AS
BEGIN
DECLARE #SQL nvarchar(max)
SELECT #SQL = 'ALTER TABLE ' + OBJECT_SCHEMA_NAME(parent_object_id) + '.[' + OBJECT_NAME(parent_object_id) + '] DROP CONSTRAINT ' + name
FROM sys.foreign_keys WHERE referenced_object_id=object_id(#TableName)
EXEC #SQL
END
EXEC DropDependencies 'TableName'
Whenever using EXEC though from constructed strings, ensure you aren't vulnerable to SQL Injection attacks.
Try Out SP_ExecuteSQL or Exec. I see that you are using Exec. Perhaps SP_ExecuteSQL will work?

How to secure dynamic SQL stored procedure?

I have a stored procedure that takes in the name of a table as a parameter and uses dynamic sql to perform the select. I tried to pass #TableName as a parameter and use sp_executesql but that threw an error. I decided to go with straight dynamic sql without using sp_executesql.
Is there anything else I should be doing to secure the #TableName parameter to avoid sql injection attacks?
Stored procedure below:
CREATE PROCEDURE dbo.SP_GetRecords
(
#TableName VARCHAR(128) = NULL
)
AS
BEGIN
/* Secure the #TableName Parameter */
SET #TableName = REPLACE(#TableName, ' ','')
SET #TableName = REPLACE(#TableName, ';','')
SET #TableName = REPLACE(#TableName, '''','')
DECLARE #query NVARCHAR(MAX)
/* Validation */
IF #TableName IS NULL
BEGIN
RETURN -1
END
SET #query = 'SELECT * FROM ' + #TableName
EXEC(#query)
END
This failed when using sp_executesql instead:
SET #query = 'SELECT * FROM #TableName'
EXEC sp_executesql #query, N'#TableName VARCHAR(128)', #TableName
ERROR: Must declare the table variable
"#TableName".
See here:
How should I pass a table name into a stored proc?
you of course can look at the sysobjects table and ensure that it exists
Select id from sysobjects where xType = 'U' and [name] = #TableName
Further (more complete example):
DECLARE #TableName nVarChar(255)
DECLARE #Query nVarChar(512)
SET #TableName = 'YourTable'
SET #Query = 'Select * from ' + #TableName
-- Check if #TableName is valid
IF NOT (Select id from sysobjects where xType = 'U' and [name] = #TableName) IS NULL
exec(#Query)