Azure Synapse Serverless Pool Operation DROP FUNCTION is not allowed for a replicated database - azure-synapse

When I attempt to execute the following DROP FUNCTION on Azure Synapse Serverless Pool I get the following error
Operation DROP FUNCTION is not allowed for a replicated database
The code that generates the error is:
DECLARE #FunctionScript nvarchar(max) = 'IF object_id(N''dbo.GetOptionSetLabel'') is not null DROP FUNCTION [dbo].[GetOptionSetLabel]'
Any thoughts on how to overcome the error?
I think I got marked down on this question because I didn't include the full code, so here it is...
USE [LakeDatabaseEnriched] --Specify the name of the database in which GetOptionSetLabel function will be created
DECLARE
#OptionSetLabelFunctionDatabase sysname, --It will be automatically be set with the name of the database that has GetOptionSetLabel function
#OptionSetLabelFunctionDatabaseSchema sysname, --Specify the name of the database in which GetOptionSetLabel function will be created
#OptionSetLabelFunctionDatabaseCollation varchar(256), --It will be automatically be set with the collation of the database that has GetOptionSetLabel function
#SynapseLinkDatabase sysname, --Specify the name of the database corresponding to your Synapse Link for Dataverse
#CurrentDatabaseCollation varchar(256)
SET #OptionSetLabelFunctionDatabase = QUOTENAME(DB_NAME())
SET #OptionSetLabelFunctionDatabaseCollation = CONVERT(varchar(256), DATABASEPROPERTYEX(DB_NAME(), 'collation'));
SET #OptionSetLabelFunctionDatabaseSchema = 'dbo'
SET #SynapseLinkDatabase = 'dataverse_xxxx_org5a2bcccf'
DECLARE #FunctionScript nvarchar(max) = 'IF object_id(N''dbo.GetOptionSetLabel'') is not null DROP FUNCTION [dbo].[GetOptionSetLabel]'
EXEC sp_executesql #FunctionScript
SET #FunctionScript = '
SET QUOTED_IDENTIFIER ON
SET ANSI_NULLS ON
'
EXEC sp_executesql #FunctionScript
SET #FunctionScript = '
CREATE FUNCTION [dbo].[GetOptionSetLabel]
(
#EntityName nvarchar(max), #ColumnName nvarchar(max), #Value nvarchar(max), #LanguageCode int
)
RETURNS
nvarchar(max)
AS
BEGIN
declare #Values table (
value nvarchar(max)
);
insert into #Values
SELECT Value
FROM STRING_SPLIT(#Value, '';'')
declare #Labels table(
AttributeValue int,
LabelText nvarchar(max)
);
insert into #Labels
select distinct *
from (
select gosm.[Option], gosm.LocalizedLabel Value
from '+#SynapseLinkDatabase+'.'+#OptionSetLabelFunctionDatabaseSchema+'.GlobalOptionsetMetadata gosm
inner join #Values v on cast(v.[Value] as int) = gosm.[Option]
where gosm.OptionSetName = #ColumnName
and gosm.LocalizedLabelLanguageCode = #LanguageCode
and TRY_CAST(v.[Value] as int) is not null
union
select osm.[Option], osm.LocalizedLabel Value
from '+#SynapseLinkDatabase+'.'+#OptionSetLabelFunctionDatabaseSchema+'.OptionsetMetadata osm
inner join #Values v on cast(v.[Value] as int) = osm.[Option]
where osm.OptionSetName = #ColumnName
and osm.EntityName = #EntityName
and osm.LocalizedLabelLanguageCode = #LanguageCode
and TRY_CAST(v.[Value] as int) is not null
) t
order by [Option] asc
declare #optionsetLabel nvarchar(max)
set #optionsetLabel = isnull(
(
select string_agg(LabelText, '', '')
from #Labels
),
#Value)
RETURN #optionsetLabel
END'
PRINT 'Beginning function creation'
--PRINT #FunctionScript
EXEC sp_executesql #FunctionScript
PRINT 'Completed function creation'
USE [dataverse_xxxx_org5a2bcccf] --Specify the name of the database corresponding to your Synapse Link for Dataverse to be used from here onward
--=================================================================================================
--PROVIDE INPUT PARAMETERS:
--=================================================================================================
DECLARE
#EnrichedViewDatabase sysname, --Specify the name of the database in which views with enriched entities will be created
#EnrichedViewSchema sysname, --Specify the name of the database schema in which views with enriched entities will be created
#EnrichedColumnSuffix varchar(50), --Specify the suffix for columns enriched with human-readable descriptions. For example, the suffix of "label" will change a statecode column in the base table to a statelabel column in the enriched view.
#LanguageCode varchar(10), --Specify the language code for localized labels. For example, English - United States is 1033 (https://learn.microsoft.com/en-us/openspecs/office_standards/ms-oe376/6c085406-a698-4e12-9d4d-c3b0ee3dbc4a)
#BaseTableSuffix varchar(50), --If applicable, specify the suffix in the names of the base tables or views (e.g., '_partitiond'). The default is an empty string.
#PreviewOnly bit --Indicate whether to preview the SQL Script (without creating the views) = 1 ; Create views = 0;
SET #EnrichedViewDatabase = 'LakeDatabaseEnriched'
SET #EnrichedViewSchema = 'dbo'
SET #EnrichedColumnSuffix = 'label'
SET #LanguageCode = 1033
SET #BaseTableSuffix = ''
SET #PreviewOnly = 0
SET #CurrentDatabaseCollation = CONVERT(varchar(256), DATABASEPROPERTYEX(DB_NAME(), 'collation'));
-- 'If still collation error occurs in script, search for Latin1_General_100_CI_AS_SC_UTF8 and replace it with getting collation from (SELECT #CurrentDatabaseCollation) query
--=================================================================================================
-- Do not edit the script below this point
--=================================================================================================
--Get column metadata from the Lake Database managed by Synapse Link for Dataverse
--The column metadata will be stored as a JSON document in a scalar variable
--This is needed as a workaround for the limitation of not allowing system objects to be used in distributed queries
DECLARE #ColumnMetadata nvarchar(MAX), #ColumnMetadataSQL nvarchar(MAX)
--Define the SQL statement to retrieve column metadata from the Lake Database managed by Synapse Link for Dataverse
--Results will be stored as a JSON document in a variable
SET #ColumnMetadataSQL = 'SET #ColumnMetadataOUT = (
SELECT TABLE_SCHEMA,
TABLE_NAME,
COLUMN_NAME,
ORDINAL_POSITION,
DATA_TYPE
FROM INFORMATION_SCHEMA.COLUMNS
WHERE TABLE_SCHEMA = ''dbo''
AND TABLE_NAME NOT IN (''OptionsetMetadata'', ''GlobalOptionsetMetadata'',''StateMetadata'',''StatusMetadata'', ''TargetMetadata'')
AND TABLE_NAME LIKE ''%' + (#BaseTableSuffix COLLATE Latin1_General_100_CI_AS_SC_UTF8) + '''
FOR JSON AUTO)'
DECLARE #ParmDefinition NVARCHAR(MAX);
SET #ParmDefinition = N'#ColumnMetadataOUT NVARCHAR(MAX) OUTPUT';
EXECUTE sp_executesql #ColumnMetadataSQL, #ParmDefinition, #ColumnMetadataOUT=#ColumnMetadata OUTPUT;
--Declare a variable to store a SQL statement for creating enriched views
DECLARE #SQL nvarchar(MAX) = ''
; WITH CM AS (
--Parse column metadata variable and construct a table based on its content
SELECT JSON_VALUE(CM.value, '$.TABLE_SCHEMA') AS TableSchema,
JSON_VALUE(CM.value, '$.TABLE_NAME') AS TableName,
LEFT(JSON_VALUE(CM.value, '$.TABLE_NAME'), LEN(JSON_VALUE(CM.value, '$.TABLE_NAME'))-LEN(#BaseTableSuffix)) AS EntityName,
JSON_VALUE(CM.value, '$.COLUMN_NAME') AS ColumnName,
CAST(JSON_VALUE(CM.value, '$.ORDINAL_POSITION') AS INT) AS OrdinalPosition,
JSON_VALUE(CM.value, '$.DATA_TYPE') AS DataType
FROM OPENJSON (#ColumnMetadata) AS CM
)
, OSM AS (
--Get Option Set Metadata
SELECT DISTINCT
EntityName,
OptionSetName,
QUOTENAME(EntityName + '_' + OptionSetName) AS Alias
FROM dbo.[OptionsetMetadata]
WHERE LocalizedLabelLanguageCode = #LanguageCode
)
, GOSM AS (
--Get Global Option Set Metadata
SELECT DISTINCT
OptionSetName,
QUOTENAME('Global_' + OptionSetName) AS Alias
FROM dbo.[GlobalOptionsetMetadata]
WHERE LocalizedLabelLanguageCode = #LanguageCode
)
, GOSMM AS (
--Get Global Option Set Metadata
SELECT DISTINCT
OptionSetName,
QUOTENAME('Global_Multiselect_' + OptionSetName) AS Alias
FROM dbo.[GlobalOptionsetMetadata]
WHERE LocalizedLabelLanguageCode = #LanguageCode
)
, StateM AS (
--Get State Metadata
SELECT DISTINCT
EntityName,
QUOTENAME(EntityName + '_State') AS Alias
FROM dbo.[StateMetadata]
WHERE LocalizedLabelLanguageCode = #LanguageCode
)
, StatusM AS (
--Get Status Metadata
SELECT DISTINCT
EntityName,
QUOTENAME(EntityName + '_Status') AS Alias
FROM dbo.[StatusMetadata]
WHERE LocalizedLabelLanguageCode = #LanguageCode
)
, SQLStatement AS (
--Enumerate all lines in the source table and replace codes with labels where applicable
SELECT CM.EntityName,
--Before the first column of each table, construct a CREATE OR ALTER VIEW statement
CASE WHEN CM.OrdinalPosition = 1
THEN 'CREATE OR ALTER VIEW ' + QUOTENAME(#EnrichedViewSchema) + '.' + CM.EntityName + '
AS
SELECT '
ELSE ' ,'
END
--For each column, check if it needs to be replaced with a suitable localized label
+ CASE
WHEN OSM.OptionSetName IS NOT NULL THEN OSM.Alias + '.[LocalizedLabel] AS ' + REPLACE(QUOTENAME(CM.ColumnName), 'code]', (#EnrichedColumnSuffix COLLATE Latin1_General_100_CI_AS_SC_UTF8) + ']')
WHEN GOSM.OptionSetName IS NOT NULL THEN GOSM.Alias + '.[LocalizedLabel] AS ' + REPLACE(QUOTENAME(CM.ColumnName), 'code]', (#EnrichedColumnSuffix COLLATE Latin1_General_100_CI_AS_SC_UTF8) + ']')
--Uncomment below if you want to show _value columns as well
--WHEN GOSMM.OptionSetName IS NOT NULL THEN '[Base].' + QUOTENAME(CM.ColumnName) + ' as ' + CM.ColumnName + '_value, ' + ((#OptionSetLabelFunctionDatabase COLLATE Latin1_General_100_CI_AS_SC_UTF8) + '.' + (#OptionSetLabelFunctionDatabaseSchema COLLATE Latin1_General_100_CI_AS_SC_UTF8) + '.GetOptionSetLabel(''' + CM.TableName + ''', ''' + CM.ColumnName + ''', '+ '[Base].' + QUOTENAME(CM.ColumnName) + ', ' + #LanguageCode + ') as ' + CM.ColumnName)
--Comment below if you want to show _value columns as well
WHEN GOSMM.OptionSetName IS NOT NULL THEN ((#OptionSetLabelFunctionDatabase COLLATE Latin1_General_100_CI_AS_SC_UTF8) + '.' + (#OptionSetLabelFunctionDatabaseSchema COLLATE Latin1_General_100_CI_AS_SC_UTF8) + '.GetOptionSetLabel(''' + CM.TableName + ''', ''' + CM.ColumnName + ''', '+ '[Base].' + QUOTENAME(CM.ColumnName) + ', ' + #LanguageCode + ') as ' + CM.ColumnName)
WHEN StateM.EntityName IS NOT NULL THEN StateM.Alias + '.[LocalizedLabel] AS ' + REPLACE(QUOTENAME(CM.ColumnName), 'code]', (#EnrichedColumnSuffix COLLATE Latin1_General_100_CI_AS_SC_UTF8) + ']')
WHEN StatusM.EntityName IS NOT NULL THEN StatusM.Alias + '.[LocalizedLabel] AS ' + REPLACE(QUOTENAME(CM.ColumnName), 'code]', (#EnrichedColumnSuffix COLLATE Latin1_General_100_CI_AS_SC_UTF8) + ']')
ELSE '[Base].' + QUOTENAME(CM.ColumnName)
END AS [SQLLine],
CM.OrdinalPosition
FROM CM
LEFT JOIN OSM
ON CM.EntityName = OSM.EntityName
AND CM.ColumnName = OSM.OptionSetName
AND CM.DataType LIKE '%int' --Only include columns with integer data type
LEFT JOIN GOSM
ON CM.ColumnName = GOSM.OptionSetName
AND CM.DataType LIKE '%int' --Only include columns with integer data type
LEFT JOIN GOSMM
ON CM.ColumnName = GOSMM.OptionSetName
AND CM.DataType LIKE '%varchar%' --Only include columns with varchar data type that can potentially have multiselect values
LEFT JOIN StateM
ON CM.EntityName = StateM.EntityName
AND CM.ColumnName = 'statecode'
AND CM.DataType LIKE '%int' --Only include columns with integer data type
LEFT JOIN StatusM
ON CM.EntityName = StatusM.EntityName
AND CM.ColumnName = 'statuscode'
AND CM.DataType LIKE '%int' --Only include columns with integer data type
UNION ALL
--Construct the first line of the FROM clause, referencing external tables created by Synapse Link for Dataverse
SELECT DISTINCT
CM.EntityName,
'FROM ' + QUOTENAME(DB_NAME()) + '.' + QUOTENAME(CM.TableSchema) + '.' + QUOTENAME(CM.TableName) + ' AS Base' AS SQLLine,
10000 AS OrdinalPosition
FROM CM
UNION ALL
--Construct LEFT JOIN statements for each relevant OptionSetMetadata field
SELECT DISTINCT OSM.EntityName AS EntityName,
' LEFT JOIN ' + QUOTENAME(DB_NAME()) + '.[dbo].[OptionSetMetadata] AS ' + OSM.Alias + '
ON ' + OSM.Alias + '.EntityName = ''' + OSM.EntityName + '''
AND ' + OSM.Alias + '.OptionSetName = ''' + OSM.OptionSetName + '''
AND [Base].' + QUOTENAME(OSM.OptionSetName) + ' = ' + OSM.Alias + '.[Option]
AND ' + OSM.Alias + '.LocalizedLabelLanguageCode = ' + #LanguageCode + '' AS SQLLine,
20000 AS OrdinalPosition
FROM OSM
JOIN CM
ON CM.EntityName = OSM.EntityName
AND CM.ColumnName = OSM.OptionSetName
WHERE CM.DataType LIKE '%int' --Only capture columns with Integer Data Types
UNION ALL
--Construct LEFT JOIN statements for each relevant GlobalOptionSetMetadata field
SELECT DISTINCT CM.EntityName AS EntityName,
' LEFT JOIN ' + QUOTENAME(DB_NAME()) + '.[dbo].[GlobalOptionSetMetadata] AS ' + Alias + '
ON ' + Alias + '.OptionSetName = ''' + OptionSetName + '''
AND [Base].' + QUOTENAME(OptionSetName) + ' = ' + Alias + '.[Option]
AND ' + Alias + '.LocalizedLabelLanguageCode = ' + #LanguageCode + '' AS SQLLine,
30000 AS OrdinalPosition
FROM GOSM
JOIN CM
ON CM.ColumnName = GOSM.OptionSetName
WHERE CM.DataType LIKE '%int' --Only capture columns with Integer Data Types
UNION ALL
--Construct LEFT JOIN statements for each relevant State Metadata field
SELECT DISTINCT CM.EntityName AS EntityName,
' LEFT JOIN ' + QUOTENAME(DB_NAME()) + '.[dbo].[StateMetadata] AS ' + StateM.Alias + '
ON ' + StateM.Alias + '.EntityName = ''' + StateM.EntityName + '''
AND [Base].statecode' + ' = ' + StateM.Alias + '.[State]
AND ' + StateM.Alias + '.LocalizedLabelLanguageCode = ' + #LanguageCode + '' AS SQLLine,
40000 AS OrdinalPosition
FROM StateM
JOIN CM
ON CM.EntityName = StateM.EntityName
AND CM.ColumnName = 'statecode'
WHERE CM.DataType LIKE '%int' --Only capture columns with Integer Data Types
UNION ALL
--Construct LEFT JOIN statements for each relevant Status Metadata field
SELECT DISTINCT CM.EntityName AS EntityName,
' LEFT JOIN ' + QUOTENAME(DB_NAME()) + '.[dbo].[StatusMetadata] AS ' + StatusM.Alias + '
ON ' + StatusM.Alias + '.EntityName = ''' + StatusM.EntityName + '''
AND [Base].statuscode' + ' = ' + StatusM.Alias + '.[Status]
AND ' + StatusM.Alias + '.LocalizedLabelLanguageCode = ' + #LanguageCode + '' AS SQLLine,
40000 AS OrdinalPosition
FROM StatusM
JOIN CM
ON CM.EntityName = StatusM.EntityName
AND CM.ColumnName = 'statuscode'
WHERE CM.DataType LIKE '%int' --Only capture columns with Integer Data Types
UNION ALL
--Add statement terminator
SELECT DISTINCT
EntityName,
'; ' + CHAR(10) AS SQLLine,
100000 AS OrdinalPosition
FROM CM
)
--Construct individual statements to create views (1 view per row)
--Since CREATE VIEW statement must be the first statement in a batch, assign each view definition to a variable
--and use the EXEC(#variable) command to create view as part of its own, separate batch.
, ViewDefinitions AS (
SELECT 'DECLARE #' + EntityName + ' NVARCHAR(MAX) = ''
' + REPLACE(STRING_AGG(CAST(SQLLine as varchar(MAX)), CHAR(10)) WITHIN GROUP (ORDER BY EntityName, OrdinalPosition, SQLLine), '''', '''''') + ''' ' + CHAR(10) + 'EXEC [' + (#EnrichedViewDatabase COLLATE Latin1_General_100_CI_AS_SC_UTF8) + '].dbo.sp_executesql #' + EntityName + CHAR(10) AS ViewDefinition
FROM SQLStatement
GROUP BY EntityName
)
--Construct a comprehensive SQL statement to create all views
SELECT #SQL = STRING_AGG(ViewDefinition, ';' + CHAR(10) + CHAR(10))
FROM ViewDefinitions
--Return a preview of the SQL Script to be generated or go ahead and create the views.
IF #PreviewOnly = 1
BEGIN
--Return the final SQL statement
SELECT '--================================================================================================='+ CHAR(10) +' ' + CHAR(10) AS [--SQL Statement]
UNION ALL
SELECT '-- A preview of the script to generate enriched views is provided below.' AS [--SQL Statement]
UNION ALL
SELECT '-- No database objects have been created.' AS [--SQL Statement]
UNION ALL
SELECT '-- Re-run this script with the #PreviewOnly parameter set to 0 to actually create the views.' AS [--SQL Statement]
UNION ALL
SELECT '--================================================================================================='+ CHAR(10) +' ' + CHAR(10) AS [--SQL Statement]
UNION ALL
SELECT VALUE AS [--SQL Statement] FROM STRING_SPLIT((#SQL COLLATE Latin1_General_100_CI_AS_SC_UTF8), CHAR(10))
END
ELSE
BEGIN
--Execute the SQL statement
PRINT 'Beginning views creation'
--PRINT #SQL
EXEC sp_executesql #SQL
PRINT 'Completed views creation'
END

Operation DROP FUNCTION is not allowed for a replicated database
In Synapse Serverless SQL there are some operations are not allowed
You may see above problems while attempting to create SQL objects, users, or alter permissions in a database. When you attempt to create objects in a database that is shared with the Spark pool, this error is given. Read-only databases are replicated from Apache Spark pools. A replicated database we cannot create new objects added using T-SQL.
To workaround this, create a custom SQL database (serverless) that will contain the custom schemas, views, and functions that will reference Lake database external tables using the 3-part names.
Refer - Operation isn't allowed for a replicated database

Related

Dynamic update issue

I have a table, let's call it "table_X" in that table I have multiple columns (46) and in the future there is a possibility that we will expand it to have more columns, since the source of a table is an old ERP system, we need to transform the dataset in some cases, one of the transformation is that when we replace the '' values with NULLs and here is where I have problem, I wrote a dynamic update, because the previously mentioned reason (in the future we will have more columns), but I got error message and right now I am stuck.
DECLARE #SQL_columnnull NVARCHAR(max)
DECLARE #db2 NVARCHAR(max)
DECLARE #table2 NVARCHAR(max)
SET #db2 = 'db'
SET #table2 = 'table_X'
SELECT #SQL_columnnull_part_1 = STRING_AGG(CAST( N' UPDATE '+#db2+'.[dbo].'+#table2+' WITH (TABLOCK) SET ' AS NVARCHAR(MAX))
+QUOTENAME(COLUMN_NAME,'['']') + N' = NULL WHERE '
+QUOTENAME(COLUMN_NAME,'['']') ,+ N' = '''';')
FROM INFORMATION_SCHEMA.COLUMNS
WHERE TABLE_NAME = #table2
AND INFORMATION_SCHEMA.COLUMNS.ORDINAL_POSITION <= 3
for the two first column, the code is able to populate the command parts properly, but when it reaches the last column then the "='';" won't be populated
UPDATE db.[dbo].table_X SET [Column_1] = NULL WHERE [Column_1] = '';
UPDATE db.[dbo].table_X SET [Column_2] = NULL WHERE [Column_2] = '';
UPDATE db.[dbo].table_X SET [Column_3] = NULL WHERE [Column_3]
You are messing a bit about with your STRING_AGG
The syntax is
STRING_AGG ( expression, separator )
Your separator is
+ N' = '''';'
Since the separator is not applied after the last entry, you get the result you see!
I would also be wary of the cast, you are casting the start of the expression as nvarchar(max), however you are the concatenationg non varchar strings.
Finally - why are you doing separate updates for each column? this is very poor performance!
First, a fixed query that does what you want would be:
SELECT
#SQL_columnnull =
STRING_AGG(
CAST(
' UPDATE ' + #db2 + '.[dbo].' + #table2 + ' WITH (TABLOCK) SET '
+ QUOTENAME(COLUMN_NAME, '['']') + N' = NULL WHERE '
+ QUOTENAME(COLUMN_NAME, '['']') +N' = '''''
AS NVARCHAR(MAX))
,';')
FROM INFORMATION_SCHEMA.COLUMNS
WHERE TABLE_NAME = #table2
AND INFORMATION_SCHEMA.COLUMNS.ORDINAL_POSITION <= 3
I have included the full string within the cast, and the separator is now simply ";"
For performance I would however do this in stead:
SELECT
#SQL_columnnull =
N' UPDATE ' + #db2 + '.[dbo].' + #table2 + ' WITH (TABLOCK) SET ' +
STRING_AGG(
CAST(
QUOTENAME(COLUMN_NAME, '['']') + N'='+N'IIF('+ QUOTENAME(COLUMN_NAME, '['']') + N'= '''',NULL,'+ QUOTENAME(COLUMN_NAME, '['']')+') '
AS NVARCHAR(MAX))
,',
')
+'
WHERE '+
STRING_AGG(
CAST(
QUOTENAME(COLUMN_NAME, '['']') + N'= '''' '
AS NVARCHAR(MAX))
,' OR ')
FROM INFORMATION_SCHEMA.COLUMNS
WHERE TABLE_NAME = #table2
AND INFORMATION_SCHEMA.COLUMNS.ORDINAL_POSITION <= 3
This does just one pass over your table, and updates the columns that have the wrong data.
Finally I would check that all the columns are of a varchar or nvarchar data type, to exclude columns of othe datattypes which might give errors
This gives
UPDATE db.[dbo].table_X
SET [Column_1] = IIF([Column_1] = '', NULL, [Column_1])
,[Column_2] = IIF([Column_2] = '', NULL, [Column_2])
,[Column_3] = IIF([Column_3] = '', NULL, [Column_3])
WHERE [Column_1] = ''
OR [Column_2] = ''
OR [Column_3] = ''

Is there a way to Replace(*) in sql?

So simply I'm doing something similar to:
select
[BadData], [WorseDate], [IQuitData]
into
#BadDataTempTable
from
SomeoneElsesMess
what I want to do now is something similar to:
Select
Replace(#BadDataTempTable.*, ',', ' ')
from
#BadDataTempTable -- Replace all commas in every column `with a space--`
Is this possible? If so please show me the easiest (non-function) way to do so.
Thanks. SQL Server 2012 I think. I'm using SSMS 17
No, the columns have to be specified. You could use dynamic SQL to build your update / query. Then just copy the command you want from the results.
Maybe this will help get you started:
BEGIN
-- Set the replace value
DECLARE #ls_replaceValue NVARCHAR(MAX) = ',';
-- Set the with value
DECLARE #ls_withValue NVARCHAR(MAX) = ' ';
-- Set the table name we want to query
DECLARE #ls_table NVARCHAR(MAX) = 'some_table';
-- Get all of the columns and provide the replace parameters
DECLARE #ls_columns NVARCHAR(MAX) = '';
SELECT #ls_columns = #ls_columns + ', ' + name + ' = REPLACE(' + name + ', ' + '' + '''' + REPLACE(#ls_replaceValue, '''', '''''''') + '''' + ', ' + '''' + REPLACE(#ls_withValue, '''', '''''''') + '''' + ')'
FROM sys.all_columns
WHERE object_id = OBJECT_ID(#ls_table)
AND collation_name IS NOT NULL; -- Skip columns that aren't character based
-- Remove the first ', ' from the column list
SET #ls_columns = SUBSTRING(#ls_columns, 3, LEN(#ls_columns));
IF #ls_columns = ''
BEGIN
PRINT 'Table not found'
RETURN
END
-- Build a query
DECLARE #ls_query_sql NVARCHAR(MAX) = '';
SET #ls_query_sql = 'SELECT ' + #ls_columns + ' FROM ' + #ls_table;
-- Show the results
SELECT #ls_query_sql AS querySQL;
END
Just since the OP asked about how you might do this in dynamic SQL, here's how I'd approach it. Basically get the table schema information and concatenate all the columns, plus the REPLACE logic you want using FOR XML. This basically constructs the statement Rigerta posted, but does it dynamically.
use tempdb
go
if object_id('tempdb.dbo.#SomeoneElsesBadData') is not null drop table #SomeoneElsesBadData
create table #SomeoneElsesBadData
(
BadData varchar(250),
WorseData varchar(250),
IQuitData varchar(250)
)
declare #sql nvarchar(max)
select #sql = 'select '
+ stuff((select ', '
+ name
+ ' = replace(' + name + ''','', '''')'
from tempdb.sys.columns
where object_id = object_id('tempdb.dbo.#SomeoneElsesBadData')
for xml path('')), 1, 1, '')
+ ' into #BadDataTempTable
from #SomeoneElsesBadData'
exec sp_executesql #sql
All things being equal, the data should probably be cleaned before it gets into SQL, but reality is rarely fair.

using information_schema_tables and concatenate

I want to know how can I use the information_schema_tables select query to look up #tablename, so that, that table's catalog and schema is shown, and then concatenate it together so that #tablename is displayed as table_catalog.table_schema.table name'?
At the moment I am just calling on the table name using select #tablename = Value
declare #tablename varchar(MAX)
declare #tableschema varchar(MAX)
declare #loop int = 1
select a.* into #tmp
from
(
select RID,
v.value('local-name(.)', 'VARCHAR(MAX)') 'Field',
v.value('./text()[1]', 'VARCHAR(MAX)') 'Value'
from #XMLTemp
cross apply Field.nodes ('/Record/*') x(v)
where v.value('local-name(.)', 'VARCHAR(MAX)') not in ('Update', 'Filter', 'Insert', 'Delete')
) as a
where RID = #loop
...
select Table_Catalog, Table_Schema
from Information_Schema.Tables
...
select #tablename = ''
select #tablename = Value
from #tmp
where Field='tableName'
and RID = #loop
...
print 'update ' + #tablename + '
...
select #tablename = Value from #tmp where Field = 'TableName'
...
set #loop = #loop+1
In SQL Server you can use "+" to concatenate strings.
declare #tablename varchar(MAX)
select #tablename = TABLE_CATALOG + '.' + TABLE_SCHEMA + '.' + TABLE_NAME
from INFORMATION_SCHEMA.TABLES
where TABLE_NAME = 'TableName'
Keep in mind that if your query returns multiple rows #tablename variable will contains the last value returned.
select quotename(db_name()) + '.' + quotename( schemas.name ) + '.' + quotename( tables.name )
from sys.tables
join sys.schemas on tables.schema_id = schemas.schema_id
A couple of notes: "Catalog" in ANSI speak is Database in SQL Server, so within a database it's pretty much a constant value - the name of the current database.
In SQL Server I find the system views are more consistent and reliable than INFORMATION_SCHEMA, which mostly works but has some quirky issues.
According to your last question I'd like to suggest the following UDF:
You pass in your XML and a catalog's name (or NULLor DEFAULT) and the same with the schema's name. The function will use COALESCE to use the right portion:
CREATE FUNCTION dbo.CreateUpdateStatement
(
#XmlData XML
,#CatalogName VARCHAR(100) = NULL
,#SchemaName VARCHAR(100) = NULL
)
RETURNS VARCHAR(MAX)
BEGIN
DECLARE #RetVal VARCHAR(MAX);
WITH XMLNAMESPACES('http://www.w3.org/2001/XMLSchema-instance' AS xsi)
SELECT #RetVal=
'UPDATE '
+ COALESCE(#CatalogName + '.',TheTable.TABLE_CATALOG + '.', '')
+ COALESCE(#SchemaName + '.',TheTable.TABLE_SCHEMA + '.', 'dbo.')
+ One.Record.value('TableName[1]','varchar(max)')
+ ' SET ' + One.Record.value('(Update/FieldName)[1]','varchar(max)') + '=''' + One.Record.value('(Update/NewValue)[1]','varchar(max)') + ''' '
+ ' WHERE ' + One.Record.value('KeyField[1]','varchar(max)') + '=''' + One.Record.value('TableRef[1]','varchar(max)') + ''';'
FROM #XmlData.nodes('/Task/Record') AS One(Record)
OUTER APPLY
(
SELECT TOP 1 TABLE_CATALOG,TABLE_SCHEMA,TABLE_NAME
FROM INFORMATION_SCHEMA.TABLES
WHERE TABLE_NAME=One.Record.value('TableName[1]','varchar(max)')
) AS TheTable;
RETURN #RetVal;
END
GO
This is how you call it (I used one existing table's name spz.dbo.AuditRow in one of my catalogs):
DECLARE #x xml=
'<Task xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
<Record>
<order>1</order>
<TableName>AuditRow</TableName>
<KeyField>ProductPersonID</KeyField>
<TableRef>32420</TableRef>
<Update>
<FieldName>StatusID</FieldName>
<OldValue>3</OldValue>
<NewValue>8</NewValue>
</Update>
</Record>
</Task>';
SELECT dbo.CreateUpdateStatement(#x,DEFAULT,DEFAULT);
--UPDATE spz.dbo.AuditRow SET StatusID='8' WHERE ProductPersonID='32420';
SELECT dbo.CreateUpdateStatement(#x,'MyCatalog',DEFAULT);
--UPDATE MyCatalog.dbo.AuditRow SET StatusID='8' WHERE ProductPersonID='32420';
SELECT dbo.CreateUpdateStatement(#x,DEFAULT,'MySchema');
--UPDATE spz.MySchema.AuditRow SET StatusID='8' WHERE ProductPersonID='32420';
SELECT dbo.CreateUpdateStatement(#x,'MyCatalog','MySchema');
--UPDATE MyCatalog.MySchema.AuditRow SET StatusID='8' WHERE ProductPersonID='32420';
You might execute this immediately with
EXEC (SELECT dbo.CreateUpdateStatement(#x,NULL,NULL));

Using Dynamic SQL in User Defined Function to return string (not modify data)

Our document storage application has a unique database for each of our clients which are almost identical to each other, but one table DocumentIndexes is unique for each client and can have any number of columns and types.
I am trying to create a generic function (within our "master" database called MYAPP_MASTER) that I can call and simply pass in a database name and a document ID value and get back the column names and values from from the specified database's DocumentIndexes table. Because I have to pass in the database name, I have to generate the selection SQL dynamically and call sp_executesql.
I have the following code which polls the INFORMATION_SCHEMA.COLUMNS table to determine the columns needed and it works just fine in a stored procedure, but I hate having to copy all this code in every stored procedure that needs these to retrieve these dynamic column values. I would rather have one function that returns the string value of these columns regardless of database and have the function exist once in our MYAPP_MASTER database. Again, this code works, but SQL won't allow me to put it into a function. Is there anyway around this?
USE MYAPP_MASTER
GO
DECLARE #DatabaseName varchar(255)
DECLARE #DocumentId int
SET #DatabaseName = 'SAMPLE_CLIENT_DB'
SET #DocumentId = 1234
DECLARE #DynamicIndexes nvarchar(max)
DECLARE #DynamicIndexesParam nvarchar(max)
DECLARE #DynamicIndexesSql nvarchar(max)
SET #DynamicIndexesParam = '#Indexes varchar(max) OUTPUT'
SET #DynamicIndexesSql = 'SELECT #Indexes = COALESCE(#Indexes + ''+ '''', '', '''') + CAST(COLUMN_NAME as varchar(max)) + '': '''''' + '' + CASE WHEN DI.'' + COLUMN_NAME + '' IS NOT NULL THEN CAST(DI.'' + COLUMN_NAME + '' as varchar(max)) ELSE '''''''' END '' FROM ' + #DatabaseName + '.INFORMATION_SCHEMA.COLUMNS WHERE table_name = ''DocumentIndexes'' AND COLUMN_NAME <> ''DocumentID''; '
EXEC sp_executesql #DynamicIndexesSql, #DynamicIndexesParam, #Indexes = #DynamicIndexes OUTPUT
SET #DynamicIndexes = '''' + #DynamicIndexes
DECLARE #SelectionSql nvarchar(max)
SET #SelectionSql = 'SELECT ' + #DynamicIndexes + ' as DocumentIndexes FROM ' + #DatabaseName + '..Document D LEFT OUTER JOIN ' + #DatabaseName + '..DocumentIndexes DI ON D.DocumentId = DI.DocumentId WHERE D.DocumentID = ' + CAST(#DocumentId as varchar(10))
EXEC sp_executesql #SelectionSql
If the SAMPLE_CLIENT_DB datababase DocumentIndexes table has columns for Name, Office and Classification, this code will return a simple string that looks like the following:
Name: Foo, Office: Bar, Classification: 123
You can't run an Exec command inside a SQL function, but you could use a stored procedure with an output variable assuming the DocumentIndexes table is unique at the DocumentID level.
Create Proc DocID_DocIndexes #retval Varchar(Max) Output
As
...
(Your code logic minus last two lines)
...
-- Populate your dynamic SQL into a variable to assign it to the output variable
SET #SelectionSql = 'SELECT #result = ' + #DynamicIndexes + ' as DocumentIndexes FROM ' + #DatabaseName + '..Document D LEFT OUTER JOIN ' + #DatabaseName + '..DocumentIndexes DI ON D.DocumentId = DI.DocumentId WHERE D.DocumentID = ' + CAST(#DocumentId as varchar(10))
EXEC sp_executesql #SelectionSql, N'#result Varchar(Max) Output', #result = #retval Output
Return

Quick way to backup SQL SP and Functions?

I have a long list of SPs (stored procedure) and Functions in my SQL server db. I could save them one by one by right clicking and script XXX to Alter To. Is there any way in TSQL to query all SPs and functions save them to xxx.sql files?
For example, for sp_mySP1, I would like to save it to sp_mySP1.sql which is a text file. The database is too big and I would like save only SPs and functions as a backup of source codes.
In management studio; find the database, right-click, tasks, generate scripts;
next-next-next until you "Choose Object Types". Select "Stored procedures" and "User-defined functions", next, Select All; choose an output; go!
1) Right-click on your Database name in the Object Explorer
2) Select "Tasks > Generate Scripts..." from the Context menu
3) Select your Database in the list and click Next
4) Click Next on the Chose Script Options
5) In Object Types, check Stored Procedures and User-defined functions, click Next
6) Click Select All on the Stored Procedures selection screen, click Next
7) Click Select All on the Functions selection screen, click Next
8) Select 'Script to New Query Window' and click Finish
Here's a proc that will export SOME types of data.
if exists ( select * from sysobjects where name = 'ExportData_P' )
drop proc ExportData_P
go
CREATE PROC dbo.ExportData_P (
#tableName varchar(500),
#where varchar(5000) = '(1=1)'
)
AS
BEGIN
SET NOCOUNT ON
DECLARE #sql varchar(8000)
DECLARE #fieldList varchar(8000)
DECLARE #valueList varchar(8000)
SELECT #fieldList = '', #valueList = ''
DECLARE #cols TABLE ( column_name nvarchar(250), data_type varchar(250) )
DECLARE #c nvarchar(250), #data_type varchar(250)
INSERT INTO #cols
select column_name, data_type
from information_Schema.columns
where table_name = #tableName
WHILE EXISTS ( SELECT TOP 1 * FROM #cols )
BEGIN
SELECT TOP 1 #c = column_name, #data_type = data_type FROM #cols
SELECT
#fieldList = #fieldList + #c + ', ',
#valueList = #valueList + CHAR(13) + 'case when ' + #c + ' is null then ''NULL'' else '''''''' + ' +
case when #data_type in ('text','ntext','char', 'nvarchar', 'varchar' ) then
' REPLACE ( REPLACE ( REPLACE ( '
else ''
end +
'IsNull ( convert(varchar' +
( -- change this section to pass the length of varchar to convert
case when #data_type in ( 'uniqueidentifier' ) then '(50)'
when #data_type in ( 'text', 'ntext' ) then '(8000)'
else '' end
) +
', ' +
#c +
'), '''' )' + -- end is null
case when #data_type in ('text','ntext','char', 'nvarchar', 'varchar' ) then
', CHAR(39), CHAR(39)+CHAR(39) ), CHAR(13), '''' + CHAR(13) + ''''), CHAR(9), '''' + CHAR(9) + '''') '
else ''
end +
' + '''''''' end + '', '' + '
DELETE FROM #cols WHERE column_name = #c
END
SELECT #fieldList = LEFT ( #fieldList, LEN(#fieldList)-1 ),
#valueList = LEFT ( #valueList, LEN(#valueList)-1 )
SELECT #sql = 'select ''insert into ' + #tableName + ' (' + #fieldList + ') ' +
' VALUES ( ''+ ' + left ( #valueList, len(#valueList)-5) + ''') '' from ' + #tableName +
' WHERE ' + #where
-- into [#mcoe_temp_export' + #tableName + ']
print #sql
EXEC ( #sql )
--EXEC ( 'select * from [#mcoe_temp_export' + #tableName + ']' )
SET NOCOUNT OFF
END
go
Use like:
exec ExportData_P 'tablename'
you could query syscomments to get your sql object creation text, but I don't know how to save them all in separate files using just TSQL.
select * from syscomments