OPENJSON in compatibility level 100 SQL SERVER 2016 - json

I need to use the functionality of OPENJSON() in an old database with compatibility level 100. The server runs SQL SERVER 2016. So i came up with this idea: Create another DB "GeneralUTILS" (lvl 130) in the same server and call this function from lvl 100 DB:
CREATE FUNCTION [dbo].[OPENJSON_](#json NVARCHAR(MAX))
RETURNS #Results TABLE ([Key] nVARCHAR (4000) , [Value] NVARCHAR(MAX), [Type] INT)
AS
BEGIN
INSERT INTO #Results
SELECT * from OPENJSON(#json)
RETURN
END
But i don't have the WITH clause to modify the output table in the lvl 100 database.

Most important might be the question why you need this at all...
I hope I got correctly, what you need:
(Hint: This needs at least SQL-Server 2016)
--create two mock-up-databases
CREATE DATABASE dbOld;
GO
ALTER DATABASE dbOld SET COMPATIBILITY_LEVEL = 100; --v2008
GO
CREATE DATABASE dbForJsonIssues;
GO
ALTER DATABASE dbForJsonIssues SET COMPATIBILITY_LEVEL = 130; --v2016
GO
--Now we will create a stored procedure in the "higher" database
USE dbForJsonIssues;
GO
--Attention: replacing FROM is a very hacky way... Read the hints at the end...
--You might use parameters for the JSON-string and the JSON-path, but then you must use sp_executesql
CREATE PROCEDURE EXEC_Json_Command #Statement NVARCHAR(MAX), #TargetTable NVARCHAR(MAX)
AS
BEGIN
DECLARE #statementWithTarget NVARCHAR(MAX)=REPLACE(#Statement,'FROM',CONCAT(' INTO ',#TargetTable,' FROM'));
PRINT #statementWithTarget; --you can out-comment this line...
EXEC(#statementWithTarget);
END
GO
--Now we go into the "lower" database
USE dbOld;
GO
--A synonym is not necessary, but allows for easier code
CREATE SYNONYM dbo.ExecJson FOR dbForJsonIssues.dbo.EXEC_Json_Command;
GO
--This is how to use it
DECLARE #json NVARCHAR(MAX)=N'[{"someObject":[{"attr1":"11", "attr2":"12"},{"attr1":"21", "attr2":"22"}]}]';
DECLARE #Statement NVARCHAR(MAX)=CONCAT(N'SELECT * FROM OPENJSON(N''',#json,N''',''$[0].someObject'') WITH(attr1 INT,attr2 INT)');
--the target table will be created "on the fly"
--You can use ##SomeTarget too, but be careful with concurrencies in both approaches...
EXEC ExecJson #Statement=#Statement,#TargetTable='dbOld.dbo.SomeTarget';
SELECT * FROM SomeTarget;
--We can drop this table after dealing with the result
DROP TABLE SomeTarget;
GO
--Clean-up (carefull with real-data!)
USE master;
GO
DROP DATABASE dbOld;
DROP DATABASE dbForJsonIssues;
The most important concepts:
We cannot use the JSON-statements directly within the database, but we can create a statement on string base, pass it to the stored procedure and use EXEC() for its execution.
Using SELECT * INTO SomeDb.SomeSchema.SomeTargetTable FROM ... will create a table with the fitting structure. Make sure to use a table not existing in your database.
It is not really needed to pass the target table as parameter, you might place this in the statement yourself. Replacing the FROM in the stored procedure is a very shrewed way and could lead into troubles if from is found in another place.
You might use similar procedures for various needs...

Yeah. No way this would pass the smoke screen at our office. Anyway someone asked me to do something similar, but the use case was for parsing json arrays only. Since Json_Query and Json_Value are available I hacked this together just to give them something to work with. My colleague liked the results. Turns out he's much cooler than I am after he modified it.
Declare #Fields NVarchar(2000) = 'Name,Coolness'
Declare #Delimiter As Varchar(10) = ',';
Declare #Xml As Xml = Cast(('<V>' + Replace(#Fields, #delimiter, '</V><V>') + '</V>' ) As Xml);
Declare #Json Nvarchar(4000) = N'{"Examples":[{"Name": "Chris","Coolness": "10"},{"Name": "Jay","Coolness": "1"}]}';
Exec ('Begin Try Drop Table #JsonTemp End Try Begin Catch End Catch');
Create Table #JsonTemp (JsonNode Nvarchar(1000));
Declare #Max INTEGER = 100;
Declare #Index INTEGER = 0;
While #Index < #Max
Begin
Declare #Affected Integer = 0;
Declare #Select Nvarchar(200) = '''' + 'lax$.Examples[' + Convert(Nvarchar, #Index) + ']' + '''';
Declare #Statement Nvarchar(2000)= 'Select Json_Query(' + '''' + #Json + '''' + ', ' + #Select + ') Where Json_Query(' + '''' + #Json + '''' + ', ' + #Select + ') Is Not Null';
Insert Into #JsonTemp (JsonNode) Exec sp_executesql #Statement;
Set #Affected = ##RowCount;
If (#Affected = 0) Begin Break End
Set #Index = #Index + 1;
End
Declare #Table Table(Field NVarchar(200));
Declare #Selector NVarchar(500) = 'Json_Value(' + '''' + '{"Node":' + '''' + ' + ' + 'JsonNode' + ' + ' + '''' + '}' + '''' + ', ' + '''' + '$.Node.#Field' + '''' + ')';
Insert Into #Table(Field)
Select N.value('.', 'Varchar(10)') As Field
From #XML.nodes('V') As A(N);
Declare #Selectors Varchar(8000);
Select #Selectors = Coalesce(#Selectors + ', ', '') + Replace(#Selector, '#Field', Field) + ' As ' + Field
From #Table
Exec ('Select ' + #Selectors + ' From #JsonTemp');

Related

Convert dynamically SQL Server 2008 Table to HTML table

Is there a way I can convert a SQL Server 2008 Table to HTML table text, without knowing the structure of the table first?
I tried this:
USE [Altiris]
GO
SET ANSI_NULLS ON
GO
SET QUOTED_IDENTIFIER ON
GO
ALTER PROCEDURE [dbo].[spCustomTable2HTML] (
#TABLENAME NVARCHAR(500),
#OUTPUT NVARCHAR(MAX) OUTPUT,
#TBL_STYLE NVARCHAR(1024) = '',
#HDR_STYLE NVARCHAR(1024) = '')
AS
-- #exec_str stores the dynamic SQL Query
-- #ParmDefinition stores the parameter definition for the dynamic SQL
DECLARE #exec_str NVARCHAR(MAX)
DECLARE #ParmDefinition NVARCHAR(500)
--We need to use Dynamic SQL at this point so we can expand the input table name parameter
SET #exec_str= N'
DECLARE #exec_str NVARCHAR(MAX)
DECLARE #ParmDefinition NVARCHAR(500)
--Make a copy of the original table adding an indexing columnWe need to add an index column to the table to facilitate sorting so we can maintain the
--original table order as we iterate through adding HTML tags to the table fields.
--New column called CustColHTML_ID (unlikely to be used by someone else!)
--
select CustColHTML_ID=0,* INTO #CustomTable2HTML FROM ' + #TABLENAME + '
--Now alter the table to add the auto-incrementing index. This will facilitate row finding
DECLARE #COUNTER INT
SET #COUNTER=0
UPDATE #CustomTable2HTML SET #COUNTER = CustColHTML_ID=#COUNTER+1
-- #HTMLROWS will store all the rows in HTML format
-- #ROW will store each HTML row as fields on each row are iterated through
-- using dymamic SQL and a cursor
-- #FIELDS will store the header row for the HTML Table
DECLARE #HTMLROWS NVARCHAR(MAX) DECLARE #FIELDS NVARCHAR(MAX)
SET #HTMLROWS='''' DECLARE #ROW NVARCHAR(MAX)
-- Create the first HTML row for the table (the table header). Ignore our indexing column!
SET #FIELDS=''<tr ' + #HDR_STYLE + '>''
SELECT #FIELDS=COALESCE(#FIELDS, '' '','''')+''<td>'' + name + ''</td>''
FROM tempdb.sys.Columns
WHERE object_id=object_id(''tempdb..#CustomTable2HTML'')
AND name not like ''CustColHTML_ID''
SET #FIELDS=#FIELDS + ''</tr>''
-- #ColumnName stores the column name as found by the table cursor
-- #maxrows is a count of the rows in the table, and #rownum is for marking the
-- ''current'' row whilst processing
DECLARE #ColumnName NVARCHAR(500)
DECLARE #maxrows INT
DECLARE #rownum INT
--Find row count of our temporary table
SELECT #maxrows=count(*) FROM #CustomTable2HTML
--Create a cursor which will look through all the column names specified in the temporary table
--but exclude the index column we added (CustColHTML_ID)
DECLARE col CURSOR FOR
SELECT name FROM tempdb.sys.Columns
WHERE object_id=object_id(''tempdb..#CustomTable2HTML'')
AND name not like ''CustColHTML_ID''
ORDER BY column_id ASC
--For each row, generate dymanic SQL which requests the each column name in turn by
--iterating through a cursor
SET #rowNum=0
SET #ParmDefinition=N''#ROWOUT NVARCHAR(MAX) OUTPUT,#rowNum_IN INT''
While #rowNum < #maxrows
BEGIN
SET #HTMLROWS=#HTMLROWS + ''<tr>''
SET #rowNum=#rowNum +1
OPEN col
FETCH NEXT FROM col INTO #ColumnName
WHILE ##FETCH_STATUS=0
BEGIN
--Get nth row from table
--SET #exec_str=''SELECT #ROWOUT=(select top 1 ['' + #ColumnName + ''] from (select top '' + cast(#rownum as varchar) + '' * from #CustomTable2HTML order by CustColHTML_ID ASC) xxx order by CustColHTML_ID DESC)''
SET #exec_str=''SELECT #ROWOUT=(select ['' + #ColumnName + ''] from #CustomTable2HTML where CustColHTML_ID=#rowNum_IN)''
EXEC sp_executesql
#exec_str,
#ParmDefinition,
#ROWOUT=#ROW OUTPUT,
#rowNum_IN=#rownum
SET #HTMLROWS =#HTMLROWS + ''<td>'' + #ROW + ''</td>''
FETCH NEXT FROM col INTO #ColumnName
END
CLOSE col
SET #HTMLROWS=#HTMLROWS + ''</tr>''
END
SET #OUTPUT=''''
IF #maxrows>0
SET #OUTPUT= ''<table ' + #TBL_STYLE + '>'' + #FIELDS + #HTMLROWS + ''</table>''
DEALLOCATE col
'
DECLARE #ParamDefinition nvarchar(max)
SET #ParamDefinition=N'#OUTPUT NVARCHAR(MAX) OUTPUT'
--Execute Dynamic SQL. HTML table is stored in #OUTPUT which is passed back up (as it's
--a parameter to this SP)
EXEC sp_executesql #exec_str,
#ParamDefinition,
#OUTPUT=#OUTPUT OUTPUT
RETURN 1
but when I execute the procedure
DECLARE #HTML NVARCHAR(MAX)
EXEC SpCustomTable2HTML 'Users', #HTML OUTPUT
SELECT #HTML
it keeps returning null.
Any ideas?
This SQL Fiddle DEMO shows your problem. When ALL the columns in ALL rows have values, you get a proper HTML table. When ANY NULLs exist, it turns the entire thing into NULL because
NULL + <any> = NULL
To fix it, simply change line 90 to handle nulls, i.e.
SET #HTMLROWS =#HTMLROWS + '''' + ISNULL(#ROW,'''') + ''''
The fixed SQL Fiddle DEMO
I realise it's been a while (to say the least) since this question was active but I thought I would post a few comments on this thread, as it turned up in a recent search.
Apologies if this (unintentionally) annoys the question asker but I believe the approach being used is both inefficient and difficult to understand - and therefore maintain.
There's no need to copy the database data before using it to generate the HTML table. It's just my humble opinion, but using dynamic SQL to generate dynamic SQL is also counter-intuitive.
Furthermore, database data values that contain HTML tags (or, worse still, malformed HTML tags) need to be escaped, so that they can be rendered correctly. For example, database data such as "value > 10" needs to generate the HTML "<td>value > 10</td>".
The following code addresses all of the above points, by using the built-in FOR XML clause:
CREATE PROCEDURE dbo.spCustomTable2HTML
#TABLENAME nvarchar(500),
#TBL_STYLE nvarchar(1024) = '',
#HDR_STYLE nvarchar(1024) = '',
#OUTPUT nvarchar(MAX) OUTPUT
AS
BEGIN
SET NOCOUNT ON;
SET XACT_ABORT ON;
-- Declare variables
DECLARE #Columns nvarchar(MAX) = '',
#Data nvarchar(MAX),
#SQL nvarchar(MAX);
-- Snapshot columns (to force use of tempdb)
IF OBJECT_ID('tempdb.dbo.##spCustomTable2HTMLColumns') IS NOT NULL
BEGIN
DROP TABLE ##spCustomTable2HTMLColumns;
END
SET #SQL =
'SELECT TOP 0 *
INTO ##spCustomTable2HTMLColumns
FROM ' + #TABLENAME;
EXEC sp_executesql #SQL;
-- Build header row
SET #OUTPUT = (SELECT name AS td
FROM tempdb.sys.columns
WHERE object_id = OBJECT_ID('tempdb.dbo.##spCustomTable2HTMLColumns')
ORDER BY column_id
FOR XML RAW(''), ELEMENTS);
SET #OUTPUT += '</tr>'
-- Build column list
SELECT #Columns += '[' + name + '] AS td,'
FROM tempdb.sys.columns
WHERE object_id = OBJECT_ID('tempdb.dbo.##spCustomTable2HTMLColumns')
ORDER BY column_id;
SET #Columns = LEFT(#Columns, LEN(#Columns) - 1); -- Strip trailing comma
-- Delete columns snapshot
DROP TABLE ##spCustomTable2HTMLColumns;
-- Build data rows
SET #SQL =
'SET #Data = CONVERT(varchar(MAX),
(SELECT ' + #Columns +
' FROM ' + #TABLENAME +
' FOR XML RAW (''tr''), ELEMENTS XSINIL))';
EXEC sp_executesql #SQL, N'#Data NVARCHAR(MAX) OUTPUT', #Data = #Data OUTPUT;
SET #Data = REPLACE(#Data, ' xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"', ''); -- Remove XSI namespace
SET #Data = REPLACE(#Data, ' xsi:nil="true"', ''); -- Remove XSI attributes
SET #OUTPUT += #Data;
-- Prefix table/row headers
SET #OUTPUT = REPLACE(#OUTPUT, ' ', ' '); -- Use non-breaking spaces
SET #OUTPUT = REPLACE(#OUTPUT, '</tr>', '</tr>' + CHAR(13) + CHAR(10)); -- Add new line per row (to improve rendering in Microsoft Outlook)
SET #OUTPUT = '<table ' + #TBL_STYLE + '>' +
'<tr ' + #HDR_STYLE + '>' +
#OUTPUT +
'</table>';
END

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

Copy Scalar Functions from one Database to another

How do I copy just the Scalar Functions from one Database to another? I'm not worried about copying any tables or data. I tried performing an Export Task but that seemed to only let me move tables.
These steps were done on SQL Server 2008 R2 in SSMS.
In short, I used Task -> Generate Scripts... instead of Script Database as -> Create To. The latter only returned a SQL script to create the Database (e.g. Create Database, Alter Database, and Filegroups) without creating any other objects in the Database (e.g. Tables, Views or Functions).
Here are the exact steps with screenshots:
Right click on the database with the functions you want and go to Generate Scripts
Click through the first screen of the Wizard
Choose User-Defined Functions
Finish the wizard.
Also, this answer, while it isn't an exact corollary, prompted me to look for the Generate Scripts option.
-- This program copies (CREATE OR ALTER THE FUNCTION) a single Function from one database to another
-- *** Note that all objects mentioned in the function must exist in the target database ***
declare #SourceDatabase nvarchar(50);
declare #SourceSchemaName nvarchar(50)
declare #TargetDatabase nvarchar(50);
declare #FunctionName nvarchar(50);
set #SourceDatabase = N'Northwind' -- The name of the Source database
set #SourceSchemaName = N'dbo' -- The name of the Function SCHEME
set #FunctionName = N'WriteToTextFile' -- The name of the Function
set #TargetDatabase = N'AdventureWorks' -- The name of the Target database
declare #sql nvarchar(max)
-- If the Function SCHEME does not exist, create it
set #sql = ' use [' +#TargetDatabase +'] ' +
' IF NOT EXISTS (SELECT * FROM sys.schemas WHERE lower(name) = lower(''' + #SourceSchemaName + ''')) '+
' BEGIN ' +
' EXEC('' CREATE SCHEMA '+ #SourceSchemaName +''') ' +
' END'
exec (#sql);
-- CREATE OR ALTER THE FUNCTION
set #sql = ''
set #sql = #sql + ' use [' + #TargetDatabase +'] ;' +
' declare #sql2 nvarchar(max) ;' +
' SELECT #sql2 = coalesce(#sql2,'';'' ) + [ROUTINE_DEFINITION] + '' ; '' ' +
' FROM ['+#sourceDatabase+'].[INFORMATION_SCHEMA].[ROUTINES] ' +
' where ROUTINE_TYPE = ''FUNCTION'' and ROUTINE_SCHEMA = ''' +#SourceSchemaName +''' and lower(ROUTINE_NAME) = lower(N''' + #FunctionName + ''') ; ' +
' set #sql2 = replace(#sql2,''CREATE FUNCTION'',''CREATE OR ALTER FUNCTION'')' +
' exec (#sql2)'
exec (#sql)