Sql Server Stored Procedure for adding columns to a table - sql

I m trying to create a stored procedure that will add two columns onto the end of a table. The SP takes 3 inputs, a table name, a state, and a school year.
When I run the SP I get an error:
The name "CA" is not permitted in this context. Valid expressions are constants, constant expressions, and (in some contexts) variables. Column names are not permitted.
Here is my SP:
CREATE PROCEDURE sp_addScoolEnrollYear
#state nvarchar(2),
#shcoolYear nvarchar(15),
#tableName nvarchar(100)
AS
BEGIN
SET NOCOUNT ON;
DECLARE #Sql NVARCHAR(MAX);
SET #Sql= N'alter table ' + quotename(#tableName) +
N'add StateEnrollID varchar(2) default ' + quotename(#state) + N'not null,
SchoolYearKey varchar(20) default ' + quotename(#shcoolYear) + N'not null'
EXECUTE sp_executesql #Sql
END
GO
This is how I execute:
[dbo].[sp_addScoolEnrollYear] N'CA', N'2014-2015' , N'[dbo].[test_copy]'

You were adding extra square brackets around the table name, and you need to use ' instead of square brackets around your strings, and some more spaces between words, and each column added needs to be a separate alter:
CREATE PROCEDURE sp_addScoolEnrollYear
#state nvarchar(2),
#shcoolYear nvarchar(15),
#tableName sysname
AS
BEGIN
SET NOCOUNT ON;
DECLARE #Sql NVARCHAR(MAX);
SET #Sql=N'alter table ' + #tablename
+ N' add StateEnrollID varchar(2) default ''' + #state + N''' not null;' +char(10)
+ N'alter table ' + #tablename
+ N' add SchoolYearKey varchar(20) default ''' + #shcoolYear + N''' not null;'
select #sql;
EXECUTE sp_executesql #Sql;
END
END
GO
exec [dbo].[sp_addScoolEnrollYear] N'CA', N'2014-2015' , N'[dbo].[test_copy]'
code generated:
alter table dbo.test_copy add StateEnrollID varchar(2) default 'CA' not null;
alter table dbo.test_copy add SchoolYearKey varchar(20) default '2014-2015' not null;
rextester demo: http://rextester.com/HEKC53724

Related

Dynamic SQL not working for changing variable column name in SQL Server

I want to write a stored procedure which can be used to update IDs of owner name, backup contact, other contacts etc in a table. These IDs are to fetched from some other table. Instead of writing different stored procedure for all these contact information, I want to write a single dynamic SQL in which I can pass the column name as a variable name.
My stored procedure looks like:
CREATE PROCEDURE spUpdateUser
(#recordid [NVARCHAR](50),
#id [NVARCHAR](10),
#user [NVARCHAR](50))
AS
BEGIN
DECLARE #sql NVARCHAR(MAX);
SET NOCOUNT ON;
SET #sql = N'UPDATE [dbo].[table1]
SET'+ QUOTENAME(#user) + ' = (SELECT [dbo].[table2].User
FROM [dbo].[table2]
WHERE [dbo].[table2].id = ' + QUOTENAME(#id) + ')
WHERE record = ' + QUOTENAME(#recordid)
EXEC sp_executesql #sql;
END;
GO
After executing the query it runs without error but the user is not changed in table1.
What is missing in the procedure?
Don't inject your parameters, parametrise them:
CREATE PROCEDURE spUpdateUser
-- Add the parameters for the stored procedure here
( #recordid [nvarchar](50), --Are your IDs really an nvarchar?
#id [nvarchar](10), --Are your IDs really an nvarchar?
#user sysname --As this is an object, lets use the correct datatype
AS
BEGIN
-- SET NOCOUNT ON added to prevent extra result sets from
-- interfering with SELECT statements.
DECLARE #sql NVARCHAR(MAX),
#CRLF nchar(2) = NCHAR(13) + NCHAR(10);
SET NOCOUNT ON;
-- Insert statements for procedure here
SET #sql= N'UPDATE [dbo].[table1]' + #CRLF +
N'SET ' + QUOTENAME(#user) + N' = (SELECT [table2].User' + #CRLF + --3 part naming for columns is deprecated, don't use it
N' FROM [dbo].[table2]' + #CRLF +
N' WHERE [table2].id= #id)' + #CRLF + --3 part naming for columns is deprecated, don't use it
N'WHERE record = #recordid;';
--PRINT #SQL; --Your Best Friend
EXEC sp_executesql #sql, N'#id nvarchar(10), #recordid nvarchar(50)', #id, #recordid; --Assumes that IDs are an nvarchar again
END;
GO
Note I've left some comments in there for you to consume and review as well.

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.

SQL Pass a string to a Stored Procedure that is not a Column Name

Can I pass a String to a Stored Procedure that is not a Column Name?
I need to call the StoredProcedure from C#.
The following does not work as the parameter can't be defined without it's Type, but shows what I am trying to do. Problem is that Sql is looking at #stringToIdentifyDataTable as a ColumnName, which seems fair, but not what I am trying to do.
Alter PROCEDURE [dbo].[PutNewTypeSource] #stringToIdentifyDataTable,
#ID int, #Description varchar(50), #Active bit
AS
DECLARE
#Query AS VARCHAR(MAX),
#Field_Out AS VARCHAR(MAX)
SELECT #Field_Out = CASE #stringToIdentifyDataTable
WHEN 'ReferralSource' THEN '[Adm].[JobReferralSource]'
WHEN 'ReferralSource2' THEN '[Adm].[JobReferralSource2]'
END
SET #Query = concat('
IF EXISTS (SELECT ID FROM ',#Field_Out,' WHERE Description= ',#Description,')
BEGIN
UPDATE ',#Field_Out,
'SET Active = ',#Active,
'WHERE Description= ',#Description,';
END')
EXEC (#Query)
exec [PutNewTypeSource] 'ReferralSource', 1, 'Description1', 0
If I understand correctly what you could do is this. note that I properly quote your object, and importantly parametrise you parameters. What you have before was wide open to injection:
ALTER PROCEDURE [dbo].[PutNewTypeSource] #Referral varchar(50), #Description varchar(50), #Active bit --I remvoed #ID as it was never used
AS BEGIN
DECLARE #Schema sysname,
#Table sysname;
SET #Schema = CASE WHEN #Referral IN ('ReferralSource','ReferralSource2') THEN N'adm' END;
SET #Table = CASE #Referral WHEN 'ReferralSource' THEN N'JobReferralSource'
WHEN 'ReferralSource2' THEN N'JobReferralSource2' END;
DECLARE #SQL nvarchar(MAX);
SET #SQL = N'UPDATE ' + QUOTENAME(#Schema) + N'.' + QUOTENAME(#Table) + NCHAR(13) + NCHAR(10) +
N'SET Active = #Active' + NCHAR(13) + NCHAR(10) +
N'WHERE Description= #Description;';
EXEC sp_executesql #SQL, N'#Description varchar(50), #Active bit', #Description = #Description, #Active = #Active;
END;

Sql server using a built int function in a stored procedure

I am trying to write a SP that uses the substring function inside a stored procedure where I use the column name as input. However when I run
exec sp_addCountySchoolDistrict N'table', N'districtCountySchoolCode'
It uses the 'districtCountySchoolCode' for the substring and not the value from the row...
For example
create table [dbo].[test] (districtCountySchoolCode nvarchar(100))
insert into dbo.test values ('1234567891234')
go
CREATE PROCEDURE sp_addCountySchoolDistrict
#tableName nvarchar(100),
#colName nvarchar(100)
AS
BEGIN
SET NOCOUNT ON;
DECLARE #Sql NVARCHAR(MAX);
SET #Sql= N'alter table ' + replace(replace(quotename(#tableName),']',''),'[','') +
N' add countyCode as ''' + substring(#colName, 1,2) + N',
districtCode as ''' + substring(#colName, 3,5) + N',
schoolCode as ''' + substring(#colName, 8,7) + N';'
select #sql;
EXECUTE sp_executesql #Sql
END
GO
exec sp_addCountySchoolDistrict N'[dbo].[test]', N'districtCountySchoolCode'
select * from test_copy
Coder Wall
Put the substring in the literal part of the dynamic sql, like this:
N' add countyCode as substring(''' + #colName+ N', 1,2) ,
try this
exec sp_addCountySchoolDistrict N'[dbo].[test]', N'1234567891234'
Your mistake is that you pass the "column name" as a string but your code nowhere selects the value from test table for the specified column. You have to perform a select statement first, to get the value and then use the value in the rest code.
This should work
create table [dbo].[test] (districtCountySchoolCode nvarchar(100))
insert into dbo.test values ('1234567891234')
go
CREATE PROCEDURE sp_addCountySchoolDistrict
#tableName nvarchar(100),
#colName nvarchar(100)
AS
BEGIN
declare #val nvarchar(100);
declare #initialQuery nvarchar(100);
SET NOCOUNT ON;
DECLARE #Sql NVARCHAR(MAX);
SET #initialQuery = 'SELECT #value=' + quotename(#colName) + ' FROM ' + replace(replace(quotename(#tableName),']',''),'[','')
EXECUTE sp_executesql #initialQuery, N'#value nvarchar(100) OUTPUT', #value=#val OUTPUT
SET #Sql= N'alter table ' + replace(replace(quotename(#tableName),']',''),'[','') +
N' add countyCode as ''' + substring(#val, 1,2) + N''',
districtCode as ''' + substring(#val, 3,5) + N''',
schoolCode as ''' + substring(#val, 8,7) + N''';'
select #sql;
EXECUTE sp_executesql #Sql
END
GO
exec sp_addCountySchoolDistrict N'[dbo].[test]', N'districtCountySchoolCode'
select * from test
But you have to add a where condition i suppose if you have more than one values in test table.

Passing either null or a string to a stored procedure doesn't work (because of quotes?)

I am using a stored procedure below. For parameters called LockedByName and LockedByLoginName I want to pass either a string (which can contain spaces etc. - so I quote it with single quotes) or a NULL value.
The procedure works with strings but doesn't work with null value. I assume this is because it somehow silently fails because it cannot put the NULL value into quotes.
What could be the way to both keep the quotes and also keep the ability to use null value?
It similarly fails when the parameter is an empty string which would be fine to use too.
ALTER PROCEDURE [dbo].[ChangeLockFormStatus]
#FormID as int,
#LockedByName as nvarchar(50) = null,
#LockedByLoginName as nvarchar(50) = null,
#TableName as varchar(50)
AS
BEGIN
SET NOCOUNT ON;
DECLARE #ActualTableName AS varchar(255)
SELECT #ActualTableName = QUOTENAME( TABLE_NAME )
FROM INFORMATION_SCHEMA.TABLES
WHERE TABLE_NAME = #TableName
DECLARE #sql AS NVARCHAR(MAX)
SELECT #sql = 'UPDATE ' + #ActualTableName
+ ' SET LockedByName=''' + #LockedByName + ''','
+ 'LockedByLoginName=''' + #LockedByLoginName
+ ''' WHERE [ID] = ' + CAST(#FormID AS VARCHAR(25)) + ';'
EXEC(#sql)
END
If you use sp_executesql, you can continue to use your parameters directly, rather than having to mangle them into the string. Something like:
ALTER PROCEDURE [dbo].[ChangeLockFormStatus]
#FormID as int,
#LockedByName as nvarchar(50) = null,
#LockedByLoginName as nvarchar(50) = null,
#TableName as varchar(50)
AS
BEGIN
SET NOCOUNT ON;
DECLARE #ActualTableName AS varchar(255)
SELECT #ActualTableName = QUOTENAME( TABLE_NAME )
FROM INFORMATION_SCHEMA.TABLES
WHERE TABLE_NAME = #TableName
DECLARE #sql AS NVARCHAR(MAX)
SELECT #sql = 'UPDATE ' + #ActualTableName
+ ' SET LockedByName=#LockedByName,'
+ 'LockedByLoginName=#LockedByLoginName'
+ ' WHERE [ID] = #FormID;'
EXEC sp_executesql #sql,
N'#LockedByName nvarchar(50),#LockedByLoginName nvarchar(50),#FormID int',
#LockedByName,#LockedByLoginName,#FormID
END