SQL Server : create table using variables - sql-server

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 [].

Related

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 find out sum of dynamic columns in SQL Pivot Query

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.

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

TSQL sp_executesql

I have a simple table:
CREATE TABLE [dbo].[test]
(
[eins] [varchar](50) NOT NULL,
[zwei] [varchar](50) NULL,
CONSTRAINT [PK_test]
PRIMARY KEY CLUSTERED ([eins] ASC)
WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF,
IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS = ON,
ALLOW_PAGE_LOCKS = ON) ON [PRIMARY]
) ON [PRIMARY]
with two columns eins und zwei, both varchar(50)
with the values
insert into test(eins, zwei) values(1,2)
1 and 2 in the corresponding columns.
The query
select eins from test
gives the correct result of 1
the following code also gives the correct result of 1 in the results window:
declare
#in varchar(50),
#sql nvarchar(500)
set #in = 'eins'
set #sql = 'select ' + #in + ' from test'
Exec(#sql)
However, it doesn't make use of an output parameter and I need the result for further processing.
So, I try:
exec sp_executesql N' Select #1 from test where zwei = #2',N'#1 nvarchar(100),#2 nvarchar(100)',#1=N'eins',#2=N'2'
with an expected result of 1. However: the result is eins, i.e., the column name, not the value.
How can I query for something like Select #Variable from #Variable2 where #variabel3 = #Variable4?
The table and columns can be non-variable, if need be, what's primarily important is, the Select #Variable. I need this value for further processing.
Try something like this
DECLARE #result int
exec sp_executesql
N'Select #1=eins from test where zwei = #2',
N'#1 nvarchar(100) OUTPUT,#2 nvarchar(100)',
#1=#result OUTPUT,#2=N'2'
SELECT #result
What that does is say that the #1 is an OUTPUT variable inside the EXECed query string. Then it binds #result to the #1, so you can retrieve it. I've never found OUTPUT parameters very intuitive to use.
The Code from DWright in the last post has the correct result, but the main problem isn't solved.
I dont know the name of the column while writing the code. The following code seems to be correct:
Declare #result int
Declare #sql nvarchar(500)
Declare #columnname nvarchar(50)
set #columnname = 'eins'
set #sql= N'Select #1= ' + #columnname +' from test1 where zwei = #2'
exec sp_executesql
#sql,
N'#1 nvarchar(100) OUTPUT,#2 nvarchar(100)',
#1=#result OUTPUT,#2=N'2'
SELECT #result
And the result is the expectet 1
Thank you for helping

Quoted_Identifier SQL Server - Off - Unsure How

We've been having an issue every few months where all of a sudden a job or procedure will start failing due to a quoted_Identifier issue. The quoted identifier in the proc or even table will change from 1 to 0 and we don't see any recent modified date on that record. We aren't sure how this is happening and because it's so sporadic, I can't reproduce it or trace it easily. Any ideas as to why this is occurring or what I can do to find out. I've done a lot of research without luck so far.
Thanks
You could create a DDL trigger activated by CREATE/ALTER PROCEDURE events. Inside this trigger you could use EVENTDATE function to get information about SQL statements execute including ANSI_NULLS and QUOTED_IDENTIFIER settings.
For example, you could use ddlDatabaseTriggerLog ddl trigger from Adventure Works OLTP database that insert into [dbo].[DatabaseLog] all ddl changes from current database.
DDL Trigger:
CREATE TRIGGER [ddlDatabaseTriggerLog] ON DATABASE
FOR DDL_DATABASE_LEVEL_EVENTS AS
BEGIN
SET NOCOUNT ON;
SET ANSI_WARNINGS ON;
DECLARE #data XML;
DECLARE #schema sysname;
DECLARE #object sysname;
DECLARE #eventType sysname;
SET #data = EVENTDATA();
SET #eventType = #data.value('(/EVENT_INSTANCE/EventType)[1]', 'sysname');
SET #schema = #data.value('(/EVENT_INSTANCE/SchemaName)[1]', 'sysname');
SET #object = #data.value('(/EVENT_INSTANCE/ObjectName)[1]', 'sysname')
IF #object IS NOT NULL
PRINT ' ' + #eventType + ' - ' + #schema + '.' + #object;
ELSE
PRINT ' ' + #eventType + ' - ' + #schema;
IF #eventType IS NULL
PRINT CONVERT(nvarchar(max), #data);
INSERT [dbo].[DatabaseLog]
(
[PostTime],
[DatabaseUser],
[Event],
[Schema],
[Object],
[TSQL],
[XmlEvent]
)
VALUES
(
GETDATE(),
CONVERT(sysname, CURRENT_USER),
#eventType,
CONVERT(sysname, #schema),
CONVERT(sysname, #object),
#data.value('(/EVENT_INSTANCE/TSQLCommand)[1]', 'nvarchar(max)'),
#data
);
END;
GO
[dbo].[DatabaseLog]
SET ANSI_NULLS ON
GO
SET QUOTED_IDENTIFIER ON
GO
CREATE TABLE [dbo].[DatabaseLog](
[DatabaseLogID] [int] IDENTITY(1,1) NOT NULL,
[PostTime] [datetime] NOT NULL,
[DatabaseUser] [sysname] NOT NULL,
[Event] [sysname] NOT NULL,
[Schema] [sysname] NULL,
[Object] [sysname] NULL,
[TSQL] [nvarchar](max) NOT NULL,
[XmlEvent] [xml] NOT NULL,
CONSTRAINT [PK_DatabaseLog_DatabaseLogID] PRIMARY KEY NONCLUSTERED
(
[DatabaseLogID] 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
Who/when changed dbo.uspGetEmployeeManagers procedure ?
SELECT *
FROM [dbo].[DatabaseLog] x
WHERE x.[Event] IN ('CREATE_PROCEDURE', 'ALTER_PROCEDURE')
AND x.[Schema] = 'dbo'
AND x.[Object] = 'uspGetEmployeeManagers'
XmlEvent column content:
<EVENT_INSTANCE>
<EventType>CREATE_PROCEDURE</EventType>
...
<TSQLCommand>
<SetOptions ANSI_NULLS="ON" ANSI_NULL_DEFAULT="ON" ANSI_PADDING="ON" QUOTED_IDENTIFIER="ON" ENCRYPTED="FALSE" />
<CommandText>
CREATE PROCEDURE [dbo].[uspGetEmployeeManagers]
#BusinessEntityID [int]
AS
...
Those scripts are helpful and I'll use them in the future. I figured out what the issue is. A stored procedure had quoted_identifier set to 0 and it had been that way for at least a year. However, someone created a filtered index on a table that the stored procedure was using. They didn't realize that the filtered index required quoted_identifier to be set to 1.

Resources