Select into tables dynamically with variables - sql

I have some code to create tables based on a set of dates I define.
Example, I have 5 dates, and they are aren't consecutive. For any of these dates, I want to create a table and I am currently using a Select into.
I am having to do this 5 times, even though the only thing changing is the name of the new table created and the date. Is there a way to do this in an elegant way.
I started writing some code, but I am struggling to get it to loop through all the dates I want. The way I have written it currently, I only works if I edit the date at the start.
DECLARE #MyDate DATE;
SET #MyDate = '2019-01-01';
SET #TableName = 'Table1';
SELECT *
into #TableName
FROM Original_Table
WHERE Query_Date = #MyDate;

Is this a one time thing or do you have to do this on a regular basis?
If it's the first, than I would just do it and get it over with.
If it's the latter, then I suspect something is very wrong with the way that system is designed - but assuming that can't be changed, you can create a stored procedure that will do this using dynamic SQL.
Something like this can get you started:
CREATE PROCEDURE dbo.CreateTableBasedOnDate
(
#MyDate DATE,
-- sysname is a system data type for identifiers: a non-nullable nvarchar(128).
#TableName sysname
)
AS
-- 200 is long enough. Yes, I did the math.
DECLARE #Sql nvarchar(200) =
-- Note: I'm not convinced that quotename is enough to protect you from sql injection.
-- you should be very careful with what user is allowed to execute this procedure.
N'SELECT * into '+ QUOTENAME(#TableName) +N'
FROM Original_Table
WHERE Query_Date = #MyDate;';
-- When dealing with dynamic SQL, Print is your best friend.
-- Remark this row and unremark the next only once you've verified you get the correct SQL
PRINT #SQL;
--EXEC sp_ExecuteSql #Sql, N'#MyDate Date', #MyDate
GO
Usage:
EXEC CreateTableBasedOnDate '2018-01-01', 'zohar';

Use dynamic SQL:
DECLARE #MyDate DATE, #TableName varchar(50);
SET #MyDate = '2019-01-01';
SET #TableName = 'Table1';
DECLARE #sql NVARCHAR(4000);
DECLARE #params NVARCHAR(4000);
SELECT #sql=N'
SELECT *
INTO ' + QUOTENAME(#TableName) + '
FROM Original_Table
WHERE Query_Date = #MyDate;';
SELECT #params = N'#MyDate DATE';
EXEC sys.sp_executesql #sql, #params, #MyDate=#MyDate
Note that dynamic SQL can be dangerous as it opens up a path for SQL injection. Its fine if you are just using it in your own local scripts, but take care if you e.g. wrap this in a procedure that is more widely accessible.

I would use dynamic SQL although I would add another variables for the schema:
DECLARE
#MyDate nVarchar(50) = '2019-01-01',
#Schema nVarchar (50) = 'dbo',
#TableName nVarchar(250) = 'Table1',
#SQL nVarchar(500);
Set #SQL = '
SELECT *
into '+ QUOTENAME(#Schema)+'.'+ QUOTENAME(#TableName) +'
FROM Original_Table
WHERE Query_Date = '+ #MyDate +';
'
--print #SQL
Exec(#SQL)
You can use the print statement to see how the SQL will look before executing this properly. You may also want to look at adding this as a stored procedure.

Related

Need to dynamically check for columns in other databases

My application runs over several databases, and it needs to be able to check from one to see if a column exists in the other. Unfortunately, I won't know the name of the second database until runtime, so it needs to be dynamic. Also, it has to do this in multiple places, so ideally I'd like to make it into a function, but this gives me problems because functions won't run dynamic SQL.
This is the (non-working) function I wrote.....
CREATE FUNCTION [dbo].[fn_checkcolexists] (
#dbname VARCHAR(100)
,#tablename VARCHAR(100)
,#colname VARCHAR(100)
)
RETURNS BIT
AS
BEGIN
DECLARE #sqlstring NVARCHAR(2000)
SET #sqlstring = 'select #retVal = 1 from ' + #dbname + '.sys.columns cols inner join yodata_dev_load.sys.tables tabs
on cols.object_ID=tabs.object_ID where cols.name=''' + #colname + ''' and tabs.name=''' + #tablename + ''''
DECLARE #retVal INT
EXEC sp_executesql #sqlstring
,N'#retVal int output'
,#retVal OUTPUT
RETURN #retval
END
Has anyone got any suggestions how I can accomplish this? I can't find a way to access the column information for every database. Does this information exist in the system databases anywhere?
Alternatively, can I create some sort of synonym for the other database?
Edit: How to find column names for all tables in all databases in SQL Server isn't an ideal solution, because it also relies on dynamic SQL, so I couldn't use this as a function
Use stored procedure and use one of these
One of the methods is to use undocumented
EXEC sp_msforeachdb 'SELECT table_catalog FROM ?.INFORMATION_SCHEMA.COLUMNS
where table_name=''your_table'' and column_name=''your_column_name'''
or simulate it
declare #sql varchar(max), #table_name varchar(100)
select #sql='', #table_name='your_table'
select #sql=#sql+ 'SELECT table_catalog
FROM '+name+'.INFORMATION_SCHEMA.TABLES
where table_name='''+#table_name+''' and
column_name=''your_column_name''' from sys.databases
exec(#sql)
I think I've got the solution I was after. I am using COL_LENGTH, which seems to do the job. You can specify a dbname to is, and even pass that as a parameter, and it returns a null if the column does not exist.
eg
declare #dbname varchar(200)='dbname'
select COL_LENGTH(#dbname + '.dbo.tablename','columnname')
if this returns a null, the column doesn't exist
Many thanks for all the contributors to this thread
Hope this works for you
CREATE FUNCTION [dbo].[fn_checkcolexists]
(
#dbname VARCHAR(100)
,#tablename VARCHAR(100)
,#colname VARCHAR(100)
)
RETURNS INT
AS
BEGIN
DECLARE #RECCOUNT INT = 0
SELECT #RECCOUNT = COUNT(*) FROM information_schema.columns WHERE TABLE_CATALOG = #dbname AND COLUMN_NAME = #colname AND TABLE_NAME = #tablename
RETURN #RECCOUNT
END
GO

Execute sp_executesql, Table Variabe not Declared

I am Using SQL server 2012 and i want to select random columns from my table by applying where condition in this query:
EXECUTE sp_executesql
N'SELECT *
FROM #table
WHERE #Col = #Value',
N'#Value nvarchar(44),#table nvarchar(55),#Col nvarchar(30)',
#Value = 'Cus_1',#Col='CustId',#table='SaleOrder';
But when I execute it, it shows error
Must declare the table variable "#table"
I also tried it to declare by this: #table table(Id nvarchar(30)), but thin it shows again an error on table type...
Please help
This is what you are trying to run:
EXECUTE sp_executesql
N'SELECT * FROM #table WHERE #Col = #Value',
N'#Value nvarchar(44), #table nvarchar(55), #Col nvarchar(30)',
#Value = 'Cus_1', #Col='CustId', #table='SaleOrder';
Alas. You cannot substitute in a table name or column name using parameter substitution. So, SQL Server is looking for a table variable called #table. You can fix this by putting the values directly into the string:
declare #Col = 'CustId', #table = 'SaleOrder';
declare #sql nvarchar(max) = N'SELECT * FROM ' + #table + ' WHERE ' + #Col + ' = #Value';
EXECUTE sp_executesql #sql,
N'#Value nvarchar(44)',
#Value = 'Cus_1';
Unfortunately, I cannot find a good reference in the documentation that explains what is happening. When a statement is compiled, it is allowed to have parameters. However, the parameters are for values in the statement, not for column, table, database, or UDF names or for keywords. The statement itself is compiled, with place holders for the parameters, and in order to be compiled, the SQL engine needs to resolve all object names.

Send query as parameter to SQL function

I want to create a SQL tabled-value function that will receive a query as n parameter through my API. In my function I want execute that query. The query will be a SELECT statement.
This is what I have done so far and what to achieve but it is not the correct way to do so.
CREATE FUNCTION CUSTOM_EXPORT_RESULTS (
#query varchar(max),
#guid uniqueidentifier,
#tableName varchar(200))
RETURNS TABLE
AS
RETURN
(
-- Execute query into a table
SELECT *
INTO #tableName
FROM (
EXEC(#query)
)
)
GO
Please suggest the correct way!
Try this one -
CREATE PROCEDURE dbo.sp_CUSTOM_EXPORT_RESULTS
#query NVARCHAR(MAX) = 'SELECT * FROM dbo.test'
, #guid UNIQUEIDENTIFIER
, #tableName VARCHAR(200) = 'test2'
AS BEGIN
SELECT #query =
REPLACE(#query,
'FROM',
'INTO [' + #tableName + '] FROM')
DECLARE #SQL NVARCHAR(MAX)
SELECT #SQL = '
IF OBJECT_ID (N''' + #tableName + ''') IS NOT NULL
DROP TABLE [' + #tableName + ']
' + #query
PRINT #SQL
EXEC sys.sp_executesql #SQL
RETURN 0
END
GO
Output -
IF OBJECT_ID (N'test2') IS NOT NULL
DROP TABLE [test2]
SELECT * INTO [test2] FROM dbo.test
What I see in your question is encapsulation of:
taking a dynamic SQL expression
executing it to fill a parametrized table
Why do you want to have such an encapsulation?
First, this can have a negative impact on your database performance. Please read this on EXEC() and sp_executesql() . I hope your SP won't be called from multiple parts of your application, because this WILL get you into trouble, at least performance-wise.
Another thing is - how and where are you constructing your SQL? Obviously you do it somewhere else and it seems its manually created. If we're talking about a contemporary application, there are lot of OR/M solutions for this and manual construction of TSQL in runtime should be always avoided if possible. Not to mention EXEC is not guarding you against any form of SQL injection attacks. However, if all of this is a part of some database administration TSQL bundle, forget his paragraph.
At the end, if you want to simply load a new table from some existing table (or part of it) as a part of some administration task in TSQL, consider issuing a SELECT ... INTO ... This will create a new target table structure for you (omitting indexes and constraints) and copy the data. SELECT INTO will outperform INSERT INTO SELECT because SELECT INTO gets minimally logged.
I hope this will get you (and others) at least a bit on the right track.
You can use stored procedure as well, here is the code that you can try.
CREATE FUNCTION CUSTOM_EXPORT_RESULTS
(
#query varchar(max),
#guid uniqueidentifier,
#tableName varchar(200)
)
RETURNS TABLE
AS
RETURN
(
declare #strQuery nvarchar(max)
-- Execute query into a table
SET #strQuery = REPLACE(#query,'FROM', 'INTO '+#tableName+' FROM')
exec sp_executesql #strQuery
)
GO

Creating SQL table using dynamic variable name

I want to create backup SQL tables using variable names.
something along the lines of
DECLARE #SQLTable Varchar(20)
SET #SQLTable = 'SomeTableName' + ' ' + '20100526'
SELECT * INTO quotename(#SQLTable)
FROM SomeTableName
but i'm getting
Incorrect syntax near '#SQLTable'.
It's just part of a small script for maintence so i don't have to worry about injections.
DECLARE #MyTableName sysname;
DECLARE #DynamicSQL nvarchar(max);
SET #MyTableName = 'FooTable';
SET #DynamicSQL = N'SELECT * INTO ' + QUOTENAME(#MyTableName) + ' FROM BarTable';
EXEC sp_executesql #DynamicSQL;
Unfortunately, you can't use bind variables for table names, column names, etc. IN this case you must generate dynamic SQL and use exec.
DECLARE #Script NVARCHAR(MAX);
SET #Script = N'SELECT * INTO SomeTableName_' + N'20100526' + N' FROM SomeTableName';
EXEC sp_executesql #Script
I've left the date separate as I assume you want to calculate it for every run.
You should look into using synonyms:
-- Create a synonym for the Product table in AdventureWorks2008R2.
CREATE SYNONYM MyProduct
FOR AdventureWorks2008R2.Production.Product;
GO
-- Query the Product table by using the synonym.
USE tempdb;
GO
SELECT ProductID, Name
FROM MyProduct
WHERE ProductID < 5;
GO
http://msdn.microsoft.com/en-us/library/ms177544.aspx
DECLARE #MyTableName nvarchar(20);
DECLARE #DynamicSQL nvarchar(1000);
SET #MyTableName = "FooTable";
SET #DynamicSQL = N'SELECT * INTO ' + #MyTableName + ' FROM BarTable';
exec #DynamicSQL;
this query is correct but just use single quote at the ("FooTable")='FooTable'

Using variable inside dynamic SQL

I have the below SQL..What I am trying to do is use the Parameter defined at the stored procedure level inside dynamic SQL:
CREATE PROCEDURE [dbo].[Test]
(#DealID NVARCHAR(500),
#OUTPUT NVARCHAR(MAX) OUTPUT,
#FeeType CHAR(1)
) -- I want to use this parameter inside dynamic SQL query
AS
DECLARE #exec_str NVARCHAR(MAX)
DECLARE #ParmDefinition NVARCHAR(MAX)
BEGIN
SET #exec_str = N'DECLARE #ParmDefinition NVARCHAR(500)
SELECT * FROM #FeeType' --This is where I want to use the variable
DECLARE #ParamDefinition nvarchar(max)
SET #ParamDefinition = N'#OUTPUT NVARCHAR(MAX) OUTPUT'
EXEC sp_executesql #exec_str, #ParamDefinition
Can someone please tell me how to do it?
Thanks
In SQL Server Identifiers can't be parameterized.
Since you are using dynamic SQL anyway, you can do something like this:
SET #exec_str= N'Select * from '+ #FeeType
EXEC(#exec_str)
However, this is vulnerable to SQL injection attacks. To reduce the risk to minimum you should check first that such a table name exists, and I would also use quotename just to be on the safe side:
IF EXISTS
(
SELECT 1
FROM Information_Schema.Tables
WHERE TABLE_NAME = #FeeType
)
BEGIN
SET #exec_str= N'Select * from '+ QUOTENAME(#FeeType)
EXEC(#exec_str)
END