SQL Query for paramaterized dynamic table - sql

One of the product teams I manage has an abundance of queries that use dynamic sql queries injecting the table in using concatenation. While it has sanitisation, I am trying to completely remove dynamic sql.
Is there a way to parameterise the table name?
I am trying to think of how I can query a table something like:
SELECT * FROM (SELECT DISTINCT Table_Name FROM INFORMATION_SCHEMA.Tables WHERE Table_Name = :queryParam)
Is this possible?

There is no way to "properly" prevent SQL injection completely from within SQL, the calling application layer should do this prior to executing any SQL statement.
Solve the problem by using an ORM or building the code to protect yourself from SQL injection when you generate the SQL in the application code.
This feels like a classic XY problem, try to take a step back and consider that you need to protect the access to the SQL server itself rather than sanitise everything from within "after" your SQL server has been accessed.

I am trying to completely remove dynamic sql.
You can't do it without Dynamic SQL.
Is this possible?
No, it's not possible, you cannot parameterize identifiers in SQL queries.
Why?
From the Books online page for Variables, Variables can be used only in expressions, not in place of object names or keywords. To construct dynamic SQL statements, use EXECUTE.
This is the only way to do it:
DECLARE #Column SysName = N'Table_Name',
#Param NVARCHAR(128) = N'ParamValue';
DECLARE #SQL NVARCHAR(MAX)=N'SELECT *
FROM(
SELECT '+ QUOTENAME(#Column) +
'FROM INFORMATION_SCHEMA.Tables
WHERE ' + QUOTENAME(#Column) + ' = #Param
) T';
EXECUTE sp_executesql #SQl,
N'#Param NVARCHAR(128)',
#Param;

Related

create dynamic temp table in sql

Is there a way to create a dynamic temp table. Below sql code is declaring a variable #tic. I am planning to insert contents from table1 to temp table #df. So instead of giving directly as #df, I am passing as a variable. But below is code is not successful. Can anyone help me here?
declare #tic as varchar(100) = 'df'
select *
into '#' + #tic from (
select * from [dbo].[table1])
select * from #df
Is there a way? Well, I think of the answer as "yes and no and maybe".
As far as I know, there is no way to do this using a local temporary table. As Stu explains in the comment, you would need dynamic SQL to define the table name and then the table would not be visible in the outer scope, because it is a local temporary table.
The "yes" is because one type of temporary table are global temporary tables. These are tables that persist across different scopes. And they are defined using ## instead of # as the prefix. So this works:
declare #tic as varchar(100) = 'df'
declare #sql nvarchar(max);
set #sql = 'select * into ##' + #tic + ' from table1';
select #sql;
exec sp_executesql #sql;
select * from ##df;
(Here is a db<>fiddle.)
The "maybe" is because I'm quite skeptical that you really need this. Dynamic table names are rarely useful in SQL systems, precisely because they depend on dynamic SQL. Introducing dynamic names into SQL (whether columns or tables) is dangerous, both because of the danger of SQL injection and also because it can introduce hard-to-debug syntax errors.
If you are trying to solve a real problem, there might be alternative approaches that are better suited to SQL Server.

Alter table to add dynamic columns based off some previously selected query

Is it possible to alter a table and add columns with a dynamic name/data type based off some previously select query?
The pseudo equivalent for what I'm looking to do in SQL would be:
foreach row in tableA
{
alter tableB add row.name row.datatype
}
This is for SQL Server.
As mentioned, you can do this with dynamic sql. Something along these lines:
Declare #SQL1 nvarchar(4000)
SELECT #SQL1=N'ALTER TABLE mytable'+NCHAR(13)+NCHAR(10)
+N' ADD COLUMN '+ my_new_column_name + ' varchar(25)'+NCHAR(13)+NCHAR(10)
-- SELECT LEN(#SQL1), #SQL1
EXECUTE (#SQL1)
Apart from the fact that this is messy, error prone, a security risk, requires high authorization to execute and needs multiple variables for batches bigger than 4000 characters, it is usually also a bad idea from a design point of view (depending on when/why you are doing this).
Sure, you can do this with dynamic sql.

SQL JOIN based on table contents

I have a single table that contains questions with corresponding references to another table and field that contain the answers. Something like:
I would like to query the questions table and return QID, QuestionText and the value contained in the [ResponseTable].[ResponseField] for each QID. The design seamed flexible at the time. However the app developer is expecting a stored procedure and the SQL developer was counting on an in app solution for this issue.
I am at the end of my rope trying to build this query. How would you suggest accomplishing this task?
I don't think you'll like hearing this answer because it will likely mean some major rework, but I think it's the right answer. Get rid of the questions table and put the questions into new Question fields in the Client1, Client9, and Jobs tables; one for each response.
For example the Client1 table will have these fields:
ColorPref
ColorPrefQuestion
Rating
RatingQuestion
...and so on
Working around that design will be manageable where working around the design you have now will be a headache.
It sounds like a redesign should be considered (storing all responses in one table, for example), but if that's not a possibility then dynamic SQL (using sp_executesql) can be used. However, it can be dangerous to use as it is vulnerable to SQL injection. There are some precautions that can be taken, such as using QUOTENAME on table and column names. This is also a good read before using dynamic SQL: The Curse and Blessings of Dynamic SQL.
DECLARE #tableName NVARCHAR(50)
DECLARE #columnName NVARCHAR(50)
DECLARE #query NVARCHAR(MAX)
SET #tableName = 'Client1'
SET #columnName = 'ColorPref'
SET #query = 'SELECT ' + QUOTENAME(#columnName) + ' FROM ' + QUOTENAME(#tableName)
EXEC sp_executesql #query
Until you get to the rewrite you mentioned, consider the idea of using a view to bring these response tables together.
CREATE VIEW ClientResponses AS
SELECT QID, ResponseField FROM [Client1]
UNION
SELECT QID, ResponseField FROM [Jobs]
UNION
SELECT QID, ResponseField FROM [Client9]
-- ..... add the new tables as they are created
This will
Avoid dynamic SQL
Give you a single place to maintain querying
Provide a pretty simple, readable way to hobble this together

How to EXEC a query generated in a CTE

I have a CTE in which a SELECT statement is generated, but SQL Server (2012) does not allow calling EXEC on it. Here's the query:
DECLARE #guidToFind uniqueidentifier = 'E4069560-091A-4026-B519-104F1C7693B3';
WITH GuidCols (TableName, ColName, Query) As
(
SELECT
C.TABLE_NAME,
C.COLUMN_NAME,
'SELECT ' +
QUOTENAME(C.TABLE_NAME) + '.' +
QUOTENAME(C.COLUMN_NAME) + '
FROM ' +
QUOTENAME(C.TABLE_NAME) + '
WHERE ' +
QUOTENAME(C.COLUMN_NAME) + ' = ''' + cast(#guidToFind AS VARCHAR(50))+
''''
FROM
INFORMATION_SCHEMA.COLUMNS C
INNER JOIN INFORMATION_SCHEMA.TABLES T
ON C.TABLE_NAME = T.TABLE_NAME AND
T.TABLE_TYPE = 'BASE TABLE'
WHERE
C.DATA_TYPE = 'uniqueidentifier'
)
-- SELECT * FROM
EXEC( GuidCols.Query )
The problem is not solve if I uncomment the SELECT statement after CTE.
The purpose of this query is to find all instances of a GUID in a database. Currently I'm solving this problem using a script like this. However I would be happier to solve this problem without iterating through rows, using set operations and other techniques.
You can not use exec as a part of a CTE.
From WITH common_table_expression (Transact-SQL)
A CTE must be followed by a single SELECT, INSERT, UPDATE, or DELETE
statement that references some or all the CTE columns.
What you can do instead is to store the result from your query in a temp table, loop over the rows and execute one statement at a time.
The EXEC() function is available from T-SQL, but it is not accessible from within a SQL statement. I would assume this is the case because SQL is executed in several steps... something like:
Your statement is parsed.
An execution plan is generated.
The execution plan is run against the database and results are returned.
The EXEC() function is designed to dynamically execute a statement, so in order to do what you're asking, SQL Server would have to perform the first two steps, then while executing the statement, it would have to return back to the first step to parse/execute the dynamic statement. This would have to be repeated for each row in your results.
I assume this type of 'feature' is not available because it could adversely affect the performance and complexity of the db engine; so they leave it to us to execute the first statement, then loop through each record to execute the dynamic statements individually.
Exec executes a T-SQL command that you embed in its parentheses. The difference is that the EXEC() function can execute a string or string variable, as you saw in the preceiding code. The argument for EXEC() can be a literal string, but the real power of EXEC() comes to light when you use a variable.
It executes the command contained in the variable, but in a different context from the session calling the function. That's important, because it introduces some severe constraints. Any tables you create are visible to the EXEC() context, but variables declared by the calling session aren't visible to the EXEC() context, and vice versa.
I found an interesting article related to this at : http://msdn.microsoft.com/en-us/library/aa175921(v=sql.80).aspx
Although above is self sufficient, you can also go through : http://blog.sqlauthority.com/2007/09/13/sql-server-difference-between-exec-and-execute-vs-exec-use-execexecute-for-sp-always/
You may use OPENQUERY instead of EXEC:
SELECT * FROM OPENQUERY (OracleSvr, 'SELECT name FROM joe.titles WHERE name = ''NewTitle''');
Reference: https://learn.microsoft.com/en-us/sql/t-sql/functions/openquery-transact-sql?view=sql-server-2017

use output of SQL statement as the table to run a query on

I believe what I am attempting to achieve may only be done through the use of Dynamic SQL. However, I have tried a couple of things without success.
I have a table in database DB1 (lets say DB1.dbo.table1, in a MS SQL server) that contains the names of other databases in the server (DB2,DB3, etc). Now, all the dbs listed in that table contain a particular table (lets call it desiredTable) which I want to query. So what I'm looking for is a way of creating a stored procedure/script/whatever that queries DB1.dbotable1 for the other DBs and then run a statement on each of the dbs retrieved, something like:
#DBNAME = select dbName from DB1.dbo.table1
select value1 from #DBNAME.dbo.desiredTable
Is that possible? I'm planning on running the sp/script in various systems DB1.dbo.table1 being a constant.
You need to build a query dinamically and then execute it. Something like this:
DECLARE #MyDynamicQuery VARCHAR(MAX)
DECLARE #MyDynamicDBName VARCHAR(20)
SELECT #MyDynamicDBName = dbName
FROM DB1.dbo.table1
SET #MyDynamicQuery = 'SELECT value1 FROM ' + #MyDynamicDBName + '.dbo.desiredTable'
EXEC(#MyDynamicQuery)
You can use the undocumented stored procedure, sp_MSForEachDB. The usual warnings about using an undocumented stored procedure apply though. Here's an example of how you might use it in your case:
EXEC sp_MSForEachDB 'SELECT value1 FROM ?.dbo.desiredTable'
Notice the use of ? in place of the DB name.
I'm not sure how you would limit it to only DBs in your own table. If I come up with something, then I'll post it here.