SQL: inner join if view exist - sql

select a_table.fname,a_table.lname,a_view.tell
from a_table
if exists(select 1 from sys.views where name='a_view' and type='v')
{
inner join a_view on a_table.id=a_view.id
}
My questions:
inner join my table with a view if i created view before
and if a_view exist then show a_view.tell because a_view is in a_view!
its incorrect if i select a_view.tell when a_view is not exist

A dynamic sql could be used for this.
DECLARE #DynSql VARCHAR(max);
SET #DynSql = 'select fname, lname from a_table';
IF OBJECT_ID('a_view', 'v') IS NOT NULL
BEGIN
SET #DynSql = 'select
t.fname, t.lname, v.tell
from a_table t
join a_view v on v.id = t.id';
END
EXEC (#DynSql);

This is too long for a comment.
Create the view! Don't make your queries conditional on whether or not tables or views exist.
A database is supposed to have a structure, and there is little to no reason to not have the tables and views that you need.
So, long before you get to where this query is needed, be sure the view exists. I'm not sure what this requires in your environment but it might include:
Creating the view with the right permissions so it cannot be accidentally deleted.
Scheduling a job that checks to be sure the view exists and/or updates the view periodically.
Creating the view earlier in the script where this should be used.

Related

Stored Procedure - delete from table with joins

I'm trying to learn SQL and I've run into a problem with stored procedures. I've got a tables structure like in the picture, where Repair.RepairerId and Repair.CarId are foreign keys for the appropriate tables.
What I need to do is to create a stored procedure that allows a user to delete entities from table Repair where user can select the car model and the repairer name in Microsoft SQL Server 2017.
For now I have the next code:
CREATE PROCEDURE [dbo].[DeleteRepairInfo]
#Name nvarchar(MAX),
#Model nvarchar(MAX)
AS
DELETE Repair.*
FROM Repair INNER JOIN Repairer ON Repair.RepairerId = Repairer.Id
INNER JOIN Car ON Repair.CarId = Car.Id
WHERE Car.Model LIKE #Model AND Repairer.Name LIKE #Name
GO
However SQL Editor in Visual Studio 2017 gives me the error:
SQL46010: Incorrect syntax near ..
Also all the INNER JOIN statements and their = signs are greyed out as well as words LIKE, AND, and the final LIKE. (I'm not sure if this is okay).
You can see this on the next picture:
I would write the logic as:
CREATE PROCEDURE [dbo].[DeleteRepairInfo] (
#Name nvarchar(MAX),
#Model nvarchar(MAX)
) AS
BEGIN
DELETE r
FROM Repair r INNER JOIN
Repairer rr
ON r.RepairerId = rr.Id INNER JOIN
Car c
ON r.CarId = c.Id
WHERE c.Model LIKE #Model AND rr.Name LIKE #Name;
END; -- DeleteRepairInfo
The issues with your query is the syntax Repair.*. That is not valid. Note some other things:
This introduces table aliases so the query is easier to write and to read.
The body of the stored procedure is surrounded by BEGIN/END so it is easy to see.
The END is commented with the name of the stored procedure.
The arguments are surrounded by parentheses.
These are all "optional". But writing clear code is a good habit to learn.
How are you?
I think the problem is that you're not using the DELETE properly
The follow example will help you!
CREATE PROCEDURE [dbo].[DeleteRepairInfo]
#Name nvarchar(MAX),
#Model nvarchar(MAX)
AS
DELETE FROM Repair as R
WHERE R.CarId in (select CarId from Car where Model = #Model)
and R.RepairerId in (select RepairerId from Repairer where Name = #Name)
GO
Assuming that you can have more than 1 car with the same model and more than one repairer with the same name!
Good luck :)

How to drop and re-create a view on all DB's on a server

I recently had a need to drop and recreate a view across all DB's on a server. Our original script used a cursor which we found to be a bit inefficient. In an earlier qution that I asked on here, the sys.sp_MSforeachdb prcedure was brought to my attention. I was able to use it to do exactly what was needed.
You just have to be mindful of the length of the exec statement. Apparently there is a length limit, the exact scripts I had were throwing errors until I removed all the aliases and bunched up the select statement. I had about 80 columns in it on separate lines.There were some aliases that were necessary, so I obviously left those where needed.
This is the script I ended up with:
USE [Master]
EXECUTE master.sys.sp_MSforeachdb
'USE [?]; IF db_name() NOT IN (''master'',''model'',''msdb'',''ReportServer'',''ReportServerTempDB'',''tempdb'')
BEGIN USE ?
IF EXISTS (SELECT * FROM sys.views WHERE object_id = OBJECT_ID(N''[ViewName]''))
DROP VIEW [ViewName]
EXEC(''
CREATE VIEW ViewName AS
SELECT
db_name() DBName, a.Col1,a.Col2,a.Col3,t.Col1
FROM Activity a
LEFT OUTER JOIN TerminologyCache t ON a.ActivityTypeName = t.TerminologyKeyName
WHERE
a.activityProviderName = ''''Parm1''''
and (ISNULL(t.TerminologyCultureName,''''en-US'''') = ''''en-US'''')
'')
END'

Drop view if exists

I have script where I want to first drop view and then create it.
I know how to drop table:
IF EXISTS (SELECT * FROM sys.tables WHERE name = 'table1' AND type = 'U') DROP TABLE table1;
so I did the same for views:
IF EXISTS (SELECT * FROM sys.views WHERE name = 'view1' AND type = 'U') DROP VIEW view1;
create view1 as(......)
and then I got error:
'CREATE VIEW' must be the first statement in a query batch.
your exists syntax is wrong and you should seperate DDL with go like below
if exists(select 1 from sys.views where name='tst' and type='v')
drop view tst;
go
create view tst
as
select * from test
you also can check existence test, with object_id like below
if object_id('tst','v') is not null
drop view tst;
go
create view tst
as
select * from test
In SQL 2016,you can use below syntax to drop
Drop view if exists dbo.tst
From SQL2016 CU1,you can do below
create or alter view vwTest
as
select 1 as col;
go
Regarding the error
'CREATE VIEW' must be the first statement in a query batch.
Microsoft SQL Server has a quirky reqirement that CREATE VIEW be the only statement in a batch. This is also true of a few other statements, such as CREATE FUNCTION. It is not true of CREATE TABLE, so go figure …
The solution is to send your script to the server in small batches. One way to do this is to select a single statement and execute it. This is clearly inconvenient.
The more convenient solution is to get the client to send the script in small isolated batches.
The GO keyword is not strictly an SQL command, which is why you can’t end it with a semicolon like real SQL commands. Instead it is an instruction to the client to break the script at this point and to send the portion as a batch.
As a result, you end up writing something like:
DROP VIEW IF EXISTS … ;
GO
CREATE VIEW … AS … ;
GO
None of the other database servers I have encountered (PostgreSQL, MySQL, Oracle, SQLite) have this quirk, so the requirement appears to be Microsoft Only.
DROP VIEW if exists {ViewName}
Go
CREATE View {ViewName} AS
SELECT * from {TableName}
Go
To cater for the schema as well, use this format in SQL 2014
if exists(select 1 from sys.views V inner join sys.[schemas] S on v.schema_id = s.schema_id where s.name='dbo' and v.name = 'someviewname' and v.type = 'v')
drop view [dbo].[someviewname];
go
And just throwing it out there, to do stored procedures, because I needed that too:
if exists(select 1
from sys.procedures p
inner join sys.[schemas] S on p.schema_id = s.schema_id
where
s.name='dbo' and p.name = 'someprocname'
and p.type in ('p', 'pc')
drop procedure [dbo].[someprocname];
go

Update statement with nontouchable field

I have mssql2008 r2 sql server
The problem:
User has some column permissions on the table. He could update some of the columns of the table (not all). We need to create UPDATE statement so that it will not violate permissions.
Preferably without dynamic query.
Is there this ability in MSSQL server?
Without dynamic SQL (or dynamic query construction in the app or API layer)? I don't think it will be very pretty. The UPDATE command doesn't have any inherent knowledge of what permissions the user might have on the affected column(s). It is going to submit the query to the engine and hope for the best. If the user doesn't have permissions on all the columns, it's going to return an error, not try to circumvent that by altering the intended statement. I think this would actually be a very bad thing to continue with the update even though not all intended columns have been updated.
That all said, I suppose you could do something like this, but it is not going to be pretty at all - in fact it will be a lot easier if you are not relying on database principals:
DECLARE
#dpid INT = DATABASE_PRINCIPAL_ID(),
#obj INT = OBJECT_ID('dbo.foo'),
#col SYSNAME = N'bar';
UPDATE dbo.foo SET bar = CASE
WHEN EXISTS -- check they've been granted UPDATE at column or table level:
(
SELECT 1
FROM sys.database_permissions AS dp
INNER JOIN sys.objects AS o
ON dp.major_id = o.[object_id]
LEFT OUTER JOIN sys.columns AS c
ON dp.minor_id = COALESCE(c.column_id, 0)
WHERE dp.grantee_principal_id = #dpid
AND o.[object_id] = #obj
AND (c.name = #col OR c.column_id IS NULL)
AND dp.[permission_name] = 'UPDATE'
AND dp.[state] = 'G' -- GRANT
)
AND NOT EXISTS -- since DENY trumps GRANT, make sure that doesn't also exist:
(
SELECT 1
FROM sys.database_permissions AS dp
INNER JOIN sys.objects AS o
ON dp.major_id = o.[object_id]
LEFT OUTER JOIN sys.columns AS c
ON dp.minor_id = COALESCE(c.column_id, 0)
WHERE dp.grantee_principal_id = #dpid
AND o.[object_id] = #obj
AND (c.name = #col OR c.column_id IS NULL)
AND dp.[permission_name] = 'UPDATE'
AND dp.[state] = 'D' -- DENY
)
THEN #bar ELSE bar END
-- WHERE...
;
This isn't exactly what you're asking for; technically it updates the column but sets it to itself (so it will still be indicated as an updated column in a trigger, for example) but it prevents the input from being applied to the table. I also did not check against permissions granted in ways other than an explicit GRANT UPDATE or DENY UPDATE to the specified user or role - for example GRANT ALL, or permissions inherited by AD group membership, can complicate this. Of course it is not going to be much fun at all to manage this if you have multiple columns to check.
You may want to add other conditionals to the WHEN clause, e.g. to avoid the check for dbo (who ) or users you want to explictly bypass the check, you could have:
CASE
WHEN DATABASE_PRINCIPAL_ID() = 1 THEN #bar
WHEN SUSER_SNAME = 'some_user' THEN #bar
WHEN (...stuff from above...)
ELSE bar
END
-- WHERE...
;

Stored procedures and the tables used by them

Is there a way to know what are the tables used by one stored procedure by doing an SQL query?
Best regards, and thanks for the help.
P.S.: I'm using SQL Server 2005.
This article on TechRepublic
Finding dependencies in SQL Server 2005
describes a way to do that:
This tutorial will show how you can
write a procedure that will look up
all of the objects that are dependent
upon other objects.
Here is the code to create the system stored procedure for finding object dependencies:
USE master
GO
CREATE PROCEDURE sp_FindDependencies
(
#ObjectName SYSNAME,
#ObjectType VARCHAR(5) = NULL
)
AS
BEGIN
DECLARE #ObjectID AS BIGINT
SELECT TOP(1) #ObjectID = object_id
FROM sys.objects
WHERE name = #ObjectName
AND type = ISNULL(#ObjectType, type)
SET NOCOUNT ON ;
WITH DependentObjectCTE (DependentObjectID, DependentObjectName, ReferencedObjectName, ReferencedObjectID)
AS
(
SELECT DISTINCT
sd.object_id,
OBJECT_NAME(sd.object_id),
ReferencedObject = OBJECT_NAME(sd.referenced_major_id),
ReferencedObjectID = sd.referenced_major_id
FROM
sys.sql_dependencies sd
JOIN sys.objects so ON sd.referenced_major_id = so.object_id
WHERE
sd.referenced_major_id = #ObjectID
UNION ALL
SELECT
sd.object_id,
OBJECT_NAME(sd.object_id),
OBJECT_NAME(referenced_major_id),
object_id
FROM
sys.sql_dependencies sd
JOIN DependentObjectCTE do ON sd.referenced_major_id = do.DependentObjectID
WHERE
sd.referenced_major_id <> sd.object_id
)
SELECT DISTINCT
DependentObjectName
FROM
DependentObjectCTE c
END
This procedure uses a Common Table
Expression (CTE) with recursion to
walk down the dependency chain to get
to all of the objects that are
dependent on the object passed into
the procedure. The main source of data
comes from the system view
sys.sql_dependencies, which contains
dependency information for all of your
objects in the database.
Try sp_depends, although you should probably recompile the stored procedure to update the statistics in the database.
Look up sp_depends system stored proc.
I think that as long as the stored procedure and the tables are all in the same database then you can right click on the procedure in SSMS and click "View Dependencies". I don't know the query behind the dialog though...
As others indicated you can use the Dependancies stored procedures; however, in my experience and this was back on SQL Server 2000, the depandancies were not always reliable. In some cases they weren't being updated. You can always go to the sysComments table assuming your schema is not encrypted.
declare #crlfSearch varchar(max),#objectSearch varchar(max),#escapeSearch varchar(max)
set #crlfSearch=('%bid' + char(13)+'%')
set #objectSearch='%bid %'
set #escapeSearch ='%[[]Bid]%'
select distinct so.name
from syscomments sc
inner join sysobjects so
on sc.id=so.id
where text like #objectSearch or text like #crlfSearch
or text like #escapesearch
This query looks for three common cases you might have to add some but basically we find where the table name has a space after it, (This helps to limit cases where the table name is part of another table name), Has a return at the end of it, or is escaped within brackets.