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
Related
i have a situation where i want to check a certain column ( like version number) and then apply a bunch of ddl changes
trouble is i am not able to do it with in a IF BEGIN END block, since DDL statements require a GO separator between them, and TSQL wont allow that.
I am wondering if there is any way around to accomplish this
You don't need to use a full block. A conditional will execute the next statement in its entirety if you don't use a BEGIN/END -- including a single DDL statement. This is equivalent to the behavior of if in Pascal, C, etc. Of course, that means that you will have to re-check your condition over and over and over. It also means that using variables to control the script's behavior is pretty much out of the question.
[Edit: CREATE PROCEDURE doesn't work in the example below, so I changed it to something else and moved CREATE PROCEDURE for a more extended discussion below]
If ((SELECT Version FROM table WHERE... ) <= 15)
CREATE TABLE dbo.MNP (
....
)
GO
If ((SELECT Version FROM table WHERE... ) <= 15)
ALTER TABLE dbo.T1
ALTER COLUMN Field1 AS CHAR(15)
GO
...
Or something like that, depending on what your condition is.
Unfortunately, CREATE/ALTER PROCEDURE and CREATE/ALTER VIEW have special requirements that make it much harder to work with. They are pretty much required to be the only thing in a statement, so you can't combine them with IF at all.
For many scenarios, when you want to "upgrade" your objects, you can work it as a conditional drop followed by a create:
IF(EXISTS(SELECT * FROM sys.objects WHERE type='p' AND object_id = OBJECT_ID('dbo.abc')))
DROP PROCEDURE dbo.abc
GO
CREATE PROCEDURE dbo.abc
AS
...
GO
If you do really need conditional logic to decide what to do, then the only way I know of is to use EXECUTE to run the DDL statements as a string.
If ((SELECT Version FROM table WHERE... ) <= 15)
EXECUTE 'CREATE PROC dbo.abc
AS
....
')
But this is very painful. You have to escape any quotes in the body of the procedure and it's really hard to read.
Depending on the changes that you need to apply, you can see all this can get very ugly fast. The above doesn't even include error checking, which is a royal pain all on its own. This is why hordes of toolmakers make a living by figuring out ways to automate the creation of deployment scripts.
Sorry; there is no easy "right" way that works for everything. This is just something that TSQL supports very poorly. Still, the above should be a good start.
GO is recognised by client tools, not by the server.
You can have CREATEs in your stored procedures or ad-hoc queries with no GO's.
Multiple "IF" statements? You can test then for the success of subsequent DDL statements
Dynamic SQL? EXEC ('ALTER TABLE foo WITH CHECK ADD CONSTRAINT ...')?
As mentioned, GO is a client only batch separator to break down a single SQL text block into batches that are submitted to the SQL Server.
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.
Given:
code inside a stored proc:
select bleh
into #tblTemp
from FunctionThatReturnsTable('some','params')
-- do some stuff
drop table #tblTemp
-- Error on this command:
-- 'There is already an object named '#tblTemp' in the database.'
select bleh
into #tblTemp
from FunctionThatReturnsTable('some','other params')
Problem:
I can't recreate this temp table. My work around is to use #tmpTable1, #tmpTable2, #tempTable3 etc. Is there a way I can get around this? It would be nice just use one temp table each time.
If not, what is the reason for this?
As my comment reflected, I'm going to suggest that the answer is that you use a different #temp table name for each object that you create. It's kind of like saying to the doctor, "it hurts when I do this." His likely response is going to be, "stop doing that!"
The reason this is a problem is that SQL Server's parser attempts to parse the entire batch in one shot. It can clearly see that you are trying to create the same #temp table multiple times, but ignores the DROP command in between (I can't tell you exactly why that is, as I don't have access to the source code). This is the same reason you can't do this:
IF (1=1)
CREATE TABLE #foo(i INT);
ELSE
CREATE TABLE #foo(i VARCHAR(32));
The parser sees the two identical names, but can't really follow the IF/ELSE logic.
In addition to avoiding the problems multiple identically-named #temp tables causes the parser, another benefit to using unique names is that they can be re-used if you don't explicitly drop them. This will lighten the load on tempdb in terms of metadata / locking.
I ran into this problem with deleting+inserting column. The problem is probably with the parser, that it 'recognizes' the table on first create, and cannot see it was deleted.
I'd suggest using exec sp_executesql 'create table'
This is a feature by design and is clarified by Microsoft against Microsoft Connect Bug ID 666430
Please see a case study on the same at
temporary-table-could-not-be-re-created
I have a database project in my VS2010 solution. I recently changed a view and and changed a number of functions to use this view instead of going directly against a table.
But now when I deploy I get errors on most of these functions because the column asked for does not exists in the view yet.
The update of the view happends later than the update of UDF's. Is there any way to change this behaviour?
Wouldn't the best thing be if the deploy script updated in this order: tables, views, SP and UDF. It seems like tables is updated first, but the views are just thrown in somewhere in the middle of the deploy script.
Since UDFs may be used in views, and views may be used in UDFs, it would have to analyse all of them to determine a coherent deployment order - it would have to parse all of the SQL. And who knows what it's to do if you have dependencies on other databases.
Edit
There's no documented/supported way to force a deployment order, so far as I can see. With some minimal testing, it appears to me that UDFs (at least table valued) are always deployed before views.
Edit 2
Even stranger, it turns out it does do the dependency analysis. Looking at the .dbschema output file for a db project, I can see it produces a <Relationship Name="BodyDependencies"> element for the function, that does list the view/columns it depends on. But the deployment sql script still puts the view later on. Curious.
Edit 3
Probably final edit. I think, in general, that the problem is unsolvable. (Admittedly, the following only errors because I've specified schemabinding). If the old function definition relies on the old view definition, and the new function definition relies on the new view definition, there's no right way to alter the database using ALTERs:
create table dbo.T1 (
ID int not null,
C1 varchar(10) not null,
C2 varchar(10) not null,
C3 varchar(10) not null
)
go
create view dbo.V1
with schemabinding
as
select ID,C1,C2
from dbo.T1
go
create function dbo.F1()
returns table
with schemabinding
as
return select ID,C1,C2 from dbo.V1 where ID=1
go
alter view dbo.V1
with schemabinding
as
select ID,C1,C3
from dbo.T1
go
alter function dbo.F1()
returns table
with schemabinding
as
return select ID,C1,C3 from dbo.V1 where ID=1
go
result:
Msg 3729, Level 16, State 3, Procedure V1, Line 4
Cannot ALTER 'dbo.V1' because it is being referenced by object 'F1'.
Msg 207, Level 16, State 1, Procedure F1, Line 5
Invalid column name 'C3'.
I have a lot of code I am trying to run where I'm querying the sysobjects table to check if an object exists before I drop it and create it again.
Issue being, sometimes if I go:
if not exists (select name from sysobjects o where o.name = 'my_table' and o.type = 'U')
CREATE TABLE my_table (..)
go
it works, no worries. However, when I came back to run it again, I get this lovely error:
SQL Server Error on (myserver) Error:2714 at Line:10 Message:There is already an object named 'my_table' in the database.
Thanks for that, SQL Programmer. I actually asked for you not to create this table if it already exists. -_-
Any ideas?
the logic to what you are doing doesn't seem quite right. based on your statement:
"I am trying to run where I'm querying the sysobjects table to check if an object exists before I drop it and create it again"
you should simply do a delete followed by a create. This way is usually better because it ensures that the table will be updated. if the table existed and you had changes, you are probably not getting what you want.
The immediate issue you are running into is an assumed db ownership that was not consistent between runs.
based on your clarification below - here is what you can do:
IF EXISTS (SELECT * FROM sys.objects WHERE object_id = OBJECT_ID(N'[dbo].[XXXX]') AND type in (N'U'))
DROP TABLE [dbo].[XXXX]
GO
CREATE TABLE [dbo].[XXXX(...
GO
you can run this over and over again...
The sybase parsers object validation pass is global and not based on conditional evaluation. Even though your code can not execute CREATE TABLE the statement is still checked for syntax and applicability which fails when the system sees that the table already exists.
The only way around this that I know of is to put your create statements inside of an EXEC() which would be evaluated only if the section was executed.
yes, the entire batch of SQL is normalized and compiled so as to create an "execution plan" for the entire batch. During normalization, the "possible" "create table" statement is a problem if it already exists at compile time.
My solution: rename -
if exists (select 1 from ....)
begin
drop table xyz
create table xyz_zzzz ( ... )
exec sp_rename 'xyz_zzzz','xyz'
end