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

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.

Related

Dynamic Schema name in SQL View

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.

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.

stored procedures and temp tables

I have to create a 10 stored procedure as follows:
In stored procedure # 1 I create temp table 1 and this temp table is used in stored procedure 2 to create another temp table and this new tem table is used in another STORED PROCEDURE and so on.
I am not sure how to create these stored procedure, because for these stored procedure I need to have temporary tables present in temdb.
Any help
Can you user Global Temporary Tables?
SELECT * INTO ##Users FROM UserTable
The Global temp tables will remain in tempdb until deleted and can be used across different stored procs.
Assuming you want to name the table (or some of its columns) that's about to be created based on the data present in the temp table, you might want to resort to dynamic SQL, since you can't use variables like this:
declare #foo varchar(50)
select #foo = tableName from #tempTable
create table #foo (fooColumn int)
But before you even think of using dynamic SQL, you've got to ask yourself whether you really need this solution.

MySQL How to INSERT INTO [temp table] FROM [Stored Procedure]

This is very similar to question 653714, but for MySQL instead of SQL Server.
Basically, I have a complicated select that is the basis for several stored procedures. I would like to share the code across the stored procedures, however, I'm not sure how to do this. One way I could do this is by making the shared select a stored procedure and then calling that stored procedure from the other ones. I can't figure out how to work with the result set of the nested stored procedure. If I could put them in a temp table I could use the results effectively, but I can't figure out how to get them in a temp table. For example, this does not work:
CREATE TEMPORARY TABLE tmp EXEC nested_sp();
The problem is, Stored Procedures don't really return output directly. They can execute select statements inside the script, but have no return value.
MySQL calls stored procedures via CALL StoredProcedureName(); And you cannot direct that output to anything, as they don't return anything (unlike a function).
MySQL Call Command
You cannot "SELECT INTO" with stored procedures.
Create the temporary table first and have your stored procedure to store the query result into the created temporary table using normal "INSERT INTO". The temporary table is visible as long as you drop it or until the connection is closed.
i know this is coming really late but since it took me ages to find a real solution i might as well share. I worked on an example that is below.
the tables created are:
CREATE TABLE BOOK(
B_ID INT NOT NULL AUTO_INCREMENT,
PRIMARY KEY(B_ID),
TITLE VARCHAR(100),
DESCRIPTION VARCHAR(30),
PRICE DOUBLE);
CREATE TABLE BOOK_COMMENT(
PRIMARY KEY(B_C_ID),
B_C_ID INT NOT NULL AUTO_INCREMENT,
REMARK VARCHAR(120),
B_ID INT,
FOREIGN KEY(B_ID) REFERENCES BOOK(B_ID));
CREATE TABLE AUTHOR(
A_ID INT NOT NULL AUTO_INCREMENT,
PRIMARY KEY(A_ID),
A_NAME CHAR(15),
B_ID INT,
FOREIGN KEY(B_ID) REFERENCES BOOK(B_ID));
DELIMITER
CREATE PROCEDURE BOOK_IMPORTANT( _PRICE DOUBLE, _B_ID INT, A_NAME CHAR(15), _BD_ID INT)
BEGIN
INSERT INTO BOOK(PRICE)
VALUES(_PRICE);
SET _B_ID=LAST_INSERT_ID();
INSERT INTO BOOK_COMMENT(B_ID)
VALUES(_B_ID);
SET _BD_ID=LAST_INSERT_ID();
INSERT INTO AUTHOR(A_NAME,B_ID)
VALUES(A_NAME,_BD_ID);
END
then use the following to insert the values.
CALL BOOK_IMPORTANT('0.79',LAST_INSERT_ID(),'',LAST_INSERT_ID());
LAST_INSERT_ID() takes the last auto increment of the table and inserts it into the referencing column of the child table.
In the procedure parameters _B_ID and _BD_ID represent the B_ID since I need B_ID as a foreign key in both tables.
Sorry for the excess wording. All the other guys expect you to automatically know how to do it. Hope it helps
My first reaction was "That sounds like a view to me". Doesn't that abstract it enough so you can just add the variability into an SP per case?
Anything that adds a temp table that wouldn't otherwise be there is a very likely antipattern.
Maybe it's a closed topic, but I would like to offer a solution based on the properties of MySQL temporary tables. First, the way to create the temporary table would not be to call the stored procedure "CREATE TEMPORARY TABLE tmp EXEC nested_sp ();". The query is to the temporary table of "infrastructure", (to name it somehow).
To achieve the desired result, it is necessary to create 2 stored procedures, the first stored procedure processes the data and fills the temporary "infrastructure" table, the second stored procedure, reads this table and continues with the process and finally "DROP" the "infrastructure" table
This is the first stored procedure:
CREATE DEFINER = 'root'#'localhost'
PROCEDURE cajareal.priv_test()
BEGIN
CREATE TEMPORARY TABLE IF NOT EXISTS tmp(
column1 TEXT
, column2 TEXT
, column3 TEXT
);
INSERT INTO tmp(column1, column2 , column3) VALUES(CURDATE(), CURRENT_DATE(), CURRENT_TIMESTAMP());
END
This is the second stored procedure:
CREATE DEFINER = 'root'#'localhost'
PROCEDURE cajareal.priv_caller()
BEGIN
CALL priv_test;
-- Read data of "infrastructure" table
SELECT * FROM tmp;
-- Do the business logic
-- Delete the "infrastructure" table
DROP TABLE tmp;
END
I use this technique to analyze a string and convert it to the table