How to find out sum of dynamic columns in SQL Pivot Query - sql-server

I have a stored procedure which accepts date and month as parameters that results monthly attendance sheet. And I have to find out the monthly working hours of particular employees.
CREATE TABLE [dbo].[employee] (
[EmpID] [int] IDENTITY(100,1) NOT NULL,
[Name] [varchar](50) NULL,
[DOB] [date] NULL,
[DOJ] [date] NULL,
[Email] [varchar](50) NULL,
[Mob] [varchar](50) NULL,
[Address] [varchar](max) NULL,
CONSTRAINT [PK_tbl_employee] PRIMARY KEY CLUSTERED
(
[EmpID] ASC
) WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON) ON [PRIMARY] ) ON [PRIMARY] TEXTIMAGE_ON [PRIMARY]
GO
CREATE TABLE [dbo].[Attendace] (
[EmpID] [int] NOT NULL,
[AttendaceDate] [date] NULL,
[WorkHours] [int] NULL,
[AtID] [int] IDENTITY(1,1) NOT NULL,
CONSTRAINT [PK_Attendace] PRIMARY KEY CLUSTERED
(
[AtID] ASC
) WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON) ON [PRIMARY] ) ON [PRIMARY]
GO
Stored Procedure
ALTER procedure [dbo].[sps_AttendanceShowModified] #mon int, #year int
As begin DECLARE #cols AS NVARCHAR(MAX)=''; DECLARE #query AS
NVARCHAR(MAX)='';
set #query = 'SELECT * from (
select e.Name, a.WorkHours, DAY(a.AttendaceDate) AS d1
from Attendace a, employee e
where e.EmpID = a.EmpID and
MONTH(a.AttendaceDate) = ' + CONVERT(VARCHAR(12), #mon)+ ' AND
YEAR(a.AttendaceDate) = ' + CONVERT(VARCHAR(12), #year)+ ' ) src pivot
(
max(WorkHours) for d1 in ([1],[2],[3],[4],[5],[6],[7],[8],[9],[10],[11],[12],[13],[14],[15],[16],[17],[18],[19],[20],[21],[22],[23],[24],[25],[26],[27],[28],[29],[30])
) piv'
execute(#query) end

In your are case you can add a column in your select list like following.
sum(WorkHours) over(partition by name order by (select 1)) as Total
I have created a Demo, you can have a look.
Demo Online
Your final query should look like following.
ALTER procedure [dbo].[sps_AttendanceShowModified] #mon int, #year int
As begin DECLARE #cols AS NVARCHAR(MAX)=''; DECLARE #query AS
NVARCHAR(MAX)='';
set #query = 'SELECT name,Total,[1],[2],[3] from (
select e.Name, a.WorkHours, DAY(a.AttendaceDate) AS d1, sum(WorkHours) over(partition by e.name order by (select 1)) as Total
from Attendace a, employee e
where e.EmpID = a.EmpID and
MONTH(a.AttendaceDate) = ' + CONVERT(VARCHAR(12), #mon)+ ' AND
YEAR(a.AttendaceDate) = ' + CONVERT(VARCHAR(12), #year)+ ' ) src pivot
(
max(WorkHours) for d1 in ([1],[2],[3],[4],[5],[6],[7],[8],[9],[10],[11],[12],[13],[14],[15],[16],[17],[18],[19],[20],[21],[22],[23],[24],[25],[26],[27],[28],[29],[30])
) piv'
execute(#query) end
Note: You need to include other columns in your select.

Related

Copy rows into same table except primary key and auto increment columns

Is it possible to copy some rows in a table excluding the primary key and auto increment columns?
I found only one solution:
create a temp table
drop primary column
Is there any easier way to do this?
INSERT INTO TableName(Column1, Column2, Column3)
SELECT Column1, Column2, Column3
FROM TableName
WHERE <<your logic to filter rows>>
Update
CREATE TABLE [dbo].[MyTable]
(
[Id] [int] IDENTITY(1,1) NOT NULL,
[Column1] [varchar](50) NOT NULL,
[Column2] [varchar](50) NOT NULL,
[Column3] [varchar](50) NOT NULL,
CONSTRAINT [PK_MyTable]
PRIMARY KEY CLUSTERED ([Id] ASC)
WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF,
IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS = ON,
ALLOW_PAGE_LOCKS = ON) ON [PRIMARY]
) ON [PRIMARY]
GO
DECLARE #TABLE_SCHEMA Varchar(100) = 'dbo',
#TABLE_Name Varchar(100) = 'MyTable',
#TableColumns Varchar(1000),
#RequiredColumns Varchar(1000),
#SQLStatement VARCHAR(4000)
SET #TableColumns = ''
SELECT
#TableColumns = #TableColumns + Name + ' , '
FROM syscolumns
WHERE id = object_id(#TABLE_SCHEMA+'.'+#TABLE_Name)
AND Name NOT IN (SELECT col.name
FROM sys.tables tab
INNER JOIN sys.indexes pk ON tab.object_id = pk.object_id
AND pk.is_primary_key = 1
INNER JOIN sys.index_columns ic ON ic.object_id = pk.object_id
AND ic.index_id = pk.index_id
INNER JOIN sys.columns col ON pk.object_id = col.object_id
AND col.column_id = ic.column_id
WHERE tab.object_id = object_id(#TABLE_SCHEMA+'.'+#TABLE_Name))
SET #RequiredColumns = Substring(#TableColumns,1,len(#TableColumns)-1)
SELECT #SQLStatement = 'INSERT INTO MyTable (' + #RequiredColumns + ') SELECT ' + Substring(#TableColumns,1,len(#TableColumns)-1) + ' FROM '+#TABLE_SCHEMA+'.'+#TABLE_Name
-- you can add the required where condition to filter rows
EXEC(#SQLStatement)

SQL Server delete all tables under special schema with temporal tables

I'm trying to delete a database scheme with temporal tables.
Non of the existing scripts found through googling, supports temporal tables.
Is there anyone already done this?
There are many temporal tables on that scheme with many constraints with dependencies. so when I try to drop the scheme it complain about dependencies.
Basically I'm looking for a stored procedure or something that go through all the DB objects and remove one by one.
Script to Create sample tables
USE [master];
GO
CREATE DATABASE [TestDb];
GO
USE [TestDb];
GO
CREATE SCHEMA [TestScheme];
GO
SET ANSI_NULLS ON;
GO
SET QUOTED_IDENTIFIER ON;
GO
CREATE TABLE [TestScheme].[Country]
(
[CountryCode] [char](2) NOT NULL,
[Country] [varchar](60) NOT NULL,
[ValidFrom] [datetime2](2) GENERATED ALWAYS AS ROW START NOT NULL,
[ValidTo] [datetime2](2) GENERATED ALWAYS AS ROW END NOT NULL,
CONSTRAINT [PK_TestScheme_Country_CountryCode]
PRIMARY KEY CLUSTERED([CountryCode] ASC)
WITH (PAD_INDEX = ON, STATISTICS_NORECOMPUTE = OFF,
IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS = ON,
ALLOW_PAGE_LOCKS = ON, FILLFACTOR = 95) ON [PRIMARY],
PERIOD FOR SYSTEM_TIME([ValidFrom], [ValidTo])
) ON [PRIMARY]
WITH (SYSTEM_VERSIONING = ON (HISTORY_TABLE = [TestScheme].[CountryHistory]));
GO
SET ANSI_NULLS ON;
GO
SET QUOTED_IDENTIFIER ON;
GO
CREATE TABLE [TestScheme].[Address]
(
[AddressId] [int] IDENTITY(1, 1) NOT NULL,
[City] [varchar](100) NOT NULL,
[CountryCode] [char](2) NOT NULL,
[ValidFrom] [datetime2](7) GENERATED ALWAYS AS ROW START NOT NULL,
[ValidTo] [datetime2](7) GENERATED ALWAYS AS ROW END NOT NULL,
CONSTRAINT [PK_TestScheme_Address_AddressId]
PRIMARY KEY CLUSTERED([AddressId] ASC)
WITH (PAD_INDEX = ON, STATISTICS_NORECOMPUTE = OFF,
IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS = ON,
ALLOW_PAGE_LOCKS = ON, FILLFACTOR = 100) ON [PRIMARY],
PERIOD FOR SYSTEM_TIME([ValidFrom], [ValidTo])
)
ON [PRIMARY]
WITH (SYSTEM_VERSIONING = ON(HISTORY_TABLE = [TestScheme].[AddressHistory]));
GO
ALTER TABLE [TestScheme].[Address] WITH CHECK
ADD CONSTRAINT [FK_TestScheme_CountryCode]
FOREIGN KEY([CountryCode]) REFERENCES [TestScheme].[Country]([CountryCode]);
GO
ALTER TABLE [TestScheme].[Address] CHECK CONSTRAINT [FK_TestScheme_CountryCode];
GO
Query to drop scheme:
USE [TestDb];
GO
DROP SCHEMA [TestScheme];
GO
Query to delete table:
USE [TestDb]
GO
ALTER TABLE [TestScheme].[Country] SET (SYSTEM_VERSIONING = OFF)
GO
IF EXISTS (SELECT * FROM sys.objects
WHERE object_id = OBJECT_ID(N'[TestScheme].[Country]') AND type in (N'U'))
DROP TABLE [TestScheme].[Country]
GO
IF EXISTS (SELECT * FROM sys.objects
WHERE object_id = OBJECT_ID(N'[TestScheme].[CountryHistory]') AND type in (N'U'))
DROP TABLE [TestScheme].[CountryHistory]
GO
So the problem is there are many DB objects that I really don't want to create a huge script to delete one by one.
Thanks!
Thanks every one, following is the script I created and it worked for me.
USE TestDb;
GO
DECLARE #SchemeName varchar(50)= 'TestScheme';
DECLARE #DatabaseName varchar(50)= 'TestDb';
DECLARE #sql nvarchar(max)= '';
/*Removing versioning on temporal tables*/
WITH selectedTables
AS (SELECT concat('[', #DatabaseName, '].[', #SchemeName, '].[', name, ']') AS TableName
FROM SYS.TABLES WHERE history_table_id IS NOT NULL AND SCHEMA_NAME(schema_id) = #SchemeName)
SELECT #sql = COALESCE(#sql, N'') + 'ALTER TABLE ' + TableName + ' SET ( SYSTEM_VERSIONING = OFF );'
FROM selectedTables;
SELECT #sql;
EXEC sp_executesql #sql;
/*Remove constraints*/
SET #sql = N'';
SELECT #sql = COALESCE(#sql, N'') + N'ALTER TABLE ' + QUOTENAME(s.name) + N'.' + QUOTENAME(t.name) + N' DROP CONSTRAINT ' + QUOTENAME(c.name) + ';'
FROM SYS.OBJECTS AS c INNER JOIN SYS.TABLES AS t ON c.parent_object_id = t.[object_id]
INNER JOIN SYS.SCHEMAS AS s ON t.[schema_id] = s.[schema_id]
WHERE c.[type] IN( 'D', 'C', 'F', 'PK', 'UQ' ) AND s.name = #SchemeName
ORDER BY c.[type];
SELECT #sql;
EXEC sp_executesql #sql;
/*Delete Tables*/
SET #sql = N'';
SELECT #sql = COALESCE(#sql, N'') + N'DROP TABLE ['+#SchemeName+'].' + QUOTENAME(TABLE_NAME) + N';' + CHAR(13)
FROM INFORMATION_SCHEMA.TABLES WHERE TABLE_SCHEMA = #SchemeName AND TABLE_TYPE = 'BASE TABLE';
SELECT #sql
EXEC sp_executesql #sql;
/*Drop scheme*/
SET #sql = N'';
SELECT #sql = COALESCE(#sql, N'') + N'DROP SCHEMA IF EXISTS ' + #SchemeName + ';' + CHAR(13);
SELECT #sql
EXEC sp_executesql #sql;
GO
Thanks again!

How to make two column pivot and other as normal

I have 3 table name class , Student and Religion and below data are as follows
Class Table
ClassId ClassName
1 class-1
2 class-2
3 class-3
Religion Table
ReligionId RegionName
1 Hindu
2 Muslim
Student Table
employeeid StudentName religionid dateofbirth classid
1 A 1 1990-12-04 1
2 B 2 1999-12-04 2
3 C 2 2000-12-04 1
4 D 2 1988-12-04 1
5 E 2 2003-12-04 2
6 F NULL 2002-12-04 1
How Can I achieve below record from above tables
CLASSNAME HINDU MUSLIM Noreligion
class-1 1 2 1
class-2 0 2 0
You can create the tables from the below scripts
CREATE TABLE [dbo].[class](
[ClassId] [int] IDENTITY(1,1) NOT NULL,
[ClassName] [varchar](250) NULL,
CONSTRAINT [PK_class] PRIMARY KEY CLUSTERED
(
[ClassId] ASC
)WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON) ON [PRIMARY]
) ON [PRIMARY]
GO
CREATE TABLE [dbo].[Religion](
[ReligionId] [int] NOT NULL,
[RegionName] [varchar](50) NULL,
CONSTRAINT [PK_Religion] PRIMARY KEY CLUSTERED
(
[ReligionId] ASC
)WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON) ON [PRIMARY]
) ON [PRIMARY]
GO
CREATE TABLE [dbo].[Student](
[employeeid] [int] IDENTITY(1,1) NOT NULL,
[StudentName] [varchar](150) NULL,
[religionid] [int] NULL,
[dateofbirth] [date] NULL,
[classid] [int] NULL,
CONSTRAINT [PK_Student] PRIMARY KEY CLUSTERED
(
[employeeid] ASC
)WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON) ON [PRIMARY]
) ON [PRIMARY]
GO
SET ANSI_PADDING OFF
GO
SET IDENTITY_INSERT [dbo].[class] ON
INSERT [dbo].[class] ([ClassId], [ClassName]) VALUES (1, N'class-1')
INSERT [dbo].[class] ([ClassId], [ClassName]) VALUES (2, N'class-2')
INSERT [dbo].[class] ([ClassId], [ClassName]) VALUES (3, N'class-3')
SET IDENTITY_INSERT [dbo].[class] OFF
INSERT [dbo].[Religion] ([ReligionId], [RegionName]) VALUES (1, N'hindu')
INSERT [dbo].[Religion] ([ReligionId], [RegionName]) VALUES (2, N'muslim')
SET IDENTITY_INSERT [dbo].[Student] ON
INSERT [dbo].[Student] ([employeeid], [StudentName], [religionid], [dateofbirth], [classid]) VALUES (1, N'A', 1, CAST(N'1990-12-04' AS Date), 1)
INSERT [dbo].[Student] ([employeeid], [StudentName], [religionid], [dateofbirth], [classid]) VALUES (2, N'B', 2, CAST(N'1999-12-04' AS Date), 2)
INSERT [dbo].[Student] ([employeeid], [StudentName], [religionid], [dateofbirth], [classid]) VALUES (3, N'C', 2, CAST(N'2000-12-04' AS Date), 1)
INSERT [dbo].[Student] ([employeeid], [StudentName], [religionid], [dateofbirth], [classid]) VALUES (4, N'D', 2, CAST(N'1988-12-04' AS Date), 1)
INSERT [dbo].[Student] ([employeeid], [StudentName], [religionid], [dateofbirth], [classid]) VALUES (5, N'E', 2, CAST(N'2003-12-04' AS Date), 2)
INSERT [dbo].[Student] ([employeeid], [StudentName], [religionid], [dateofbirth], [classid]) VALUES (6, N'F', NULL, CAST(N'2002-12-04' AS Date), 1)
SET IDENTITY_INSERT [dbo].[Student] OFF
Do it require Pivot table to be use.
I was not able to made the query getting such records.
This is more conditional aggregation that pivoting. This gets you the result you're after:
SELECT C.ClassName,
COUNT(CASE R.RegionName WHEN 'Hindu' THEN 1 END) AS Hindu,
COUNT(CASE R.RegionName WHEN 'Muslim' THEN 1 END) AS Muslim,
COUNT(CASE WHEN R.RegionName IS NULL THEN 1 END) AS NoReligion
FROM dbo.class C
JOIN dbo.Student S ON C.ClassId = S.classid
LEFT JOIN dbo.Religion R ON S.religionid = R.ReligionId
GROUP BY C.ClassName;
If you don't understand the syntax, please ask.
Edit: OP has now stated that there are more religions than just the 2 they provided, and needs to be dynamic. This therefore gives:
--Additional sample row if you wish:
INSERT [dbo].[Religion] ([ReligionId], [RegionName])
VALUES (3, N'Catholic');
GO
DECLARE #SQL nvarchar(MAX);
SET #SQL = N'SELECT C.ClassName,' + NCHAR(10) +
STUFF((SELECT N',' + NCHAR(10) +
N' COUNT(CASE R.RegionName WHEN ' + QUOTENAME(R.RegionName,N'''') + N' THEN 1 END) AS ' + QUOTENAME(R.RegionName)
FROM dbo.Religion R
ORDER BY R.ReligionId
FOR XML PATH(N'')),1,2,N'') + N',' + NCHAR(10) +
N'COUNT(CASE WHEN R.RegionName IS NULL THEN 1 END) AS NoReligion' + NCHAR(10) +
N'FROM dbo.class C' + NCHAR(10) +
N' JOIN dbo.Student S ON C.ClassId = S.classid' + NCHAR(10) +
N' LEFT JOIN dbo.Religion R ON S.religionid = R.ReligionId' + NCHAR(10) +
N'GROUP BY C.ClassName;';
SELECT #SQL;
EXEC sp_executesql #SQL;
This is a Pivot with dynamic columns. You can have unknown number of Religions
DECLARE #cols AS NVARCHAR(MAX), #sql AS NVARCHAR(MAX);
SET #cols = STUFF((SELECT distinct ',' + QUOTENAME(ReligionName)
FROM Religion c
FOR XML PATH(''), TYPE
).value('.', 'NVARCHAR(MAX)')
,1,1,'')
Set #sql = '
SELECT ClassId, '+ #cols + ',[NoReligion]
FROM
(
Select
IIf (ReligionName IS NULL, ''NoReligion'', ReligionName) As ReligionName,
employeeid,s.ClassId
From
Student s INNER JOIN
Class ON s.classid = Class.ClassId FULL OUTER JOIN
Religion ON s.religionid = Religion.ReligionId) As src
Pivot
(
Count(employeeid)
FOR ReligionName IN('+#cols+',[NoReligion])
) As pvt'
EXECUTE sp_executesql #sql

Sending email using sp_send_dbmail() does not work sometimes

I am sending notification email using SQL Server but sometimes emails aren't sent to users.
Here is my SQL table that I store emails which it will be sent to users
CREATE TABLE [dbo].[EmailNotification](
[Id] [INT] IDENTITY(1,1) NOT NULL,
[EmailAdress] [NVARCHAR](50) NULL,
[EmailBody] [NVARCHAR](500) NULL,
[EmailSubject] [NVARCHAR](250) NULL,
[Attachment] [NVARCHAR](500) NULL,
[EmailSent] [BIT] NULL CONSTRAINT [DF_EmailNotification_EmailSent] DEFAULT
((0)),
[EmailCreateDate] [DATETIME] NULL CONSTRAINT
[DF_EmailNotification_EmailCreateDate] DEFAULT (GETDATE()),
[EmailSentDate] [DATETIME] NULL,
CONSTRAINT [PK_EmailNotification] PRIMARY KEY CLUSTERED
([Id] ASC
)WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, IGNORE_DUP_KEY = OFF,
ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON) ON [PRIMARY]
) ON [PRIMARY]
And I have created a job which executes this procedure every 1 minute
CREATE PROCEDURE [dbo].[spSendEmail]
AS
BEGIN
BEGIN TRAN
DECLARE #id BIGINT
DECLARE #max_id BIGINT
DECLARE #query NVARCHAR(1000)
DECLARE #EmailBody NVARCHAR(1000)
DECLARE #EmailAdress NVARCHAR(500)
DECLARE #EmailSubject NVARCHAR(500)
DECLARE #attachments NVARCHAR(1000)
if exists (SELECT * FROM dbo.EmailNotification where EmailSent=0)
begin
SELECT #id=MIN(id) FROM dbo.EmailNotification where EmailSent=0
SELECT #EmailAdress=EmailAdress,#EmailBody=EmailBody,#EmailSubject=EmailSubject,#attachments=Attachment
FROM EmailNotification WHERE id = #id
exec [msdb].[dbo].[sp_send_dbmail] #profile_name='Notification',
#recipients=#EmailAdress,
#blind_copy_recipients='example.email.com',
#subject=#EmailSubject,
#body=#EmailBody,
#file_attachments=#attachments
end
IF(##ERROR>0)
BEGIN
ROLLBACK
END
ELSE
BEGIN
UPDATE EmailNotification set EmailSent=1, EmailSentDate=getdate() WHERE Id=#id
COMMIT
END
What do you mean : spSendEmail is not triggering sp_send_dbmail? sp_send_dbmail is triggered but doesn't do anything....?
Please get the return code of sp_send_dbmail :
0 => OK
<> 0 => Error occured
DECLARE #result int;
DECLARE #ErrorNb int;
EXECUTE #result = exec [msdb].[dbo].[sp_send_dbmail] #profile_name='EDMS email notification',
#recipients=#EmailAdress,
#blind_copy_recipients='example.email.com',
#subject=#EmailSubject,
#body=#EmailBody,
#file_attachments=#attachments
SET #ErrorNb = ##ERROR
IF #result <> 0
BEGIN
-- Something goes wrong
SELECT #result,#ErrorNb
END
You can also use TRY :
BEGIN TRY
EXECUTE exec [msdb].[dbo].[sp_send_dbmail] #profile_name='EDMS email notification',
#recipients=#EmailAdress,
#blind_copy_recipients='example.email.com',
#subject=#EmailSubject,
#body=#EmailBody,
#file_attachments=#attachments
END TRY
BEGIN CATCH
SELECT ERROR_MESSAGE();
END CATCH

SQL Server : create table using variables

When trying to execute the following, I get the errors
Msg 102, Level 15, State 1, Line 9
Incorrect syntax near 'GO'.
Msg 102, Level 15, State 1, Line 11
Incorrect syntax near 'GO'.
Msg 102, Level 15, State 1, Line 13
Incorrect syntax near 'GO'.
Can someone tell me where I am going wrong? I don't understand how the syntax is wrong
DECLARE #table nvarchar(100);
DECLARE #sql nvarchar(max);
SET #table = 'FooTable';
SET #sql = N'CREATE TABLE [dbo].[' + #table + '](
[id] [int] IDENTITY(1,1) NOT NULL,
[AddedBy] [int] NOT NULL,
[AddedDate] [datetime2](7) NOT NULL,
CONSTRAINT [PK_' + #table + '] PRIMARY KEY CLUSTERED
( [id] ASC
)WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON) ON [PRIMARY]
) ON [PRIMARY]
GO
ALTER TABLE [dbo].[' + #table + '] ADD CONSTRAINT [DF_' + #table + '_AddedBy] DEFAULT ((-1)) FOR [AddedBy]
GO
ALTER TABLE [dbo].[' + #table + '] ADD CONSTRAINT [DF_' + #table + '_AddedDate] DEFAULT (getutcdate()) FOR [AddedDate]
GO';
exec (#sql)
Thanks for your assistance.
SQL server is not happy about sending batches in a dynamic context using exec(). So forget about GO. Just slice up your query where GO should be and exec() slices one by one:
DECLARE #table nvarchar(100);
DECLARE #sql nvarchar(max);
SET #table = 'FooTable';
SET #sql = N'CREATE TABLE [dbo].[' + #table + '](
[id] [int] IDENTITY(1,1) NOT NULL,
[AddedBy] [int] NOT NULL,
[AddedDate] [datetime2](7) NOT NULL,
CONSTRAINT [PK_' + #table + '] PRIMARY KEY CLUSTERED
( [id] ASC
)WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON) ON [PRIMARY]
) ON [PRIMARY]'
exec (#sql)
SET #sql = 'ALTER TABLE [dbo].[' + #table + '] ADD CONSTRAINT [DF_' + #table + '_AddedBy] DEFAULT ((-1)) FOR [AddedBy]'
exec (#sql)
SET #sql = 'ALTER TABLE [dbo].[' + #table + '] ADD CONSTRAINT [DF_' + #table + '_AddedDate] DEFAULT (getutcdate()) FOR [AddedDate]'
exec (#sql)
The syntax would be fine if you executed the code inside the management tool which understands the GO command as a batch separator (just like the isql and osql tools). When you execute the code using exec() the GO command is not understood, which is why you get the error.
The solution is to either remove the GO statements (or replace them with ; which ends a statement), and it will execute fine, or inline the constraints and skip the alter table statements altogether (which looks cleaner in my opinion):
SET #sql =
N'CREATE TABLE [dbo].[' + #table + '](
[id] int IDENTITY(1,1) NOT NULL,
[AddedBy] int NOT NULL CONSTRAINT [DF_' + #table + '_AddedBy] DEFAULT ((-1)),
[AddedDate] datetime2(7) NOT NULL CONSTRAINT [DF_' + #table + '_AddedDate] DEFAULT (getutcdate()) ,
CONSTRAINT [PK_' + #table + '] PRIMARY KEY CLUSTERED ( [id] ASC )
WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON
) ON [PRIMARY]
) ON [PRIMARY]
';
Also, there is really no need to use quoted identifiers with the types (or even the column names), so you might as well remove the brackets [].

Resources