0

I have numerous tables I that I want to have created and populated dynamically based on views.

I want to perform something like a combination of these two posts:

Create Table from View

Is there a way to loop through a table variable in TSQL without using a cursor?

Select *
Into dbo.##tblTemp
From databasename.sys.views

Declare @TableName NVARCHAR(128)

While (Select COUNT(*) From #Temp) > 0

Begin

    Select Top 1 @TableName = name from databasename.sys.views

        Select * into @TableName from databasename.sys.views

    Delete databasename.sys.views Where name = @TableName

End

Am I better off with a stored procedure that dynamically creates the sql statement to create the table?

EDIT:

Per Sebastian, I am running the below code to accomplish this:

DECLARE @cmd NVARCHAR(MAX) = ( SELECT TOP 10 'exec sp_rename '
                                    + '@objname =''' + OBJECT_SCHEMA_NAME(object_id)
                                    + '.'
                                    + OBJECT_NAME(object_id) + ''
                                    + ''', @newname = '
                                    + '''v_' + name + ''
                                    + ''';'
                                    + 'SELECT * INTO '
                                    + OBJECT_SCHEMA_NAME(object_id)
                                    + '.'                                   
                                    + OBJECT_NAME(object_id)
                                    + ' FROM '
                                    + OBJECT_SCHEMA_NAME(object_id)
                                    + '.v_'                                 
                                    + OBJECT_NAME(object_id)
                                    + ';'
                                    + 'DROP VIEW '
                                    + OBJECT_SCHEMA_NAME(object_id)
                                    + '.v_' 
                                    + OBJECT_NAME(object_id)
                                    + ';'
                            FROM db.sys.views
                            FOR XML PATH(''), TYPE).value('.', 'NVARCHAR(MAX)');

EXEC (@cmd)
--Select @cmd

Community
  • 1
  • 1
Geoff Dawdy
  • 886
  • 6
  • 15
  • 42
  • What are you trying to achieve? Do you want to create a table for each view that has the same schema as the view? Do you want data in those table? What should happen to the view? -- Also, what is the underlying problem you are trying to solve? – Sebastian Meine Jan 07 '13 at 18:06
  • Yes, I would like a table for each view that has the same schema as the view. And yes I would also like to populate the data in those tables. The view will need to be replaced by the table. – Geoff Dawdy Jan 07 '13 at 18:57
  • 1
    Would you mind me asking why you need to do that? You are aware that there is no good mechanism to keep that data in those new tables in sync with the tables that it originally came from? – Sebastian Meine Jan 07 '13 at 20:22
  • I'm new to this position and am only a Jr. DBA but I'm told that this these views are creating too many locks on the data so they would like to have a separate database composed of tables based on the views. – Geoff Dawdy Jan 07 '13 at 21:31

1 Answers1

1

To copy all the data seems to be the wrong approach to solve your problem. If your problem is poor performance do to too many reads caused by views there are two things to look out for first.

1) Check that you tables have appropriate indexes. You could even add indexes to your views. There are many resources out there that tell you how to go about index tuning. Or you could hire a consultant (like me) to help you out with that.

2) If your queries join views, it often happens that unnecessary tables make it into the mix. For example if view v1 joins table a and b and view v2 joins table b and c and your query then joins v1 with v2, it effectively joins a with b with b with c. Such a query often can be rewritten to join to b only once helping tremendously with performance. So if you have queries joining views with views you should review those.

If after all that you still want to go forward with copying the data, you can use this:

DECLARE @cmd NVARCHAR(MAX) = ( SELECT 'SELECT * INTO '
                                      + QUOTENAME(OBJECT_SCHEMA_NAME(object_id))
                                      + '.'
                                      + QUOTENAME('tbl_'+OBJECT_NAME(object_id))
                                      + ' FROM '
                                      + QUOTENAME(OBJECT_SCHEMA_NAME(object_id))
                                      + '.' + QUOTENAME(OBJECT_NAME(object_id))
                                      + ';'
                               FROM   sys.views
                               FOR XML PATH(''), TYPE).value('.', 'NVARCHAR(MAX)');

EXEC (@cmd);     

It creates a command that uses SELECT INTO to create a table for each view in the database. Because SQL Server does not allow name collisions even for objects of differing type, I prefixed the names of the tables with "tbl_".

If you need to create the tables in a different database, you need to prefix the table names with "dbname.". In that case you can remove the "tbl_"prefix.


EDIT:

You had a few missing quotes in your version. Try this:

DECLARE @cmd NVARCHAR(MAX) = ( SELECT TOP 1 'exec sp_rename '''
                                + QUOTENAME(OBJECT_SCHEMA_NAME(object_id))
                                + '.'
                                + QUOTENAME(OBJECT_NAME(object_id))
                                + ''', '''
                                + QUOTENAME(OBJECT_SCHEMA_NAME(object_id))
                                + '.'
                                + QUOTENAME('v_' +OBJECT_NAME(object_id))
                                + ''';'
                                + 'SELECT * INTO '
                                + QUOTENAME(OBJECT_SCHEMA_NAME(object_id))
                                + '.'                                   
                                + QUOTENAME(OBJECT_NAME(object_id))
                                + ' FROM '
                                + QUOTENAME(OBJECT_SCHEMA_NAME(object_id))
                                + '.'                                   
                                + QUOTENAME('v_' +OBJECT_NAME(object_id))
                                + ';'
                                + 'DROP VIEW '
                                + QUOTENAME(OBJECT_SCHEMA_NAME(object_id))
                                + '.' 
                                + QUOTENAME('v_' +OBJECT_NAME(object_id))
                                + ';'
                        FROM sys.views
                        FOR XML PATH(''), TYPE).value('.', 'NVARCHAR(MAX)');

You also can use PRINT @cmd instead of EXEC(@cmd) to see if the command put together makes sense.

Sebastian Meine
  • 11,260
  • 29
  • 41
  • Works great. However, since I would like have the table contain the same name as the view. Therefore the view should be removed when the table is created. – Geoff Dawdy Jan 08 '13 at 22:02
  • you could add a `+'DROP VIEW '+QUOTENAME(OBJECT_SCHEMA_NAME(object_id))+ '.' + QUOTENAME(OBJECT_NAME(object_id))+';'` after the `+ ';'` and then something similar with `EXEC sp_rename`. - However, if you remove the views you take any chance for a data refresh away, so I'd strongly advise against it. Maybe you can rename the views first and then create the tables with the original names. – Sebastian Meine Jan 08 '13 at 22:26
  • I'm working in a dev environment so I'm comfortable with losing data (which I can restore from a backup if needed). When would I need to perform the sp_rename so that the tables that are created still contain the original views? – Geoff Dawdy Jan 08 '13 at 22:43
  • DECLARE @cmd NVARCHAR(MAX) = ( SELECT top 1 'SELECT * INTO ' + QUOTENAME(OBJECT_SCHEMA_NAME(object_id)) + ... + ';' + 'DROP VIEW ' + QUOTENAME(OBJECT_SCHEMA_NAME(object_id)) + '.' + QUOTENAME(OBJECT_NAME(object_id)) + ';' + 'exec sp_rename ' + QUOTENAME(OBJECT_NAME(object_id)) + ', ' + QUOTENAME('v'+OBJECT_NAME(object_id)) + ';' FROM dbname.sys.views FOR XML PATH(''), TYPE).value('.', 'NVARCHAR(MAX)'); EXEC (@cmd); – Geoff Dawdy Jan 08 '13 at 22:51
  • the order should be either (sp_rename view, create table). or (create table, drop view, rename table). The rename you've written looks good for the first approach (but for that it has to move) – Sebastian Meine Jan 08 '13 at 23:00
  • I would now like to limit this query to specific tables. In other words, I want to apply the renaming of the views to tables for only TableA, TableB, and TableC. How would I go about limiting it to just those few tables? I can ask this as a separate question if you would like to post a separate answer. – Geoff Dawdy Jan 25 '13 at 21:12
  • `sys.views` is a system view that returns one row per view in the database. So you can just add a `WHERE` clause after the `FROM sys.views` in the query above to filter out unwanted views. – Sebastian Meine Jan 25 '13 at 22:52