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/
Related
I have a table that is the result of an INNER JOIN and then to that table I have to a apply a bunch of queries, so at the end the whole code in SQL is really big and in the future (and that's the main problem) I will have problems to understand what did I do.
So for this reason I am considering the possibility of creating views, so in each step I have a view created and I know what does each one of the queries.
So with that goal I started to use IF ELSE statements to create views if they dont' exist etc. but I'm stumbling with a lot of errors and problems.
First of all, this is the way I'm creating a view with the IF statement:
-- This portion of code is common in all the views
IF NOT EXISTS
(
SELECT 1
FROM sys.views
WHERE Name = 'NAME_OF_THE_VIEW'
)
BEGIN
EXEC('CREATE VIEW NAME_OF_THE_VIEW AS SELECT 1 as Val')
END
GO
ALTER VIEW NAME_OF_THE_VIEW
AS
-- Here I put the query of the view
SELECT *
FROM table_1
When I execute this code it works, but the SQL Server Management Studio underlines "NAME_OF_THE_VIEW" in the ALTER VIEW statement and when I hover the mouse it says: Invalid object name 'NAME_OF_THE_VIEW'. I don't understand why if there's a supposed error it still works.
The other problem is that when I introduce more code like the code above in order to create other views in the same script, the whole ALTER VIEW statement is underlined and when I hover this message appears; Incorrect syntax: 'ALTER VIEW' must be the only statement in the batch.
So the question is: hoy can I put everything in the same script, where I can create views to avoid doing a lot of subqueries, and without getting this errors? The SQL-Server version is 15.
So the question is: hoy can I put everything in the same script, where I can create views to avoid doing a lot of subqueries, and without getting this errors?
There's no need to check for existence of the view. Just CREATE OR ALTER VIEW
CREATE OR ALTER VIEW NAME_OF_THE_VIEW
AS
-- Here I put the query of the view
SELECT *
FROM table_1
GO
CREATE OR ALTER VIEW NAME_OF_THE_OTHER_VIEW
AS
-- Here I put the query of the view
SELECT *
FROM table_1
I am stuck with the following stored procedure where I can't seem to get the IF EXISTS and DROP parts to work, leading to a failure in the SELECT INTO part.
Both database A and database B are on the same server, I have full permissions in both databases. The stored procedure is in database A.
I have copied the IF EXISTS syntax from somewhere (can't remember where) so I don't really understand the structure of it. I gathered the problem lies in the IF EXISTS statement because when I try and execute IF EXISTS component of the stored procedure, I get something if I have selected DatabaseB in the top left-hand corner drop-down box in Management Studio but if I have DatabaseA selected in there, I get nothing.
I have also tried to run similarly structured stored procedures in DatabaseA (where there is an IF EXISTS and DROP statements pointing to DatabaseB followed by a SELECT INTO from DatabaseA into DatabaseB) and I have got some to work before, while some others failed. I cant seem to pinpoint what is causing it to work sometimes and sometimes not.
USE [DatabaseA]
GO
SET ANSI_NULLS ON
GO
SET QUOTED_IDENTIFIER ON
GO
--DROP TABLE A if exists--
IF EXISTS (SELECT * FROM sys.objects
WHERE object_id = OBJECT_ID(N'DatabaseB.dbo.TableA') AND type IN (N'U'))
DROP TABLE DatabaseB.dbo.TableA
--Select INTO TableA on DatabaseB--
SELECT *
INTO DatabaseB.dbo.TableA
FROM DatabaseA.dbo.TableA
I usually use if object_id('databaseB.dbo.TableA') is not null instead of the exists check to avoid having to fully qualify sys.objrcts.
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
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