Dynamic Schema name in SQL View - sql

I have two datasets:
one is data about dogs [my data]
the second is a lookup table of matching keys [I have no control over this data]
The matching keys are updated regularly, and I want to create a View (or something that fulfills the same purpose) of the Dog dataset, which always joins on the most recent matching keys. Furthermore, I need to be able to reference it inline - as though it was a table.
The match updates in the lookup table are differentiated by their schema names, so to get the most recent, I just have to identify the latest schema name and swap it out of the query.
Given that both Views and Table Valued Functions prohibit dynamic SQL, and Stored Procedures can't be referenced like a table can be how can I achieve this in just SQL?

The match updates in the lookup table are differentiated by their schema names, so to get the most recent, I just have to identify the latest schema name and swap it out of the query.
You can use a view to solve this problem, but you need some way of altering it whenever new data is entered into the database.
I'm assuming that whenever a new schema is created, a new table is also created in that schema, but the table name and it's column names are always the same. Note that this assumption is critical to the solution I'm about to propose - and that solution is to use a DDL trigger listening to the create_table event on the database level to alter your view so that it will reference the schema of the newly created table.
Another assumption I'm making is that you either already have the initial view, or that you are working with SQL Server 2016 or higher (that allows create or alter syntax).
So first, let's create the initial view:
CREATE VIEW dbo.TheView
AS
SELECT NULL As Test
GO
Then, I've added the DML trigger, which creates and executes a dynamic alter view statement based on the schema of the newly created table:
CREATE TRIGGER AlterViewWhenSchemaChanges
ON DATABASE
FOR CREATE_TABLE
AS
DECLARE #Sql nvarchar(max),
#NewTableName sysname,
#NewSchemaName sysname;
SELECT #NewSchemaName = EVENTDATA().value('(/EVENT_INSTANCE/SchemaName)[1]', 'NVARCHAR(255)'),
#NewTableName = EVENTDATA().value('(/EVENT_INSTANCE/ObjectName)[1]', 'NVARCHAR(255)');
-- We only want to alter the view when this specific table is created!
IF #NewTableName = 'TableName'
BEGIN
SELECT #Sql =
'ALTER VIEW dbo.TheView
AS
SELECT Col as test
FROM '+ #NewSchemaName +'.'+ #NewTableName
EXEC(#Sql)
END
GO
This way, whenever a new table with the specific name (TableName in my example) is created, the view gets altered to reference the last TableName created (which is obviously created in the newest schema).
Testing the script:
SELECT * FROM dbo.TheView;
GO
Results:
Test
NULL
Create a new schema with the table TableName
CREATE SCHEMA SchemaName
CREATE TABLE SchemaName.TableName (Col int);
GO
-- insert some data
INSERT INTO SchemaName.TableName(Col) VALUES (123);
-- get the data from the altered view
SELECT * FROM dbo.TheView
Results:
test
123
You can see a live demo on Rextester.

Related

How can I view the definition of a temporary table?

I make a temporary table in SQL Server:
create table #stun (name varchar(40),id int,gender varchar(40))
How can I view its definition afterwards?
you can check this way-
SELECT *
INTO #TempTable
FROM table_name -- any table from database
EXEC tempdb..sp_help '#TempTable'
DROP TABLE #TempTable
Solution 1 :
You can query data against it within the current session :
SELECT
*
FROM
#yourtemporarytable;
Sometimes, you may want to create a temporary table that is accessible across connections. In this case, you can use global temporary tables.
Unlike a temporary table, the name of a global temporary table starts with a double hash symbol (##).
CREATE TABLE ##global_temp (
...
);
SELECT
*
FROM
##global_temp
Solution 2 :
Using SSMS, you can find the table in the left pane >> Design >> get the table structure.

There is already an object named '#DIR_Cat' in the database

In my Stored procedure, I have added a command to create a hash temp table #DIR_CAT. But every time I execute the procedure I get this error:
"There is already an object named '#DIR_Cat' in the database."
Even when I have already created an Exists clause at the start of SP to check and drop the table if it is present. Any help is much appreciated.
The code goes like this.
if exists (select * from dbo.sysobjects where id = object_id(N'#DIR_Cat') )
drop table #DIR_Cat
/* some lines of code*/
CREATE TABLE #DIR_Cat (XMLDta xml)
/* some lines of code*/
INSERT #DIR_Cat exec (#stmt)
/* some lines of code*/
drop table #DIR_Cat
Main issue is you're not fully qualifying your objects. Your temp table lives in tempdb, whereas the system views use whatever database you're currently connected to by default. So essentially you're looking for the temp table, but you're looking in whatever database your currently connected to (which I'm guessing is not tempdb).
I'm assuming you're using SQL Server here, although you did also mention mysql in the tags. If that's what you're using, this code may not apply.
Here's the snippet I use for temp table drop/create
if object_id('tempdb.dbo.#<TableName, sysname, >') is not null drop table #<TableName, sysname, >
create table #<TableName, sysname, >
(
)
Side note, don't use dbo.sysobjects. That's a really old compatibility view. If you want to use objects, use sys.objects instead.
temp table does not exists in local DB sys.objects, it is in tempdb
you need to query tempb.sys.objects
the name of the temp table does not appear exactly as it is in the tempdb.sys.objects.
You can't query it just like
select *
from tempdb.sys.objects
where name = '#DIR_Cat' -- This does not works
you need to use object_id()
select *
from tempdb.sys.objects
where object_id = object_id('tempdb..#DIR_Cat')

Passing temp table from one execution to another

I want to pass a temp table from one execution path to another one nested in side it
What I have tried is this:
DECLARE #SQLQuery AS NVARCHAR(MAX)
SET #SQLQuery = '
--populate #tempTable with values
EXECUTE('SELECT TOP (100) * FROM ' + tempdb..#tempTable)
EXECUTE sp_executesql #SQLQuery
but it fails with this error message:
Incorrect syntax near 'tempdb'
Is there a another\better way to pass temporary table between execution contexts?
You can create a global temp table using the ##tablename syntax (double hash). The difference is explained on the TechNet site:
There are two types of temporary tables: local and global. They differ from each other in their names, their visibility, and their availability. Local temporary tables have a single number sign (#) as the first character of their names; they are visible only to the current connection for the user, and they are deleted when the user disconnects from the instance of SQL Server. Global temporary tables have two number signs (##) as the first characters of their names; they are visible to any user after they are created, and they are deleted when all users referencing the table disconnect from the instance of SQL Server.
For example, if you create the table employees, the table can be used by any person who has the security permissions in the database to use it, until the table is deleted. If a database session creates the local temporary table #employees, only the session can work with the table, and it is deleted when the session disconnects. If you create the global temporary table ##employees, any user in the database can work with this table. If no other user works with this table after you create it, the table is deleted when you disconnect. If another user works with the table after you create it, SQL Server deletes it after you disconnect and after all other sessions are no longer actively using it.
If a temporary table is created with a named constraint and the temporary table is created within the scope of a user-defined transaction, only one user at a time can execute the statement that creates the temp table. For example, if a stored procedure creates a temporary table with a named primary key constraint, the stored procedure cannot be executed simultaneously by multiple users.
The next suggestion may be even more helpful:
Many uses of temporary tables can be replaced with variables that have the table data type. For more information about using table variables, see table (Transact-SQL).
Your temp table will be visible inside the dynamic sql with no problem. I am not sure if you are creating the temp table inside the dynamic sql or before.
Here it is with the table created BEFORE the dynamic sql.
create table #Temp(SomeValue varchar(10))
insert #Temp select 'made it'
exec sp_executesql N'select * from #Temp'
The reason for your syntax error is that you are doing an unnecessary EXECUTE inside an EXECUTE, and you didn't escape the nested single-quote. This would be the correct way to write it:
SET #SQLQuery='
--populate #tempTable with values
SELECT TOP 100 * FROM tempdb..#tempTable'
However, I have a feeling that the syntax error is only the beginning of your problems. Impossible to tell what you're ultimately trying to do here, only seeing this much of the code, though.
Your quotations are messed up. Try:
SET #SQLQuery='
--populate #tempTable with values
EXECUTE(''SELECT TOP 100 * FROM '' + tempdb..#tempTable + '') '

Dynamic temp table

I have to create a dynamic temp table in sql server, based in a list of columns, for example, I have a table ListOfColumns that has many names of columns inside that refers to a real table
ListOfColumns
ColumnNameA
ColumnNameB
ColumnNameC
I create a function to get a string with all these columns in this format:
"ColumnNameA, ColumnNameB, ColumnNameC"
Now I need to create my temp table based on those columns that are in a real table called Report. I can have more or less columns (Its a dynamic report column generator)
I need to do a dynamic SQL, I don't have the datatypes for each column, I would like to create a temp table with dynamic sql and inherit the datatypes of my Report table. Is there any way of doing this?
By the way... I don't want to use global variables.
Thank you.
If, for example, your Report table looks something like this:
create table Report (
ColumnNameA varchar(4),
ColumnNameB integer,
ColumnNameC integer,
ColumnNameD varchar(8),
ColumnNameE bit,
ColumnNameF integer
);
You can create the following procedure:
create proc copy_table #col_names varchar(128)
as
DECLARE #SQLQuery AS NVARCHAR(500)
SET #SQLQuery = 'SELECT ' + #col_names + ' into ReportTemp from Report where 1 = 0'
EXEC(#SQLQuery);
and call it using the comma separated columns string you have, as below:
exec copy_table #col_names = 'ColumnNameA, ColumnNameB, ColumnNameC'
and you should have your table created with the correct column types. To test it, insert one row and then query it.
insert into ReportTemp values ('abc', 1, 2 );
select * from ReportTemp;
To see this in action, check out this sql fiddle.
Note that, this will not carry over constraints, indices, etc -- just the column names and types.
You can't create a dynamic temporary table.
The reason is that the temporary table is associated with the SQL session. When you execute dynamic SQL, it creates a new session that terminates at the end of the session. So, the table gets created during the exec. Then it gets dropped (or out of context) when control moves back to the calling procedure.
Here are some work-arounds, none of which you might like:
Create a table with a canonical prefix, such as "_" to represent a working table. Then drop this table in the stored procedure and catch exceptions to drop it in almost all cases.
Create a temporary table with all possible column values.
Create your own "temporary" database for such working tables.
Use generic column names and keep the correspondence from these generic columns to your columns somewhere else.

How can I conditionally construct a table name for an SQL CREATE TABLE statement?

Within an SQL stored procedure, I would like to have the ability to construct a table name and create it.
Example: I just logged into my database under company 03 and a customer table does not exist, so I would like for the proc to CREATE TABLE CUSTOMER03.
Is there a way to append company_id char(2) to CUSTOMER and feed it to the CREATE TABLE statement? maybe like
CREATE TABLE $tablename or $tablename+company_id?
In Oracle the syntax would be something like
BEGIN
EXECUTE IMMEDIATE 'CREATE TABLE CUSTOMER_'||v_company_id||' (..)';
END;
However this is probably a really bad idea. Six months down the line you'll want to add a column to the table and you'll need to work out which tables you need to add it to.
Also, stored procedures in Oracle need a fixed table name (of an existing table) or you'd have to reference everything through dynamic SQL which is a pain.
Better to have a single customer table with the company_id as an attribute. Then use Fine Grained Access Control to securely filter on the company_id to control who see's what company's data.
You would need to use dynamic SQL eg:
DECLARE #company_id char(2)
SET #company_id = '02'
EXEC('CREATE TABLE CUSTOMER' + #company_id + ' (id int PRIMARY KEY)')
(tested)
Use the IF NOT EXISTS modifier to the CREATE TABLE statement. This will cause the table to be created only if it does not already exist.