Using foreach loop container to create new columns in a table- can't solve "incorrect syntax near '#P1'" error - sql

I'm trying to use a foreach loop container to take row values and make them into fields. But for some reason I can't get it to work without running into this error:
alter table /databasename/.dbo.cp_hh_foo..." failed with the following error: "Incorrect syntax near '#P1'.
The problem appears to be in the final execute SQL statement. The first two execute SQL statements work fine. I think I've made a mistake in my variable/parameter mappings, but I'm not sure.
My data flow looks like this:
Basically what's going on is this:
First Execute SQL Task creates the new table
Second Execute SQL Task selects a table with full result set going into an object-type variable "AllocItems"
Foreach Loop container (configured as an ADO enumerator) maps specific row from "AllocItems" onto variable "AllocItemsSQL1". These are the strings which should become field names in the table I'm creating
Execute SQL Task within foreach loop container alters the table. The SQL query: alter table MIT_Client_Profitability.dbo.cp_hh_footprint add ? varchar(255)
Things I've tried:
within the final execute sql task, adding parentheses around the parameter: "(?)" instead of "?"
within final execute sql task, changing parameter name to "Param1"
within final execute sql task, changing parameter size
within second execute sql task, changing "result name"
within final execute sql task, changing query to "declare #SQL varchar(255) set #SQL = 'alter table MIT_Client_Profitability.dbo.cp_hh_footprint add ? varchar(255)' exec(#SQL)"
Thanks in advance for any suggestions!

To build on David's answer
Create a new SSIS variable, #[User::Sql] of type String and in the Expression box, add the following syntax
"alter table MIT_Client_Profitability.dbo.cp_hh_footprint add " + #[User::AllocItemSQL1] + " varchar(255);"
The nice thing about this approach is that you can put a breakpoint on the Execute SQL Task and see what the statement looks like prior to the task attempting to execute it. And then modify the Execute SQL Task to use the new variable and remove the parameter.
Otherwise, the dynamic tsql approach ought to have worked, just you needed to modify the syntax. The token replacement won't work inside the string. Something more like this should work according to my mental model
declare #newcolumn varchar(255) = ?;
declare #SQL varchar(255) = 'alter table MIT_Client_Profitability.dbo.cp_hh_footprint add ' + #newcolumn + ' varchar(255)';
exec(#SQL);

This
alter table MIT_Client_Profitability.dbo.cp_hh_footprint add ? varchar(255)
is a Data Definition Language (DDL) statement. DDL cannot be paramterized. You'll have to create the statement with string concatenation.

Related

Create a dynamic table in dynamic SQL and reference it outside of dynamic SQL

Please see the code below:
select top 1 * into #dbusers from dbusers
declare #tsql as varchar(1000)
set #tsql = 'select * from #dbusers'
exec (#tsql)
This works as I would expect i.e. one row is returned by the dynamic SQL. Is it possible to do this:
declare #tsql as varchar(1000)
set #tsql = 'select top 1 * into #dbusers from dbusers'
exec (#tsql)
select * from #dbusers
Here I get the error:
Invalid object name '#dbusers'
Is there a workaround?
I realise that you can have output parameters with dynamic SQL. However, I also know that when using stored procedures you cannot return tables as output parameters.
Is it possible to do this? Is there a workaround (except creating a physical table)?
Temporary tables are only available within the session that created them. With Dynamic SQL this means it is not available after the Dynamic SQL has run. Your options here are to:
Create a global temporary table, that will persist outside your session until it is explicitly dropped or cleared out of TempDB another way, using a double hash: create table ##GlobalTemp
Because this table persists outside your session, you need to make sure you don't create two of them or have two different processes trying to process data within it. You need to have a way of uniquely identifying the global temp table you want to be dealing with.
You can create a regular table and remember to drop it again afterwards.
Include whatever logic that needs to reference the temp table within the Dynamic SQL script
For your particular instance though, you are best off simply executing a select into which will generate your table structure from the data that is selected.

How to alter table add column then update within single script?

We have a custom database updater which runs various SQL scripts on SQL Server. Some of the scripts need to add a new column to a table, then populate the values, in a single script, within a transaction:
using (var scope = new TransactionScope()) {
... alter table MyTable add FOOBAR int;
... update schappointment set FOOBAR = 1;
}
Problem is, SQL spits back "Invalid column name 'FOOBAR' because the "alter table" command hasn't taken effect. Wrapping it in exec() makes no difference:
... exec('alter table MyTable add FOOBAR int;')
... update schappointment set FOOBAR = 1;
It works OK within SQL Management Studio because it splits it up with GO commands (which I know are not valid T-SQL commands).
I'd prefer not to create any new dependencies in the project.
My preference is not to split the schema & data scripts as this doubles the number of scripts for no reason other than to make SQL happy.
How can I solve this?
You can't do this exactly in a single statement (or batch) and it seems the tool you are using does not support GO as a batch delimiter.
You can use EXEC to run it in a child batch though.
ALTER TABLE A
ADD c1 INT, c2 VARCHAR(10);
EXEC('
UPDATE A
SET c1 = 23,
c2 = ''ZZXX'';
');
NB: All single quotes in the query need to be doubled up as above to escape them inside a string literal.
I tried this approach this is working.
alter table then update in single statement
Our solution was to sprinkle "GO" commands throughout the scripts, and customize our script execution function to split them up and run them seperately.
This mimics what SQL management studio does and seems to work.

SQL Server procedure / function to copy table to another database

I need to copy a table from one database to another. My first guess was using following statement from my source code:
SELECT *
INTO TARGETDB.SCHEMA.TABLENAME
FROM SCHEMA.TABLENAME
(Edit: I know it won't set Primary Key's etc, that's okay)
Sadly the classes I have to use in my project pretty much destroy this statement and I have no possibility to work around that.
My next idea was creating a function or procedure in the SQL Server database, so I could use
SCHEMA.FUNCTNAME paramTARGETDB, paramTABLENAME
as statement from my code.
My current procedure looks like this:
CREATE PROCEDURE [SCHEMA].FUNCTNAME
#pVC_TARGETDB VARCHAR(240),
#pVC_TABLENAME VARCHAR(240)
AS
BEGIN
SELECT *
INTO #pVC_TARGETDB.SYSADM.#pVC_TABLENAME
FROM SCHEMA.#pVC_TABLENAME
END
but my knowledge of SQL isn't that big, and SQL Server Management Studio tells me there are syntax errors. Every #pVC_* is marked (Edit: In the first two occurances, they're not marked. Only in the block between BEGIN and END). The message I get is:
"Syntax Error near #pVC_* Expecting ID, QUOTED_ID or '.'."
I tried nearly every way of writing it I could imagine or find with Google, maybe it's easy to solve, maybe it's not. I couldn't do it, please help me.
You can dynamically create the SQL statement then execute it
DECLARE #SQL NVARCHAR(MAX)
SET #SQL =
('SELECT *
INTO ' + #pVC_TARGETDB + '.SYSADM.' + #pVC_TABLENAME +
' FROM
SCHEMA.' + #pVC_TABLENAME)
EXEC (#SQL)

How to Parse and Append text to a stored procedure in SQL Server 2005 via a parameter

Does anyone know of a way to append text to a stored procedure from within another stored procedure? I would like to do something like the following in SQL Server 2005:
Declare str as Nvarchar(Max) = ''
set #spStr = dbo.spTest + 'Where testCol1 = ''Test'''
exec(#spStr)
I understand this may open some discussion about SQL injection attacks. I'm simply looking to see if syntax exsists to extend a stored procedure by passing it a where clause dynamically in the above manner.
There is no syntax like this available in Sql Server any version. You've got a couple of options:
You could obviously modify the procedure to include a parameter that the procedure code itself would handle as a filter in the final statement(s) that returned the result set from the procedure call. Though I'd advise against it, you could certainly have a parameter that was just a varchar/nvarchar data type which included the actual 'where' clause you want to add and have the procedure code append it to these final select statement(s) as well
Use the insert/exec syntax to populate a temp table with the results of the stored procedure execution and then simply run a filtered select against that temp table.
There are some options.
You can alter the actual SP using the metadata in INFORMATION_SCHEMA.ROUTINES (not really what I think you are wanting to be doing)
You can parameterize the SP - this should not be vulnerable to injection if the SP uses the variable directly and not to dynamically make SQL.
You might consider using a view or an inline or multi-step table-valued function instead, which can be used like a parameterized view (inline being more efficient) - SELECT * FROM udf_Test WHERE TestCol1 = 'Test'.
You can take the results of the SP and put them in a temporary table or table variable and query against that.

Why does a T-SQL block give an error even if it shouldn't even be executed?

I was writing a (seemingly) straight-forward SQL snippet that drops a column after it makes sure the column exists.
The problem: if the column does NOT exist, the code inside the IF clause complains that it can't find the column! Well, doh, that's why it's inside the IF clause!
So my question is, why does a piece of code that shouldn't be executed give errors?
Here's the snippet:
IF exists (select * from syscolumns
WHERE id=object_id('Table_MD') and name='timeout')
BEGIN
ALTER TABLE [dbo].[Table_MD]
DROP COLUMN timeout
END
GO
...and here's the error:
Error executing SQL script [...]. Invalid column name 'timeout'
I'm using Microsoft SQL Server 2005 Express Edition.
IF exists (select * from syscolumns
WHERE id=object_id('Table_MD') and name='timeout')
BEGIN
DECLARE #SQL nvarchar(1000)
SET #SQL = N'ALTER TABLE [dbo].[Table_MD] DROP COLUMN timeout'
EXEC sp_executesql #SQL
END
GO
Reason:
When Sql server compiles the code, they check it for used objects ( if they exists ). This check procedure ignores any "IF", "WHILE", etc... constructs and simply check all used objects in code.
It may never be executed, but it's parsed for validity by Sql Server. The only way to "get around" this is to construct a block of dynamic sql and then selectively execute it
Here's how I got it to work:
Inside the IF clause, I changed the ALTER ... DROP ... command with exec ('ALTER ... DROP ...')
It seems the SQL server does a validity check on the code when parsing it, and sees that a non-existing column gets referenced somewhere (even if that piece of code will never be executed).
Using the exec(ute) command wraps the problematic code in a string, the parser doesn't complain, and the code only gets executed when necessary.
Here's the modified snippet:
IF exists (select * from syscolumns
WHERE id=object_id('Table_MD') and name='timeout')
BEGIN
exec ('ALTER TABLE [dbo].[Table_MD] DROP COLUMN timeout')
END
GO
By the way, there is a similar issue in Oracle, and a similar workaround using the "execute immediate" clause.