I want to use Dynamic SQL within a stored procedure to create a table.
Here is the call to the stored procedure:
EXEC [spCreateAColDiffTable] 'hq193.dbo.arch_con_col_s193_s202'
Here are the relevant parts of the stored procedure:
CREATE PROCEDURE sp_createAColDiffTable (#table_name nvarchar(128))
...
SET #sSQL = 'CREATE TABLE ' + #table_name + ' ( ' +
' [table_name] VARCHAR (128) NOT NULL, ' +
' [column_name] VARCHAR (128) NULL, ' +
' [what_changed] VARCHAR (128) NULL, ' +
' [sr_data_type] VARCHAR (128) NOT NULL, ' +
' [tr_data_type] VARCHAR (128) NOT NULL, ' +
' [sr_max_length] SMALLINT NOT NULL, ' +
' [tr_max_length] SMALLINT NOT NULL, ' +
' [sr_is_nullable] CHAR NULL, ' +
' [tr_is_nullable] CHAR NULL, ' +
' [sr_precision] SMALLINT NULL, ' +
' [tr_precision] SMALLINT NULL, ' +
' [sr_scale] SMALLINT NULL, ' +
' [tr_scale] SMALLINT NULL ) ' +
' ON [PRIMARY] WITH (DATA_COMPRESSION = NONE)'
PRINT #sSQL
Exec #sSQL
GO
When I run the stored procedure I receive the error:
SQL Server Database Error: The name 'CREATE TABLE
hq193.dbo.arch_con_col_s193_s202 ( [table_name] VARCHAR (128) NOT
NULL, [column_name] VARCHAR (128) NULL, [what_changed] VARCHAR (128)
NULL, [sr_data_type] VARCHAR (128) NOT NULL, [tr_data_type] VARCHAR
(128) NOT NULL, [sr_max_length] SMALLINT NOT NULL, [tr_max_length]
SMALLINT NOT NULL, [sr_is_nullable] CHAR NULL, [tr_is_nullable] CHAR
NULL, [sr_precision] SMALLINT NULL, [tr_precision] SMALLINT NULL,
[sr_scale] SMALLINT NULL, [tr_scale] SMALLINT NULL ) ON [PRIMARY] WITH
(DATA_COMPRESSION = NONE)'
is not a valid identifier.
Notice in the stored procedure I printed out the SQL before I executed it. If I cut and paste the SQL that gets printed into a query editor window it works fine.
What am I missing?
Try it like this:
EXEC(#sSQL)
This is a very common error. Without the parenthesis, EXEC #sSQL means "execute a stored procedure whose name is in the #sSQL variable", rather than what you want which is probably "Execute the command string in the variable #sSQL."
I see this is an old post, but I've had a similar issue where I need to read a text file where the columns may of changed by having more and less depending on how the file was pulled. So I wrote a program to read the text file and put it into a dynamically created temp table where I can work with the output.
Perhaps this can help someone else..
DECLARE #NUM_COL AS INT
DECLARE #I AS INT
DECLARE #CREATE_TBL AS NVARCHAR(MAX)
DECLARE #DATA AS NVARCHAR (MAX)
DECLARE #XML_ROW AS XML
DECLARE #MAX_CHAR AS INT
--Sets the column max characters for temp table ##FILE_TABLE
SET #MAX_CHAR = 1000
--File import of data as single rows, no columns
IF OBJECT_ID('tempdb..#FILE_ROWS') IS NOT NULL
DROP TABLE #FILE_ROWS
CREATE TABLE #FILE_ROWS
( [Row_data] NVARCHAR(MAX) NULL )
--Global temp table used because the table is being built dynamically.
IF OBJECT_ID('tempdb..##FILE_TABLE') IS NOT NULL
DROP TABLE ##FILE_TABLE
--This is only so the debugger thinks the table is created when referenced in later SQL code.
IF 1 <> 1 CREATE TABLE ##FILE_TABLE (X INT)
BULK INSERT #FILE_ROWS
FROM 'C:\Users\Wayne\Desktop\777434633016764.txt'
WITH
(
FIELDTERMINATOR = '\t' --Tab Delimited
,ROWTERMINATOR = '\n'
)
--Figures out how many columns were in the file.
SET #NUM_COL = (SELECT MAX(LEN(Row_data) - LEN(REPLACE(Row_data, CHAR(9), ''))) + 1 AS [NO_COL] FROM #FILE_ROWS)
SET #CREATE_TBL = 'CREATE TABLE ##FILE_TABLE ( ID INT IDENTITY(1,1),'
SET #I = 1
Declare COUNTER Cursor for
SELECT
CAST('<A>' + REPLACE(Row_data, CHAR(9), '</A><A>') + '</A>' AS XML)
FROM #FILE_ROWS
open COUNTER
fetch next from COUNTER into #XML_ROW
while ##fetch_Status != -1
begin
IF #I = 1
BEGIN
SELECT #CREATE_TBL = #CREATE_TBL
+ '[' + REPLACE(dbo.Trim(DATA.value('.','char(30)')), N'''', '`')
+ ']' + ' NVARCHAR(' + CAST(#MAX_CHAR AS NVARCHAR(5)) + ') NULL,'
FROM #XML_ROW.nodes('/A') AS x(DATA)
SET #CREATE_TBL = LEFT(#CREATE_TBL, LEN(#CREATE_TBL) - 1) + ')'
EXEC(#CREATE_TBL)
SET #I = 2
END
--ELSE --If you do not want the column names in the first row, remove the ELSE
BEGIN
SET #DATA = 'INSERT INTO ##FILE_TABLE SELECT '
SELECT #DATA = #DATA
+ '''' + REPLACE(dbo.Trim(DATA.value('.','char(30)')), N'''', '`')
+ '''' + ','
FROM #XML_ROW.nodes('/A') AS x(DATA)
SET #DATA = LEFT(#DATA, LEN(#DATA) -1)
EXEC(#DATA)
END
FETCH NEXT FROM COUNTER INTO #XML_ROW
END
CLOSE COUNTER
DEALLOCATE COUNTER
SELECT * from ##FILE_TABLE
Related
I want to create a table with 2 columns: Name of the customer of type varchar and age of type int.
My code:
CREATE PROCEDURE CreateTable
(#tablename varchar(50),
#column varchar(50),
#type varchar(50),
#column2 varchar(20),
#type2 int,
#extra varchar(20) = NULL)
AS
BEGIN
DECLARE #sqlQuery AS varchar(MAX)
SET #sqlQuery = 'CREATE TABLE ' + #tablename + '( id int '+ #column +' ' + #type + #column2 +' ' + #type2+' PRIMARY KEY(id))'
PRINT (#sqlQuery)
EXEC (#sqlQuery)
END
GO
EXEC CreateTable 'Customers', 'Name', 'varchar(50)', 'Age', 'int'
The error is:
Procedure or function CreateTable has too many arguments specified
I don't know where my error is. Please help me.
Should be OK now:
CREATE PROCEDURE CreateTable (
#tablename VARCHAR(50)
,#column VARCHAR(50)
,#type VARCHAR(50)
,#column2 VARCHAR(20)
,#type2 VARCHAR(50)
,#extra VARCHAR(20) = NULL
)
AS
BEGIN
DECLARE #sqlQuery AS VARCHAR(MAX)
SET #sqlQuery = 'create table ' + #tablename + '( id int ,' + #column + ' ' + #type + ' , ' + #column2 + ' ' + #type2 + ' PRIMARY KEY(id))'
PRINT (#sqlQuery)
EXEC (#sqlQuery)
END
GO
EXEC CreateTable 'Customers'
,'Name'
,'varchar(50)'
,'Age'
,'int'
If you compare the parameters you will find the type of #type2 was specified as int while it should be varchar to be part of the executed statement.
You were also missing comma(,) in your executable string inside the stored procedure.
I am getting error like:
Msg 241, Level 16, State 1, Procedure spQueryMgt, Line 106 [Batch
Start Line 13] Conversion failed when converting date and/or time
from character string.
ALTER PROCEDURE [dbo].[spQueryMgt]
#Mode varchar(50)='',
#Query_Form varchar(20)='',
#Patient_ID bigint=0,
#Verified_By bigint = 0,
#Verified_Date datetime=''
AS
BEGIN
IF(#mode='Post_Query')
BEGIN
DECLARE #sql NVARCHAR(MAX);
SET #sql = N'Update '+ CONVERT(varchar(12), #Query_Form) +' Set
Verified_By='+CONVERT(VARCHAR(12), #Verified_By)+',
Verified_Date='''+CONVERT(datetime, #Verified_Date,20)+'''
where Patient_ID = '+CONVERT(varchar(12), #Patient_ID)
EXEC sp_executeSQL #sql;
END
END
Change your query to -
SET #sql = N'Update '+ CONVERT(varchar(12), #Query_Form) +' Set
Verified_By='+CONVERT(VARCHAR(12), #Verified_By)+',
Verified_Date='''+CONVERT(VARCHAR(20), #Verified_Date,20)+'''
where Patient_ID = '+CONVERT(varchar(12), #Patient_ID)
There is issue with your convert function. You are converting #Verified_Date to datetime and concatenating datetime to varchar string.
The problem was that you where converting datetime back into datetime, and then tried to concatinate which gives you that exception.
But also, by initializing parameters to '' and 0 you are filling your database with unnesesary values like '' for varchars and 1900-01-01 for datetime columns and even worse 0 in integer columns.
Is that what you really want ? I doubt it.
It will be impossible to determine if a field was intentionaly set to this value or was left empty and get you into troubles later.
You can do your procedure like this to get NULL values in empty parameters
ALTER PROCEDURE [dbo].[spQueryMgt] (
#Mode varchar(50) = NULL,
#Query_Form varchar(20) = NULL,
#Patient_ID bigint = NULL,
#Verified_By bigint = NULL,
#Verified_Date datetime = NULL
)
AS
BEGIN
SET NOCOUNT ON;
IF #mode ='Post_Query'
BEGIN
DECLARE #sql NVARCHAR(MAX);
if (#Query_Form is not null) and (#Patient_ID is not null) -- nu use in updating when no table or id is given
begin
SET #sql = N' Update ' + CONVERT(varchar(12), #Query_Form) +
' Set Verified_By = ' + isnull(CONVERT(VARCHAR(12), #Verified_By), 'null') + ',' +
' Verified_Date = ' + case when #Verified_Date is null then 'null' else '''' + CONVERT(varchar(20), #Verified_Date, 20) + '''' end +
' where Patient_ID = ' + isnull(CONVERT(varchar(12), #Patient_ID), 'null')
EXEC sp_executeSQL #sql;
end
END
END
EDIT
As mentioned in the comments you should also take care of sql injection, and that makes your procedure safer, but also even easier
create PROCEDURE [dbo].[spQueryMgt] (
#Mode varchar(50) = NULL,
#Query_Form varchar(20) = NULL,
#Patient_ID bigint = NULL,
#Verified_By bigint = NULL,
#Verified_Date datetime = NULL
)
AS
BEGIN
SET NOCOUNT ON
IF #mode = 'Post_Query'
BEGIN
DECLARE #sql NVARCHAR(MAX)
if (#Query_Form is not null) and (#Patient_ID is not null) -- nu use in updating when no table or id is given
begin
SET #sql = N'Update #P0 ' +
'set Verified_By = #P1, ' +
' Verified_Date = #P2 ' +
'where Patient_ID = #P3'
EXEC sp_executesql #sql,
N'#P0 varchar(20), #P1 bigint, #P2 bigint, #P3 datetime',
N'#P0 = #Query_Form, #P1 = #Verified_By, #P2 = #Verified_Date, #P3 = #Patient_ID'
end
END
END
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
I am receiving the above error when I try to execute a query on MS SQL Server 2005. It is a dynamic query built up in multiple parts. Here is the simplified structure and data:
CREATE TABLE [record_fields](
[field_id] [int] NOT NULL,
[campaign_id] [int] NOT NULL,
[record_import_type_id] [int] NOT NULL,
[fieldname] [varchar](150) NOT NULL,
[import_file_column_index] [smallint] NOT NULL,
[records_fieldname] [varchar](50) NOT NULL,
[show_field] [bit] NOT NULL,
[field_order] [int] NOT NULL,
[dialler_field_required] [bit] NULL,
[dialler_field_name] [varchar](50) NULL,
[dialler_field_order] [int] NULL,
[field_group_id] [int] NOT NULL
);
INSERT INTO [record_fields] VALUES(1,2,1,'Record Id',47,'record_id',0,1,1,'Record Id',NULL,1);
INSERT INTO [record_fields] VALUES(2,2,1,'Field Name 1',46,'field01',0,1,1,'Field 1',NULL,1);
INSERT INTO [record_fields] VALUES(3,2,1,'Field Name 2',46,'field02',0,1,1,'Field 2',NULL,1);
CREATE TABLE [records](
[record_id] [int] NOT NULL,
[campaign_id] [int] NOT NULL,
[dialler_entry_created] BIT NOT NULL,
[field01] [VARCHAR](50) NULL,
[field02] [VARCHAR](50) NULL
);
INSERT INTO [records] VALUES(1,2,0,'Field01 Value','Field02 Value');
INSERT INTO [records] VALUES(1,2,0,'Field01 Value','Field02 Value');
And the query I am attempting to run is as follows:
DECLARE #campaignId INT
SET #campaignId = 2
DECLARE #FieldName VARCHAR(250)
DECLARE #ColumnName VARCHAR(250)
DECLARE #SelectQuery VARCHAR(2000)
DECLARE #InsertQuery VARCHAR(2000)
SET #SelectQuery = ''
SET #InsertQuery = ''
declare #FieldNames cursor for SELECT records_fieldname, dialler_field_name FROM record_fields where campaign_id = #campaignid AND dialler_field_required = 1 ORDER BY dialler_field_order
open #FieldNames
fetch next from #FieldNames into #FieldName, #ColumnName
while ##fetch_status = 0
begin
-- Build up a dymamic string of columns to read in the select query
SET #SelectQuery = #SelectQuery + '''"''+' + #FieldName + '+''"'', '
-- Build up a dynamic string of column names to add to our temp table
SET #InsertQuery = #InsertQuery + '[' + #ColumnName + '] varchar(255), '
fetch next from #FieldNames into #FieldName, #ColumnName
end
close #FieldNames
deallocate #FieldNames
IF Len(#SelectQuery) > 1 AND Len(#InsertQuery) > 1
BEGIN
-- Trim the trailing ','
SET #InsertQuery = Left(#InsertQuery,Len(#InsertQuery)-1)
SET #SelectQuery = Left(#SelectQuery,Len(#SelectQuery)-1)
EXEC ('DECLARE #RecordData TABLE (' + #InsertQuery + ');'
+ 'INSERT INTO #RecordData SELECT ' + #SelectQuery + ' from records WHERE campaign_id =' + #campaignId + ' AND ISNULL(dialler_entry_created, 0) = 0; '
+ 'SELECT * FROM #RecordData;')
END
The problem seems to stem from trying to select on record_id, which gives the 'Conversion failed when converting the varchar value '"' to datatype int' error
If I do not include the record_id column (which is the only INT column in the select list) it seems to work ok.
I have tried to apply CONVERT(VARCHAR(250), record_id) but cannot seem to get the syntax correct.
Any help would be greatly appreciated
There were 2 errors. In constructing select query and campaignid in exec function:
DECLARE #campaignId INT
SET #campaignId = 2
DECLARE #FieldName VARCHAR(250)
DECLARE #ColumnName VARCHAR(250)
DECLARE #SelectQuery VARCHAR(2000)
DECLARE #InsertQuery VARCHAR(2000)
SET #SelectQuery = ''
SET #InsertQuery = ''
declare #FieldNames cursor for SELECT records_fieldname, dialler_field_name FROM record_fields where campaign_id = #campaignid AND dialler_field_required = 1 ORDER BY dialler_field_order
open #FieldNames
fetch next from #FieldNames into #FieldName, #ColumnName
while ##fetch_status = 0
begin
-- Build up a dymamic string of columns to read in the select query
SET #SelectQuery = #SelectQuery + #FieldName + ', '
-- Build up a dynamic string of column names to add to our temp table
SET #InsertQuery = #InsertQuery + '[' + #ColumnName + '] varchar(255), '
fetch next from #FieldNames into #FieldName, #ColumnName
end
close #FieldNames
deallocate #FieldNames
IF Len(#SelectQuery) > 1 AND Len(#InsertQuery) > 1
BEGIN
-- Trim the trailing ','
SET #InsertQuery = Left(#InsertQuery,Len(#InsertQuery)-1)
SET #SelectQuery = Left(#SelectQuery,Len(#SelectQuery)-1)
Declare #result nvarchar(max) ='DECLARE #RecordData TABLE (' + #InsertQuery + ');'
+ 'INSERT INTO #RecordData SELECT ' + #SelectQuery + ' from records WHERE campaign_id =' + cast(#campaignId as nvarchar(50))+ ' AND ISNULL(dialler_entry_created, 0) = 0; '
+ 'SELECT * FROM #RecordData;'
Exec(#result)
END
Here is working fiddle: http://sqlfiddle.com/#!6/e450c/23
I think this is your problem:
DECLARE #campaignId INT
SET #campaignId = 2
. . .
+ 'INSERT INTO #RecordData SELECT ' + #SelectQuery + ' from records WHERE campaign_id =' + #campaignId + ' AND ISNULL(dialler_entry_created, 0) = 0; '
Notice the + #campaignId.
Try this instead:
DECLARE #campaignId varchar(255);
SET #campaignId = '2';
SET #SelectQuery = #SelectQuery + '''"''+' + #FieldName + '+''"'', '
Change to
SET #SelectQuery = #SelectQuery + ' ' + #FieldName + ' ,'
If you want an often discouraged (but nonetheless very handy) shortcut to converting numbers to string, change the bit at the bottom from
#campaignId
to
RTRIM#campaignId)
Does the character conversion and trims off trailing etc all in one word.
Take for example this example as an illustration so you can see what Î'm trying to do.
This is how the final table of the pivoted information looks like.
Create Table [#Comparative]
(
Branch char(32),
[2004_January] numeric (18,2),
[2005_January] numeric (18,2),
[2006_January] numeric (18,2),
[2007_January] numeric (18,2),
[2008_January] numeric (18,2),
)
INSERT INTO [#Comparative]
VALUES ('Angeles', NULL, 13550.20, 7820.50, NULL, NULL),
('Detroit', NULL, 13550.20, 7820.50, NULL, NULL),
('New York', NULL, 13550.20, 7820.50, NULL, NULL),
('Arizona', NULL, 13550.20, 7820.50, NULL, NULL)
Select * from [#Comparative]
How could i create a procedure or statement
to drop the set of columns that contains only NULL values taking
into account the columns on the table will be changing as the table is created from other
query that takes information of daily sales to group sum(sales) monthly depending
on the interval of the selected date.
Dynamically create a SQL statement and then run that command. This script drop set of columns with only null values from a temporary table(passed as parameter in SP).
CREATE PROC dbo.dropColumn
#tempTableName nvarchar(100)
AS
BEGIN
DECLARE #dml nvarchar(max) = N''
SELECT #dml += 'IF (SELECT MIN(' + QUOTENAME(c.name) + ') FROM [dbo].' + QUOTENAME(#tempTableName) + ') IS NULL' + CHAR(13) + CHAR(10) +
'BEGIN' + CHAR(13) + CHAR(10) +
' EXEC (''ALTER TABLE [dbo].' + QUOTENAME(#tempTableName) + ' DROP COLUMN ' + QUOTENAME(c.name) + ''')' + CHAR(13) + CHAR(10) +
'END' + CHAR(13) + CHAR(10)
FROM tempdb.sys.tables t JOIN tempdb.sys.columns c ON t.object_id = c.object_id
WHERE t.object_id = OBJECT_ID('[tempdb].[dbo].' + QUOTENAME(#tempTableName))
--PRINT #dml
EXEC sp_executesql #dml
END
EXEC dbo.dropColumn '#Comparative'
Result:
Branch 2005_January 2006_January
----------------------------------------------------------------
Angeles 13550.20 7820.50
Detroit 13550.20 7820.50
New York 13550.20 7820.50
Arizona 13550.20 7820.50
try this : this is one example.
CREATE Stored Procedure.
CREATE PROCEDURE TestSP
(
#IsDroped BIT
)AS
BEGIN
declare #test int
declare #testcount int
set #testcount = (select COUNT(*) from mtp )
set #test = (select distinct 1 from mtp where name is null group by name having COUNT(*)=#testcount )
if #test = 1 AND #IsDroped = 1
begin
alter table mtp drop column name
end
END
Execute this SP
EXEC TestSP 1