Adding a table variable in Dynamic SQL - sql

I am a beginner in Dynamic SQL and trying to write a dynamic sql code in which I need to include a table variable .When I try to include the table variable I am getting an error saying that I need to include the Static variable.
Below is just part of the code
DECLARE #NAMES TABLE (name varchar)
DECLARE #fields varchar(max);
.
.
.
Select #fields=FROM sys.columns
WHERE object_id=object_id('dbo.employees')
order by name
SET #SQL=''
SET #SQL='Select '+ #fields
SET #SQL=#SQL + ' from dbo.test()'
SET #SQL = #SQL + ' WHERE name IN'
SET #SQL= #SQL + '(Select name from '
SET #SQL=#SQL + #NAMES +')'

To start with, your SELECT to get the column names won't work at all. You need something like this:
DECLARE #fields nvarchar(max);
SET #fields = N'';
SELECT #fields += CASE WHEN LEN(#fields) = 0 THEN N'' ELSE N', ' END + name
FROM sys.columns
WHERE object_id=object_id('dbo.employees')
ORDER BY column_id
PRINT #fields;
I'm honestly not sure what the rest of your SQL is trying to do... it's trying to select the fields from the employee table from the return of a table-valued function called test()? Where the 'name' column from that test() result set is in ... what? You'll need to be a lot more clear before I can give you a complete answer. One major problem is that you've never actually filled your table variable with anything, so ... I have no idea what you're going for. Knowing the definition of the Test() function would also be helpful.
But at least the above will give you the #fields variable set correctly to the list of columns in the table dbo.employee.
When doing Dynamic SQL, it really helps to write out the end resulting SQL you want, and then identify what parts of that SQL should be filled in by variable data, and work backwards. It's then a lot easier to construct your SQL to build your dynamic SQL.

You need to include the tablevariable logic to dynamic sql
--You need to load #names inside dynamic sql
DECLARE #fields varchar(max);
.
.
.
Select #fields=FROM sys.columns
WHERE object_id=object_id('dbo.employees')
order by name
SET #SQL='DECLARE #NAMES TABLE (name varchar); '
SET #SQL +=' Select '+ #fields
SET #SQL=#SQL + ' from dbo.test()'
SET #SQL = #SQL + ' WHERE name IN'
SET #SQL= #SQL + '(Select name from '
SET #SQL=#SQL + ' #NAMES )'
Or you can load into temp table and access the temptable inside dynamic sql. This means you will be loading temptable like #names and then use that in dynamic sql...

Related

How can I create a dynamic Select statement within a SQL Select statement?

I have a SELECT statement that can produce a list of values:
DECLARE #ValueList varchar(Max);
SELECT #ValueList = COALESCE(#ValueList + ',', '') + CAST(Val AS varchar(max))
FROM
(SELECT TOP (100) PERCENT tblSampleTable.SomeIDNumber AS Val
FROM tblSampleTable) AS ValuesThisYear
PRINT #ValList
This returns a list with values something like
val1,val2,val4,val9,
etc., ehich I can then feed into a stored procedure, or manage some other way.
Now I want to have the query that gets assessed for the list of values to be dynamic, maybe passed in or from another stored procedure, similar to this:
DECLARE #ValueList varchar(Max);
DECLARE #TSQL varchar(Max);
SET #TSQL = {stored proc to get base query}
SELECT #ValueList = COALESCE(#ValueList + ',', '') + CAST(Val AS varchar(max))
FROM
(#TSQL) AS ValuesThisYear
PRINT #ValList
I know that's the wrong syntax for including #TSQL, and that's what I'm trying to find out. I've viewed a number of threads and tried a number of methods, but am still not able to incorporate this dynamic part.
The tricky part seems to be the making of the list (the COALESCE and CAST statements), where I incorporate #ValList as part of the returned string.
Any help would be appreciated!
Dynamic SQL is usually about
Creating a variable that contains the exact SQL you want to run
Then using EXEC (#SQLvariable) to run that code
For example (not for production yet!) I've added a new variable #CustomSQL
DECLARE #ValueList varchar(Max);
DECLARE #TSQL varchar(Max);
DECLARE #CustomSQL varchar(Max);
SET #TSQL = {stored proc to get base query}
SET #CustomSQL =
'SELECT COALESCE(#ValueList + '','', '''') + CAST(Val AS varchar(max))
FROM (
' + #TSQL + '
) As ValuesThisYear;'
PRINT #CustomSQL
EXEC (#CustomSQL)
Notice that adding text/strings (e.g., the #TSQL variable) have to be entered as exact strings rather than their variable names. Also note apostrophes - you need to use '' every time you wish to refer to a '.
I also removed the variable name from the SELECT #ValueList = ... because the dynamic SQL cannot actually reference the variables - it has its own scope (?cannot remember the correct word) and doesn't have access to the variables. Solutions to this include
Using a temporary table e.g., #temp which can be referenced
Using the OUTPUT clause
Personally, I would approach it a different way - use the T-Sql provided to put data into a temporary table. Then use the temporary table in the other statement e.g.,
DECLARE #ValueList varchar(Max);
DECLARE #TSQL varchar(Max);
SET #TSQL = {stored proc to get base query}
DECLARE #CustomSQL varchar(Max)
CREATE TABLE #temp (Val varchar(1000))
SET #CustomSQL = 'INSERT INTO #temp (Val) ' + #TSQL
EXEC (#CustomSQL)
SELECT #ValueList = COALESCE(#ValueList + ',', '') + CAST(Val AS varchar(max))
FROM #temp As ValuesThisYear;
PRINT #ValList
I almost never get my dynamic SQL correct first try. Suggestions
Keep it as simple as possible
Before having a version that runs (e.g., EXEC (#CustomSQL)), comment the EXEC out and PRINT it instead.
Here are some examples from previous posts I've done recently
Query for R Machine Learning Services - Filtering Categories in Where Clause
Bottom of Dynamic columns depend on previous dynamic columns - TSQL

List all values ​in each SQL table

I need to make a generic listing that will bring me all the values, ​​I have in the bank, every bank line.
My application needs to read information that changes and transits in the bank.
If you want to display all tables values/data in your Database without writing a select statement for each table you can use below SQL:
Try like this:
DECLARE #sqlText VARCHAR(MAX)
SET #sqlText = ''
SELECT #sqlText = #sqlText + ' SELECT * FROM ' + QUOTENAME(name) + CHAR(13) FROM sys.tables
EXEC(#sqlText)

How to Create DELETE Statement Stored Procedure Using TableName, ColumnName, and ColumnValue as Passing Parameters

Here is what i'm trying to do. I'm trying to create a stored procedure where I could just enter the name of the table, column, and column value and it will delete any records associated with that value in that table. Is there a simple way to do this? I don't know too much about SQL and still learning about it.
Here is what I have so far.
ALTER PROCEDURE [dbo].[name of stored procedure]
#TABLE_NAME varchar(50),
#COLUMN_NAME varchar(50),
#VALUE varchar(5)
AS
BEGIN
SET NOCOUNT ON;
DECLARE #RowsDeleted int;
DECLARE #sql VARCHAR(500);
SET #sql = 'DELETE FROM (name of table).' + #TABLE_NAME + ' WHERE ' + #COLUMN_NAME + '=' + '#VALUE'
EXEC(#sql)
SET #RowsDeleted=##ROWCOUNT
END
GO
Couple issues
First, you don't need (name of table)
SET #sql = 'DELETE FROM ' + #TABLE_NAME + etc.
In general you should try to include the appropriate schema prefix
SET #sql = 'DELETE FROM dbo.' + #TABLE_NAME + etc.
And in case your table name has special characters perhaps it should be enclosed in brackets
SET #sql = 'DELETE FROM dbo.[' + #TABLE_NAME + ']' + etc.
Since #Value is a string, you must surround it with single quotes when computing the value for #SQL. To insert a single quote into a string you have to escape it by using two single quotes, like this:
SET #SQL = 'DELETE FROM dbo.[' + #TABLE_NAME + '] WHERE [' + #COLUMN_NAME + '] = '''' + #VALUE + ''''
If #VALUE itself contains a single quote, this whole thing will break, so you need to escape that as well
SET #SQL = 'DELETE FROM dbo.[' + #TABLE_NAME + '] WHERE [' + #COLUMN_NAME + '] = '''' + REPLACE(#VALUE,'''','''''') + ''''
Also, ##ROWCOUNT will not populate from EXEC. If you want to be able to read ##ROWCOUNT, use sp_ExecuteSQL instead
EXEC sp_ExecuteSql #SQL
And finally, let me editorialize for a minute--
This sort of stored procedure is not a great idea. I know it seems pretty cool because it is flexible, and that kind of thinking is usually smart when it comes to other languages, but in the database world this approach causes problems, e.g. there are security issues (e.g. injection, and the fact that you need elevated privileges to call sp_executeSql) and there issues with precompilation/performance (because the SQL isn't known ahead of time, SQL Server will need to generate a new query plan each and every time you call this) and since the caller can supply any value for table and column name you have no idea whether this delete statement will be efficient and use indexes or if it will cause a huge performance issue because the table is large and the column is not indexed.
The proper approach is to have a series of appropriate stored procedures with strongly-typed inputs that are specific to each data use case where you need to delete based on criteria. Database engineers should not be trying to make things flexible; you should be forcing people to think through what exactly they are going to need, and implement that and only that. That is the only way to ensure people are following the rules, keeping R/I intact, efficient use of indexes, etc.
Yes, this may seem like repetitive and redundant work, but c'est la vie. There are tools available to generate the code for CRUD operations if you don't like the extra typing.
In addition to some of the information John Wu provided you have to worry about data types and ##ROWCOUNT may not be accurate if there are triggers on your tables and things..... You can get around both of those issues though by casting to nvarchar() and using OUTPUT clause with a temp table to do the COUNT().
So just for fun here is a way you can do it:
CREATE PROCEDURE dbo.[ProcName]
#TableName SYSNAME
,#ColumnName SYSNAME
,#Value NVARCHAR(MAX)
,#RecordCount INT OUTPUT
AS
BEGIN
DECLARE #SQL NVARCHAR(1000)
SET #SQL = N'IF OBJECT_ID(''tempdb..#DeletedOutput'') IS NOT NULL
BEGIN
DROP TABLE #DeletedOutput
END
CREATE TABLE #DeletedOutput (
ID INT IDENTITY(1,1)
ColumnValue NVARCHAR(MAX)
)
DELETE FROM dbo.' + QUOTENAME(#TableName) + '
OUTPUT deleted.' + QUOTENAME(#ColumnName) + ' INTO #DeletedOutput (ColumnValue)
WHERE CAST(' + QUOTENAME(#ColumnName) + ' AS NVARCHAR(MAX)) = ' + CHAR(39) + #Value + CHAR(39) + '
SELECT #RecordCountOUT = COUNT(ID) FROM #DeletedOutput
IF OBJECT_ID(''tempdb..#DeletedOutput'') IS NOT NULL
BEGIN
DROP TABLE #DeletedOutput
END'
DECLARE #ParmDefinition NVARCHAR(200) = N'#RecordCountOUT INT OUTPUT'
EXECUTE sp_executesql #SQL, #ParmDefinition, #RecordCountOUT = #RecordCount OUTPUT
END
So the use of QOUTENAME will help against the injection attack but not be perfect. And I use CHAR(39) instead of the escape sequence for a single quote on value because I find it easier when string building at that point.... By using Parameter OUTPUT from sp_executesql you can still return your count.
Keep in mind just because you can do something in SQL doesn't always mean you should.

Using table columns in prepared statement parameter

My page has a dropdown list that let a user choose any search category like title, description and so forth. So I have this SQL statement:
select * from table where "selected value from dropdown list" = "searchform"
I would like to pass it to the prepared statement like this:
select * from table where ? = ?
Since my select statements have the same form, only the columns in the where clause are different, is there a way to do this without manually creating select statements for every column?
Yes, it is called dynamic sql.
DECLARE #sql AS VARCHAR(MAX)
SET #sql = 'select * from table where ' + #column + ' = ''' + #value + ''''
EXEC(#sql)
You must check if the column is of numeric type.
You should also be careful for sql injection. My example is a very simplistic one, so you have to do your own checks.
For instance use of QUOTENAME would be useful:
DECLARE #sql AS VARCHAR(MAX)
SET #sql = 'select * from table where ' + QUOTENAME(#column) + ' = ''' + #value + ''''
EXEC(#sql)
The above examples are simply TSQL. In your prepared statement i think you could have the following:
PreparedStatement pstmt = con.prepareStatement("
DECLARE #sql AS VARCHAR(MAX)
SET #sql = 'select * from table where ' + QUOTENAME(?) + ' = ? '
EXEC(#sql)
");
pstm.setString(1,columnName);
pstm.setString(2,filterValue);
Unfortunately i am not familiar with JAVA, so i have not tested this. I think it worths a try though.
The above #sql variable will produce a statement like :
select * from table where [columnname] = filtervalue
columnname wrapped with brackets will help against SQL injection.

Update data in a SQL Server temp table where the column names are unknown

In a stored procedure I dynamically create a temp table by selecting the name of applications from a regular table. Then I add a date column and add the last 12 months. The result looks like this:
So far so good. Now I want to update the data in columns by querying another regular table. Normally it would be something like:
UPDATE ##TempTable
SET [columName] = (SELECT SUM(columName)
FROM RegularTable
WHERE FORMAT(RegularTable.Date,'MM/yyyy') = FORMAT(##TempMonths.x,'MM/yyyy'))
However, since I don't know what the name of the columns are at any given time, I need to do this dynamically.
So my question is, how can I get the column names of a temp table dynamically while doing an update?
Thanks!
I think you can use something like the following.
select name as 'ColumnName'
from tempdb.sys.columns
where object_id = object_id('tempdb..##TempTable');
And then generate dynamic sql using something like the following.
DECLARE #tableName nvarchar(50)
SET #tableName = 'RegularTable'
DECLARE #sql NVARCHAR(MAX)
SET #sql = ''
SELECT #sql = #sql + ' UPDATE ##TempTable ' + CHAR(13) +
' SET [' + c.name + '] = (SELECT SUM([' + c.name + ']) ' + CHAR(13) +
' FROM RegularTable' + CHAR(13) +
' WHERE FORMAT(RegularTable.Date,''MM/yyyy'') = FORMAT(##TempMonths.x,''MM/yyyy''));' + CHAR(13)
from tempdb.sys.columns c
where object_id = object_id('tempdb..##MyTempTable');
print #sql
-- exec sp_executesql #sql;
Then print statement in above snippet shows that the #sql variable has the following text.
UPDATE ##TempTable
SET [Test Application One] = (SELECT SUM([Test Application One])
FROM RegularTable
WHERE FORMAT(RegularTable.Date,'MM/yyyy') = FORMAT(##TempMonths.x,'MM/yyyy'));
UPDATE ##TempTable
SET [Test Application Two] = (SELECT SUM([Test Application Two])
FROM RegularTable
WHERE FORMAT(RegularTable.Date,'MM/yyyy') = FORMAT(##TempMonths.x,'MM/yyyy'));
So now, you use sp_exec to execute the updates as follows (un-comment it from above snippet).
exec sp_executesql #sql;
If it's a 1 time UPDATE you can PRINT the dynamic SQL statement (as shown above) and then execute it in the SSMS Query Windows.
I recommend you use the print statement first to make sure the UPDATE statements generated are what you want, and then do the sp_executesql or run the printed UPDATE statement in the query window.