Dynamic SQL statement (unicode encoding needed?) - sql-server-2008

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;

Related

Drop All constraints in a Table

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;

Replace views with tables

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.

Creating 'util' - stored procedure section, as with .net helper classes

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

copy entire row (without knowing field names)

Using SQL Server 2008, I would like to duplicate one row of a table, without knowing the field names. My key issue: as the table grows and mutates over time, I would like this copy-script to keep working, without me having to write out 30+ ever-changing fields, ugh.
Also at issue, of course, is IDENTITY fields cannot be copied.
My code below does work, but I wonder if there's a more appropriate method than my thrown-together text string SQL statement?
So thank you in advance. Here's my (yes, working) code - I welcome suggestions on improving it.
Todd
alter procedure spEventCopy
#EventID int
as
begin
-- VARS...
declare #SQL varchar(8000)
-- LIST ALL FIELDS (*EXCLUDE* IDENTITY FIELDS).
-- USE [BRACKETS] FOR ANY SILLY FIELD-NAMES WITH SPACES, OR RESERVED WORDS...
select #SQL = coalesce(#SQL + ', ', '') + '[' + column_name + ']'
from INFORMATION_SCHEMA.COLUMNS
where TABLE_NAME = 'EventsTable'
and COLUMNPROPERTY(OBJECT_ID('EventsTable'), COLUMN_NAME, 'IsIdentity') = 0
-- FINISH SQL COPY STATEMENT...
set #SQL = 'insert into EventsTable '
+ ' select ' + #SQL
+ ' from EventsTable '
+ ' where EventID = ' + ltrim(str(#EventID))
-- COPY ROW...
exec(#SQL)
-- REMEMBER NEW ID...
set #EventID = ##IDENTITY
-- (do other stuff here)
-- DONE...
-- JUST FOR KICKS, RETURN THE SQL STATEMENT SO I CAN REVIEW IT IF I WISH...
select EventID = #EventID, SQL = #SQL
end
No, there isn't any magic way to say "SELECT all columns except <foo>" - the way you're doing it is how you'll have to do it (the hack in the other answer aside).
Here is how I would alter your code, with these changes (some are hyperlinked so you can read my opinion about why):
use sys.columns over INFORMATION_SCHEMA.COLUMNS
use nvarchar instead of varchar
use scope_identity instead of ##identity
use sp_executesql instead of exec
use stuff instead of coalesce
use SET NOCOUNT ON
add semi-colons
use the schema prefix
use QUOTENAME since it's safer than '[' + ... + ']'
ALTER PROCEDURE dbo.spEventCopy
#EventID INT
AS
BEGIN
SET NOCOUNT ON;
DECLARE #sql NVARCHAR(MAX) = N'';
SELECT #sql += ',' + QUOTENAME(name)
FROM sys.columns
WHERE [object_id] = OBJECT_ID('dbo.EventsTable')
AND is_identity = 0;
SET #sql = STUFF(#sql, 1, 1, '');
SET #sql = N'INSERT dbo.EventsTable(' + #sql + ')
SELECT ' + #sql + ' FROM dbo.EventsTable
WHERE EventID = ' + CONVERT(VARCHAR(12), #EventID) + ';';
EXEC sp_executesql #sql;
SELECT #EventID = SCOPE_IDENTITY();
-- do stuff with the new row here
SELECT EventID = #EventID, SQL = #SQL;
END
If you know the what your identity column is called (and it won't be the column changing), you could do this:
SELECT * INTO #dummy FROM EventsTable where EventID = #EventID;
ALTER TABLE #dummy
DROP COLUMN MyIdentityColumn
INSERT EventsTable SELECT * FROM #dummy
DROP TABLE #dummy
Since a table can only every have one identity column, specifying that in the query shouldn't limit you too much.
As Aaron Bertrand points out, there are risks associated with this approach. Please read the discussion in the comments below.

sql update with dynamic column names

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, '');