How to check if a view exists that uses a table - sql

Is it possible to check if a table is part of a view in same or different database using SQL Server Management Studio?
If it can be done through some plugins, that would be fine too.

Like this:
SELECT *
FROM INFORMATION_SCHEMA.VIEW_TABLE_USAGE
WHERE TABLE_SCHEMA = 'dbo' --(or whatever your Schema name is)
AND TABLE_NAME = 'YourTableName'
Should work on any ISO SQL compliant database, not just SQL Server.
Note that cross-database dependencies are another matter. In theory, they should show up here however, in practice this may be inconsistent because SQL Server does allow deferred resolution, even for Views, when it comes to cross-database references.

SELECT QUOTENAME(OBJECT_SCHEMA_NAME([object_id]))
+ '.' + QUOTENAME(OBJECT_NAME([object_id]))
FROM sys.sql_dependencies
WHERE referenced_major_id = OBJECT_ID(N'dbo.your_table_name');
Or:
SELECT referencing_schema_name, referencing_entity_name
FROM sys.dm_sql_referencing_entities(N'dbo.your_table_name', N'OBJECT');
However note that some of these methods, including sp_depends, INFORMATION_SCHEMA, sysdepends etc. are all prone to falling out of sync. More information here:
Keeping sysdepends up to date in SQL Server 2008
A quick example:
CREATE TABLE dbo.table1(id INT);
GO
CREATE VIEW dbo.view1
AS
SELECT id FROM dbo.table1;
GO
SELECT QUOTENAME(OBJECT_SCHEMA_NAME([object_id]))
+ '.' + QUOTENAME(OBJECT_NAME([object_id]))
FROM sys.sql_dependencies
WHERE referenced_major_id = OBJECT_ID('dbo.table1');
-- returns 1 row
GO
DROP TABLE dbo.table1;
GO
CREATE TABLE dbo.table1(id INT);
GO
SELECT QUOTENAME(OBJECT_SCHEMA_NAME([object_id]))
+ '.' + QUOTENAME(OBJECT_NAME([object_id]))
FROM sys.sql_dependencies
WHERE referenced_major_id = OBJECT_ID('dbo.table1');
-- returns 0 rows!!!!
If you execute the following, it will return rows again:
EXEC sp_refreshsqlmodule N'dbo.view1';
But who wants to be refreshing every view in the system, every time you want to check the metadata?
So you may want to combine this method with brute force parsing of the text for all your views:
SELECT name FROM sys.views
WHERE OBJECT_DEFINITION([object_id])
LIKE N'%your_table_name%';
That is liable to get some false positives depending on the name of your table, but it's probably a good cross-check.
To avoid this kind of issue, I've tried to get into the habit of creating my views WITH SCHEMABINDING (or just avoiding views as much as possible). Sure, that can become a pain when you need to change the table in a way that doesn't affect the view, but table changes should be taken seriously anyway.

For same databse, you can check dependencies for that table and see what other objects uses it.
EXEC sp_depends #objname = N'your_table_name' ;

Related

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'

SQL Server - find SPs which don't drop temp tables

(1) Is there a good/reliable way to query the system catalogue in order
to find all stored procedures which create some temporary tables in their
source code bodies but which don't drop them at the end of their bodies?
(2) In general, can creating temp tables in a SP and not dropping
them in the same SP cause some problems and if so, what problems?
I am asking this question in the contexts of
SQL Server 2008 R2 and SQL Server 2012 mostly.
Many thanks in advance.
Not 100% sure if this is accurate as I don't have a good set of test data to work with. First you need a function to count occurrences of a string (shamelessly stolen from here):
CREATE FUNCTION dbo.CountOccurancesOfString
(
#searchString nvarchar(max),
#searchTerm nvarchar(max)
)
RETURNS INT
AS
BEGIN
return (LEN(#searchString)-LEN(REPLACE(#searchString,#searchTerm,'')))/LEN(#searchTerm)
END
Next make use of the function like this. It searches the procedure text for the strings and reports when the number of creates doesn't match the number of drops:
WITH CreatesAndDrops AS (
SELECT procedures.name,
dbo.CountOccurancesOfString(UPPER(syscomments.text), 'CREATE TABLE #') AS Creates,
dbo.CountOccurancesOfString(UPPER(syscomments.text), 'DROP TABLE #') AS Drops
FROM sys.procedures
JOIN sys.syscomments
ON procedures.object_id = syscomments.id
)
SELECT * FROM CreatesAndDrops
WHERE Creates <> Drops
1) probably no good / reliable way -- though you can extract the text of sp's using some arcane ways that you can find in other places.
2) In general - no this causes no problems -- temp tables (#tables) are scope limited and will be flagged for removal when their scope disappears.
and table variables likewise
an exception is for global temp tables (##tables) which are cleaned up when no scope holds a reference to them. Avoid those guys -- there are usually (read almost always) better ways to do something than with a global temp table.
Sigh -- if you want to go down the (1) path then be aware that there are lots of pitfalls in looking at code inside sql server -- many of the helper functions and information tables will truncate the actual code down to a NVARCHAR(4000)
If you look at the code of sp_helptext you'll see a really horrible cursor that pulls the actual text..
I wrote this a long time ago to look for strings in code - you could run it on your database -- look for 'CREATE TABLE #' and 'DROP TABLE #' and compare the outputs....
DECLARE #SearchString VARCHAR(255) = 'DELETE FROM'
SELECT
[ObjectName]
, [ObjectText]
FROM
(
SELECT
so.[name] AS [ObjectName]
, REPLACE(comments.[c], '#x0D;', '') AS [ObjectText]
FROM
sys.objects AS so
CROSS APPLY (
SELECT CAST([text] AS NVARCHAR(MAX))
FROM syscomments AS sc
WHERE sc.[id] = so.[object_id]
FOR XML PATH('')
)
AS comments ([c])
WHERE
so.[is_ms_shipped] = 0
AND so.[type] = 'P'
)
AS spText
WHERE
spText.[ObjectText] LIKE '%' + #SearchString + '%'
Or much better - use whatever tool of choice you like on your codebase - you've got all your sp's etc scripted out into source control somewhere, right.....?
I think SQL Search tool from red-gate would come handy in this case. You can download from here. This tool will find the sql text within stored procedures, functions, views etc...
Just install this plugin and you can find sql text easily from SSMS.

SQL Server 2014 'Incorrect syntax near the keyword 'select'.'

Is there anything wrong in the following sql code:
ALTER TABLE [idconfirm].[EPS_ENROLLMENT]
DROP CONSTRAINT select d.name from syscolumns c,sysobjects d, sysobjects t
where c.id=t.id AND d.parent_obj=t.id AND d.type='D' AND t.type='U'
AND c.name='ENC_TOKEN_KEK_SALT_SIZE' AND t.name='EPS_ENROLLMENT';
You can't just say
ALTER TABLE FOO DROP CONSTRAINT;
You must give a constraint name:
ALTER TABLE FOO DROP CONSTRAINT CONS_SOME_CONS_NAME;
It seems you are trying to use the latter query to return a result set of constraint names? You can't feed the ALTER statement with a SELECT like that, it doesn't work either. Either use SQL to generate the script, then save the script and run it, or use dynamic SQL.
Dynamic SQL is one of the most dangerous practices to use when dropping objects, yet I see it suggested so casually on StackOverflow. You don't use it unless you need automation and you need a script that will adapt to potential differences (such as deploying a change script to a customer). Otherwise, you should be practicing safe-DBA and generating the script to a text window, then validate it before executing it. Dynamic SQL jumps directly from the generation to the execution. Take the 15 extra seconds to validate.
You can save it and deploy it to other databases with the knowledge that only the actions in that script will be run. Deploying dynamic SQL also deploys potentially a different action to each database; sometimes this is what you want, and sometimes it is not.
To generate the DDL like so, from the SSMS menu pick "Results to Text", or CTRL-T for short, then run:
SELECT 'ALTER TABLE T DROP CONSTRAINT ' + d.name
from syscolumns c, sysobjects d, sysobjects t
where c.id=t.id AND d.parent_obj=t.id AND d.type='D' AND t.type='U'
AND c.name='ENC_TOKEN_KEK_SALT_SIZE' AND t.name='EPS_ENROLLMENT';
Then you have your script, copy-paste it, verify it, save it. This technique works in any database with a data dictionary catalog.
ALTER TABLE .. DROP CONSTRAINT SELECT .. is not valid syntax; an identifier ("constraint_name") is required after DROP CONSTRAINT.
As with all SQL DDL, the column identifiers are not values and cannot be the result of an expression (SELECT or otherwise). To use a value in such a statement requires building and executing Dynamic SQL.
For instance,
declare #name varchar(200)
-- I recommend not using implicit cross-joins, but ..
select #name=d.name
from syscolumns c, sysobjects d, sysobjects t
where c.id=t.id AND d.parent_obj=t.id AND d.type='D' AND t.type='U'
AND c.name='ENC_TOKEN_KEK_SALT_SIZE' AND t.name='EPS_ENROLLMENT';
-- Build dynamic SQL
SET #sql = 'ALTER TABLE idconfirm.EPS_ENROLLMENT DROP CONSTRAINT ' + #name;
-- And execute it
EXEC (#sql)
You really should not be using sysobjects as this has been deprecated for a long time. In fact I'm struggling to understand why no one else has pointed this out yet. Whilst other people are correct in their analysis of the problems with your SQL statement (notably #mrjoltcola who has give a good explanation of why your statement is wrong), as supposed experts answering this question we should also be educating those asking the questions to ensure that they don't use outdated and deprecated syntax. In addition, where there is an opportunity to improve the quality of the OPs code, we should also take it in order to reduce the possibility that at some point they will come back and complain that their code has broken because they upgraded their server or whatever (granted, I know this is about SQL 2014 but the next update will likely cut off sysobjects at the knees as it has had the Sword of Damocles hanging over it long enough).
So, with all that in mind I would adapt mrjoltcola's answer as follows, using sys.columns, sys.tables and sys.default_constraints as well as using explicit INNER JOINS.
SELECT 'ALTER TABLE [idconfirm].[EPS_ENROLLMENT] DROP CONSTRAINT ' + d.[name]
FROM sys.tables t
INNER JOIN sys.columns c
ON c.[object_id] = t.[object_id]
INNER JOIN sys.default_constraints d
ON d.[parent_object_id] = t.[object_id]
AND d.[parent_column_id] = c.[column_id]
WHERE t.name='EPS_ENROLLMENT'
AND c.name='ENC_TOKEN_KEK_SALT_SIZE';
That will give you drop code for that one table and column. Of course, you could assign the result of that query to an NVARCHAR variable and then call sp_executesql if you needed to perform the whole operation programatically like this.
EXEC sp_executesql #sql
However it's all too easy to get into trouble using dynamic SQL so if you're at all unsure then I'd avoid it wherever possible - it should really be a method of last resort.
Do you not need to provide constraint name to drop in your earlier alter statement?

Drop all temporary tables for an instance

I was wondering how / if it's possible to have a query which drops all temporary tables?
I've been trying to work something out using the tempdb.sys.tables, but am struggling to format the name column to make it something that can then be dropped - another factor making things a bit trickier is that often the temp table names contain a '_' which means doing a replace becomes a bit more fiddly (for me at least!)
Is there anything I can use that will drop all temp tables (local or global) without having to drop them all individually on a named basis?
Thanks!
The point of temporary tables is that they are.. temporary. As soon as they go out of scope
#temp create in stored proc : stored proc exits
#temp created in session : session disconnects
##temp : session that created it disconnects
The query disappears. If you find that you need to remove temporary tables manually, you need to revisit how you are using them.
For the global ones, this will generate and execute the statement to drop them all.
declare #sql nvarchar(max)
select #sql = isnull(#sql+';', '') + 'drop table ' + quotename(name)
from tempdb..sysobjects
where name like '##%'
exec (#sql)
It is a bad idea to drop other sessions' [global] temp tables though.
For the local (to this session) temp tables, just disconnect and reconnect again.
The version below avoids all of the hassles of dealing with the '_'s. I just wanted to get rid of non-global temp tables, hence the '#[^#]%' in my WHERE clause, drop the [^#] if you want to drop global temp tables as well, or use a '##%' if you only want to drop global temp tables.
The DROP statement seems happy to take the full name with the '_', etc., so we don't need to manipulate and edit these. The OBJECT_ID(...) NOT NULL allows me to avoid tables that were not created by my session, presumably since these tables should not be 'visible' to me, they come back with NULL from this call. The QUOTENAME is needed to make sure the name is correctly quoted / escaped. If you have no temp tables, #d_sql will be the empty string still, so we check for that before printing / executing.
DECLARE #d_sql NVARCHAR(MAX)
SET #d_sql = ''
SELECT #d_sql = #d_sql + 'DROP TABLE ' + QUOTENAME(name) + ';
'
FROM tempdb..sysobjects
WHERE name like '#[^#]%'
AND OBJECT_ID('tempdb..'+QUOTENAME(name)) IS NOT NULL
IF #d_sql <> ''
BEGIN
PRINT #d_sql
-- EXEC( #d_sql )
END
In a stored procedure they are dropped automatically when the execution of the proc completes.
I normally come across the desire for this when I copy code out of a stored procedure to debug part of it and the stored proc does not contain the drop table commands.
Closing and reopening the connection works as stated in the accepted answer. Rather than doing this manually after each execution you can enable SQLCMD mode on the Query menu in SSMS
And then use the :connect command (adjust to your server/instance name)
:connect (local)\SQL2014
create table #foo(x int)
create table #bar(x int)
select *
from #foo
Can be run multiple times without problems. The messages tab shows
Connecting to (local)\SQL2014...
(0 row(s) affected)
Disconnecting connection from (local)\SQL2014...

SQL clone record with a unique index

Is there a clean way of cloning a record in SQL that has an index(auto increment). I want to clone all the fields except the index. I currently have to enumerate every field, and use that in an insert select, and I would rather not explicitly list all of the fields, as they may change over time.
Not unless you want to get into dynamic SQL. Since you wrote "clean", I'll assume not.
Edit: Since he asked for a dynamic SQL example, I'll take a stab at it. I'm not connected to any databases at the moment, so this is off the top of my head and will almost certainly need revision. But hopefully it captures the spirit of things:
-- Get list of columns in table
SELECT INTO #t
EXEC sp_columns #table_name = N'TargetTable'
-- Create a comma-delimited string excluding the identity column
DECLARE #cols varchar(MAX)
SELECT #cols = COALESCE(#cols+',' ,'') + COLUMN_NAME FROM #t WHERE COLUMN_NAME <> 'id'
-- Construct dynamic SQL statement
DECLARE #sql varchar(MAX)
SET #sql = 'INSERT INTO TargetTable (' + #cols + ') ' +
'SELECT ' + #cols + ' FROM TargetTable WHERE SomeCondition'
PRINT #sql -- for debugging
EXEC(#sql)
There's no easy and clean way that I can think of off the top of my head, but from a few items in your question I'd be concerned about your underlying architecture. Maybe you have an absolutely legitimate reason for wanting to do this, but usually you want to try to avoid duplicates in a database, not make them easier to cause. Also, explicitly naming columns is usually a good idea. If you're linking to outside code, it makes sure that you don't break that link when you add a new column. If you're not (and it sounds like you probably aren't in this scenario) I still prefer to have the columns listed out because it forces me to review the effects of the change/new column - even if it's just to look at the code and decide that adding the new column is not a problem.
DROP TABLE #tmp_MyTable
SELECT * INTO #tmp_MyTable
FROM MyTable
WHERE MyIndentID = 165
ALTER TABLE #tmp_MyTable
DROP Column MyIndentID
INSERT INTO MyTable
SELECT *
FROM #tmp_MyTable
This also deals with a unique key projectnum as well as the primary key.
CREATE TEMPORARY TABLE projecttemp SELECT * FROM project WHERE projectid='6';
ALTER TABLE projecttemp DROP COLUMN projectid;
UPDATE projecttemp SET projectnum = CONCAT(projectnum, ' CLONED');
INSERT INTO project SELECT NULL,projecttemp.* FROM projecttemp;
You could create an insert trigger to do this, however, you would lose the ability to do an insert with an explicit ID. It would, instead, always use the value from the sequence.
You could create a trigger to do it for you. To make sure that trigger only works for cloning, you could create a separate username CLONE and log in with it. Or, even better, if your DBMS supports it, create a role named CLONE and any user can log in using that role and do the cloning. The trigger code would be something like:
if (CURRENT_ROLE = 'CLONE') then
new.ID = assign new id from generator/sequence
Of course, you would grant that role only to the users who are allowed to clone records.