How to write dynamic SQL using values from a table - sql

If I have a table that actually holds the table name and column name of another table like this:
TestTable
TheTable nvarchar
TheColumn nvarchar
Sudo Logic
SELECT
t.Something
,t.SomethingElse
,t.TheTable
,t.TheColumn
,(SELECT *TheColumn* FROM *TheTable*) AS Something
FROM TestTable t
Even writing this question I'm getting several suggestions of similar questions, but I'm hoping for a much simpler example if there is one, so no intention to duplicate a question. Other examples seem specific and are quite extensive.
Thank you.

Here's some dynamic sql loosely based on your sample code:
/* set up a few parameters */
DECLARE #table_name AS nvarchar(100)
DECLARE #column_name AS nvarchar(100)
DECLARE #SQLQuery AS NVARCHAR(1000)
/* set the parameter values */
SELECT #table_name=TheTable, #column_name=TheColumn
FROM TestTable
WHERE -- ADD SOME LOGIC HERE TO GET SINGLE ROW
/* Build SQL String with parameters */
SET #SQLQuery =
'SELECT ' + #column_name +
' FROM ' + #table_name
-- + ' WHERE clause if required'
/* Execute SQL */
EXECUTE(#SQLQuery)

Yes, this can be done using dynamic sql. Below are a couple of links to get you started:
http://www.codeproject.com/Articles/20815/Building-Dynamic-SQL-In-a-Stored-Procedure
http://www.mssqltips.com/sqlservertip/1160/execute-dynamic-sql-commands-in-sql-server/
You should also be aware of the potential dangers of using Dynamic sql.

Related

SQL Server - select into from statement?

I have this query in SQL Server:
select column
from table_53;
Now, I want to get this 53 from another table, so what I want to do is something like this:
select column
from table_(select id from table2);
Is there any way to do this in SQL Server?
This is definitely not the way SQL thinks and works. Maybe your suggested approach can be mimicked by way of writing stored procedures in which you create SQL-statements which are then evaluated. However, this will not be very efficient.
A better approach would be to store the values of all your individual separate tables into one master table and mark them in a separate column tblid with their number (e.g. 53). Then you can always filter them from this master table by looking for this tblid.
You need dynamic sql query here.
declare #sqlQuery = 'select column
from table_(';
set #sqlQuery = #sqlQuery + 'select id from table2)';
EXEC (#sqlQuery)
Note :- One of cons of using dynamic sql query is sql injection. I would suggest to have better table structure or try to used parameterized query.
Yes, you can, but using something like this:
DECLARE #ID INT;
DECLARE #QUERY NVARCHAR(MAX);
SELECT #ID = ID FROM TABLE_2;
--IF #ID EQUALS 53 THEN
SET #QUERY = 'SELECT COLUMN FROM TABLE_' + CAST(#ID AS NVARCHAR(10));
-- #QUERY EQUALS TO 'SELECT COLUMN FROM TABLE_53'
EXEC (#QUERY);

T-SQL: Variable Scope

I am trying to store the results of an SQL query into a variable.The query simply detects the datatype of a column, hence the returned result is a single varchar.
SET #SQL =
'declare ##x varchar(max) SET ##x = (select DATA_TYPE FROM INFORMATION_SCHEMA.COLUMNS
WHERE Table_name = ' +char(39)+#TabName+char(39) +
' AND column_name = ' +char(39)+#colName+char(39) + ')'
EXECUTE (#SQL)
Anything within the 'SET declaration' cannot access any variables outside of it and vice versa, so I am stuck on how to store the results of this query in a varchar variable to be accessed by other parts of the stored procedure.
You dont need a dynamic query to achieve what you want, below query will give the same result as yours.
declare #x varchar(max)
declare #tableName varchar(100), #ColumnName varchar(50)
set #tableName = 'Employee'
set #ColumnName = 'ID'
select #x = DATA_TYPE FROM INFORMATION_SCHEMA.COLUMNS
where
Table_Name = #tableName
and column_name = #ColumnName
select #x
All user-defined variables in T-SQL have private local-scope only. They cannot be seen by any other execution context, not even nested ones (unlike #temp tables, which can be seen by nested scopes). Using "##" to try to trick it into making a global-variable doesn't work.
If you want to execute dynamic SQL and return information there are several ways to do it:
Use sp_ExecuteSQL and make one of the parameters an OUTPUT parameter (recommended for single values).
Make a #Temp table before calling the dynamic SQL and then have the Dynamic SQL write to the same #Temp table (recommended for multiple values/rows).
Use the INSERT..EXEC statement to execute your dynamic SQL which returns its information as the output of a SELECT statement. If the INSERT table has the same format as the dynamic SQL's SELECT output, then the data output will be inserted into your table.
If you want to return only an integer value, you can do this through the RETURN statement in dynamic SQL, and receive it via #val = EXEC('...').
Use the Session context-info buffer (not recommended).
However, as others have pointed out, you shouldn't actually need dynamic SQL for what you are showing us here. You can do just this with:
SET #x = ( SELECT DATA_TYPE FROM INFORMATION_SCHEMA.COLUMNS
WHERE Table_name = #TabName
AND column_name = #colName )
You may want to consider using the sp_executesql stored procedure for dynamic sql.
The following link provides a good usage example of sp_executesql procedure with output parameters:
http://support.microsoft.com/kb/262499

SQL Variables as Column names in Where Clause [duplicate]

This question already has answers here:
Can I pass column name as input parameter in SQL stored Procedure
(9 answers)
Closed 4 years ago.
I need some help with my SQL logic, and I've been working (and researching) this for 2 days now with zero success.
My goal is to try an pass a variable from an ASP page to a stored procedure, which is utilizing the variable as criteria for a column name in the where clause.
So for example (a simplified version of my query):
#strDept nvarchar(10), #strUser nvarchar(30)
-- The asp page will pass f18 to #strDept & Ted Lee to strUser
-- f18 is the column name in my database that I need in the where.
select x, y, z from table1 where #strDept in (#strUser)
-- and this is the select statement, notice the where clause.
The stored procedure does execute, but it returns no values and I know its treating the #strDept as a literal nvarchar and not a column name.
So I guess my question is, how do I get SQL Server 2005 to treat my #sqlDept variable as a column name?
The reason you can't find guidance on how to do this is that it's a really bad idea.
Sooner or later, someone is going to pass a "column name" of 1 ;drop database badidea. Which will be a blessing for all concerned.
Read up on SQL Injection, and rethink your design.
If this is an internal company application why is everyone re-iterating and beating SQL Injection to death... Its very simple to just use Dynamic SQL.
If you are comfortable that these are only internal users using this then its very simple. Here is the concept. You essentially write a SQL Statement that writes a string that is really a SQL statement and then execute it.
CREATE Procedure myDynamicProcedure
#strDept nvarchar(10),
#strUser nvarchar(30)
as
BEGIN
1. Declare a variable to store the SQL Statement.
DECLARE #SQL varchar(max)
2. SET your #SQL Variable to be the SELECT Statement. Basically you are building it so it returns what you are wanting to write. Like this:
SET #SQL = 'select x, y, z from table1 where' + #strDept +
' in ' + #strUser
3. Execute the #SQL Statement and it will be exactly like you ran:
SELECT x,y,z from table1 where f18 = 'Ted Lee'
EXEC (#SQL)
END
Why do you want to make column name dynamic? What do you plan to achieve? You can use dynamic query like answer above but injection attacks may start.
If you explain what you want to do with that maybe we can recommend another solution.
You can use some dynamic sql e.g.
DECLARE #sqlDept VARCHAR(100)='CURRENT_TIMESTAMP';
EXEC('SELECT '+#sqlDept)
In your case this will be
DECLARE #strDept nvarchar(10)='dept1'
,#strUser nvarchar(30)='user1';
DECLARE #DynamicSql nvarchar(1000);
SET #DynamicSql='select x, y, z from table where '+#strDept+' in ('''+#strUser+''')';
Then
SELECT #DynamicSql;
Will give you:
select x, y, z from table where dept1 in ('user1')
To execute this statement you do this as
EXEC(#DynamicSql);
Another alternative is to use a small bit of substitution in the proc. This still uses dynamic SQL, but you are never executing user supplied values.
DECLARE #userSuppliedValue VARCHAR(50) = 'JOHNNY DROP TABLES'
DECLARE #substValue VARCHAR(50)
IF #userSuppliedValue = 'Table1'
SET #substValue = 'Table1'
IF #userSuppliedValue = 'Table2'
SET #substValue = 'Table2'
/*Repeat for N permutations*/
/* Throw an error if you think its necessary to do so when no match is found*/
IF #substValue IS NULL
RAISERROR(1,1,'errah')
EXEC ('SELECT * FROM ' + #substValue)
I think the best way is to build a dynamic SQL and add a lookup to see if the column exist and prevent SQL injection in the column name.
declare #strDept nvarchar(10), #strUser nvarchar(30),
#sql nvarchar(300), #found smallint
set #strDept = 'f18'
set #strUser = 'Ted Lee'
set #found = (SELECT count(*)
FROM syscolumns
WHERE id=OBJECT_ID('table1') AND name=''+#strDept+'')
set #sql = 'select x, y, z from table1 where ' + #strDept + ' in ('''+#strUser+''')'
if #found = 1 exec (#sql)
SQL injection testing : See SQL FIDDLE : http://www.sqlfiddle.com/#!6/df3f6/18/0
DECLARE #value varchar(10)
SET #value = 'intStep'
DECLARE #sqlText nvarchar(1000);
SET #sqlText = N'SELECT ' + #value + ' FROM dbo.tblBatchDetail'
Exec (#sqlText)

SQL Dynamic SELECT statement from values stored in a table

I have been researching this for a couple of days and feel like I am going around in circles. I have basic knowledge of SQL but there are many areas I do not understand.
I have a table that stores the names and fields of all the other tables in my database.
tblFields
===================================================
TableName FieldName BookmarkName
---------------------------------------------------
Customer FirstName CustomerFirstName
Customer LastName CustomerLastName
Customer DOB CustomerDOB
I want to write a SELECT statement like the following but i am unable to get it work:
SELECT (SELECT [FieldName] FROM [TableName]) FROM tblFields
Is this possible? The application I have developed requires this for user customization of reports.
If i understand what you are trying to do, i think this will help you. It is not pretty and it works for SQL Server 2005 and above, but maybe this is what you are looking for:
declare #tableName nvarchar(100)
declare #sqlQuery nvarchar(max)
declare #fields varchar(500)
set #tableName = 'YourTableName'
set #fields = ''
select #fields = #fields + QUOTENAME(t.fieldname) + ',' from (
select distinct fieldname from tblfields where tablename = #tableName)t
set #sqlQuery = 'select ' + left(#fields, LEN(#fields)-1) + ' from ' + QUOTENAME(#tableName)
execute sp_executesql #sqlQuery
Edit: As Martin suggested, i edited so that the columns and tablename are using QUOTENAME
If I understand correctly what you are trying to do, you are probably better off doing this as two separate queries from your program. One which gets the fields you want to select which you then use in your program to build up the second query which actually gets the data.
If it must be done entirely in SQL, then you will need to tell us what database you are using. If it is SQL Server, you might be able to user a cursor over the first query to build up the second query which you then execute with the sp_executesql stored procedure. But doing doing it outside of SQL would be recommended.

Using Parameter Values In SQL Statement

I am trying to write a database script (SQL Server 2008) which will copy information from database tables on one server to corresponding tables in another database on a different server.
I have read that the correct way to do this is to use a sql statement in a format similar to the following:
INSERT INTO <linked_server>.<database>.<owner>.<table_name> SELECT * FROM <linked_server>.<database>.<owner>.<table_name>
As there will be several tables being copied, I would like to declare variables at the top of the script to allow the user to specify the names of each server and database that are to be used. These could then be used throughout the script. However, I am not sure how to use the variable values in the actual SQL statements. What I want to achieve is something like the following:
DECLARE #SERVER_FROM AS NVARCHAR(50) = 'ServerFrom'
DECLARE #DATABASE_FROM AS NVARCHAR(50) = 'DatabaseTo'
DECLARE #SERVER_TO AS NVARCHAR(50) = 'ServerTo'
DECLARE #DATABASE_TO AS NVARCHAR(50) = 'DatabaseTo'
INSERT INTO #SERVER_TO.#DATABASE_TO.dbo.TableName SELECT * FROM #SERVER_FROM.#DATABASE_FROM.dbo.TableName
...
How should I use the # variables in this code in order for it to work correctly?
Additionally, do you think my method above is correct for what I am trying to achieve and should I be using NVARCHAR(50) as my variable type or something else?
Thanks
There is probably a better way to do this, but what you are probably trying to do in your example is what's called dynamic SQL where you treat the statement as a string and the execute it. This would be section #2 here:
http://www.mssqltips.com/tip.asp?tip=1160
There are some major downsides to dynamic SQL. You see a couple other approaches that might be better in that article.
If you want to execute a dynamically generated query then you have to use sp_ExecuteSQL
HTH
For the nvarchar(50) - you'd be better using sysname. This is a synonym in SQL Server (for nvarchar(128)) and represents the maximum length of an object identifier.
have a look at http://msdn.microsoft.com/en-us/library/ms188001.aspx - sp_executesql takes a parameter that is a string and executes the sql in that string. so you'd need to concatenate #SERVER_FROM and other params with the INSERT INTO part to make the entire sql statement, and then pass to sp_executesql.
nvarchar(50) is fine, unless your server/database names are longer than that :)
You can create the select statement by concatenating all the information together and then use sp_executesql
so:
sp_executesql 'INSERT INTO ' + #SERVER_TO + '.' + #DATABASE_TO +
'.dbo.TableName SELECT * FROM ' + #SERVER_FROM + '.' +
#DATABASE_FROM+'.dbo.TableName'
I like to make templates for dynamic SQL things like this - it's a lot easier to maintain complex statements and also sometimes easier to handle nested quotes - and definitely easier when terms need to be repeated in multiple places (like column lists):
DECLARE #sql AS nvarchar(max);
SET #sql = 'INSERT INTO {#SERVER_TO}.{#DATABASE_TO}.dbo.TableName
SELECT *
FROM {#SERVER_FROM}.{#DATABASE_FROM}.dbo.TableName'
SET #sql = REPLACE(#sql, '{#SERVER_TO}', QUOTENAME(#SERVER_TO))
SET #sql = REPLACE(#sql, '{#DATABASE_TO}', QUOTENAME(#DATABASE_TO))
SET #sql = REPLACE(#sql, '{#SERVER_FROM}', QUOTENAME(#SERVER_FROM))
SET #sql = REPLACE(#sql, '{#DATABASE_FROM}', QUOTENAME(#DATABASE_FROM))
EXEC(#sql)