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.
Related
I have a simple query that I want to convert to dynamic SQL. I have 2 input parameters: a table and a datetime. And the output is the rowcount for the table and this specific datetime.
CREATE PROCEDURE [etl].[ROWCOUNT_TST2]
(#P_LOAD_TARGET nvarchar(250),
#P_LOAD_DATE DATETIME)
AS
BEGIN
DECLARE #SQL nvarchar(1000)
SET #SQL = 'SELECT COUNT(*) as Inserted FROM'+#P_LOAD_TARGET +' WHERE VALID_FROM ='''+ #P_LOAD_DATE+''' AND VALID_TO IS NULL'
EXEC (#SQL)
END;
GO
I tried different solutions. I tried the query with execute sp_executesql, I tied to add the the ''' before and after the #P_LOAD_DATE. I am probably missing something here.
When I execute the stored procedure with a table name and datetime like 2021-05-06 06:41:52.557, I get the following error:
Conversion failed when converting date and/or time from character string.
But why?
I even tried to add a conversion to datetime like this, but I still get the same error.
SET #SQL = 'SELECT COUNT(*) as Inserted FROM'+#P_LOAD_TARGET +' WHERE VALID_FROM = convert(datetime,'''+ #P_LOAD_DATE+''') AND VALID_TO IS NULL'
But when I execute SELECT convert(datetime, '2021-05-06 06:41:52.557') it works out fine. I am just confused right now and can't find the root of the problem.
Edit: valid_from is a datetime in the target table. So that is also not the reason for the problem
You need to properly and safely inject your dynamic object name and parametrise your parameter:
CREATE PROCEDURE [etl].[ROWCOUNT_TST2](#P_LOAD_SCHEMA sysname = N'dbo', --Always define your schema
#P_LOAD_TARGET sysname, --sysname is the data type for objects, a synonym of nvarchar(128) NOT NULL
#P_LOAD_DATE datetime) AS
BEGIN
DECLARE #SQL nvarchar(MAX),
#CRLF nchar(2) = NCHAR(13) + NCHAR(10);
SET #SQL = N'SELECT COUNT(*) AS Inserted' + #CRLF +
N'FROM ' + QUOTENAME(#P_LOAD_SCHEMA) + N'.' + QUOTENAME(#P_LOAD_TARGET) + #CRLF +
N'WHERE VALID_FROM = #P_LOAD_DATE' + #CRLF +
N' AND VALID_TO IS NULL;';
--PRINT #SQL; --Your best friend.
EXEC sys.sp_executesql #SQL, N'#P_LOAD_DATE datetime', #P_LOAD_DATE;
END;
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.
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;
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.
I am attempting to create a table in T-SQL using sp_executesql. The name of the database containing the table is dynamic.
DECLARE #ID int = 1031460
DECLARE #TableName nvarchar(max) = '[MyDatabase' + CAST(#ID as nvarchar(10)) + '].[dbo].[MyTable]'
DECLARE #CreateTable nvarchar(max) = N''
SELECT #CreateTable =
N'
CREATE TABLE #TableName
(
ID int
)
'
EXECUTE sp_executeSQL #CreateTable, N'#TableName nvarchar(max)', #TableName = #TableName
This script results in this error:
Msg 102, Level 15, State 1, Line 2
Incorrect syntax near '#TableName'.
What is the best way to specify the name of the table to create dynamically based on parameters of sp_executeSQL?
You cannot pass Tablename as a parameter even we using sp_executesql procedure. You have to build you own dynamic sql query
SELECT #CreateTable =
N'
CREATE TABLE ' + #TableName + '
(
ID int
)
'
Exec sp_executesql #CreateTable
When you are passing the table name as a parameter to sp_executesql, it treats it as a literal string, to make it treat it as an object name try something like this......
DECLARE #ID int = 1031460
DECLARE #TableName nvarchar(max) = QUOTENAME('MyDatabase' + CAST(#ID as nvarchar(10)))
+ '.[dbo].[MyTable]'
DECLARE #CreateTable nvarchar(max);
SET #CreateTable = N' CREATE TABLE ' + #TableName
+ N' (
ID int
)'
Exec sp_executesql #CreateTable
EDIT
It is good practice to check if an object already exists before you try to create it.
You can check if the object already exists and drop it and then create the new one, you code should look something like.....
DECLARE #ID int = 1031460
DECLARE #TableName nvarchar(max) = QUOTENAME('MyDatabase' + CAST(#ID as nvarchar(10)))
+ '.[dbo].[MyTable]'
DECLARE #CreateTable nvarchar(max), #DropTable nvarchar(max);
-- Drop table if already exists
SET #DropTable = N' IF OBJECT_ID('''+ #TableName +''') IS NOT NULL '
+ N' DROP TABLE ' + #TableName
Exec sp_executesql #DropTable
SET #CreateTable = N' CREATE TABLE ' + #TableName
+ N' (
ID int
)'
Exec sp_executesql #CreateTable