SQL Server Execute update result sets from dynamic query writer - sql-server-2008

This link is where I found part of an answer to my problem.
SQL replace all NULLs
Simon posted
"Be a boss. Write something like:
select 'update ' + table_name + ' set [' + column_name + '] = '''' where [' + column_name + '] is null'
from tempdb.information_schema.columns
where table_name = 'YourTableName'
It'll spit out a big ol' query for you.
You're welcome"
But I would like to know if there a way to use the results set in a parameter and execute all of the update statements.
I tried something like this
DECLARE #sql2 AS NVARCHAR(MAX) = N'
SELECT ''UPDATE '' + table_name + '' SET ['' + column_name + ''] = '''''''' WHERE ['' + column_name + ''] IS NULL''
FROM tempdb.information_schema.columns
WHERE table_name = ''##tempF'''
EXEC sp_executesql #stmt = #sql2;
DECLARE #sql3 AS NVARCHAR(MAX);
SET #sql3 = (SELECT #sql2);
EXEC sp_executesql #stmt = #sql3;
but it two result sets like listed below:
UPDATE ##tempF SET [claimid] = '' WHERE [claimid] IS NULL
UPDATE ##tempF SET [hdr_status] = '' WHERE [hdr_status] IS NULL
UPDATE ##tempF SET [memid] = '' WHERE [memid] IS NULL
Many thanks to you all.
Cheers!
Tim

Like this
--initialize variables
DECLARE #UpdateColumns varchar(max) = ''
DECLARE #IsNullColumns varchar(max) = ''
SELECT
#UpdateColumns = #UpdateColumns + ',[' + COLUMN_NAME + '] = ISNULL([' + COLUMN_NAME + '],'''')',
#IsNullColumns = #IsNullColumns + ' OR [' + COLUMN_NAME + '] IS NULL'
FROM tempdb.information_schema.columns
WHERE table_name = '##tempF'
This should fill in the two variables with the following values:
#UpdateColumns = ',[claimid] = ISNULL([claimid],''''),[hdr_status] = ISNULL([hdr_status],''''),[memid] = ISNULL([memid],'''')'
#IsNullColumns = ' OR [claimid] IS NULL OR [hdr_status] IS NULL OR [memid] IS NULL'
Then you need to assemble it all (remember to remove the first characters of each of the variables (the STUFF function is great for that):
DECLARE #qry varchar(max) = 'UPDATE ##tempF SET '
+ STUFF(#UpdateColumns,1,1,'') + ' WHERE '
+ STUFF(#IsNullColumns,1,4,'') --the 4 in here is to get rid of ' OR ' (4 chars)
EXEC(#qry)

Related

Getting bad format on float values when exporting query to html table

I need some help on this. I use this code to export SQL queries to html tables (got code from Convert a SQL query result table to an HTML table for email)
-- Description: Turns a query into a formatted HTML table. Useful for emails.
-- Any ORDER BY clause needs to be passed in the separate ORDER BY parameter.
-- =============================================
CREATE PROC [dbo].[spQueryToHtmlTable]
(
#query nvarchar(MAX), --A query to turn into HTML format. It should not include an ORDER BY clause.
#orderBy nvarchar(MAX) = NULL, --An optional ORDER BY clause. It should contain the words 'ORDER BY'.
#html nvarchar(MAX) = NULL OUTPUT --The HTML output of the procedure.
)
AS
BEGIN
SET NOCOUNT ON;
IF #orderBy IS NULL BEGIN
SET #orderBy = ''
END
SET #orderBy = REPLACE(#orderBy, '''', '''''');
DECLARE #realQuery nvarchar(MAX) = '
DECLARE #headerRow nvarchar(MAX);
DECLARE #cols nvarchar(MAX);
SELECT * INTO #dynSql FROM (' + #query + ') sub;
SELECT #cols = COALESCE(#cols + '', '''''''', '', '''') + ''['' + name + ''] AS ''''td''''''
FROM tempdb.sys.columns
WHERE object_id = object_id(''tempdb..#dynSql'')
ORDER BY column_id;
SET #cols = ''SET #html = CAST(( SELECT '' + #cols + '' FROM #dynSql ' + #orderBy + ' FOR XML PATH(''''tr''''), ELEMENTS XSINIL) AS nvarchar(max))''
EXEC sys.sp_executesql #cols, N''#html nvarchar(MAX) OUTPUT'', #html=#html OUTPUT
SELECT #headerRow = COALESCE(#headerRow + '''', '''') + ''<th>'' + name + ''</th>''
FROM tempdb.sys.columns
WHERE object_id = object_id(''tempdb..#dynSql'')
ORDER BY column_id;
SET #headerRow = ''<tr>'' + #headerRow + ''</tr>'';
SET #html = ''<table border="1">'' + #headerRow + #html + ''</table>'';
';
EXEC sys.sp_executesql #realQuery, N'#html nvarchar(MAX) OUTPUT', #html=#html OUTPUT
END
GO
The code works perfect, but has one problem formatting float values.
For example:
SELECT Name, Weight FROM Products
The query returns something like this when executed from Management Studio:
Name1 | 1073,822
Name2 | 179,554
When I use the stored procedure to export this to html table, then I get the results like this:
Name1 | 1.073822000000000e+003
Name2 | 1.795540000000000e+002
Don't know exactly how to change the stored procedure to adapt it in order to avoid this wrong formatting on float values.
Any help on this would be appreciated.
you are using cast to show the values in the html-table:
SET #cols = ''SET #html = CAST(( SELECT '' + #cols + '' FROM #dynSql ' + #orderBy + ' FOR XML PATH(''''tr''''), ELEMENTS XSINIL) AS nvarchar(max))''
MS-Help tells you more, but basically this is the automatic behavior of cast with a float value.
You should try to already convert your float-fields to the desired format in the query you pass, using the STR() function, see str function description
Many thanks for your reply
EDIT: If I take out the casting to varchar(max), the result seems to be the same.
But I've just tried to do something like this:
SELECT Name, CAST(Weight AS varchar(max)) FROM Products
And I still can see the right formatted values:
Name1 | 1073.82
Name2 | 179.554
Otherwise, I use this SP to be called from many sites and processes. Is not possible to check every case and try to cast the float fields.
I need a solution to be implemented at the SP level.
I've tried to use the STR() function, so changed the line:
SET #cols = ''SET #html = CAST(( SELECT '' + #cols + '' FROM #dynSql ' + #orderBy + ' FOR XML PATH(''''tr''''), ELEMENTS XSINIL) AS nvarchar(max))''
To
SET #cols = ''SET #html = STR(( SELECT '' + #cols + '' FROM #dynSql ' + #orderBy + ' FOR XML PATH(''''tr''''), ELEMENTS XSINIL))''
But then I'm getting the error: Error converting data type nvarchar to float.
Not sure if this is what you mentioned.
Would be a way to check the field type in the SP, and use a different casting for the float cases?
How could I do it?
Regards,

How to pass column name along with other parameters

I have below SPROC in which i am passing column name(value) along with other parameters(Place,Scenario).
ALTER PROCEDURE [dbo].[up_GetValue]
#Value varchar(20), #Place varchar(10),#Scenario varchar(20), #Number varchar(10)
AS BEGIN
SET NOCOUNT ON;
DECLARE #SQLquery AS NVARCHAR(MAX)
set #SQLquery = 'SELECT ' + #Value + ' from PDetail where Place = ' + #Place + ' and Scenario = ' + #Scenario + ' and Number = ' + #Number
exec sp_executesql #SQLquery
END
GO
when executing : exec [dbo].[up_GetValue] 'Service', 'HOME', 'Agent', '123697'
i am getting the below error msg
Invalid column name 'HOME'.
Invalid column name 'Agent'.
Do i need to add any thing in the sproc??
First: You tagged your question as mysql but I think your code is MSSQL.
Anyway, your problem is that you need to add quotes around each string valued parameter.
Like this:
alter PROCEDURE [dbo].[up_GetValue]
#Value varchar(20), #Place varchar(10),#Scenario varchar(20), #Number varchar(10)
AS BEGIN
SET NOCOUNT ON;
DECLARE #SQLquery AS NVARCHAR(MAX)
set #SQLquery = 'SELECT ' + QUOTENAME(#Value) + ' from PDetail where Place = ''' + #Place + ''' and Scenario = ''' + #Scenario + ''' and Number = ''' + #Number +''''
print #SQLquery
exec sp_executesql #SQLquery
END
GO
Update:
Use QUOTENAME to make sure it works.
QUOTENAME:
Returns a Unicode string with the delimiters added to make the input string a valid SQL Server delimited identifier.
You need to quote column names with ` (backtick) and string values with ".
set #SQLquery = 'SELECT `' + #Value + '` from PDetail where Place = "' + #Place + '" and Scenario = "' + #Scenario + '" and Number = ' + #Number
Try using a prepared statement instead of concatinating the string.
Example:
PREPARE stmt1 FROM 'SELECT ? from PDetail where Place = ? and Scenario = ? and Number = ?;
EXECUTE stmt1 USING #Value, #Place, #Scenario, #Number;

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

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

Dynamic SQL concatenation

Having an issue concatenating the following statement.
Basically I want the length column to add inches after but it will not run. I am going to create a function out of this in the future but unable to get past this step. What gives?
declare #column varchar(255)
declare #sql varchar(5000)
declare #additional varchar(500)
set #column = 'length'
set #additional = 'inches'
select #sql = 'select distinct ps.p_c_id, '
select #sql = #sql + #column + ' '+#additional+ ' ' + ' as value'
select #sql = #sql
select #sql = #sql + ' from dbo.Product ps
inner join dbo.ProductAttributes psa on psa.p_s_id = ps.p_s_id
where ps.p_c_id is not null and ' + #column + ' is not null'
exec (#sql)
You are concatenating, what i'm assuming is an int or float value to a string ' inches'...have to cast the "length" value as a varchar...
just select your #sql next time to see the resulting syntax and it should jump out at you. here is changes that should work
BTW...look at implementing EXEC sp_executesql ...makes dynamic sql less suseptable to injection by using parameters, etc... look up in Books OnLine
Sorry...eating Crow...sp_executesql does not protect from injection just improves performance in general...see article MSDN SQL Injection
declare #column varchar(255)
declare #sql varchar(5000)
declare #additional varchar(500)
set #column = 'psa.length'
set #additional = 'inches'
select #sql = 'select distinct ps.p_c_id, '
select #sql = #sql + 'CAST(' + #column + ' AS varchar(10)) + ' + ''' '+#additional+ ''' ' + ' as value'
select #sql = #sql
select #sql = #sql + ' from dbo.Product ps
inner join dbo.ProductAttributes psa on psa.p_s_id = ps.p_s_id
where ps.p_c_id is not null and ' + #column + ' is not null'
--select #sql AS [ExecutableSQL]
exec(#sql)
Your output is;
select distinct ps.p_c_id, length inches as value from dbo.Product ps
inner join dbo.ProductAttributes psa on psa.p_s_id = ps.p_s_id
where ps.p_c_id is not null and length is not null
So it looks like a missing , between length inches assuming you want both;
select #sql = #sql + #column + ','+ #additional+ ' ' + ' as value'