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
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.
Related
This doesn't seem to have been answered anywhere (although very similar cases have been answered)...
I have an issue where I am trying to update a column's value in a table within a stored procedure. However, I pass more than one table to this stored procedure and some tables have a certain column and others don't. Thus I need to check if the column exists before I run this update. Now, because it's in a stored procedure, SQL seems to be parsing the entire chunk of code up front and complains that this column doesn't exist.
Code:
IF COL_LENGTH(''DBName' + #date+ '..' + #TableName + #date+''', ''ColumnName' + #specifictocolumn + 'restofcolumnname'') IS NOT NULL
update DBName' + #date+ '..' + #TableName + #date+ ' set ColumnName' + #specifictocolumn + 'restofcolumnname = 0
Alternatively
IF EXISTS(SELECT 1 FROM sys.columns WHERE Name = N''ColumnName' + #specifictocolumn + 'restofcolumnname '' AND Object_ID = Object_ID(N''DBName' + #date+ '..' + #TableName + #date+'''))
update DBName' + #date+ '..' + #TableName + #date+ ' set ColumnName' + #specifictocolumn + 'restofcolumnname = 0
Both of these give the error (column name removed for IP purposes):
Msg 207, Level 16, State 1, Line 6
Invalid column name 'ColumnName'.
There is a question on stack overflow called "Disable TSQL script check" that I looked at, but they suggest that you call the check of the column outside of the dynamic sql and then only execute if it passes the check. This won't work for me because part of the if-statement has variables in it that need to be in dynamic sql.
You can still split the dynamic SQL in 2 parts:
check if the column exists
do the actual update when 1. returns true
you'll probably want to use sp_executesql for this and an OUTPUT parameter.
Something along the lines of this:
DECLARE #sql nvarchar(max),
#result int
SELECT #sql = 'SELECT #col_length = COL_LENGTH(''DBName' + #date + '..' + #TableName + #date + ''', ''ColumnName' + #specifictocolumn + 'restofcolumnname'')'
EXEC sp_executesql #stmt = #sql,
#params = N'#col_length int OUTPUT',
#col_length = #result OUTPUT
IF #result IS NOT NULL
BEGIN
EXEC ('update DBName' + #date+ '..' + #TableName + #date+ ' set ColumnName' + #specifictocolumn + 'restofcolumnname = 0')
END
Or you could go 'dynamic inside dynamic', but will become a mess very quickly.
Am trying to write script for removing Constraints.
I have the below function to select the Constarints in my DataBase
SELECT name
FROM sys.foreign_keys
And I have written alter scripts using the above scripts
SELECT
'ALTER TABLE ' + OBJECT_NAME(parent_object_id) +
' DROP CONSTRAINT ' + name
FROM sys.foreign_keys
Using the above query how can I execute these constraints ?
I can use DROP DATABASE DBName. But am just trying to drop tables by dropping Constraints.
is it possible without going for SP ? Or any easy ways I can proceed?
Well you can always copy the output from the bottom pane, paste it into the top pane, and hit F5. Or you can build a string to execute directly:
DECLARE #sql NVARCHAR(MAX) = N'';
SELECT #sql += N'
ALTER TABLE ' + QUOTENAME(OBJECT_SCHEMA_NAME(parent_object_id))
+ '.' + QUOTENAME(OBJECT_NAME(parent_object_id)) +
' DROP CONSTRAINT ' + QUOTENAME(name) + ';'
FROM sys.foreign_keys;
PRINT #sql;
-- EXEC sp_executesql #sql;
(When you are happy with the PRINT output, comment it out and uncomment the EXEC. Note that the print output will be truncated to 8K in Management Studio but the variable really holds the entire command.)
Also I don't know how this really relates to whether you are using a stored procedure or not, or why you are trying to do it "w/o going for SP"... this query can be run as a stored procedure or not, it all depends on how often you're going to call it, where the procedure lives, etc.
This worked for me in SQL Server 2008:
DECLARE #SQL NVARCHAR(MAX) = N'';
SELECT #SQL += N'
ALTER TABLE ' + OBJECT_NAME(PARENT_OBJECT_ID) + ' DROP CONSTRAINT ' + OBJECT_NAME(OBJECT_ID) + ';'
FROM SYS.OBJECTS
WHERE TYPE_DESC LIKE '%CONSTRAINT' AND OBJECT_NAME(PARENT_OBJECT_ID) = 'YOUR_TABLE';
PRINT #SQL
--EXECUTE(#SQL)
Of course, uncomment the EXECUTE(#SQL) when ready to run
The correct-marked question does not work for me. But this works for me in SQL Server 2017:
DECLARE #sql NVARCHAR(MAX) = N'';
SELECT #sql += N'
ALTER TABLE ' + QUOTENAME(OBJECT_SCHEMA_NAME(parent_object_id))
+ '.' + QUOTENAME(OBJECT_NAME(parent_object_id)) +
' DROP CONSTRAINT ' + QUOTENAME(name) + ';'
FROM sys.objects
WHERE type_desc LIKE '%CONSTRAINT'
AND OBJECT_NAME(PARENT_OBJECT_ID) LIKE 'your_table_name';
EXEC sp_executesql #sql;
A few minutes ago I was only searching for a simple syntax (SQL server) query that will copy a table Row .
This is usually done from time to time, when working on a ASP.net project, testing data with queries
inside the SQL SERVER management studio . so one of the routine actions is copying a row, altering the required columns to be different from each other, then testing data with queries
So I've encountered - this stored procedure- ,as answer by Dan Atkinson
but adding it to where all non testing purpose are stored lead me to think
is it possible to store them in sorted order so I could Distinguish
'utils' or 'testingPurpose' ones from those used in projects
(default folder inside managment treeview is Programmabilty) could this be another folder too
or this is not an option ?
if not , I thought of Utils. prefix like that (if no other way exist)
dbo.Utils.CopyTableRow
dbo.Utils.OtherRoutineActions ....
Or there's a designated way to achieve what I was thinking of.
this is a first "Util" stored procedure i've made , found it's only solution
prefexing it via Util_
ALTER PROCEDURE [dbo].[Utils_TableRowCopy](
#TableName VARCHAR(50) ,
#RowNumberToCopy INT
)
AS
BEGIN
declare #RowIdentity sysname =
(SELECT name FROM sys.identity_columns WHERE object_id = object_id(#TableName)
)
DECLARE #columns VARCHAR(5000), #query VARCHAR(8000);
SET #query = '' ;
SELECT #columns =
CASE
WHEN #columns IS NULL THEN column_name
ELSE #columns + ',' + column_name
END
FROM INFORMATION_SCHEMA.COLUMNS
WHERE (
TABLE_NAME = LTRIM(RTRIM(#TableName))
AND
column_name <> LTRIM(RTRIM(#RowIdentity))
);
SET #query = 'INSERT INTO ' + #TableName + ' (' + #columns + ') SELECT ' + #columns + ' FROM ' + #TableName + ' WHERE ' + #RowIdentity + ' = ' + CAST(#RowNumberToCopy AS VARCHAR);
--SELECT SCOPE_IDENTITY();
declare #query2 VARCHAR(100) = ' Select Top 1 * FROM '+ #TableName +' Order BY ' + #RowIdentity + ' desc' ;
EXEC (#query);
EXEC (#query2);
END
I have the following dynamic SQL code with 4 columns inserted as parameters as well as the table name.
set #SQL = 'select EVENTID, RATE,' + #LossColumn + ',' + #ExpColumn + ',' + #StdDevIndep + ',' + #StdDevCorr + ', '''','''', '''',''''
from Catastrophe.dbo.' + #CatTableName
insert into AnalyticsV2.dbo.ResultCSVCat
execute sp_executesql #SQL
I'm not sure if this is correct or whether it needs unicode encoding because of the parameters (N before the 'select statement)? It works fine if I specify all the columns and table name because then there are no parameters. I'm not quite sure how to change it around if it does need unicode because I haven't worked with that very much in Microsoft SQL.
Any help will be appreciated!
Yes, dynamic SQL strings should always be declared as NVARCHAR and you should always use the N prefix.
DECLARE #sql NVARCHAR(MAX);
SET #sql = N'select EVENTID, RATE,'
+ QUOTENAME(#LossColumn) -- QUOTENAME is safer
+ ',' + QUOTENAME(#ExpColumn)
+ ',' + QUOTENAME(#StdDevIndep)
+ ',' + QUOTENAME(#StdDevCorr)
+ ', '''','''', '''','''' -- not sure I understand this,
-- trying to insert empty strings?
FROM Catastrophe.dbo.' + QUOTENAME(#CatTableName) + ';';
INSERT INTO AnalyticsV2.dbo.ResultCSVCat -- no column list?
EXEC sp_executesql #sql;
EDIT: Database names have been modified for simplicity
I'm trying to get some dynamic sql in place to update static copies of some key production tables into another database (sql2008r2). The aim here is to allow consistent dissemination of data (from the 'static' database) for a certain period of time as our production databases are updated almost daily.
I am using a CURSOR to loop through a table that contains the objects that are to be copied into the 'static' database.
The prod tables don't change that frequently, but I'd like to make this somewhat "future proof" (if possible!) and extract the columns names from INFORMATION_SCHEMA.COLUMNS for each object (instead of using SELECT * FROM ...)
1) From what I have read in other posts, EXEC() seems limiting, so I believe that I'll need to use EXEC sp_executesql but I'm having a little trouble getting my head around it all.
2) As an added extra, if at all possible, i'd also like to exclude some columns for particular tables (structures vary slightly in the 'static' database)
here's what i have so far.
when executed, #colnames returns NULL and therefore #sql returns NULL...
could someone guide me to where i might find a solution?
any advice or help with this code is much appreciated.
CREATE PROCEDURE sp_UpdateRefTables
#debug bit = 0
AS
declare #proddbname varchar(50),
#schemaname varchar(50),
#objname varchar(150),
#wherecond varchar(150),
#colnames varchar(max),
#sql varchar(max),
#CRLF varchar(2)
set #wherecond = NULL;
set #CRLF = CHAR(10) + CHAR(13);
declare ObjectCursor cursor for
select databasename,schemaname,objectname
from Prod.dbo.ObjectsToUpdate
OPEN ObjectCursor ;
FETCH NEXT FROM ObjectCursor
INTO #proddbname,#schemaname,#objname ;
while ##FETCH_STATUS=0
begin
if #objname = 'TableXx'
set #wherecond = ' AND COLUMN_NAME != ''ExcludeCol1'''
if #objname = 'TableYy'
set #wherecond = ' AND COLUMN_NAME != ''ExcludeCol2'''
--extract column names for current object
select #colnames = coalesce(#colnames + ',', '') + QUOTENAME(column_name)
from Prod.INFORMATION_SCHEMA.COLUMNS
where TABLE_NAME = + QUOTENAME(#objname,'') + isnull(#wherecond,'')
if #debug=1 PRINT '#colnames= ' + isnull(#colnames,'null')
--replace all data for #objname
--#proddbname is used as schema name in Static database
SELECT #sql = 'TRUNCATE TABLE ' + #proddbname + '.' + #objname + '; ' + #CRLF
SELECT #sql = #sql + 'INSERT INTO ' + #proddbname + '.' + #objname + ' ' + #CRLF
SELECT #sql = #sql + 'SELECT ' + #colnames + ' FROM ' + #proddbname + '.' + #schemaname + '.' + #objname + '; '
if #debug=1 PRINT '#sql= ' + isnull(#sql,'null')
EXEC sp_executesql #sql
FETCH NEXT FROM ObjectCursor
INTO #proddbname,#schemaname,#objname ;
end
CLOSE ObjectCursor ;
DEALLOCATE ObjectCursor ;
P.S. i have read about sql injection, but as this is an internal admin task, i'm guessing i'm safe here!? any advice on this is also appreciated.
many thanks in advance.
You have a mix of SQL and dynamic SQL in your query against information_schema. Also QUOTENAME isn't necessary in the where clause and will actually prevent a match at all, since SQL Server stores column_name, not [column_name], in the metadata. Finally, I'm going to change it to sys.columns since this is the way we should be deriving metadata in SQL Server. Try:
SELECT #colnames += ',' + name
FROM Prod.sys.columns
WHERE OBJECT_NAME([object_id]) = #objname
AND name <> CASE WHEN #objname = 'TableXx' THEN 'ExcludeCol1' ELSE '' END
AND name <> CASE WHEN #objname = 'TableYy' THEN 'ExcludeCol2' ELSE '' END;
SET #colnames = STUFF(#colnames, 1, 1, '');