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

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?

Related

How to avoid showing result-window if there are no results to show?

I have a script which searches through all the available databases (those I have access to) for a specific text in a procedure.
In my server, there are many databases (in my case about 150 databases), meaning that I get shown the results for all databases eventhough there are no results for most of them (about 90%).
Is there any way to avoid getting these empty result-queries?
You can use below code to check whether stored procedure contains a text in each database. If there are stored procedures in a database only, you will have resultset.
CREATE TABLE ##DatabasesContainingSP(dbname sysname, SPName SYSNAME);
EXECUTE master.sys.sp_MSforeachdb 'USE [?];
INSERT INTO ##DatabasesContainingSP
SELECT DISTINCT
db_name() as dbname, o.name AS Object_Name
FROM sys.sql_modules m
INNER JOIN
sys.objects o
ON m.object_id = o.object_id
WHERE m.definition Like ''%ABC%'';
'
IF EXISTS(SELECT * FROM ##DatabasesContainingSP )
begin
SELECT * FROM ##DatabasesContainingSP
end
GO
IF OBJECT_ID('tempdb..##DatabasesContainingSP' , 'U') IS NOT NULL
drop TABLE ##DatabasesContainingSP;
Thank you for the quick responses.
I managed to solve it by creating a table and adding insert into this table in the beginning of my generated and concatenated code, which solved the problem since when reading the table in the end, it only shows the inserted results.
With kind regards,
Alexander

Alter table to add dynamic columns based off some previously selected query

Is it possible to alter a table and add columns with a dynamic name/data type based off some previously select query?
The pseudo equivalent for what I'm looking to do in SQL would be:
foreach row in tableA
{
alter tableB add row.name row.datatype
}
This is for SQL Server.
As mentioned, you can do this with dynamic sql. Something along these lines:
Declare #SQL1 nvarchar(4000)
SELECT #SQL1=N'ALTER TABLE mytable'+NCHAR(13)+NCHAR(10)
+N' ADD COLUMN '+ my_new_column_name + ' varchar(25)'+NCHAR(13)+NCHAR(10)
-- SELECT LEN(#SQL1), #SQL1
EXECUTE (#SQL1)
Apart from the fact that this is messy, error prone, a security risk, requires high authorization to execute and needs multiple variables for batches bigger than 4000 characters, it is usually also a bad idea from a design point of view (depending on when/why you are doing this).
Sure, you can do this with dynamic sql.

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.

How to check if a view exists that uses a table

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' ;

How do I paramaterise a T-SQL stored procedure that drops a table?

I'm after a simple stored procedure to drop tables. Here's my first attempt:
CREATE PROC bsp_susf_DeleteTable (#TableName char)
AS
IF EXISTS (SELECT name FROM sysobjects WHERE name = #TableName)
BEGIN
DROP TABLE #TableName
END
When I parse this in MS Query Analyser I get the following error:
Server: Msg 170, Level 15, State 1, Procedure bsp_susf_DeleteTable, Line 6
Line 6: Incorrect syntax near '#TableName'.
Which kind of makes sense because the normal SQL for a single table would be:
IF EXISTS (SELECT name FROM sysobjects WHERE name = 'tbl_XYZ')
BEGIN
DROP TABLE tbl_XYZ
END
Note the first instance of tbl_XYZ (in the WHERE clause) has single quotes around it, while the second instance in the DROP statement does not. If I use a variable (#TableName) then I don't get to make this distinction.
So can a stored procedure be created to do this? Or do I have to copy the IF EXISTS ... everywhere?
You should be able to use dynamic sql:
declare #sql varchar(max)
if exists (select name from sysobjects where name = #TableName)
BEGIN
set #sql = 'drop table ' + #TableName
exec(#sql)
END
Hope this helps.
Update: Yes, you could make #sql smaller, this was just a quick example. Also note other comments about SQL Injection Attacks
Personally I would be very wary of doing this. If you feel you need it for administrative purposes, please make sure the rights to execute this are extremely limited. Further, I would have the proc copy the table name and the date and the user executing it to a logging table. That way at least you will know who dropped the wrong table. You may want other protections as well. For instance you may want to specify certain tables that cannot be dropped ever using this proc.
Further this will not work on all tables in all cases. You cannot drop a table that has a foreign key associated with it.
Under no circumstances would I allow a user or anyone not the database admin to execute this proc. If you havea a system design where users can drop tables, there is most likely something drastically wrong with your design and it should be rethought.
Also, do not use this proc unless you have a really, really good backup schedule in place and experience restoring from backups.
You'll have to use EXEC to execute that query as a string. In other words, when you pass in the table name, define a varchar and assign the query and tablename, then exec the variable you created.
Edit: HOWEVER, I don't recommend that because someone could pass in sql rather than a TableName and cause all kinds of wonderful problems. See Sql injection for more information.
Your best bet is to create a parameterized query on the client side for this. For example, in C# I would do something like:
// EDIT 2: on second thought, ignore this code; it probably won't work
SqlCommand sc = new SqlCommand();
sc.Connection = someConnection;
sc.CommandType = Command.Text;
sc.CommandText = "drop table #tablename";
sc.Parameters.AddWithValue("#tablename", "the_table_name");
sc.ExecuteNonQuery();