I'm writing a script to create a view, only IF the view does not already exist. If the view does already exist, I don't want to alter it or drop and re-create it. The syntax below is obviously not complete, and generates an error because CREATE VIEW needs to be in its own batch - but what is the proper way to construct my use case?
IF OBJECT_ID('dbo.view_name') IS NULL
BEGIN
CREATE VIEW [dbo].[view_name]
AS
SELECT ...;
END
ELSE
...
SQL Server 2016 has CREATE OR ALTER.
CREATE OR ALTER VIEW vw_your_view
AS
SELECT 1 FROM your_Table
GO
This will blow up if you move it to an environment below SQL Server 2016. If that is the case, go with what you have and check for an obj ID.
I changed the SQL to be the following. To avoid the "CREATE VIEW must be in its own batch" error, I wrapped the "CREATE VIEW" inside an exec('') statement. This works!
USE [XXXXXXXXX]
GO
/****** Object: View [CXXXXXXX].[_MobileMessageTracking_v2] Script Date: 5/4/2018 3:19:04 PM ******/
SET ANSI_NULLS ON
GO
SET QUOTED_IDENTIFIER ON
GO
IF Object_id('CXXXXXXX._MobileMessageTracking_v2') IS NULL BEGIN
exec('CREATE VIEW [CXXXXXXX].[_MobileMessageTracking_v2]
AS
SELECT
/* fields here */
FROM CXXXXXXXX._MobileMessageTracking AS M WITH (NOLOCK)
WHERE M.MID = XXXXXXX
AND M.CreateDateTime >= DATEADD(mm,-6, GETDATE())
UNION
select
/* fields here */')
END
GO
Related
I'm trying to rename columns in a table for which there is a trigger. I've used SQL > Refactor > Rename to rename the column. And the trigger file also gets updated. However, when I publish, I get this:
Procedure TR_accrual_Accrual_Update, Line 134 Invalid column name
'MinHoursRule'.
That's the old column name. I'm assuming the publish is updating the table first, and sees that the current/old trigger still has the old column name.
Is it possible to rename a column, update the trigger, and publish?
The only solution I can really think of is to do this:
Delete the triggers and publish
Rename the columns
Add the triggers again
Publish
This is what I did as a work-around:
Add the new columns
Leave the old columns
Have the trigger use both sets of columns
Publish/deploy to prod soon
Remove the old columns
Publish/deploy to prod later
So, instead of renaming, I just created new columns, and then eventually deleted the old ones.
Yuck. But it worked.
Note: In our C# domain models, I only reference the new columns.
I guess that you have something wrong with the publish profile settings. You might have something disabled, for example "Do not modify triggers" or something like that. I just created new SSDT project in VS 2019 with following structure:
CREATE TABLE [dbo].[test]
(
[Id] INT ,
b int
)
GO
CREATE TRIGGER [dbo].[Trigger_test]
ON [dbo].[test]
FOR DELETE, INSERT, UPDATE
AS
BEGIN
SET NoCount ON
insert into test2 select b from inserted
END
GO
CREATE TABLE [dbo].[test2]
(
a int
)
GO
Published the project with default settings to the new database and made single insert to the dbo.test table. Made sure that there is record in dbo.test2 table. After that I re-factored dbo.test.b column to dbo.test.a then published again and everything worked. This is generated script:
/*
Deployment script for trg_test
This code was generated by a tool.
Changes to this file may cause incorrect behavior and will be lost if
the code is regenerated.
*/
GO
SET ANSI_NULLS, ANSI_PADDING, ANSI_WARNINGS, ARITHABORT, CONCAT_NULL_YIELDS_NULL, QUOTED_IDENTIFIER ON;
SET NUMERIC_ROUNDABORT OFF;
GO
:setvar DatabaseName "trg_test"
:setvar DefaultFilePrefix "trg_test"
:setvar DefaultDataPath ""
:setvar DefaultLogPath ""
GO
:on error exit
GO
/*
Detect SQLCMD mode and disable script execution if SQLCMD mode is not supported.
To re-enable the script after enabling SQLCMD mode, execute the following:
SET NOEXEC OFF;
*/
:setvar __IsSqlCmdEnabled "True"
GO
IF N'$(__IsSqlCmdEnabled)' NOT LIKE N'True'
BEGIN
PRINT N'SQLCMD mode must be enabled to successfully execute this script.';
SET NOEXEC ON;
END
GO
USE [$(DatabaseName)];
GO
PRINT N'The following operation was generated from a refactoring log file 80d0e5de-e188-465e-b83c-18f38a1cec98';
PRINT N'Rename [dbo].[test].[b] to a';
GO
EXECUTE sp_rename #objname = N'[dbo].[test].[b]', #newname = N'a', #objtype = N'COLUMN';
GO
PRINT N'Altering Trigger [dbo].[Trigger_test]...';
GO
ALTER TRIGGER [dbo].[Trigger_test]
ON [dbo].[test]
FOR DELETE, INSERT, UPDATE
AS
BEGIN
SET NoCount ON
insert into test2 select a from inserted
END
GO
-- Refactoring step to update target server with deployed transaction logs
IF OBJECT_ID(N'dbo.__RefactorLog') IS NULL
BEGIN
CREATE TABLE [dbo].[__RefactorLog] (OperationKey UNIQUEIDENTIFIER NOT NULL PRIMARY KEY)
EXEC sp_addextendedproperty N'microsoft_database_tools_support', N'refactoring log', N'schema', N'dbo', N'table', N'__RefactorLog'
END
GO
IF NOT EXISTS (SELECT OperationKey FROM [dbo].[__RefactorLog] WHERE OperationKey = '80d0e5de-e188-465e-b83c-18f38a1cec98')
INSERT INTO [dbo].[__RefactorLog] (OperationKey) values ('80d0e5de-e188-465e-b83c-18f38a1cec98')
GO
GO
PRINT N'Update complete.';
GO
I'm trying to script a pretty straight-forward indexed view but SQL is simply refusing my efforts. I've tried researching the issue but haven't had any luck, and unfortunately I'm not exactly a SQL expert so I'm not sure if there's something simple that I'm missing. The template for this script was handed to me by a DBA, but he doesn't know any more than I do. Here's the top end of the script where the first error appears:
--Set the options to support indexed views.
SET NUMERIC_ROUNDABORT OFF;
GO
SET ANSI_PADDING, ANSI_WARNINGS, CONCAT_NULL_YIELDS_NULL, ARITHABORT,
QUOTED_IDENTIFIER, ANSI_NULLS ON;
GO
--Create view with schemabinding.
IF OBJECT_ID ('[dbo].[APIMenus]', 'view') IS NOT NULL
DROP VIEW [dbo].[APIMenus] ;
GO
BEGIN
CREATE VIEW [dbo].[APIMenus]
WITH SCHEMABINDING
AS
SELECT
[t0].[GroupName] AS [defaultValue],
[t1].[GroupName] AS [transValue],.....
The error is "CREATE VIEW must be the only statement in the batch" but from what I understand, wrapping it in the BEGIN...END with proper GO statements before and after should have solved the problem, yet it persists. Can anyone spot what I'm doing wrong?
The error is accurate. Wrapping a CREATE VIEW in BEGIN/END does not work to avoid this message. If you remove the BEGIN/END entirely, so the batch starts with CREATE VIEW and ends with another GO after the view is defined, then the error will be gone, and you can continue to use this DROP before CREATE structure.
A slightly different way to handle this problem of not creating a view that already exists, is to invoke dynamic SQL:
if OBJECT_ID ('[dbo].[cars]', 'view') IS NOT NULL
exec ('create view cars as select * from vans where type=1')
go
Sometimes I have seen this:
if OBJECT_ID ('[dbo].[cars]', 'view') IS NOT NULL
exec ('create view cars as select 1')
go
alter view cars as
select * from vans where type==1
GO
This latter technique has the advantage that your complex SQL is not embedded in a string, and you can format it nicely.
Check DROP VIEW IF EXISTS and CREATE OR ALTER VIEW, both supported in SQL Server 2016.
Ref:
https://blogs.msdn.microsoft.com/sqlserverstorageengine/2016/11/17/create-or-alter-another-great-language-enhancement-in-sql-server-2016-sp1/
I get this error message in SQL Server when I write the following ALTER VIEW statement. The ALTER VIEW line is underlined in red.
IF EXISTS(SELECT * FROM sys.views WHERE name = 'SigHierarchyView')
ALTER VIEW [dbo].[SigHierarchyView] WITH SCHEMABINDING AS
(
SELECT [Sig].[Id]
,[Sig].[UniqueId]
,[Sig].[TenantId] AS [ParentTenantId]
,[Sig].[Code_FR]
,[Sig].[Code_EN]
,[Sig].[DecodedText_FR]
,[Sig].[DecodedText_EN]
,[Sig].[DecodedText_ES]
,[Sig].[DecodedText_IT]
,[Sig].[DecodedText_VI]
,[Sig].[DecodedText_CH]
,[Sig].[Timestamp]
,[Sig].[Deleted]
,[Sig].[CreationDate]
,[Sig].[CreationUserUniqueId]
,[Sig].[ModificationDate]
,[Sig].[ModificationUserUniqueId]
,TenantHierarchy.[LeafTenantId] AS [TenantId]
FROM dbo.[Sig]
INNER JOIN dbo.TenantHierarchy ON [Sig].TenantId = TenantHierarchy.ParentTenantId
WHERE TenantHierarchy.Level = (
SELECT MIN(TenantHierachieIn.Level)
FROM dbo.TenantHierarchy TenantHierachieIn
INNER JOIN dbo.[Sig] as SigIn
ON SigIn.TenantId = TenantHierachieIn.ParentTenantId
AND SigIn.UniqueId = [Sig].UniqueId
WHERE TenantHierachieIn.[LeafTenantId] = TenantHierarchy.[LeafTenantId]
)
)
GO
I tried putting a BEGIN and END but that didn't help. I'm using SQL Server 2012.
Batches in SQL Server are separated by the "GO" keyword. As you don't have any of those in your script, everything is in one batch. And, as you already found out, alter view needs to be in its own batch. One idiom that I like to use to protect against this is as follows:
if object_id('[dbo].[myView]') is not null --object exists
set noexec on;
go
-- this will only get run if the object doesn't exist
create view [dbo].[myView]
as
select 'stub' as message
go
set noexec off;
go
alter view [dbo].[myView]
as
-- actual view definition here
This way, I can use the scripting in SSMS to script out an alter view statement, throw the couple of lines of guard code on top of it, and I'm done.
ALTER VIEW will automatically overwrite the existing view. So you don't need the check first. But, for some reason, if you want to, SQL Server is telling you that a CREATE/ALTER view statement must be the first in a batch of statements. A batch can be separated by a GO. So what you can do is to drop the view first if it exists, in a batch, and then create the view in a new batch.
IF EXISTS(SELECT * FROM sys.views WHERE name = 'SigHierarchyView')
Drop View SigHierarchyView
GO
CREATE View...
Is there a way to run some function like a trigger when a table is created in the database in SQL SERVER 2008?
Yes, it's called a DDL trigger. The documentation for CREATE TRIGGER has a sample for DROP_SYNONYM (a very questionable choice for an example) but you'll want the CREATE_TABLE event instead. A better starting point in understanding how they work is probably here:
http://msdn.microsoft.com/en-us/library/ms190989.aspx
If you have more specific details, e.g. what exactly do you want to pass to this function (I assume you mean procedure), or what does the procedure do, we can provide more useful and specific help.
Yes a DDL Trigger. For example, here is some code I written to prevent some tables from being modified:
PRINT N'Create DDL trigger to prevent changes to various tables'
GO
SET QUOTED_IDENTIFIER ON
GO
CREATE TRIGGER NoEditCertainTables ON DATABASE
FOR DROP_TABLE, ALTER_TABLE, CREATE_TABLE
AS
SET CONCAT_NULL_YIELDS_NULL ON
DECLARE #AffectedTable varchar(255)
SELECT #AffectedTable = EVENTDATA().value('(/EVENT_INSTANCE/ObjectName)[1]','nvarchar(100)')
--This is the name of the table(s) you dont want messed with
IF (#AffectedTable IN ('Table1','Table2','Table3'))
BEGIN
ROLLBACK;
END
SET CONCAT_NULL_YIELDS_NULL OFF
GO
I need to alter a trigger in sql server 2005 and I want to do it using a table adapter and a sql statement, not a stored proc. I know I could run this in the query browser and execute it but I need to deploy it across several databases and use a table adapter in the update. Is this possible?
Doing Add Query -> Update -> pasting the below code -> Query Builder to see if it parses
print("USE [DataBaseName]
GO
/****** Object: Trigger [dbo].[UpdateCurrentAddress] Script Date: 10/30/2008 14:47:15 ******/
SET ANSI_NULLS ON
GO
SET QUOTED_IDENTIFIER ON
GO
ALTER TRIGGER [dbo].[UpdateCurrentAddress] ON [dbo].[PreviousAddresses]
FOR INSERT, UPDATE, DELETE
AS
-- Check to see if there was an insert/update or a deletion.
IF (SELECT COUNT(*) FROM inserted) >= 1
BEGIN
IF (SELECT CountryID FROM inserted) <> 181
...moar...");
error...
The USE [DataBaseName] SQL construct or statement is not supported.
and if i remove the top part and start at just ALTER TRIGGER
The ALTER TRIGGER SQL construct or statement is not supported.
I'm still fairly new to this and would not be surprised that either I'm either going about this the wrong way and/or it's impossible to do without a stored proc.
EDIT: Yeah I'm doing this in C#.
thanks. I can do it that way starting at ALTER TRIGGER, this will get the job done.
The TableAdapter is expecting a resultset, not an actual query. To do this successfully you will need to use the SqlCommand object to actually peform your update.
If you have not used one before it is quite simple, first you declare your connection, then you create your command using the connection. Once the command is created set the commandtext equal to your script, and then you can call the ExecuteNonQuery() method to run the script after opening the connection. If you say what language you are using, I can try to provide an example.
Edit
Here is a C# example, quick and dirty but it gets the point across. NOTE, done from memory, but should be correct.
using(SqlConnection oConnection = new SqlConnection("Yourconnectionhere"))
using(SqlCommand oCommand = new SqlCommand(oConnection))
{
//Configure the command object
oCommand.CommandText = "Your script here";
//Open connectin and run script
oConnection.Open();
oCommand.ExecuteNonQuery();
oConnection.Close();
}